Merge lp:~mardy/signon-plugin-oauth2/lp1417429-rtm into lp:signon-plugin-oauth2/rtm-14.09

Proposed by Alberto Mardegan
Status: Merged
Approved by: Timo Jyrinki
Approved revision: 68
Merged at revision: 68
Proposed branch: lp:~mardy/signon-plugin-oauth2/lp1417429-rtm
Merge into: lp:signon-plugin-oauth2/rtm-14.09
Diff against target: 212 lines (+96/-54)
3 files modified
src/oauth2plugin.cpp (+68/-52)
src/oauth2plugin.h (+4/-2)
tests/oauth2plugintest.cpp (+24/-0)
To merge this branch: bzr merge lp:~mardy/signon-plugin-oauth2/lp1417429-rtm
Reviewer Review Type Date Requested Status
David Barth (community) Approve
Review via email: mp+248544@code.launchpad.net

Commit message

OAuth2: implement a fallback mechanism when parsing replies

Description of the change

OAuth2: implement a fallback mechanism when parsing replies

To post a comment you must log in.
Revision history for this message
David Barth (dbarth) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/oauth2plugin.cpp'
2--- src/oauth2plugin.cpp 2014-09-09 13:47:22 +0000
3+++ src/oauth2plugin.cpp 2015-02-04 13:10:21 +0000
4@@ -421,6 +421,47 @@
5 }
6 }
7
8+QVariantMap OAuth2Plugin::parseReply(const QByteArray &contentType,
9+ const QByteArray &replyContent)
10+{
11+ typedef QVariantMap (OAuth2Plugin::*Parser)(const QByteArray &replyContent);
12+ Parser preferredParser;
13+ Parser fallbackParser;
14+
15+ QVariantMap map;
16+
17+ // Handling application/json content type
18+ if (contentType.startsWith(CONTENT_APP_JSON)) {
19+ TRACE() << "application/json content received";
20+ preferredParser = &OAuth2Plugin::parseJSONReply;
21+ fallbackParser = &OAuth2Plugin::parseTextReply;
22+ }
23+ // Added for facebook Graph API's (handling text/plain content type)
24+ else if (contentType.startsWith(CONTENT_TEXT_PLAIN) ||
25+ contentType.startsWith(CONTENT_APP_URLENCODED)) {
26+ TRACE() << contentType << "content received";
27+ preferredParser = &OAuth2Plugin::parseTextReply;
28+ fallbackParser = &OAuth2Plugin::parseJSONReply;
29+ } else {
30+ TRACE() << "Unsupported content type received: " << contentType;
31+ Q_EMIT error(Error(Error::OperationFailed,
32+ QString("Unsupported content type received")));
33+ return map;
34+ }
35+
36+ map = (this->*preferredParser)(replyContent);
37+ if (Q_UNLIKELY(map.isEmpty())) {
38+ TRACE() << "Parse failed, trying fallback parser";
39+ map = (this->*fallbackParser)(replyContent);
40+ if (Q_UNLIKELY(map.isEmpty())) {
41+ TRACE() << "Parse failed";
42+ Q_EMIT error(Error(Error::NotAuthorized,
43+ QString("No access token found")));
44+ }
45+ }
46+ return map;
47+}
48+
49 // Method to handle responses for OAuth 2.0 requests
50 void OAuth2Plugin::serverReply(QNetworkReply *reply)
51 {
52@@ -437,55 +478,30 @@
53
54 // Handling 200 OK response (HTTP_STATUS_OK) WITH content
55 if (reply->hasRawHeader(CONTENT_TYPE)) {
56-
57- // Handling application/json content type
58- if (reply->rawHeader(CONTENT_TYPE).startsWith(CONTENT_APP_JSON)) {
59- TRACE()<< "application/json content received";
60- QVariantMap map = parseJSONReply(replyContent);
61- QByteArray accessToken = map["access_token"].toByteArray();
62- QVariant expiresIn = map["expires_in"];
63- QByteArray refreshToken = map["refresh_token"].toByteArray();
64-
65- if (accessToken.isEmpty()) {
66- TRACE()<< "Access token is empty";
67- emit error(Error(Error::NotAuthorized,
68- QString("Access token is empty")));
69- }
70- else {
71- OAuth2PluginTokenData response;
72- response.setAccessToken(accessToken);
73- response.setRefreshToken(refreshToken);
74- response.setExpiresIn(expiresIn.toInt());
75- storeResponse(response);
76- emit result(response);
77- }
78- }
79- // Added to test with facebook Graph API's (handling text/plain content type)
80- else if (reply->rawHeader(CONTENT_TYPE).startsWith(CONTENT_TEXT_PLAIN) ||
81- reply->rawHeader(CONTENT_TYPE).startsWith(CONTENT_APP_URLENCODED)) {
82- TRACE()<< reply->rawHeader(CONTENT_TYPE) << "content received";
83- QMap<QString,QString> map = parseTextReply(replyContent);
84- QByteArray accessToken = map["access_token"].toAscii();
85- QByteArray expiresIn = map["expires"].toAscii();
86- QByteArray refreshToken = map["refresh_token"].toAscii();
87-
88- if (accessToken.isEmpty()) {
89- TRACE()<< "Access token is empty";
90- emit error(Error(Error::NotAuthorized,
91- QString("Access token is empty")));
92- }
93- else {
94- OAuth2PluginTokenData response;
95- response.setAccessToken(accessToken);
96- response.setRefreshToken(refreshToken);
97- response.setExpiresIn(expiresIn.toInt());
98- storeResponse(response);
99- emit result(response);
100- }
101- }
102- else {
103- TRACE()<< "Unsupported content type received: " << reply->rawHeader(CONTENT_TYPE);
104- emit error(Error(Error::OperationFailed, QString("Unsupported content type received")));
105+ QVariantMap map = parseReply(reply->rawHeader(CONTENT_TYPE), replyContent);
106+ if (Q_UNLIKELY(map.isEmpty())) {
107+ // The error has already been delivered
108+ return;
109+ }
110+ QByteArray accessToken = map["access_token"].toByteArray();
111+ int expiresIn = map["expires_in"].toInt();
112+ if (expiresIn == 0) {
113+ // Facebook uses just "expires" as key
114+ expiresIn = map["expires"].toInt();
115+ }
116+ QByteArray refreshToken = map["refresh_token"].toByteArray();
117+
118+ if (accessToken.isEmpty()) {
119+ TRACE()<< "Access token is empty";
120+ Q_EMIT error(Error(Error::NotAuthorized,
121+ QString("Access token is empty")));
122+ } else {
123+ OAuth2PluginTokenData response;
124+ response.setAccessToken(accessToken);
125+ response.setRefreshToken(refreshToken);
126+ response.setExpiresIn(expiresIn);
127+ storeResponse(response);
128+ emit result(response);
129 }
130 }
131 // Handling 200 OK response (HTTP_STATUS_OK) WITHOUT content
132@@ -639,7 +655,7 @@
133 TRACE() << d->m_tokens;
134 }
135
136-const QVariantMap OAuth2Plugin::parseJSONReply(const QByteArray &reply)
137+QVariantMap OAuth2Plugin::parseJSONReply(const QByteArray &reply)
138 {
139 TRACE();
140 bool ok = false;
141@@ -657,10 +673,10 @@
142 return QVariantMap();
143 }
144
145-const QMap<QString, QString> OAuth2Plugin::parseTextReply(const QByteArray &reply)
146+QVariantMap OAuth2Plugin::parseTextReply(const QByteArray &reply)
147 {
148 TRACE();
149- QMap<QString, QString> map;
150+ QVariantMap map;
151 QList<QByteArray> items = reply.split('&');
152 foreach (QByteArray item, items) {
153 int idx = item.indexOf("=");
154
155=== modified file 'src/oauth2plugin.h'
156--- src/oauth2plugin.h 2014-09-09 13:47:22 +0000
157+++ src/oauth2plugin.h 2015-02-04 13:10:21 +0000
158@@ -75,8 +75,10 @@
159 void sendOAuth2PostRequest(QUrl &postData,
160 GrantType::e grantType);
161 void storeResponse(const OAuth2PluginTokenData &response);
162- const QVariantMap parseJSONReply(const QByteArray &reply);
163- const QMap<QString, QString> parseTextReply(const QByteArray &reply);
164+ QVariantMap parseReply(const QByteArray &contentType,
165+ const QByteArray &replyContent);
166+ QVariantMap parseJSONReply(const QByteArray &reply);
167+ QVariantMap parseTextReply(const QByteArray &reply);
168 void handleOAuth2Error(const QByteArray &reply);
169 QString urlEncode(QString strData);
170
171
172=== modified file 'tests/oauth2plugintest.cpp'
173--- tests/oauth2plugintest.cpp 2014-09-09 14:02:38 +0000
174+++ tests/oauth2plugintest.cpp 2015-02-04 13:10:21 +0000
175@@ -650,6 +650,16 @@
176 "something else" <<
177 QVariantMap();
178
179+ QTest::newRow("reply code, no access token") <<
180+ "http://localhost/resp.html?code=c0d3" <<
181+ int(Error::NotAuthorized) <<
182+ "https://localhost/access_token" <<
183+ "grant_type=authorization_code&code=c0d3&redirect_uri=http://localhost/resp.html" <<
184+ int(200) <<
185+ "application/json" <<
186+ "{ \"expires_in\": 3600 }" <<
187+ QVariantMap();
188+
189 QTest::newRow("reply code, no content type") <<
190 "http://localhost/resp.html?code=c0d3" <<
191 int(Error::OperationFailed) <<
192@@ -737,6 +747,20 @@
193 "application/json" <<
194 "{ \"access_token\":\"t0k3n\", \"expires_in\": 3600 }" <<
195 response;
196+
197+ response.clear();
198+ response.insert("AccessToken", "t0k3n");
199+ response.insert("ExpiresIn", int(3600));
200+ response.insert("RefreshToken", QString());
201+ QTest::newRow("username-password, valid token, wrong content type") <<
202+ "http://localhost/resp.html?username=us3r&password=s3cr3t" <<
203+ int(-1) <<
204+ "https://localhost/access_token" <<
205+ "grant_type=user_basic&username=us3r&password=s3cr3t" <<
206+ int(200) <<
207+ "text/plain" <<
208+ "{ \"access_token\":\"t0k3n\", \"expires_in\": 3600 }" <<
209+ response;
210 }
211
212 void OAuth2PluginTest::testPluginWebserverUserActionFinished()

Subscribers

People subscribed via source and target branches