Merge lp:~online-accounts/signon-plugin-oauth2/packaging into lp:signon-plugin-oauth2

Proposed by Alberto Mardegan
Status: Merged
Approved by: David Barth
Approved revision: no longer in the source branch.
Merged at revision: 76
Proposed branch: lp:~online-accounts/signon-plugin-oauth2/packaging
Merge into: lp:signon-plugin-oauth2
Diff against target: 1366 lines (+360/-295)
8 files modified
common-vars.pri (+1/-1)
debian/changelog (+9/-0)
src/oauth2data.h (+4/-0)
src/oauth2plugin.cpp (+48/-29)
src/src.pro (+1/-9)
tests/oauth2plugintest.cpp (+296/-236)
tests/oauth2plugintest.h (+0/-12)
tests/tests.pro (+1/-8)
To merge this branch: bzr merge lp:~online-accounts/signon-plugin-oauth2/packaging
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Online Accounts Pending
Review via email: mp+256492@code.launchpad.net

Commit message

Merge from upstream:

- Return the list of granted permissions to the client
- Require Qt5 for building
- Use a "state" parameter to protect against CSRF (LP: #1432857)

Description of the change

Merge from upstream:

- Return the list of granted permissions to the client
- Require Qt5 for building
- Use a "state" parameter to protect against CSRF (LP: #1432857)

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
76. By Alberto Mardegan

Merge from upstream:

- Return the list of granted permissions to the client
- Require Qt5 for building
- Use a "state" parameter to protect against CSRF (LP: #1432857)

Approved by: PS Jenkins bot

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'common-vars.pri'
2--- common-vars.pri 2015-01-28 11:12:16 +0000
3+++ common-vars.pri 2015-04-17 08:14:56 +0000
4@@ -13,7 +13,7 @@
5 # Project version
6 # remember to update debian/* files if you changes this
7 #-----------------------------------------------------------------------------
8-PROJECT_VERSION = 0.21
9+PROJECT_VERSION = 0.22
10
11 #-----------------------------------------------------------------------------
12 # Library version
13
14=== modified file 'debian/changelog'
15--- debian/changelog 2015-03-27 11:12:54 +0000
16+++ debian/changelog 2015-04-17 08:14:56 +0000
17@@ -1,3 +1,12 @@
18+signon-plugin-oauth2 (0.22-0ubuntu1) UNRELEASED; urgency=medium
19+
20+ * New upstream release
21+ - Return the list of granted permissions to the client
22+ - Require Qt5 for building
23+ - Use a "state" parameter to protect against CSRF (LP: #1432857)
24+
25+ -- Alberto Mardegan <alberto.mardegan@canonical.com> Thu, 16 Apr 2015 16:42:35 +0300
26+
27 signon-plugin-oauth2 (0.21+15.04.20150327-0ubuntu1) vivid; urgency=medium
28
29 * New rebuild forced.
30
31=== modified file 'src/oauth2data.h'
32--- src/oauth2data.h 2014-10-31 08:09:57 +0000
33+++ src/oauth2data.h 2015-04-17 08:14:56 +0000
34@@ -106,6 +106,10 @@
35 * Access token expiry time
36 */
37 SIGNON_SESSION_DECLARE_PROPERTY(int, ExpiresIn);
38+ /*!
39+ * Granted permissions
40+ */
41+ SIGNON_SESSION_DECLARE_PROPERTY(QStringList, Scope);
42 };
43
44 } // namespace OAuth2PluginNS
45
46=== modified file 'src/oauth2plugin.cpp'
47--- src/oauth2plugin.cpp 2015-02-16 13:37:52 +0000
48+++ src/oauth2plugin.cpp 2015-04-17 08:14:56 +0000
49@@ -25,18 +25,13 @@
50 #include "oauth2plugin.h"
51 #include "oauth2tokendata.h"
52
53+#include <QJsonDocument>
54 #include <QUrl>
55+#include <QUrlQuery>
56 #include <QNetworkRequest>
57 #include <QNetworkReply>
58 #include <QDateTime>
59
60-#define USE_LIBQJSON (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
61-
62-#if USE_LIBQJSON
63-#include <qjson/parser.h>
64-#else
65-#include <QJsonDocument>
66-#endif
67
68 using namespace SignOn;
69 using namespace OAuth2PluginNS;
70@@ -54,6 +49,7 @@
71 const QString AUTH_CODE = QString("code");
72 const QString REDIRECT_URI = QString("redirect_uri");
73 const QString RESPONSE_TYPE = QString("response_type");
74+const QString STATE = QString("state");
75 const QString USERNAME = QString("username");
76 const QString PASSWORD = QString("password");
77 const QString ASSERTION_TYPE = QString("assertion_type");
78@@ -61,6 +57,7 @@
79 const QString ACCESS_TOKEN = QString("access_token");
80 const QString DISPLAY = QString("display");
81 const QString EXPIRES_IN = QString("expires_in");
82+const QString SCOPE = QString("scope");
83 const QString TIMESTAMP = QString("timestamp");
84 const QString GRANT_TYPE = QString("grant_type");
85 const QString AUTHORIZATION_CODE = QString("authorization_code");
86@@ -96,6 +93,7 @@
87 QString m_mechanism;
88 OAuth2PluginData m_oauth2Data;
89 QVariantMap m_tokens;
90+ QString m_state;
91 QString m_key;
92 QString m_username;
93 QString m_password;
94@@ -133,6 +131,8 @@
95 QUrl url(QString("https://%1/%2").arg(d->m_oauth2Data.Host()).arg(d->m_oauth2Data.AuthPath()));
96 url.addQueryItem(CLIENT_ID, d->m_oauth2Data.ClientId());
97 url.addQueryItem(REDIRECT_URI, d->m_oauth2Data.RedirectUri());
98+ d->m_state = QString::number(qrand());
99+ url.addQueryItem(STATE, d->m_state);
100 if (!d->m_oauth2Data.ResponseType().isEmpty()) {
101 url.addQueryItem(RESPONSE_TYPE,
102 d->m_oauth2Data.ResponseType().join(" "));
103@@ -153,7 +153,7 @@
104 }
105
106 // Passing list of scopes
107- url.addQueryItem(QString("scope"), d->m_oauth2Data.Scope().join(separator));
108+ url.addQueryItem(SCOPE, d->m_oauth2Data.Scope().join(separator));
109 }
110 TRACE() << "Url = " << url.toString();
111 SignOn::UiSessionData uiSession;
112@@ -360,21 +360,30 @@
113 if (d->m_mechanism == USER_AGENT) {
114 // Response should contain the access token
115 OAuth2PluginTokenData respData;
116- QString fragment;
117 if (url.hasFragment()) {
118- fragment = url.fragment();
119- QStringList list = fragment.split(QRegExp("&|="), QString::SkipEmptyParts);
120- for (int i = 1; i < list.count(); i += 2) {
121- if (list.at(i - 1) == ACCESS_TOKEN) {
122- respData.setAccessToken(list.at(i));
123- }
124- else if (list.at(i - 1) == EXPIRES_IN) {
125- respData.setExpiresIn(QString(list.at(i)).toInt());
126- }
127- else if (list.at(i - 1) == REFRESH_TOKEN) {
128- respData.setRefreshToken(list.at(i));
129- }
130- }
131+ QString state;
132+ respData.setScope(d->m_oauth2Data.Scope());
133+ QUrlQuery fragment(url.fragment());
134+ typedef QPair<QString, QString> StringPair;
135+ Q_FOREACH(const StringPair &pair, fragment.queryItems()) {
136+ if (pair.first == ACCESS_TOKEN) {
137+ respData.setAccessToken(pair.second);
138+ } else if (pair.first == EXPIRES_IN) {
139+ respData.setExpiresIn(pair.second.toInt());
140+ } else if (pair.first == REFRESH_TOKEN) {
141+ respData.setRefreshToken(pair.second);
142+ } else if (pair.first == STATE) {
143+ state = pair.second;
144+ } else if (pair.first == SCOPE) {
145+ respData.setScope(pair.second.split(' ', QString::SkipEmptyParts));
146+ }
147+ }
148+ if (state != d->m_state) {
149+ Q_EMIT error(Error(Error::NotAuthorized,
150+ QString("'state' parameter mismatch")));
151+ return;
152+ }
153+
154 if (respData.AccessToken().isEmpty()) {
155 emit error(Error(Error::NotAuthorized, QString("Access token not present")));
156 } else {
157@@ -394,6 +403,11 @@
158 // 4. Refresh Token (refresh_token)
159 QUrl newUrl;
160 if (url.hasQueryItem(AUTH_CODE)) {
161+ if (d->m_state != url.queryItemValue(STATE)) {
162+ Q_EMIT error(Error(Error::NotAuthorized,
163+ QString("'state' parameter mismatch")));
164+ return;
165+ }
166 QString code = url.queryItemValue(AUTH_CODE);
167 newUrl.addQueryItem(GRANT_TYPE, AUTHORIZATION_CODE);
168 newUrl.addQueryItem(AUTH_CODE, code);
169@@ -473,6 +487,8 @@
170 // Method to handle responses for OAuth 2.0 requests
171 void OAuth2Plugin::serverReply(QNetworkReply *reply)
172 {
173+ Q_D(OAuth2Plugin);
174+
175 QByteArray replyContent = reply->readAll();
176 TRACE() << replyContent;
177
178@@ -499,6 +515,14 @@
179 }
180 QByteArray refreshToken = map["refresh_token"].toByteArray();
181
182+ QStringList scope;
183+ if (map.contains(SCOPE)) {
184+ QString rawScope = QString::fromUtf8(map[SCOPE].toByteArray());
185+ scope = rawScope.split(' ', QString::SkipEmptyParts);
186+ } else {
187+ scope = d->m_oauth2Data.Scope();
188+ }
189+
190 if (accessToken.isEmpty()) {
191 TRACE()<< "Access token is empty";
192 Q_EMIT error(Error(Error::NotAuthorized,
193@@ -508,6 +532,7 @@
194 response.setAccessToken(accessToken);
195 response.setRefreshToken(refreshToken);
196 response.setExpiresIn(expiresIn);
197+ response.setScope(scope);
198 storeResponse(response);
199 emit result(response);
200 }
201@@ -676,15 +701,9 @@
202 QVariantMap OAuth2Plugin::parseJSONReply(const QByteArray &reply)
203 {
204 TRACE();
205- bool ok = false;
206-#if USE_LIBQJSON
207- QJson::Parser parser;
208- QVariant tree = parser.parse(reply, &ok);
209-#else
210 QJsonDocument doc = QJsonDocument::fromJson(reply);
211- ok = !doc.isEmpty();
212+ bool ok = !doc.isEmpty();
213 QVariant tree = doc.toVariant();
214-#endif
215 if (ok) {
216 return tree.toMap();
217 }
218
219=== modified file 'src/src.pro'
220--- src/src.pro 2013-04-26 09:12:55 +0000
221+++ src/src.pro 2015-04-17 08:14:56 +0000
222@@ -27,17 +27,9 @@
223 oauth2plugin.cpp \
224 plugin.cpp
225 PKGCONFIG += \
226+ libsignon-qt5 \
227 signon-plugins
228
229-lessThan(QT_MAJOR_VERSION, 5) {
230- PKGCONFIG += \
231- QJson \
232- libsignon-qt
233-} else {
234- PKGCONFIG += \
235- libsignon-qt5
236-}
237-
238 headers.files = $$public_headers
239 pkgconfig.files = signon-oauth2plugin.pc
240 include( ../common-installs-config.pri )
241
242=== modified file 'tests/oauth2plugintest.cpp'
243--- tests/oauth2plugintest.cpp 2015-02-16 12:25:29 +0000
244+++ tests/oauth2plugintest.cpp 2015-04-17 08:14:56 +0000
245@@ -21,10 +21,13 @@
246 * 02110-1301 USA
247 */
248
249+#include <QJsonDocument>
250+#include <QJsonObject>
251 #include <QNetworkAccessManager>
252 #include <QNetworkReply>
253 #include <QPointer>
254 #include <QRegExp>
255+#include <QSignalSpy>
256 #include <QTimer>
257 #include <QtTest/QtTest>
258
259@@ -38,6 +41,22 @@
260 using namespace OAuth2PluginNS;
261 using namespace SignOn;
262
263+namespace QTest {
264+template<>
265+char *toString(const QVariantMap &map)
266+{
267+ QJsonDocument doc(QJsonObject::fromVariantMap(map));
268+ return qstrdup(doc.toJson(QJsonDocument::Compact).data());
269+}
270+} // QTest namespace
271+
272+static QString parseState(const QSignalSpy &userActionRequired)
273+{
274+ UiSessionData data = userActionRequired.at(0).at(0).value<UiSessionData>();
275+ QUrlQuery query(data.OpenUrl());
276+ return query.queryItemValue("state");
277+}
278+
279 static bool mapIsSubset(const QVariantMap &set, const QVariantMap &test)
280 {
281 QMapIterator<QString, QVariant> it(set);
282@@ -190,10 +209,6 @@
283 void OAuth2PluginTest::init()
284 {
285 m_testPlugin = new Plugin();
286- m_stored = SignOn::SessionData();
287- m_response = SignOn::SessionData();
288- m_uiResponse = SignOn::UiSessionData();
289- m_error = SignOn::Error(-1);
290 }
291
292 //finnish each test by deleting plugin
293@@ -203,43 +218,6 @@
294 m_testPlugin=NULL;
295 }
296
297-//slot for receiving result
298-void OAuth2PluginTest::result(const SignOn::SessionData& data)
299-{
300- m_response = data;
301- m_loop.exit();
302-}
303-
304-//slot for receiving error
305-void OAuth2PluginTest::pluginError(const SignOn::Error &err)
306-{
307- m_error = err;
308- m_loop.exit();
309-}
310-
311-//slot for receiving result
312-void OAuth2PluginTest::uiRequest(const SignOn::UiSessionData& data)
313-{
314- Q_UNUSED(data);
315- m_uiResponse.setUrlResponse(QString("UI request received"));
316- m_loop.exit();
317-}
318-
319-//slot for store
320-void OAuth2PluginTest::store(const SignOn::SessionData &data)
321-{
322- m_stored = data;
323-}
324-
325-void OAuth2PluginTest::aborted(QNetworkReply* reply)
326-{
327- qDebug() << "aborted";
328- //we should get error code if request was aborted
329- qDebug() << reply->error();
330- QVERIFY(reply->error());
331- m_loop.exit();
332-}
333-
334 // TEST CASES
335 void OAuth2PluginTest::testPlugin()
336 {
337@@ -261,15 +239,11 @@
338
339 void OAuth2PluginTest::testPluginCancel()
340 {
341- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
342-
343 //does nothing as no active connections
344 m_testPlugin->cancel();
345
346 //then real cancel
347- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error &)),
348- this, SLOT(pluginError(const SignOn::Error &)),
349- Qt::QueuedConnection);
350+ QSignalSpy pluginError(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
351
352 OAuth2PluginData userAgentData;
353 userAgentData.setHost("https://localhost");
354@@ -280,8 +254,9 @@
355
356 m_testPlugin->process(userAgentData, QString("user_agent"));
357 m_testPlugin->cancel();
358- m_loop.exec();
359- QCOMPARE(m_error.type(), int(Error::SessionCanceled));
360+ QTRY_COMPARE(pluginError.count(), 1);
361+ Error error = pluginError.at(0).at(0).value<Error>();
362+ QCOMPARE(error.type(), int(Error::SessionCanceled));
363 }
364
365 void OAuth2PluginTest::testPluginProcess_data()
366@@ -289,7 +264,7 @@
367 QTest::addColumn<QString>("mechanism");
368 QTest::addColumn<QVariantMap>("sessionData");
369 QTest::addColumn<int>("errorCode");
370- QTest::addColumn<QString>("urlResponse");
371+ QTest::addColumn<bool>("uiExpected");
372 QTest::addColumn<QVariantMap>("response");
373 QTest::addColumn<QVariantMap>("stored");
374
375@@ -304,13 +279,13 @@
376 "ANONYMOUS" <<
377 userAgentData.toMap() <<
378 int(Error::MechanismNotAvailable) <<
379- QString() << QVariantMap() << QVariantMap();
380+ false << QVariantMap() << QVariantMap();
381
382 QTest::newRow("without params, user_agent") <<
383 "user_agent" <<
384 userAgentData.toMap() <<
385 int(Error::MissingData) <<
386- QString() << QVariantMap() << QVariantMap();
387+ false << QVariantMap() << QVariantMap();
388
389 OAuth2PluginData webServerData;
390 webServerData.setHost("https://localhost");
391@@ -324,21 +299,21 @@
392 "web_server" <<
393 webServerData.toMap() <<
394 int(Error::MissingData) <<
395- QString() << QVariantMap() << QVariantMap();
396+ false << QVariantMap() << QVariantMap();
397
398 userAgentData.setAuthPath("authorize");
399 QTest::newRow("ui-request, user_agent") <<
400 "user_agent" <<
401 userAgentData.toMap() <<
402 -1 <<
403- QString("UI request received") << QVariantMap() << QVariantMap();
404+ true << QVariantMap() << QVariantMap();
405
406 webServerData.setTokenPath("token");
407 QTest::newRow("ui-request, web_server") <<
408 "web_server" <<
409 webServerData.toMap() <<
410 -1 <<
411- QString("UI request received") << QVariantMap() << QVariantMap();
412+ true << QVariantMap() << QVariantMap();
413
414 QVariantMap tokens;
415 QVariantMap token;
416@@ -353,7 +328,7 @@
417 "web_server" <<
418 webServerData.toMap() <<
419 -1 <<
420- QString("UI request received") << QVariantMap() << QVariantMap();
421+ true << QVariantMap() << QVariantMap();
422
423 tokens.insert(webServerData.ClientId(), QVariant::fromValue(token));
424 webServerData.m_data.insert(QLatin1String("Tokens"), tokens);
425@@ -362,7 +337,7 @@
426 "web_server" <<
427 webServerData.toMap() <<
428 -1 <<
429- QString("UI request received") << QVariantMap() << QVariantMap();
430+ true << QVariantMap() << QVariantMap();
431
432 token.insert("Scopes", QStringList("scope2"));
433 tokens.insert(webServerData.ClientId(), QVariant::fromValue(token));
434@@ -372,7 +347,7 @@
435 "web_server" <<
436 webServerData.toMap() <<
437 -1 <<
438- QString("UI request received") << QVariantMap() << QVariantMap();
439+ true << QVariantMap() << QVariantMap();
440
441 token.insert("Scopes",
442 QStringList() << "scope1" << "scope3" << "scope2");
443@@ -386,14 +361,14 @@
444 "web_server" <<
445 webServerData.toMap() <<
446 -1 <<
447- QString() << response << QVariantMap();
448+ false << response << QVariantMap();
449
450 webServerData.setForceTokenRefresh(true);
451 QTest::newRow("force token refresh, without refresh token") <<
452 "web_server" <<
453 webServerData.toMap() <<
454 -1 <<
455- QString("UI request received") << QVariantMap() << QVariantMap();
456+ true << QVariantMap() << QVariantMap();
457
458 /* test the ProvidedTokens semantics */
459 OAuth2PluginData providedTokensWebServerData;
460@@ -422,7 +397,7 @@
461 "web_server" <<
462 providedTokensWebServerData.toMap() <<
463 -1 <<
464- QString() << providedTokens << stored;
465+ false << providedTokens << stored;
466 }
467
468 void OAuth2PluginTest::testPluginProcess()
469@@ -430,27 +405,35 @@
470 QFETCH(QString, mechanism);
471 QFETCH(QVariantMap, sessionData);
472 QFETCH(int, errorCode);
473- QFETCH(QString, urlResponse);
474+ QFETCH(bool, uiExpected);
475 QFETCH(QVariantMap, response);
476 QFETCH(QVariantMap, stored);
477
478- QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
479- this, SLOT(result(const SignOn::SessionData&)),Qt::QueuedConnection);
480- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error & )),
481- this, SLOT(pluginError(const SignOn::Error &)),Qt::QueuedConnection);
482- QObject::connect(m_testPlugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
483- this, SLOT(uiRequest(const SignOn::UiSessionData&)),Qt::QueuedConnection);
484- QObject::connect(m_testPlugin, SIGNAL(store(const SignOn::SessionData&)),
485- this, SLOT(store(const SignOn::SessionData&)),Qt::QueuedConnection);
486- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
487+ QSignalSpy result(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)));
488+ QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
489+ QSignalSpy userActionRequired(m_testPlugin,
490+ SIGNAL(userActionRequired(const SignOn::UiSessionData&)));
491+ QSignalSpy store(m_testPlugin, SIGNAL(store(const SignOn::SessionData&)));
492
493 m_testPlugin->process(sessionData, mechanism);
494- m_loop.exec();
495- QCOMPARE(m_error.type(), errorCode);
496 if (errorCode < 0) {
497- QCOMPARE(m_uiResponse.UrlResponse(), urlResponse);
498- QCOMPARE(m_response.toMap(), response);
499- QVERIFY(mapIsSubset(stored, m_stored.toMap()));
500+ QCOMPARE(error.count(), 0);
501+ QTRY_COMPARE(userActionRequired.count(), uiExpected ? 1 : 0);
502+ if (!response.isEmpty()) {
503+ QCOMPARE(result.count(), 1);
504+ QVariantMap resp = result.at(0).at(0).value<SessionData>().toMap();
505+ QCOMPARE(resp, response);
506+ }
507+ if (!stored.isEmpty()) {
508+ QCOMPARE(store.count(), 1);
509+ QVariantMap storedData =
510+ store.at(0).at(0).value<SessionData>().toMap();
511+ QVERIFY(mapIsSubset(stored, storedData));
512+ }
513+ } else {
514+ QTRY_COMPARE(error.count(), 1);
515+ Error err = error.at(0).at(0).value<Error>();
516+ QCOMPARE(err.type(), errorCode);
517 }
518 }
519
520@@ -462,7 +445,7 @@
521 QTest::addColumn<QString>("replyContentType");
522 QTest::addColumn<QString>("replyContents");
523 QTest::addColumn<int>("errorCode");
524- QTest::addColumn<QString>("urlResponse");
525+ QTest::addColumn<bool>("uiExpected");
526 QTest::addColumn<QVariantMap>("response");
527 QTest::addColumn<QVariantMap>("stored");
528
529@@ -480,7 +463,7 @@
530 hmacSha1Data.toMap() <<
531 int(200) << "" << "" <<
532 int(Error::MechanismNotAvailable) <<
533- QString() << QVariantMap() << QVariantMap();
534+ false << QVariantMap() << QVariantMap();
535
536 // Try without params
537 hmacSha1Data.setAuthorizationEndpoint(QString());
538@@ -489,7 +472,7 @@
539 hmacSha1Data.toMap() <<
540 int(200) << "" << "" <<
541 int(Error::MissingData) <<
542- QString() << QVariantMap() << QVariantMap();
543+ false << QVariantMap() << QVariantMap();
544
545 // Check for signon UI request for HMAC-SHA1
546 hmacSha1Data.setAuthorizationEndpoint("https://localhost/oauth/authorize");
547@@ -499,7 +482,7 @@
548 int(200) << "text/plain" <<
549 "oauth_token=HiThere&oauth_token_secret=BigSecret" <<
550 -1 <<
551- QString("UI request received") << QVariantMap() << QVariantMap();
552+ true << QVariantMap() << QVariantMap();
553
554 QTest::newRow("ui-request, PLAINTEXT") <<
555 "PLAINTEXT" <<
556@@ -507,7 +490,7 @@
557 int(200) << "text/plain" <<
558 "oauth_token=HiThere&oauth_token_secret=BigSecret" <<
559 -1 <<
560- QString("UI request received") << QVariantMap() << QVariantMap();
561+ true << QVariantMap() << QVariantMap();
562
563 /* Now store some tokens and test the responses */
564 hmacSha1Data.m_data.insert("UiPolicy", NoUserInteractionPolicy);
565@@ -527,7 +510,7 @@
566 int(200) << "text/plain" <<
567 "oauth_token=HiThere&oauth_token_secret=BigSecret" <<
568 -1 <<
569- QString("UI request received") << QVariantMap() << QVariantMap();
570+ true << QVariantMap() << QVariantMap();
571
572 // Ensure that the cached token is returned as required
573 tokens.insert(hmacSha1Data.ConsumerKey(), QVariant::fromValue(token));
574@@ -539,7 +522,7 @@
575 hmacSha1Data.toMap() <<
576 int(200) << "" << "" <<
577 -1 <<
578- QString() << response << QVariantMap();
579+ false << response << QVariantMap();
580
581 hmacSha1Data.m_data.insert("UiPolicy", RequestPasswordPolicy);
582 QTest::newRow("cached tokens, request password policy") <<
583@@ -548,7 +531,7 @@
584 int(200) << "text/plain" <<
585 "oauth_token=HiThere&oauth_token_secret=BigSecret" <<
586 -1 <<
587- QString("UI request received") << QVariantMap() << QVariantMap();
588+ true << QVariantMap() << QVariantMap();
589 hmacSha1Data.m_data.remove("UiPolicy");
590
591 hmacSha1Data.setForceTokenRefresh(true);
592@@ -558,7 +541,7 @@
593 int(200) << "text/plain" <<
594 "oauth_token=HiThere&oauth_token_secret=BigSecret" <<
595 -1 <<
596- QString("UI request received") << QVariantMap() << QVariantMap();
597+ true << QVariantMap() << QVariantMap();
598 hmacSha1Data.setForceTokenRefresh(false);
599
600 token.insert("timestamp", QDateTime::currentDateTime().toTime_t() - 50000);
601@@ -571,7 +554,7 @@
602 int(200) << "text/plain" <<
603 "oauth_token=HiThere&oauth_token_secret=BigSecret" <<
604 -1 <<
605- QString("UI request received") << QVariantMap() << QVariantMap();
606+ true << QVariantMap() << QVariantMap();
607
608 /* test the ProvidedTokens semantics */
609 OAuth1PluginData providedTokensHmacSha1Data;
610@@ -601,7 +584,7 @@
611 providedTokensHmacSha1Data.toMap() <<
612 int(200) << "" << "" <<
613 -1 <<
614- QString() << providedTokens << stored;
615+ false << providedTokens << stored;
616
617 QTest::newRow("http error 401") <<
618 "HMAC-SHA1" <<
619@@ -609,14 +592,14 @@
620 int(401) << "text/plain" <<
621 "oauth_token=HiThere&oauth_token_secret=BigSecret" <<
622 int(Error::OperationFailed) <<
623- QString() << QVariantMap() << QVariantMap();
624+ false << QVariantMap() << QVariantMap();
625
626 QTest::newRow("no content returned") <<
627 "HMAC-SHA1" <<
628 hmacSha1Data.toMap() <<
629 int(200) << "" << "" <<
630 int(Error::OperationFailed) <<
631- QString() << QVariantMap() << QVariantMap();
632+ false << QVariantMap() << QVariantMap();
633
634 QTest::newRow("no token returned") <<
635 "HMAC-SHA1" <<
636@@ -624,7 +607,7 @@
637 int(200) << "text/plain" <<
638 "oauth_token=HiThere" <<
639 int(Error::OperationFailed) <<
640- QString() << QVariantMap() << QVariantMap();
641+ false << QVariantMap() << QVariantMap();
642
643 /* Test handling of oauth_problem; this is a non standard extension:
644 * http://wiki.oauth.net/w/page/12238543/ProblemReporting
645@@ -636,21 +619,21 @@
646 int(400) << "text/plain" <<
647 "oauth_problem=user_refused" <<
648 int(Error::PermissionDenied) <<
649- QString() << QVariantMap() << QVariantMap();
650+ false << QVariantMap() << QVariantMap();
651 QTest::newRow("problem permission_denied") <<
652 "HMAC-SHA1" <<
653 hmacSha1Data.toMap() <<
654 int(400) << "text/plain" <<
655 "oauth_problem=permission_denied" <<
656 int(Error::PermissionDenied) <<
657- QString() << QVariantMap() << QVariantMap();
658+ false << QVariantMap() << QVariantMap();
659 QTest::newRow("problem signature_invalid") <<
660 "HMAC-SHA1" <<
661 hmacSha1Data.toMap() <<
662 int(400) << "text/plain" <<
663 "oauth_problem=signature_invalid" <<
664 int(Error::OperationFailed) <<
665- QString() << QVariantMap() << QVariantMap();
666+ false << QVariantMap() << QVariantMap();
667 }
668
669 void OAuth2PluginTest::testPluginHmacSha1Process()
670@@ -661,19 +644,15 @@
671 QFETCH(QString, replyContentType);
672 QFETCH(QString, replyContents);
673 QFETCH(int, errorCode);
674- QFETCH(QString, urlResponse);
675+ QFETCH(bool, uiExpected);
676 QFETCH(QVariantMap, response);
677 QFETCH(QVariantMap, stored);
678
679- QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
680- this, SLOT(result(const SignOn::SessionData&)),Qt::QueuedConnection);
681- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error & )),
682- this, SLOT(pluginError(const SignOn::Error &)),Qt::QueuedConnection);
683- QObject::connect(m_testPlugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
684- this, SLOT(uiRequest(const SignOn::UiSessionData&)),Qt::QueuedConnection);
685- QObject::connect(m_testPlugin, SIGNAL(store(const SignOn::SessionData&)),
686- this, SLOT(store(const SignOn::SessionData&)),Qt::QueuedConnection);
687- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
688+ QSignalSpy result(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)));
689+ QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
690+ QSignalSpy userActionRequired(m_testPlugin,
691+ SIGNAL(userActionRequired(const SignOn::UiSessionData&)));
692+ QSignalSpy store(m_testPlugin, SIGNAL(store(const SignOn::SessionData&)));
693
694 TestNetworkAccessManager *nam = new TestNetworkAccessManager;
695 m_testPlugin->m_networkAccessManager = nam;
696@@ -686,13 +665,25 @@
697 nam->setNextReply(reply);
698
699 m_testPlugin->process(sessionData, mechanism);
700- m_loop.exec();
701- QCOMPARE(m_error.type(), errorCode);
702+
703+ QTRY_COMPARE(result.count(), response.isEmpty() ? 0 : 1);
704+ /* In the test data sometimes we don't specify the expected stored data,
705+ * but this doesn't mean that store() shouldn't be emitted. */
706+ if (!stored.isEmpty()) { QTRY_COMPARE(store.count(), 1); }
707+ QTRY_COMPARE(userActionRequired.count(), uiExpected ? 1 : 0);
708+ QTRY_COMPARE(error.count(), errorCode < 0 ? 0 : 1);
709+
710 if (errorCode < 0) {
711+ QCOMPARE(error.count(), 0);
712+
713+ QVariantMap resp = result.count() > 0 ?
714+ result.at(0).at(0).value<SessionData>().toMap() : QVariantMap();
715+ QVariantMap storedData = store.count() > 0 ?
716+ store.at(0).at(0).value<SessionData>().toMap() : QVariantMap();
717 /* We don't check the network request if a response was received,
718 * because a response can only be received if a cached token was
719 * found -- and that doesn't cause any network request to be made. */
720- if (m_response.toMap().isEmpty()) {
721+ if (resp.isEmpty()) {
722 QCOMPARE(nam->m_lastRequest.url(),
723 sessionData.value("RequestEndpoint").toUrl());
724 QVERIFY(nam->m_lastRequestData.isEmpty());
725@@ -712,9 +703,11 @@
726 QCOMPARE(authMap.value("oauth_signature_method").toString(), mechanism);
727 }
728
729- QCOMPARE(m_uiResponse.UrlResponse(), urlResponse);
730- QVERIFY(mapIsSubset(response, m_response.toMap()));
731- QVERIFY(mapIsSubset(stored, m_stored.toMap()));
732+ QVERIFY(mapIsSubset(response, resp));
733+ QVERIFY(mapIsSubset(stored, storedData));
734+ } else {
735+ Error err = error.at(0).at(0).value<Error>();
736+ QCOMPARE(err.type(), errorCode);
737 }
738 }
739
740@@ -731,72 +724,108 @@
741 QStringList scopes = QStringList() << "scope1" << "scope2";
742 data.setScope(scopes);
743
744- QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
745- this, SLOT(result(const SignOn::SessionData&)),Qt::QueuedConnection);
746- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error & )),
747- this, SLOT(pluginError(const SignOn::Error &)),Qt::QueuedConnection);
748- QObject::connect(m_testPlugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
749- this, SLOT(uiRequest(const SignOn::UiSessionData&)),Qt::QueuedConnection);
750- QObject::connect(m_testPlugin, SIGNAL(store(const SignOn::SessionData&)),
751- this, SLOT(store(const SignOn::SessionData&)),
752- Qt::QueuedConnection);
753- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
754+ QSignalSpy resultSpy(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)));
755+ QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
756+ QSignalSpy userActionRequired(m_testPlugin,
757+ SIGNAL(userActionRequired(const SignOn::UiSessionData&)));
758+ QSignalSpy store(m_testPlugin, SIGNAL(store(const SignOn::SessionData&)));
759
760 m_testPlugin->process(data, QString("user_agent"));
761- m_loop.exec();
762- QCOMPARE(m_uiResponse.UrlResponse(), QString("UI request received"));
763+
764+ QTRY_COMPARE(userActionRequired.count(), 1);
765+ QString state = parseState(userActionRequired);
766
767 //empty data
768 m_testPlugin->userActionFinished(info);
769- m_loop.exec();
770- QCOMPARE(m_error.type(), int(Error::NotAuthorized));
771+ QTRY_COMPARE(error.count(), 1);
772+ QCOMPARE(error.at(0).at(0).value<Error>().type(), int(Error::NotAuthorized));
773+ error.clear();
774
775 //invalid data
776 info.setUrlResponse(QString("http://www.facebook.com/connect/login_success.html#access_token=&expires_in=4776"));
777 m_testPlugin->userActionFinished(info);
778- m_loop.exec();
779- QCOMPARE(m_error.type(), int(Error::NotAuthorized));
780+ QTRY_COMPARE(error.count(), 1);
781+ QCOMPARE(error.at(0).at(0).value<Error>().type(), int(Error::NotAuthorized));
782+ error.clear();
783
784 //Invalid data
785 info.setUrlResponse(QString("http://www.facebook.com/connect/login_success.html"));
786 m_testPlugin->userActionFinished(info);
787- m_loop.exec();
788- QCOMPARE(m_error.type(), int(Error::NotAuthorized));
789+ QTRY_COMPARE(error.count(), 1);
790+ QCOMPARE(error.at(0).at(0).value<Error>().type(), int(Error::NotAuthorized));
791+ error.clear();
792+
793+ // Wrong state
794+ info.setUrlResponse(QString("http://www.facebook.com/connect/login_success.html"
795+ "#access_token=123&expires_in=456&state=%1").
796+ arg(state + "Boo"));
797+ m_testPlugin->userActionFinished(info);
798+ QTRY_COMPARE(error.count(), 1);
799+ QCOMPARE(error.at(0).at(0).value<Error>().type(), int(Error::NotAuthorized));
800+ error.clear();
801
802 //valid data
803- info.setUrlResponse(QString("http://www.facebook.com/connect/login_success.html#access_token=testtoken.&expires_in=4776"));
804+ info.setUrlResponse(QString("http://www.facebook.com/connect/login_success.html#access_token=testtoken.&expires_in=4776&state=%1").
805+ arg(state));
806 m_testPlugin->userActionFinished(info);
807- m_loop.exec();
808- OAuth2PluginTokenData *result = (OAuth2PluginTokenData*)&m_response;
809- QCOMPARE(result->AccessToken(), QString("testtoken."));
810- QCOMPARE(result->ExpiresIn(), 4776);
811- QVariantMap storedTokenData = m_stored.data<OAuth2TokenData>().Tokens();
812+ QTRY_COMPARE(resultSpy.count(), 1);
813+ SessionData response = resultSpy.at(0).at(0).value<SessionData>();
814+ OAuth2PluginTokenData result = response.data<OAuth2PluginTokenData>();
815+ QCOMPARE(result.AccessToken(), QString("testtoken."));
816+ QCOMPARE(result.ExpiresIn(), 4776);
817+ QCOMPARE(result.Scope(), QStringList() << "scope1" << "scope2");
818+ resultSpy.clear();
819+ QTRY_COMPARE(store.count(), 1);
820+ SessionData storedData = store.at(0).at(0).value<SessionData>();
821+ QVariantMap storedTokenData = storedData.data<OAuth2TokenData>().Tokens();
822 QVariantMap storedClientData =
823 storedTokenData.value(data.ClientId()).toMap();
824 QVERIFY(!storedClientData.isEmpty());
825 QCOMPARE(storedClientData["Scopes"].toStringList(), scopes);
826+ store.clear();
827+
828+ //valid data, got scopes
829+ info.setUrlResponse(QString("http://www.facebook.com/connect/login_success.html#access_token=testtoken.&expires_in=4776&state=%1&scope=scope2").
830+ arg(state));
831+ m_testPlugin->userActionFinished(info);
832+ QTRY_COMPARE(resultSpy.count(), 1);
833+ response = resultSpy.at(0).at(0).value<SessionData>();
834+ result = response.data<OAuth2PluginTokenData>();
835+ QCOMPARE(result.AccessToken(), QString("testtoken."));
836+ QCOMPARE(result.ExpiresIn(), 4776);
837+ QCOMPARE(result.Scope(), QStringList() << "scope2");
838+ resultSpy.clear();
839+ store.clear();
840
841 //valid data
842- info.setUrlResponse(QString("http://www.facebook.com/connect/login_success.html#access_token=testtoken."));
843+ info.setUrlResponse(QString("http://www.facebook.com/connect/login_success.html"
844+ "#state=%1&access_token=testtoken.").
845+ arg(state));
846 m_testPlugin->userActionFinished(info);
847- m_loop.exec();
848- result = (OAuth2PluginTokenData*)&m_response;
849- QCOMPARE(result->AccessToken(), QString("testtoken."));
850- QCOMPARE(result->ExpiresIn(), 0);
851+ QTRY_COMPARE(resultSpy.count(), 1);
852+ response = resultSpy.at(0).at(0).value<SessionData>();
853+ result = response.data<OAuth2PluginTokenData>();
854+ QCOMPARE(result.AccessToken(), QString("testtoken."));
855+ QCOMPARE(result.ExpiresIn(), 0);
856+ resultSpy.clear();
857 /* Check that the expiration time has not been stored, since the expiration
858 * time was not given (https://bugs.launchpad.net/bugs/1316021)
859 */
860- storedTokenData = m_stored.data<OAuth2TokenData>().Tokens();
861+ QTRY_COMPARE(store.count(), 1);
862+ storedData = store.at(0).at(0).value<SessionData>();
863+ storedTokenData = storedData.data<OAuth2TokenData>().Tokens();
864 storedClientData = storedTokenData.value(data.ClientId()).toMap();
865 QVERIFY(!storedClientData.isEmpty());
866 QCOMPARE(storedClientData["Token"].toString(), QString("testtoken."));
867 QVERIFY(!storedClientData.contains("Expiry"));
868+ store.clear();
869
870 //Permission denied
871 info.setUrlResponse(QString("http://www.facebook.com/connect/login_success.html?error=user_denied"));
872 m_testPlugin->userActionFinished(info);
873- m_loop.exec();
874- QCOMPARE(m_error.type(), int(Error::NotAuthorized));
875+ QTRY_COMPARE(error.count(), 1);
876+ QCOMPARE(error.at(0).at(0).value<Error>().type(), int(Error::NotAuthorized));
877+ error.clear();
878 }
879
880 void OAuth2PluginTest::testPluginWebserverUserActionFinished_data()
881@@ -823,7 +852,7 @@
882 "" << "" << 0 << "" << "" << QVariantMap();
883
884 QTest::newRow("permission denied") <<
885- "http://localhost/resp.html?error=user_denied" <<
886+ "http://localhost/resp.html?error=user_denied&$state" <<
887 int(Error::NotAuthorized) <<
888 "" << "" << 0 << "" << "" << QVariantMap();
889
890@@ -833,7 +862,7 @@
891 "" << "" << 0 << "" << "" << QVariantMap();
892
893 QTest::newRow("reply code, http error 401") <<
894- "http://localhost/resp.html?code=c0d3" <<
895+ "http://localhost/resp.html?code=c0d3&$state" <<
896 int(Error::OperationFailed) <<
897 "https://localhost/access_token" <<
898 "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
899@@ -843,7 +872,7 @@
900 QVariantMap();
901
902 QTest::newRow("reply code, empty reply") <<
903- "http://localhost/resp.html?code=c0d3" <<
904+ "http://localhost/resp.html?code=c0d3&$state" <<
905 int(Error::NotAuthorized) <<
906 "https://localhost/access_token" <<
907 "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
908@@ -853,7 +882,7 @@
909 QVariantMap();
910
911 QTest::newRow("reply code, no access token") <<
912- "http://localhost/resp.html?code=c0d3" <<
913+ "http://localhost/resp.html?code=c0d3&$state" <<
914 int(Error::NotAuthorized) <<
915 "https://localhost/access_token" <<
916 "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
917@@ -863,7 +892,7 @@
918 QVariantMap();
919
920 QTest::newRow("reply code, no content type") <<
921- "http://localhost/resp.html?code=c0d3" <<
922+ "http://localhost/resp.html?code=c0d3&$state" <<
923 int(Error::OperationFailed) <<
924 "https://localhost/access_token" <<
925 "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
926@@ -873,7 +902,7 @@
927 QVariantMap();
928
929 QTest::newRow("reply code, unsupported content type") <<
930- "http://localhost/resp.html?code=c0d3" <<
931+ "http://localhost/resp.html?code=c0d3&$state" <<
932 int(Error::OperationFailed) <<
933 "https://localhost/access_token" <<
934 "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
935@@ -886,19 +915,65 @@
936 response.insert("AccessToken", "t0k3n");
937 response.insert("ExpiresIn", int(3600));
938 response.insert("RefreshToken", QString());
939- QTest::newRow("reply code, valid token") <<
940- "http://localhost/resp.html?code=c0d3" <<
941- int(-1) <<
942- "https://localhost/access_token" <<
943- "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
944- int(200) <<
945- "application/json" <<
946- "{ \"access_token\":\"t0k3n\", \"expires_in\": 3600 }" <<
947+ QTest::newRow("reply code, valid token, wrong state") <<
948+ "http://localhost/resp.html?code=c0d3&$wrongstate" <<
949+ int(Error::NotAuthorized) <<
950+ "" <<
951+ "" <<
952+ int(200) <<
953+ "application/json" <<
954+ "{ \"access_token\":\"t0k3n\", \"expires_in\": 3600 }" <<
955+ response;
956+
957+ response.clear();
958+ response.insert("AccessToken", "t0k3n");
959+ response.insert("ExpiresIn", int(3600));
960+ response.insert("RefreshToken", QString());
961+ response.insert("Scope", QStringList() << "one" << "two" << "three");
962+ QTest::newRow("reply code, valid token, no scope") <<
963+ "http://localhost/resp.html?code=c0d3&$state" <<
964+ int(-1) <<
965+ "https://localhost/access_token" <<
966+ "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
967+ int(200) <<
968+ "application/json" <<
969+ "{ \"access_token\":\"t0k3n\", \"expires_in\": 3600 }" <<
970+ response;
971+
972+ response.clear();
973+ response.insert("AccessToken", "t0k3n");
974+ response.insert("ExpiresIn", int(3600));
975+ response.insert("RefreshToken", QString());
976+ response.insert("Scope", QStringList());
977+ QTest::newRow("reply code, valid token, empty scope") <<
978+ "http://localhost/resp.html?code=c0d3&$state" <<
979+ int(-1) <<
980+ "https://localhost/access_token" <<
981+ "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
982+ int(200) <<
983+ "application/json" <<
984+ "{ \"access_token\":\"t0k3n\", \"expires_in\": 3600, \"scope\": \"\" }" <<
985+ response;
986+
987+ response.clear();
988+ response.insert("AccessToken", "t0k3n");
989+ response.insert("ExpiresIn", int(3600));
990+ response.insert("RefreshToken", QString());
991+ response.insert("Scope", QStringList() << "one" << "two");
992+ QTest::newRow("reply code, valid token, other scope") <<
993+ "http://localhost/resp.html?code=c0d3&$state" <<
994+ int(-1) <<
995+ "https://localhost/access_token" <<
996+ "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
997+ int(200) <<
998+ "application/json" <<
999+ "{ \"access_token\":\"t0k3n\", \"expires_in\": 3600, "
1000+ "\"scope\": \"one two\" }" <<
1001 response;
1002
1003 response.clear();
1004 QTest::newRow("reply code, facebook, no token") <<
1005- "http://localhost/resp.html?code=c0d3" <<
1006+ "http://localhost/resp.html?code=c0d3&$state" <<
1007 int(Error::NotAuthorized) <<
1008 "https://localhost/access_token" <<
1009 "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
1010@@ -911,8 +986,9 @@
1011 response.insert("AccessToken", "t0k3n");
1012 response.insert("ExpiresIn", int(3600));
1013 response.insert("RefreshToken", QString());
1014+ response.insert("Scope", QStringList() << "one" << "two" << "three");
1015 QTest::newRow("reply code, facebook, valid token") <<
1016- "http://localhost/resp.html?code=c0d3" <<
1017+ "http://localhost/resp.html?code=c0d3&$state" <<
1018 int(-1) <<
1019 "https://localhost/access_token" <<
1020 "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
1021@@ -925,6 +1001,7 @@
1022 response.insert("AccessToken", "t0k3n");
1023 response.insert("ExpiresIn", int(3600));
1024 response.insert("RefreshToken", QString());
1025+ response.insert("Scope", QStringList() << "one" << "two" << "three");
1026 QTest::newRow("username-password, valid token") <<
1027 "http://localhost/resp.html?username=us3r&password=s3cr3t" <<
1028 int(-1) <<
1029@@ -939,6 +1016,7 @@
1030 response.insert("AccessToken", "t0k3n");
1031 response.insert("ExpiresIn", int(3600));
1032 response.insert("RefreshToken", QString());
1033+ response.insert("Scope", QStringList() << "one" << "two" << "three");
1034 QTest::newRow("assertion, valid token") <<
1035 "http://localhost/resp.html?assertion_type=http://oauth.net/token/1.0"
1036 "&assertion=oauth1t0k3n" <<
1037@@ -954,6 +1032,7 @@
1038 response.insert("AccessToken", "t0k3n");
1039 response.insert("ExpiresIn", int(3600));
1040 response.insert("RefreshToken", QString());
1041+ response.insert("Scope", QStringList() << "one" << "two" << "three");
1042 QTest::newRow("username-password, valid token, wrong content type") <<
1043 "http://localhost/resp.html?username=us3r&password=s3cr3t" <<
1044 int(-1) <<
1045@@ -984,14 +1063,12 @@
1046 data.setClientId("104660106251471");
1047 data.setClientSecret("fa28f40b5a1f8c1d5628963d880636fbkjkjkj");
1048 data.setRedirectUri("http://localhost/resp.html");
1049+ data.setScope(QStringList() << "one" << "two" << "three");
1050
1051- QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
1052- this, SLOT(result(const SignOn::SessionData&)),Qt::QueuedConnection);
1053- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error & )),
1054- this, SLOT(pluginError(const SignOn::Error &)),Qt::QueuedConnection);
1055- QObject::connect(m_testPlugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
1056- this, SLOT(uiRequest(const SignOn::UiSessionData&)),Qt::QueuedConnection);
1057- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
1058+ QSignalSpy result(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)));
1059+ QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
1060+ QSignalSpy userActionRequired(m_testPlugin,
1061+ SIGNAL(userActionRequired(const SignOn::UiSessionData&)));
1062
1063 TestNetworkAccessManager *nam = new TestNetworkAccessManager;
1064 m_testPlugin->m_networkAccessManager = nam;
1065@@ -1004,20 +1081,26 @@
1066 nam->setNextReply(reply);
1067
1068 m_testPlugin->process(data, QString("web_server"));
1069- m_loop.exec();
1070- QCOMPARE(m_uiResponse.UrlResponse(), QString("UI request received"));
1071+ QTRY_COMPARE(userActionRequired.count(), 1);
1072+ QString state = parseState(userActionRequired);
1073
1074 if (!urlResponse.isEmpty()) {
1075+ urlResponse.replace("$state", QString("state=") + state);
1076+ urlResponse.replace("$wrongstate", QString("state=12") + state);
1077 info.setUrlResponse(urlResponse);
1078 }
1079
1080 m_testPlugin->userActionFinished(info);
1081- m_loop.exec();
1082
1083- QCOMPARE(m_error.type(), errorCode);
1084+ QTRY_COMPARE(error.count(), errorCode < 0 ? 0 : 1);
1085+ QTRY_COMPARE(result.count(), errorCode < 0 ? 1 : 0);
1086+ if (errorCode >= 0) {
1087+ QCOMPARE(error.at(0).at(0).value<Error>().type(), errorCode);
1088+ } else {
1089+ QCOMPARE(result.at(0).at(0).value<SessionData>().toMap(), response);
1090+ }
1091 QCOMPARE(nam->m_lastRequest.url(), QUrl(postUrl));
1092 QCOMPARE(QString::fromUtf8(nam->m_lastRequestData), postContents);
1093- QCOMPARE(m_response.toMap(), response);
1094
1095 delete nam;
1096 }
1097@@ -1132,13 +1215,10 @@
1098 data.setConsumerSecret("fa28f40b5a1f8c1d5628963d880636fbkjkjkj");
1099 data.setRealm("MyHost");
1100
1101- QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
1102- this, SLOT(result(const SignOn::SessionData&)),Qt::QueuedConnection);
1103- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error & )),
1104- this, SLOT(pluginError(const SignOn::Error &)),Qt::QueuedConnection);
1105- QObject::connect(m_testPlugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
1106- this, SLOT(uiRequest(const SignOn::UiSessionData&)),Qt::QueuedConnection);
1107- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
1108+ QSignalSpy result(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)));
1109+ QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
1110+ QSignalSpy userActionRequired(m_testPlugin,
1111+ SIGNAL(userActionRequired(const SignOn::UiSessionData&)));
1112
1113 TestNetworkAccessManager *nam = new TestNetworkAccessManager;
1114 m_testPlugin->m_networkAccessManager = nam;
1115@@ -1149,8 +1229,7 @@
1116 nam->setNextReply(reply);
1117
1118 m_testPlugin->process(data, mechanism);
1119- m_loop.exec();
1120- QCOMPARE(m_uiResponse.UrlResponse(), QString("UI request received"));
1121+ QTRY_COMPARE(userActionRequired.count(), 1);
1122
1123 nam->m_lastRequest = QNetworkRequest();
1124 nam->m_lastRequestData = QByteArray();
1125@@ -1168,9 +1247,16 @@
1126 }
1127
1128 m_testPlugin->userActionFinished(info);
1129- m_loop.exec();
1130+ QTRY_COMPARE(error.count(), errorCode < 0 ? 0 : 1);
1131+ QTRY_COMPARE(result.count(), errorCode < 0 ? 1 : 0);
1132+ QVariantMap resp;
1133+ if (errorCode >= 0) {
1134+ QCOMPARE(error.at(0).at(0).value<Error>().type(), errorCode);
1135+ } else {
1136+ resp = result.at(0).at(0).value<SessionData>().toMap();
1137+ QVERIFY(mapIsSubset(response, resp));
1138+ }
1139
1140- QCOMPARE(m_error.type(), errorCode);
1141 if (!expectedAuthMap.isEmpty()) {
1142 QCOMPARE(nam->m_lastRequest.url().toString(), data.TokenEndpoint());
1143 QVERIFY(nam->m_lastRequestData.isEmpty());
1144@@ -1189,9 +1275,6 @@
1145 QCOMPARE(authMap.value("oauth_signature_method").toString(), mechanism);
1146 QVERIFY(mapIsSubset(expectedAuthMap, authMap));
1147 }
1148- if (errorCode < 0) {
1149- QVERIFY(mapIsSubset(response, m_response.toMap()));
1150- }
1151
1152 delete nam;
1153 }
1154@@ -1256,16 +1339,9 @@
1155 data.setClientSecret("fa28f40b5a1f8c1d5628963d880636fbkjkjkj");
1156 data.setRedirectUri("http://localhost/resp.html");
1157
1158- QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
1159- this, SLOT(result(const SignOn::SessionData&)),
1160- Qt::QueuedConnection);
1161- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error & )),
1162- this, SLOT(pluginError(const SignOn::Error &)),
1163- Qt::QueuedConnection);
1164- QObject::connect(m_testPlugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
1165- this, SLOT(uiRequest(const SignOn::UiSessionData&)),
1166- Qt::QueuedConnection);
1167- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
1168+ QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
1169+ QSignalSpy userActionRequired(m_testPlugin,
1170+ SIGNAL(userActionRequired(const SignOn::UiSessionData&)));
1171
1172 TestNetworkAccessManager *nam = new TestNetworkAccessManager;
1173 m_testPlugin->m_networkAccessManager = nam;
1174@@ -1276,13 +1352,14 @@
1175 nam->setNextReply(reply);
1176
1177 m_testPlugin->process(data, QString("web_server"));
1178- m_loop.exec();
1179+ QTRY_COMPARE(userActionRequired.count(), 1);
1180+ QString state = parseState(userActionRequired);
1181
1182- info.setUrlResponse("http://localhost/resp.html?code=c0d3");
1183+ info.setUrlResponse("http://localhost/resp.html?code=c0d3&state=" + state);
1184 m_testPlugin->userActionFinished(info);
1185- m_loop.exec();
1186
1187- QCOMPARE(m_error.type(), expectedErrorCode);
1188+ QTRY_COMPARE(error.count(), 1);
1189+ QCOMPARE(error.at(0).at(0).value<Error>().type(), expectedErrorCode);
1190
1191 delete nam;
1192 }
1193@@ -1313,6 +1390,7 @@
1194 response.insert("AccessToken", "n3w-t0k3n");
1195 response.insert("ExpiresIn", 3600);
1196 response.insert("RefreshToken", QString());
1197+ response.insert("Scope", QStringList());
1198
1199 QTest::newRow("expired access token") << data.toMap() << response;
1200
1201@@ -1331,16 +1409,8 @@
1202
1203 SignOn::UiSessionData info;
1204
1205- QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
1206- this, SLOT(result(const SignOn::SessionData&)),
1207- Qt::QueuedConnection);
1208- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error & )),
1209- this, SLOT(pluginError(const SignOn::Error &)),
1210- Qt::QueuedConnection);
1211- QObject::connect(m_testPlugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
1212- this, SLOT(uiRequest(const SignOn::UiSessionData&)),
1213- Qt::QueuedConnection);
1214- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
1215+ QSignalSpy result(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)));
1216+ QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
1217
1218 TestNetworkAccessManager *nam = new TestNetworkAccessManager;
1219 m_testPlugin->m_networkAccessManager = nam;
1220@@ -1351,14 +1421,14 @@
1221 nam->setNextReply(reply);
1222
1223 m_testPlugin->process(sessionData, QString("web_server"));
1224- m_loop.exec();
1225+ QTRY_COMPARE(result.count(), 1);
1226+ QCOMPARE(error.count(), 0);
1227
1228- QCOMPARE(m_error.type(), -1);
1229 QCOMPARE(nam->m_lastRequest.url(), QUrl("https://localhost/access_token"));
1230 QCOMPARE(QString::fromUtf8(nam->m_lastRequestData),
1231 QString("grant_type=refresh_token&refresh_token=r3fr3sh"));
1232
1233- QCOMPARE(m_response.toMap(), expectedResponse);
1234+ QCOMPARE(result.at(0).at(0).value<SessionData>().toMap(), expectedResponse);
1235
1236 delete nam;
1237 }
1238@@ -1421,16 +1491,9 @@
1239
1240 SignOn::UiSessionData info;
1241
1242- QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
1243- this, SLOT(result(const SignOn::SessionData&)),
1244- Qt::QueuedConnection);
1245- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error & )),
1246- this, SLOT(pluginError(const SignOn::Error &)),
1247- Qt::QueuedConnection);
1248- QObject::connect(m_testPlugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
1249- this, SLOT(uiRequest(const SignOn::UiSessionData&)),
1250- Qt::QueuedConnection);
1251- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
1252+ QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
1253+ QSignalSpy userActionRequired(m_testPlugin,
1254+ SIGNAL(userActionRequired(const SignOn::UiSessionData&)));
1255
1256 TestNetworkAccessManager *nam = new TestNetworkAccessManager;
1257 m_testPlugin->m_networkAccessManager = nam;
1258@@ -1445,11 +1508,13 @@
1259 nam->setNextReply(reply);
1260
1261 m_testPlugin->process(data, QString("web_server"));
1262- m_loop.exec();
1263
1264- QCOMPARE(m_error.type(), expectedError);
1265 if (expectedError < 0) {
1266- QCOMPARE(m_uiResponse.UrlResponse(), QString("UI request received"));
1267+ QTRY_COMPARE(userActionRequired.count(), 1);
1268+ QCOMPARE(error.count(), 0);
1269+ } else {
1270+ QTRY_COMPARE(error.count(), 1);
1271+ QCOMPARE(error.at(0).at(0).value<Error>().type(), expectedError);
1272 }
1273
1274 delete nam;
1275@@ -1502,16 +1567,10 @@
1276 data.setRedirectUri("http://localhost/resp.html");
1277 data.setForceClientAuthViaRequestBody(forceAuthViaRequestBody);
1278
1279- QObject::connect(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)),
1280- this, SLOT(result(const SignOn::SessionData&)),
1281- Qt::QueuedConnection);
1282- QObject::connect(m_testPlugin, SIGNAL(error(const SignOn::Error & )),
1283- this, SLOT(pluginError(const SignOn::Error &)),
1284- Qt::QueuedConnection);
1285- QObject::connect(m_testPlugin, SIGNAL(userActionRequired(const SignOn::UiSessionData&)),
1286- this, SLOT(uiRequest(const SignOn::UiSessionData&)),
1287- Qt::QueuedConnection);
1288- QTimer::singleShot(10*1000, &m_loop, SLOT(quit()));
1289+ QSignalSpy result(m_testPlugin, SIGNAL(result(const SignOn::SessionData&)));
1290+ QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &)));
1291+ QSignalSpy userActionRequired(m_testPlugin,
1292+ SIGNAL(userActionRequired(const SignOn::UiSessionData&)));
1293
1294 TestNetworkAccessManager *nam = new TestNetworkAccessManager;
1295 m_testPlugin->m_networkAccessManager = nam;
1296@@ -1522,13 +1581,14 @@
1297 nam->setNextReply(reply);
1298
1299 m_testPlugin->process(data, QString("web_server"));
1300- m_loop.exec();
1301+ QTRY_COMPARE(userActionRequired.count(), 1);
1302+ QString state = parseState(userActionRequired);
1303
1304- info.setUrlResponse("http://localhost/resp.html?code=c0d3");
1305+ info.setUrlResponse("http://localhost/resp.html?code=c0d3&state=" + state);
1306 m_testPlugin->userActionFinished(info);
1307- m_loop.exec();
1308
1309- QCOMPARE(m_error.type(), -1);
1310+ QTRY_COMPARE(result.count(), 1);
1311+ QCOMPARE(error.count(), 0);
1312 QCOMPARE(nam->m_lastRequest.url(), QUrl("https://localhost/access_token"));
1313 QCOMPARE(QString::fromUtf8(nam->m_lastRequestData), postContents);
1314 QCOMPARE(QString::fromUtf8(nam->m_lastRequest.rawHeader("Authorization")),
1315
1316=== modified file 'tests/oauth2plugintest.h'
1317--- tests/oauth2plugintest.h 2015-02-16 12:25:29 +0000
1318+++ tests/oauth2plugintest.h 2015-04-17 08:14:56 +0000
1319@@ -35,13 +35,6 @@
1320 {
1321 Q_OBJECT
1322
1323-public slots:
1324- void result(const SignOn::SessionData &data);
1325- void pluginError(const SignOn::Error &err);
1326- void uiRequest(const SignOn::UiSessionData &data);
1327- void store(const SignOn::SessionData &data);
1328- void aborted(QNetworkReply *reply);
1329-
1330 private slots:
1331 void initTestCase();
1332 void cleanupTestCase();
1333@@ -73,11 +66,6 @@
1334
1335 private:
1336 Plugin *m_testPlugin;
1337- SignOn::Error m_error;
1338- SignOn::SessionData m_response;
1339- SignOn::UiSessionData m_uiResponse;
1340- SignOn::SessionData m_stored;
1341- QEventLoop m_loop;
1342 };
1343
1344 #endif // OAUTH2PLUGINTEST_H
1345
1346=== modified file 'tests/tests.pro'
1347--- tests/tests.pro 2015-01-28 12:09:12 +0000
1348+++ tests/tests.pro 2015-04-17 08:14:56 +0000
1349@@ -25,16 +25,9 @@
1350 $${TOP_SRC_DIR}/src \
1351 /usr/include/signon-qt
1352 PKGCONFIG += \
1353+ libsignon-qt5 \
1354 signon-plugins
1355
1356-lessThan(QT_MAJOR_VERSION, 5) {
1357- PKGCONFIG += \
1358- QJson \
1359- libsignon-qt
1360-} else {
1361- PKGCONFIG += \
1362- libsignon-qt5
1363-}
1364
1365 target.path = $${INSTALL_PREFIX}/bin
1366 testsuite.path = $${INSTALL_PREFIX}/share/$$TARGET

Subscribers

No one subscribed via source and target branches