Merge lp:~dobey/ubuntuone-credentials/signon-plugin-part2 into lp:ubuntuone-credentials
- signon-plugin-part2
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | Natalia Bidart |
Proposed branch: | lp:~dobey/ubuntuone-credentials/signon-plugin-part2 |
Merge into: | lp:ubuntuone-credentials |
Prerequisite: | lp:~dobey/ubuntuone-credentials/default-token-name |
Diff against target: |
1267 lines (+501/-370) 15 files modified
data/ubuntuone.provider (+2/-2) debian/control (+2/-1) debian/libubuntuoneauth-2.0-0.symbols (+1/-0) libubuntuoneauth/CMakeLists.txt (+4/-0) libubuntuoneauth/authenticator.cpp (+211/-0) libubuntuoneauth/authenticator.h (+78/-0) libubuntuoneauth/keyring.cpp (+50/-44) libubuntuoneauth/keyring.h (+7/-5) libubuntuoneauth/libubuntuoneauth.symbols (+2/-1) libubuntuoneauth/ssoservice.cpp (+53/-18) libubuntuoneauth/ssoservice.h (+2/-2) signon-plugin/tests/test_plugin.cpp (+40/-125) signon-plugin/ubuntuone-plugin.cpp (+42/-168) signon-plugin/ubuntuone-plugin.h (+2/-4) signon-plugin/ubuntuonedata.h (+5/-0) |
To merge this branch: | bzr merge lp:~dobey/ubuntuone-credentials/signon-plugin-part2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
unity-api-1-bot | continuous-integration | Approve | |
Alberto Mardegan (community) | Needs Fixing | ||
PS Jenkins bot | continuous-integration | Pending | |
Review via email: mp+296716@code.launchpad.net |
Commit message
Final pieces for switching to the new signon plug-in.
Description of the change
dobey (dobey) wrote : | # |
Replied inline.
Alberto Mardegan (mardy) wrote : | # |
Replied inline, added one comment.
- 266. By dobey
-
Refactor so createSession/
process are called after identity is stored.
Fix invalidateCredentials to actually invalidate the credentials. - 267. By dobey
-
Emit tokenNotFound immediately if no account is found.
- 268. By dobey
-
Need to depend on signond, since apparently nothing else does.
- 269. By dobey
-
Try to handle process() calls better.
Fix a few small typos.
Alberto Mardegan (mardy) wrote : | # |
A couple of inline comments, it looks 99.9% good :-)
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:271
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:271
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:271
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:271
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
PASSED: Continuous integration, rev:271
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Natalia Bidart (nataliabidart) wrote : | # |
Started a massive cleanup of old MPs, closing this given its age, please update and re-open if still valid.
Unmerged revisions
- 271. By dobey
-
Refactor token clearing to separate method.
Handle existing secret separately from invalidation request. - 270. By dobey
-
Set the m_invalidate flag properly.
- 269. By dobey
-
Try to handle process() calls better.
Fix a few small typos. - 268. By dobey
-
Need to depend on signond, since apparently nothing else does.
- 267. By dobey
-
Emit tokenNotFound immediately if no account is found.
- 266. By dobey
-
Refactor so createSession/
process are called after identity is stored.
Fix invalidateCredentials to actually invalidate the credentials. - 265. By dobey
-
The login() call should never emit credentialsNotF
ound.
Don't emit an error in existingCredentialsId if there is no account. - 264. By dobey
-
Must use 'unsigned int' in symbols file for quint32.
- 263. By dobey
-
Hopefully store the token correctly now too.
- 262. By dobey
-
Try to store the credentials a different way.
Preview Diff
1 | === modified file 'data/ubuntuone.provider' |
2 | --- data/ubuntuone.provider 2016-04-19 15:04:15 +0000 |
3 | +++ data/ubuntuone.provider 2016-06-21 17:27:01 +0000 |
4 | @@ -8,8 +8,8 @@ |
5 | |
6 | <template> |
7 | <group name="auth"> |
8 | - <setting name="method">password</setting> |
9 | - <setting name="mechanism">password</setting> |
10 | + <setting name="method">ubuntuone</setting> |
11 | + <setting name="mechanism">ubuntuone</setting> |
12 | </group> |
13 | </template> |
14 | </provider> |
15 | |
16 | === modified file 'debian/control' |
17 | --- debian/control 2016-05-19 17:41:38 +0000 |
18 | +++ debian/control 2016-06-21 17:27:01 +0000 |
19 | @@ -76,7 +76,7 @@ |
20 | ${misc:Pre-Depends}, |
21 | Depends: |
22 | account-plugin-tools, |
23 | - signon-plugin-password, |
24 | + signon-plugin-ubuntuone (= ${source:Version}), |
25 | sqlite3, |
26 | ubuntuone-credentials-common (= ${source:Version}), |
27 | ${misc:Depends}, |
28 | @@ -142,6 +142,7 @@ |
29 | ${misc:Pre-Depends}, |
30 | Depends: |
31 | libubuntuoneauth-2.0-0 (= ${binary:Version}), |
32 | + signond, |
33 | ${misc:Depends}, |
34 | ${shlibs:Depends}, |
35 | Description: Ubuntu One authentication library - signon plug-in |
36 | |
37 | === modified file 'debian/libubuntuoneauth-2.0-0.symbols' |
38 | --- debian/libubuntuoneauth-2.0-0.symbols 2016-04-22 08:25:24 +0000 |
39 | +++ debian/libubuntuoneauth-2.0-0.symbols 2016-06-21 17:27:01 +0000 |
40 | @@ -122,6 +122,7 @@ |
41 | (c++)"UbuntuOne::Token::addOAuthTimestamp(QString) const@Base" 15.11+16.04.20151207.1 |
42 | (c++)"UbuntuOne::Keyring::storeToken(UbuntuOne::Token)@Base" 13.08 |
43 | (c++)"UbuntuOne::Keyring::storeToken(UbuntuOne::Token, QString const&)@Base" 14.04+14.10.20140802 |
44 | + (c++)"UbuntuOne::Keyring::storeToken(UbuntuOne::Token, QString const&, unsigned int)@Base" 0replaceme |
45 | (c++)"UbuntuOne::Keyring::tokenFound(UbuntuOne::Token const&)@Base" 13.08 |
46 | (c++)"UbuntuOne::Keyring::deleteToken()@Base" 13.08 |
47 | (c++)"UbuntuOne::Keyring::handleError(SignOn::Error const&)@Base" 13.08 |
48 | |
49 | === modified file 'libubuntuoneauth/CMakeLists.txt' |
50 | --- libubuntuoneauth/CMakeLists.txt 2016-04-21 09:25:58 +0000 |
51 | +++ libubuntuoneauth/CMakeLists.txt 2016-06-21 17:27:01 +0000 |
52 | @@ -13,6 +13,10 @@ |
53 | SET (LIB_TYPE STATIC) |
54 | ENDIF (BUILD_STATIC_LIBS) |
55 | |
56 | +# Some slots are deprecated; disable warnings on deprecations, because |
57 | +# moc-generated files are using these methods |
58 | +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") |
59 | + |
60 | # The sources for building the library |
61 | FILE (GLOB SOURCES *.cpp) |
62 | # HEADERS only includes the public headers for installation. |
63 | |
64 | === added file 'libubuntuoneauth/authenticator.cpp' |
65 | --- libubuntuoneauth/authenticator.cpp 1970-01-01 00:00:00 +0000 |
66 | +++ libubuntuoneauth/authenticator.cpp 2016-06-21 17:27:01 +0000 |
67 | @@ -0,0 +1,211 @@ |
68 | +/* |
69 | + * Copyright 2016 Canonical Ltd. |
70 | + * |
71 | + * This library is free software; you can redistribute it and/or |
72 | + * modify it under the terms of version 3 of the GNU Lesser General Public |
73 | + * License as published by the Free Software Foundation. |
74 | + * |
75 | + * This program is distributed in the hope that it will be useful, |
76 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
77 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
78 | + * General Public License for more details. |
79 | + * |
80 | + * You should have received a copy of the GNU Lesser General Public |
81 | + * License along with this library; if not, write to the |
82 | + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
83 | + * Boston, MA 02110-1301, USA. |
84 | + */ |
85 | + |
86 | +#include "authenticator.h" |
87 | +#include "../signon-plugin/ubuntuonedata.h" |
88 | + |
89 | +#include <Accounts/Account> |
90 | +#include <Accounts/Service> |
91 | +#include <SignOn/AuthSession> |
92 | +#include <SignOn/Identity> |
93 | +#include <SignOn/IdentityInfo> |
94 | + |
95 | +#include <QDebug> |
96 | + |
97 | +using namespace Internal; |
98 | +using namespace UbuntuOne; |
99 | + |
100 | +Authenticator::Authenticator(Accounts::Manager *manager, QObject *parent): |
101 | + QObject(parent), |
102 | + m_manager(manager), |
103 | + m_invalidate(false), |
104 | + m_uiAllowed(true), |
105 | + m_credentialsId(0) |
106 | +{ |
107 | + if (!m_manager) { |
108 | + m_manager = new Accounts::Manager(this); |
109 | + } |
110 | +} |
111 | + |
112 | +void Authenticator::handleError(const SignOn::Error &e) |
113 | +{ |
114 | + qCritical() << "Authentication error:" << e.message(); |
115 | + Q_EMIT error(AuthenticationError, e.message()); |
116 | +} |
117 | + |
118 | +void Authenticator::handleSessionData(const SignOn::SessionData &data) |
119 | +{ |
120 | + PluginData reply = data.data<PluginData>(); |
121 | + |
122 | + auto errorCode = PluginData::ErrorCode(reply.U1ErrorCode()); |
123 | + auto message = reply.U1ErrorMessage(); |
124 | + if (errorCode != PluginData::NoError) { |
125 | + switch (errorCode) { |
126 | + case PluginData::OneTimePasswordRequired: |
127 | + qDebug() << "Error: OTP required"; |
128 | + Q_EMIT error(OneTimePasswordRequired, message); |
129 | + break; |
130 | + case PluginData::InvalidPassword: |
131 | + qDebug() << "Error: invalid password"; |
132 | + Q_EMIT error(InvalidPassword, message); |
133 | + break; |
134 | + default: |
135 | + qWarning() << "Unknown error:" << message; |
136 | + Q_EMIT error(AuthenticationError, message); |
137 | + } |
138 | + return; |
139 | + } |
140 | + |
141 | + Token token(reply.TokenKey(), reply.TokenSecret(), |
142 | + reply.ConsumerKey(), reply.ConsumerSecret(), |
143 | + reply.DateCreated(), reply.DateUpdated()); |
144 | + Q_EMIT authenticated(token); |
145 | +} |
146 | + |
147 | +quint32 Authenticator::credentialsId() |
148 | +{ |
149 | + return m_credentialsId; |
150 | +} |
151 | + |
152 | +quint32 Authenticator::existingCredentialsId() |
153 | +{ |
154 | + QString providerId("ubuntuone"); |
155 | + Accounts::AccountIdList accountIds = m_manager->accountList(providerId); |
156 | + |
157 | + if (accountIds.isEmpty()) { |
158 | + qDebug() << "authenticate(): No UbuntuOne accounts found"; |
159 | + return 0; |
160 | + } |
161 | + |
162 | + if (Q_UNLIKELY(accountIds.count() > 1)) { |
163 | + qWarning() << "authenticate(): Found '" << accountIds.count() << |
164 | + "' accounts. Using first."; |
165 | + } |
166 | + |
167 | + qDebug() << "authenticate(): Using account '" << accountIds[0] << "'."; |
168 | + |
169 | + auto account = m_manager->account(accountIds[0]); |
170 | + if (Q_UNLIKELY(!account)) { |
171 | + qDebug() << "Couldn't load account"; |
172 | + /* This could either happen because the account was deleted right while |
173 | + * we were loading it, or because the accounts DB was locked by another |
174 | + * app. Let's just return an authentication error here, so the client |
175 | + * can retry. |
176 | + */ |
177 | + Q_EMIT error(AuthenticationError, ""); |
178 | + return 0; |
179 | + } |
180 | + |
181 | + /* Here we should check that the account service is enabled; but since the |
182 | + * old code was not doing this check, and that from the API there is no way |
183 | + * of knowing which service we are interested in, let's leave it as a TODO. |
184 | + */ |
185 | + |
186 | + return account->credentialsId(); |
187 | +} |
188 | + |
189 | +void Authenticator::process(SignOn::Identity *identity, |
190 | + const QString &tokenName, |
191 | + const QString &userName, |
192 | + const QString &password, |
193 | + const QString &otp) |
194 | +{ |
195 | + auto session = identity->createSession(QStringLiteral("ubuntuone")); |
196 | + if (Q_UNLIKELY(!session)) { |
197 | + qCritical() << "Unable to create AuthSession."; |
198 | + Q_EMIT error(AuthenticationError, ""); |
199 | + return; |
200 | + } |
201 | + |
202 | + connect(session, SIGNAL(response(const SignOn::SessionData&)), |
203 | + this, SLOT(handleSessionData(const SignOn::SessionData&))); |
204 | + connect(session, SIGNAL(error(const SignOn::Error&)), |
205 | + this, SLOT(handleError(const SignOn::Error&))); |
206 | + |
207 | + PluginData data; |
208 | + data.setTokenName(tokenName); |
209 | + data.setUserName(userName); |
210 | + data.setSecret(password); |
211 | + data.setOneTimePassword(otp); |
212 | + int uiPolicy = m_uiAllowed ? |
213 | + SignOn::DefaultPolicy : SignOn::NoUserInteractionPolicy; |
214 | + data.setUiPolicy(uiPolicy); |
215 | + if (m_invalidate) { |
216 | + data.setInvalidateToken(true); |
217 | + m_invalidate = false; |
218 | + } |
219 | + |
220 | + session->process(data, QStringLiteral("ubuntuone")); |
221 | +} |
222 | + |
223 | +void Authenticator::authenticate(const QString &tokenName, |
224 | + const QString &userName, |
225 | + const QString &password, |
226 | + const QString &otp) |
227 | +{ |
228 | + SignOn::Identity *identity; |
229 | + m_credentialsId = existingCredentialsId(); |
230 | + |
231 | + if (m_credentialsId == 0) { |
232 | + SignOn::IdentityInfo info = SignOn::IdentityInfo(); |
233 | + |
234 | + info.setAccessControlList(QStringList() << "unconfined"); |
235 | + info.setStoreSecret(false); |
236 | + |
237 | + identity = SignOn::Identity::newIdentity(info, this); |
238 | + connect(identity, &SignOn::Identity::credentialsStored, |
239 | + [=](const quint32 id) { |
240 | + m_credentialsId = id; |
241 | + process(identity, tokenName, userName, password, otp); |
242 | + }); |
243 | + connect(identity, &SignOn::Identity::error, |
244 | + [=](const SignOn::Error &err) { |
245 | + qCritical() << "authenticate(): unable to store identity"; |
246 | + Q_EMIT error(AccountNotFound, ""); |
247 | + }); |
248 | + identity->storeCredentials(info); |
249 | + } else { |
250 | + identity = SignOn::Identity::existingIdentity(m_credentialsId, this); |
251 | + if (Q_UNLIKELY(!identity)) { |
252 | + qCritical() << "authenticate(): unable to load credentials" << m_credentialsId; |
253 | + Q_EMIT error(AccountNotFound, ""); |
254 | + return; |
255 | + } |
256 | + process(identity, tokenName, userName, password, otp); |
257 | + } |
258 | + |
259 | +} |
260 | + |
261 | +void Authenticator::invalidateCredentials() |
262 | +{ |
263 | + m_credentialsId = existingCredentialsId(); |
264 | + if (m_credentialsId == 0) { |
265 | + qCritical() << "invalidateCredentials(): no existing credentials."; |
266 | + Q_EMIT error(AccountNotFound, ""); |
267 | + return; |
268 | + } |
269 | + |
270 | + m_invalidate = true; |
271 | + auto identity = SignOn::Identity::existingIdentity(m_credentialsId, this); |
272 | + process(identity, Token::buildTokenName()); |
273 | +} |
274 | + |
275 | +void Authenticator::setUiAllowed(bool allowed) |
276 | +{ |
277 | + m_uiAllowed = allowed; |
278 | +} |
279 | |
280 | === added file 'libubuntuoneauth/authenticator.h' |
281 | --- libubuntuoneauth/authenticator.h 1970-01-01 00:00:00 +0000 |
282 | +++ libubuntuoneauth/authenticator.h 2016-06-21 17:27:01 +0000 |
283 | @@ -0,0 +1,78 @@ |
284 | +/* |
285 | + * Copyright 2016 Canonical Ltd. |
286 | + * |
287 | + * This library is free software; you can redistribute it and/or |
288 | + * modify it under the terms of version 3 of the GNU Lesser General Public |
289 | + * License as published by the Free Software Foundation. |
290 | + * |
291 | + * This program is distributed in the hope that it will be useful, |
292 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
293 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
294 | + * General Public License for more details. |
295 | + * |
296 | + * You should have received a copy of the GNU Lesser General Public |
297 | + * License along with this library; if not, write to the |
298 | + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
299 | + * Boston, MA 02110-1301, USA. |
300 | + */ |
301 | +#ifndef _U1_AUTHENTICATOR_H_ |
302 | +#define _U1_AUTHENTICATOR_H_ |
303 | + |
304 | +#include <Accounts/Manager> |
305 | +#include <SignOn/Identity> |
306 | + |
307 | +#include <QObject> |
308 | + |
309 | +#include "token.h" |
310 | + |
311 | +namespace Internal { |
312 | + |
313 | +class Authenticator : public QObject |
314 | +{ |
315 | + Q_OBJECT |
316 | + |
317 | +public: |
318 | + enum ErrorCode { |
319 | + NoError = 0, |
320 | + AccountNotFound, |
321 | + OneTimePasswordRequired, |
322 | + InvalidPassword, |
323 | + AuthenticationError, // will create more specific codes if needed |
324 | + }; |
325 | + |
326 | + explicit Authenticator(Accounts::Manager *manager = 0, QObject *parent = 0); |
327 | + |
328 | + void process(SignOn::Identity *identity, |
329 | + const QString &tokenName, |
330 | + const QString &userName = QString(), |
331 | + const QString &password = QString(), |
332 | + const QString &otp = QString()); |
333 | + void authenticate(const QString &tokenName, |
334 | + const QString &userName = QString(), |
335 | + const QString &password = QString(), |
336 | + const QString &otp = QString()); |
337 | + void invalidateCredentials(); |
338 | + void setUiAllowed(bool allowed); |
339 | + quint32 credentialsId(); |
340 | + |
341 | +Q_SIGNALS: |
342 | + void authenticated(const UbuntuOne::Token& token); |
343 | + void error(Internal::Authenticator::ErrorCode code, const QString& message); |
344 | + |
345 | +private: |
346 | + quint32 existingCredentialsId(); |
347 | + |
348 | +private Q_SLOTS: |
349 | + void handleError(const SignOn::Error &error); |
350 | + void handleSessionData(const SignOn::SessionData &data); |
351 | + |
352 | +private: |
353 | + Accounts::Manager *m_manager; |
354 | + bool m_invalidate; |
355 | + bool m_uiAllowed; |
356 | + quint32 m_credentialsId; |
357 | +}; |
358 | + |
359 | +} /* namespace */ |
360 | + |
361 | +#endif /* _U1_AUTHENTICATOR_H_ */ |
362 | |
363 | === modified file 'libubuntuoneauth/keyring.cpp' |
364 | --- libubuntuoneauth/keyring.cpp 2016-04-19 15:04:15 +0000 |
365 | +++ libubuntuoneauth/keyring.cpp 2016-06-21 17:27:01 +0000 |
366 | @@ -23,7 +23,9 @@ |
367 | |
368 | #include <QDebug> |
369 | |
370 | +#include "authenticator.h" |
371 | #include "keyring.h" |
372 | +#include "../signon-plugin/ubuntuonedata.h" |
373 | |
374 | using namespace Accounts; |
375 | using namespace SignOn; |
376 | @@ -68,36 +70,35 @@ |
377 | |
378 | void Keyring::findToken() |
379 | { |
380 | + using namespace Internal; |
381 | + |
382 | QString _acctName("ubuntuone"); |
383 | AccountIdList _ids = _manager.accountList(_acctName); |
384 | - Identity *identity; |
385 | - Account *account; |
386 | |
387 | - if (_ids.length() > 0) { |
388 | - if (_ids.length() > 1) { |
389 | - qDebug() << "findToken(): Found '" << _ids.length() << "' accounts. Using first."; |
390 | - } |
391 | - account = _manager.account(_ids[0]); |
392 | - qDebug() << "findToken(): Using Ubuntu One account '" << _ids[0] << "'."; |
393 | - identity = Identity::existingIdentity(account->credentialsId()); |
394 | - if (identity == NULL) { |
395 | - qCritical() << "findToken(): disabled account " << _acctName << _ids[0]; |
396 | - emit tokenNotFound(); |
397 | - return; |
398 | - } |
399 | - AuthSession *session = identity->createSession(QStringLiteral("password")); |
400 | - if (session != NULL) { |
401 | - connect(session, SIGNAL(response(const SignOn::SessionData&)), |
402 | - this, SLOT(handleSessionData(const SignOn::SessionData&))); |
403 | - connect(session, SIGNAL(error(const SignOn::Error&)), |
404 | - this, SLOT(handleError(const SignOn::Error&))); |
405 | - session->process(SessionData(), QStringLiteral("password")); |
406 | - return; |
407 | - } |
408 | - qCritical() << "Unable to create AuthSession."; |
409 | + if (_ids.isEmpty()) { |
410 | + qDebug() << "authenticate(): No UbuntuOne accounts found"; |
411 | + Q_EMIT tokenNotFound(); |
412 | + return; |
413 | } |
414 | - qDebug() << "findToken(): No accounts found matching " << _acctName; |
415 | - emit tokenNotFound(); |
416 | + |
417 | + auto authenticator = new Authenticator(&_manager); |
418 | + authenticator->setUiAllowed(false); |
419 | + |
420 | + connect(authenticator, &Authenticator::authenticated, |
421 | + [=](const Token &token) { |
422 | + Q_EMIT tokenFound(token); |
423 | + authenticator->deleteLater(); |
424 | + }); |
425 | + connect(authenticator, &Authenticator::error, |
426 | + [=](Authenticator::ErrorCode code) { |
427 | + if (code == Authenticator::AccountNotFound) { |
428 | + Q_EMIT tokenNotFound(); |
429 | + } else { |
430 | + Q_EMIT keyringError("Authentication failed"); |
431 | + } |
432 | + authenticator->deleteLater(); |
433 | + }); |
434 | + authenticator->authenticate(Token::buildTokenName()); |
435 | } |
436 | |
437 | void Keyring::handleCredentialsStored(const quint32 id) |
438 | @@ -123,6 +124,11 @@ |
439 | |
440 | void Keyring::storeToken(Token token, const QString& displayName) |
441 | { |
442 | + storeToken(token, displayName, 0); |
443 | + } |
444 | + |
445 | + void Keyring::storeToken(Token token, const QString& displayName, quint32 credentialsId) |
446 | + { |
447 | QString _acctName("ubuntuone"); |
448 | AccountIdList _ids = _manager.accountList(_acctName); |
449 | Identity *identity = NULL; |
450 | @@ -143,26 +149,26 @@ |
451 | _account->setDisplayName(displayName); |
452 | } |
453 | |
454 | - if(_account->credentialsId() == 0) { |
455 | - qDebug() << "storeToken() : creating new Identity for account " << _account->id() ; |
456 | - identity = Identity::newIdentity(); |
457 | + if (credentialsId == 0) { |
458 | + if(_account->credentialsId() == 0) { |
459 | + qDebug() << "storeToken() : creating new Identity for account " << _account->id() ; |
460 | + identity = Identity::newIdentity(); |
461 | + } else { |
462 | + qDebug() << "storeToken(): identity found."; |
463 | + identity = Identity::existingIdentity(_account->credentialsId()); |
464 | + } |
465 | + |
466 | + Q_ASSERT(identity != NULL); |
467 | + |
468 | + connect(identity, SIGNAL(error(const SignOn::Error&)), |
469 | + this, SLOT(handleError(const SignOn::Error&))); |
470 | + connect(identity, SIGNAL(credentialsStored(const quint32)), |
471 | + this, SLOT(handleCredentialsStored(const quint32))); |
472 | + |
473 | + identity->storeCredentials(); |
474 | } else { |
475 | - qDebug() << "storeToken(): identity found."; |
476 | - identity = Identity::existingIdentity(_account->credentialsId()); |
477 | + handleCredentialsStored(credentialsId); |
478 | } |
479 | - |
480 | - Q_ASSERT(identity != NULL); |
481 | - |
482 | - connect(identity, SIGNAL(error(const SignOn::Error&)), |
483 | - this, SLOT(handleError(const SignOn::Error&))); |
484 | - connect(identity, SIGNAL(credentialsStored(const quint32)), |
485 | - this, SLOT(handleCredentialsStored(const quint32))); |
486 | - |
487 | - IdentityInfo info = IdentityInfo(); |
488 | - |
489 | - info.setSecret(token.toQuery(), true); |
490 | - info.setAccessControlList(QStringList() << "unconfined"); |
491 | - identity->storeCredentials(info); |
492 | } |
493 | |
494 | void Keyring::handleAccountRemoved() |
495 | |
496 | === modified file 'libubuntuoneauth/keyring.h' |
497 | --- libubuntuoneauth/keyring.h 2016-04-21 09:25:58 +0000 |
498 | +++ libubuntuoneauth/keyring.h 2016-06-21 17:27:01 +0000 |
499 | @@ -38,22 +38,24 @@ |
500 | void findToken(); |
501 | void storeToken(Token token); |
502 | void storeToken(Token token, const QString& displayName); |
503 | - void deleteToken(); |
504 | + void storeToken(Token token, const QString& displayName, |
505 | + quint32 credentialsId); |
506 | + Q_DECL_DEPRECATED void deleteToken(); |
507 | |
508 | Q_SIGNALS: |
509 | void tokenFound(const Token& token); |
510 | void tokenNotFound(); |
511 | void tokenStored(); |
512 | - void tokenDeleted(); |
513 | + Q_DECL_DEPRECATED void tokenDeleted(); |
514 | |
515 | void keyringError(QString message); |
516 | |
517 | private Q_SLOTS: |
518 | void handleError(const SignOn::Error &error); |
519 | - void handleSessionData(const SignOn::SessionData &data); |
520 | + Q_DECL_DEPRECATED void handleSessionData(const SignOn::SessionData &data); |
521 | void handleCredentialsStored(const quint32 id); |
522 | - void handleAccountRemoved(); |
523 | - void handleDeleteError(const SignOn::Error &error); |
524 | + Q_DECL_DEPRECATED void handleAccountRemoved(); |
525 | + Q_DECL_DEPRECATED void handleDeleteError(const SignOn::Error &error); |
526 | |
527 | private: |
528 | Accounts::Manager _manager; |
529 | |
530 | === modified file 'libubuntuoneauth/libubuntuoneauth.symbols' |
531 | --- libubuntuoneauth/libubuntuoneauth.symbols 2013-07-22 15:54:02 +0000 |
532 | +++ libubuntuoneauth/libubuntuoneauth.symbols 2016-06-21 17:27:01 +0000 |
533 | @@ -1,7 +1,8 @@ |
534 | { |
535 | global: |
536 | extern "C++" { |
537 | - *UbuntuOne::*; |
538 | + UbuntuOne::*; |
539 | + *for?UbuntuOne::*; |
540 | }; |
541 | qt_*; |
542 | local: |
543 | |
544 | === modified file 'libubuntuoneauth/ssoservice.cpp' |
545 | --- libubuntuoneauth/ssoservice.cpp 2016-04-19 15:04:15 +0000 |
546 | +++ libubuntuoneauth/ssoservice.cpp 2016-06-21 17:27:01 +0000 |
547 | @@ -22,6 +22,7 @@ |
548 | #include <QNetworkRequest> |
549 | #include <QUrlQuery> |
550 | |
551 | +#include "authenticator.h" |
552 | #include "logging.h" |
553 | #include "ssoservice.h" |
554 | #include "requests.h" |
555 | @@ -45,8 +46,6 @@ |
556 | |
557 | // create the keyring that will be used to store and retrieve the different tokens |
558 | _keyring = new Keyring(this); |
559 | - _nam = new QNetworkAccessManager(this); |
560 | - |
561 | connect(_keyring, SIGNAL(tokenFound(const Token&)), |
562 | this, SLOT(handleCredentialsFound(const Token&))); |
563 | connect(_keyring, SIGNAL(tokenNotFound()), |
564 | @@ -54,18 +53,9 @@ |
565 | |
566 | connect(_keyring, SIGNAL(tokenStored()), |
567 | this, SLOT(handleTokenStored())); |
568 | - connect(_keyring, SIGNAL(tokenDeleted()), |
569 | - this, SLOT(handleTokenDeleted())); |
570 | - |
571 | connect(_keyring, SIGNAL(keyringError(QString)), |
572 | this, SLOT(handleKeyringError(QString))); |
573 | |
574 | - connect(_nam, SIGNAL(finished(QNetworkReply*)), |
575 | - this, SLOT(accountPinged(QNetworkReply*))); |
576 | - |
577 | - connect(&(_provider), |
578 | - SIGNAL(OAuthTokenGranted(const OAuthTokenResponse&)), |
579 | - this, SLOT(tokenReceived(const OAuthTokenResponse&))); |
580 | connect(&(_provider), |
581 | SIGNAL(AccountGranted(const AccountResponse&)), |
582 | this, SLOT(accountRegistered(const AccountResponse&))); |
583 | @@ -116,12 +106,35 @@ |
584 | |
585 | void SSOService::login(QString email, QString password, QString twoFactorCode) |
586 | { |
587 | - OAuthTokenRequest request(getAuthBaseUrl(), |
588 | - email, password, |
589 | - Token::buildTokenName(), twoFactorCode); |
590 | - _tempEmail = email; |
591 | - |
592 | - _provider.GetOAuthToken(request); |
593 | + using namespace Internal; |
594 | + |
595 | + // Keep this here for now to ensure ABI compat. |
596 | + OAuthTokenRequest request; |
597 | + |
598 | + auto authenticator = new Authenticator; |
599 | + /* The caller of this API is assumed to have his own UI, so no support |
600 | + * from SignOn UI is needed or even desired. */ |
601 | + authenticator->setUiAllowed(false); |
602 | + |
603 | + connect(authenticator, &Authenticator::authenticated, |
604 | + [=](const Token &token) { |
605 | + _keyring->storeToken(token, email, authenticator->credentialsId()); |
606 | + authenticator->deleteLater(); |
607 | + }); |
608 | + connect(authenticator, &Authenticator::error, |
609 | + [=](Authenticator::ErrorCode code, const QString& message) { |
610 | + if (code == Authenticator::AccountNotFound) { |
611 | + Q_EMIT requestFailed(ErrorResponse(0, "", "LOGIN_FAILED", message)); |
612 | + } else if (code == Authenticator::OneTimePasswordRequired) { |
613 | + Q_EMIT twoFactorAuthRequired(); |
614 | + } else { |
615 | + /* TODO: deliver a proper error response. */ |
616 | + Q_EMIT requestFailed(ErrorResponse(400, "", "LOGIN_FAILED", message)); |
617 | + } |
618 | + authenticator->deleteLater(); |
619 | + }); |
620 | + authenticator->authenticate(Token::buildTokenName(), |
621 | + email, password, twoFactorCode); |
622 | } |
623 | |
624 | void SSOService::handleTwoFactorAuthRequired() |
625 | @@ -160,7 +173,29 @@ |
626 | |
627 | void SSOService::invalidateCredentials() |
628 | { |
629 | - _keyring->deleteToken(); |
630 | + using namespace Internal; |
631 | + |
632 | + auto authenticator = new Authenticator; |
633 | + /* The caller of this API is assumed to have his own UI, so no support |
634 | + * from SignOn UI is needed or even desired. */ |
635 | + authenticator->setUiAllowed(false); |
636 | + |
637 | + connect(authenticator, &Authenticator::authenticated, |
638 | + [=](const Token &token) { |
639 | + Q_EMIT credentialsFound(token); |
640 | + authenticator->deleteLater(); |
641 | + }); |
642 | + connect(authenticator, &Authenticator::error, |
643 | + [=](Authenticator::ErrorCode code, const QString& message) { |
644 | + if (code == Authenticator::AccountNotFound) { |
645 | + Q_EMIT credentialsNotFound(); |
646 | + } else { |
647 | + /* TODO: deliver a proper error response. */ |
648 | + Q_EMIT requestFailed(ErrorResponse(400, "", "LOGIN_FAILED", message)); |
649 | + } |
650 | + authenticator->deleteLater(); |
651 | + }); |
652 | + authenticator->invalidateCredentials(); |
653 | } |
654 | |
655 | void SSOService::errorOccurred(const ErrorResponse& error) |
656 | |
657 | === modified file 'libubuntuoneauth/ssoservice.h' |
658 | --- libubuntuoneauth/ssoservice.h 2016-04-21 09:25:58 +0000 |
659 | +++ libubuntuoneauth/ssoservice.h 2016-06-21 17:27:01 +0000 |
660 | @@ -58,10 +58,10 @@ |
661 | private slots: |
662 | void accountPinged(QNetworkReply*); |
663 | void handleTokenStored(); |
664 | - void handleTokenDeleted() { emit credentialsDeleted(); }; |
665 | + Q_DECL_DEPRECATED void handleTokenDeleted() { emit credentialsDeleted(); }; |
666 | void handleCredentialsFound(const Token& token); |
667 | void handleCredentialsNotFound(); |
668 | - void tokenReceived(const OAuthTokenResponse& token); |
669 | + Q_DECL_DEPRECATED void tokenReceived(const OAuthTokenResponse& token); |
670 | void accountRegistered(const AccountResponse& account); |
671 | void errorOccurred(const ErrorResponse&); |
672 | void handleTwoFactorAuthRequired(); |
673 | |
674 | === modified file 'signon-plugin/tests/test_plugin.cpp' |
675 | --- signon-plugin/tests/test_plugin.cpp 2016-05-27 18:14:38 +0000 |
676 | +++ signon-plugin/tests/test_plugin.cpp 2016-06-21 17:27:01 +0000 |
677 | @@ -150,7 +150,6 @@ |
678 | void testPluginMechanisms(); |
679 | void testStoredToken_data(); |
680 | void testStoredToken(); |
681 | - void testUserInteraction(); |
682 | void testTokenCreation_data(); |
683 | void testTokenCreation(); |
684 | |
685 | @@ -223,7 +222,6 @@ |
686 | { |
687 | QTest::addColumn<QVariantMap>("sessionData"); |
688 | QTest::addColumn<int>("expectedErrorCode"); |
689 | - QTest::addColumn<bool>("uiExpected"); |
690 | QTest::addColumn<QVariantMap>("expectedResponse"); |
691 | QTest::addColumn<QVariantMap>("expectedStore"); |
692 | |
693 | @@ -233,23 +231,8 @@ |
694 | |
695 | QTest::newRow("empty") << |
696 | sessionData.toMap() << |
697 | - -1 << |
698 | - true << QVariantMap() << QVariantMap(); |
699 | - |
700 | - sessionData.setTokenName("helloworld"); |
701 | - sessionData.setSecret("consumer_key=aAa&consumer_secret=bBb&name=helloworld&token=cCc&token_secret=dDd"); |
702 | - response.setConsumerKey("aAa"); |
703 | - response.setConsumerSecret("bBb"); |
704 | - response.setTokenKey("cCc"); |
705 | - response.setTokenSecret("dDd"); |
706 | - QVariantMap storedData; |
707 | - storedData[sessionData.TokenName()] = response.toMap(); |
708 | - stored.setStoredData(storedData); |
709 | - response.setTokenName(sessionData.TokenName()); |
710 | - QTest::newRow("in secret, valid") << |
711 | - sessionData.toMap() << |
712 | - -1 << |
713 | - false << response.toMap() << stored.toMap(); |
714 | + 103 << |
715 | + QVariantMap() << QVariantMap(); |
716 | |
717 | sessionData = UbuntuOne::PluginData(); |
718 | QString tokenName = UbuntuOne::Token::buildTokenName(); |
719 | @@ -268,18 +251,46 @@ |
720 | response.setTokenSecret("ts"); |
721 | response.setTokenName(tokenName); |
722 | stored = UbuntuOne::PluginData(); |
723 | - storedData.clear(); |
724 | QTest::newRow("stored, valid") << |
725 | sessionData.toMap() << |
726 | -1 << |
727 | - false << response.toMap() << stored.toMap(); |
728 | + response.toMap() << stored.toMap(); |
729 | + |
730 | + sessionData = UbuntuOne::PluginData(); |
731 | + sessionData.setStoredData(QVariantMap { |
732 | + { tokenName, QVariantMap { |
733 | + { "ConsumerKey", "ck" }, |
734 | + { "ConsumerSecret", "cs" }, |
735 | + { "TokenKey", "tk" }, |
736 | + { "TokenSecret", "ts" }, |
737 | + }}, |
738 | + }); |
739 | + sessionData.setInvalidateToken(true); |
740 | + response = UbuntuOne::PluginData(); |
741 | + stored = UbuntuOne::PluginData(); |
742 | + QVariantMap storedData; |
743 | + stored.setStoredData(storedData); |
744 | + QTest::newRow("clearing token") << |
745 | + sessionData.toMap() << |
746 | + -1 << |
747 | + response.toMap() << stored.toMap(); |
748 | + |
749 | + sessionData = UbuntuOne::PluginData(); |
750 | + sessionData.setSecret("consumer_key=aAa&consumer_secret=bBb&name=helloworld&token=cCc&token_secret=dDd"); |
751 | + response = UbuntuOne::PluginData(); |
752 | + response.setTokenName(tokenName); |
753 | + stored = UbuntuOne::PluginData(); |
754 | + stored.setStoredData(QVariantMap()); |
755 | + QTest::newRow("in secret, clearing") << |
756 | + sessionData.toMap() << |
757 | + -1 << |
758 | + response.toMap() << stored.toMap(); |
759 | } |
760 | |
761 | void PluginTest::testStoredToken() |
762 | { |
763 | QFETCH(QVariantMap, sessionData); |
764 | QFETCH(int, expectedErrorCode); |
765 | - QFETCH(bool, uiExpected); |
766 | QFETCH(QVariantMap, expectedResponse); |
767 | QFETCH(QVariantMap, expectedStore); |
768 | |
769 | @@ -292,7 +303,7 @@ |
770 | m_testPlugin->process(sessionData, "ubuntuone"); |
771 | if (expectedErrorCode < 0) { |
772 | QCOMPARE(error.count(), 0); |
773 | - QTRY_COMPARE(userActionRequired.count(), uiExpected ? 1 : 0); |
774 | + QTRY_COMPARE(userActionRequired.count(), 0); |
775 | if (!expectedResponse.isEmpty()) { |
776 | QTRY_COMPARE(result.count(), 1); |
777 | QVariantMap resp = result.at(0).at(0).value<SessionData>().toMap(); |
778 | @@ -316,66 +327,6 @@ |
779 | } |
780 | } |
781 | |
782 | -void PluginTest::testUserInteraction() |
783 | -{ |
784 | - QSignalSpy result(m_testPlugin, SIGNAL(result(const SignOn::SessionData&))); |
785 | - QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &))); |
786 | - QSignalSpy userActionRequired(m_testPlugin, |
787 | - SIGNAL(userActionRequired(const SignOn::UiSessionData&))); |
788 | - QSignalSpy store(m_testPlugin, SIGNAL(store(const SignOn::SessionData&))); |
789 | - |
790 | - TestNetworkAccessManager *nam = new TestNetworkAccessManager; |
791 | - m_testPlugin->m_networkAccessManager = nam; |
792 | - |
793 | - UbuntuOne::PluginData sessionData; |
794 | - sessionData.setTokenName("helloworld"); |
795 | - sessionData.setUserName("tom@example.com"); |
796 | - m_testPlugin->process(sessionData, "ubuntuone"); |
797 | - |
798 | - QTRY_COMPARE(userActionRequired.count(), 1); |
799 | - QVariantMap data = |
800 | - userActionRequired.at(0).at(0).value<UiSessionData>().toMap(); |
801 | - /* We want the title to be there, but we don't care about its value here */ |
802 | - QVERIFY(data.contains(SSOUI_KEY_TITLE)); |
803 | - data.remove(SSOUI_KEY_TITLE); |
804 | - QVariantMap expectedUserInteraction; |
805 | - expectedUserInteraction[SSOUI_KEY_USERNAME] = "tom@example.com"; |
806 | - expectedUserInteraction[SSOUI_KEY_QUERYPASSWORD] = true; |
807 | - QCOMPARE(data, expectedUserInteraction); |
808 | - userActionRequired.clear(); |
809 | - |
810 | - /* Prepare network reply */ |
811 | - TestNetworkReply *reply = new TestNetworkReply(this); |
812 | - reply->setStatusCode(401); |
813 | - reply->setContent("{\n" |
814 | - " \"code\": \"TWOFACTOR_REQUIRED\",\n" |
815 | - " \"message\": \"This account requires 2-factor authentication.\",\n" |
816 | - " \"extra\": {}\n" |
817 | - "}"); |
818 | - nam->setNextReply(reply); |
819 | - |
820 | - QVariantMap userReply; |
821 | - userReply[SSOUI_KEY_USERNAME] = "tom@example.com"; |
822 | - userReply[SSOUI_KEY_PASSWORD] = "s3cr3t"; |
823 | - m_testPlugin->userActionFinished(userReply); |
824 | - |
825 | - /* Again the plugin should request user interaction, as OTP is required */ |
826 | - QTRY_COMPARE(userActionRequired.count(), 1); |
827 | - data = userActionRequired.at(0).at(0).value<UiSessionData>().toMap(); |
828 | - expectedUserInteraction.clear(); |
829 | - expectedUserInteraction[SSOUI_KEY_USERNAME] = "tom@example.com"; |
830 | - expectedUserInteraction[SSOUI_KEY_PASSWORD] = "s3cr3t"; |
831 | - expectedUserInteraction[SSOUI_KEY_QUERY2FA] = true; |
832 | - /* We want the map to contain the SSOUI_KEY_2FA_TEXT, but we don't care |
833 | - * about the value */ |
834 | - QVERIFY(data.contains(SSOUI_KEY_2FA_TEXT)); |
835 | - data.remove(SSOUI_KEY_2FA_TEXT); |
836 | - /* Same goes for the title */ |
837 | - QVERIFY(data.contains(SSOUI_KEY_TITLE)); |
838 | - data.remove(SSOUI_KEY_TITLE); |
839 | - QCOMPARE(data, expectedUserInteraction); |
840 | -} |
841 | - |
842 | void PluginTest::testTokenCreation_data() |
843 | { |
844 | QTest::addColumn<QVariantMap>("sessionData"); |
845 | @@ -385,12 +336,10 @@ |
846 | QTest::addColumn<int>("expectedErrorCode"); |
847 | QTest::addColumn<QVariantMap>("expectedResponse"); |
848 | QTest::addColumn<QVariantMap>("expectedStore"); |
849 | - QTest::addColumn<QVariantMap>("expectedUserInteraction"); |
850 | |
851 | UbuntuOne::PluginData sessionData; |
852 | UbuntuOne::PluginData response; |
853 | UbuntuOne::PluginData stored; |
854 | - QVariantMap userInteraction; |
855 | |
856 | // Successful creation, with password only |
857 | sessionData.setTokenName("helloworld"); |
858 | @@ -420,7 +369,7 @@ |
859 | " \"date_updated\": \"2013-01-11 12:43:23\"\n" |
860 | "}") << |
861 | -1 << |
862 | - response.toMap() << stored.toMap() << userInteraction; |
863 | + response.toMap() << stored.toMap(); |
864 | sessionData = UbuntuOne::PluginData(); |
865 | response = UbuntuOne::PluginData(); |
866 | stored = UbuntuOne::PluginData(); |
867 | @@ -430,8 +379,6 @@ |
868 | sessionData.setTokenName("helloworld"); |
869 | sessionData.setUserName("jim@example.com"); |
870 | sessionData.setSecret("s3cr3t"); |
871 | - userInteraction[SSOUI_KEY_USERNAME] = "jim@example.com"; |
872 | - userInteraction[SSOUI_KEY_QUERYPASSWORD] = true; |
873 | QTest::newRow("wrong password") << |
874 | sessionData.toMap() << |
875 | -1 << |
876 | @@ -441,28 +388,8 @@ |
877 | " \"extra\": {}\n" |
878 | "}") << |
879 | -1 << |
880 | - response.toMap() << stored.toMap() << userInteraction; |
881 | - sessionData = UbuntuOne::PluginData(); |
882 | - userInteraction.clear(); |
883 | - |
884 | - // Empty username |
885 | - sessionData.setTokenName("helloworld"); |
886 | - sessionData.setSecret("s3cr3t"); |
887 | - userInteraction[SSOUI_KEY_QUERYUSERNAME] = true; |
888 | - userInteraction[SSOUI_KEY_USERNAME] = ""; |
889 | - userInteraction[SSOUI_KEY_QUERYPASSWORD] = true; |
890 | - QTest::newRow("empty username") << |
891 | - sessionData.toMap() << |
892 | - -1 << |
893 | - 401 << QString("{\n" |
894 | - " \"code\": \"INVALID_CREDENTIALS\",\n" |
895 | - " \"message\": \"Missing username\",\n" |
896 | - " \"extra\": {}\n" |
897 | - "}") << |
898 | - -1 << |
899 | - response.toMap() << stored.toMap() << userInteraction; |
900 | - sessionData = UbuntuOne::PluginData(); |
901 | - userInteraction.clear(); |
902 | + response.toMap() << stored.toMap(); |
903 | + sessionData = UbuntuOne::PluginData(); |
904 | |
905 | // Network error while creating token |
906 | sessionData.setTokenName("helloworld"); |
907 | @@ -473,14 +400,13 @@ |
908 | int(QNetworkReply::SslHandshakeFailedError) << |
909 | -1 << QString() << |
910 | int(SignOn::Error::Ssl) << |
911 | - response.toMap() << stored.toMap() << userInteraction; |
912 | + response.toMap() << stored.toMap(); |
913 | sessionData = UbuntuOne::PluginData(); |
914 | |
915 | // Account needs reset |
916 | sessionData.setTokenName("helloworld"); |
917 | sessionData.setUserName("jim@example.com"); |
918 | sessionData.setSecret("s3cr3t"); |
919 | - userInteraction[SSOUI_KEY_OPENURL] = "http://www.example.com/reset"; |
920 | QTest::newRow("reset needed") << |
921 | sessionData.toMap() << |
922 | -1 << |
923 | @@ -492,9 +418,8 @@ |
924 | " }\n" |
925 | "}") << |
926 | -1 << |
927 | - response.toMap() << stored.toMap() << userInteraction; |
928 | + response.toMap() << stored.toMap(); |
929 | sessionData = UbuntuOne::PluginData(); |
930 | - userInteraction.clear(); |
931 | } |
932 | |
933 | void PluginTest::testTokenCreation() |
934 | @@ -506,7 +431,6 @@ |
935 | QFETCH(int, expectedErrorCode); |
936 | QFETCH(QVariantMap, expectedResponse); |
937 | QFETCH(QVariantMap, expectedStore); |
938 | - QFETCH(QVariantMap, expectedUserInteraction); |
939 | |
940 | QSignalSpy result(m_testPlugin, SIGNAL(result(const SignOn::SessionData&))); |
941 | QSignalSpy error(m_testPlugin, SIGNAL(error(const SignOn::Error &))); |
942 | @@ -530,16 +454,7 @@ |
943 | |
944 | m_testPlugin->process(sessionData, "ubuntuone"); |
945 | if (expectedErrorCode < 0) { |
946 | - if (!expectedUserInteraction.isEmpty()) { |
947 | - QTRY_COMPARE(userActionRequired.count(), 1); |
948 | - QVariantMap data = |
949 | - userActionRequired.at(0).at(0).value<UiSessionData>().toMap(); |
950 | - /* We don't care about the title here */ |
951 | - data.remove(SSOUI_KEY_TITLE); |
952 | - QCOMPARE(data, expectedUserInteraction); |
953 | - } else { |
954 | - QCOMPARE(userActionRequired.count(), 0); |
955 | - } |
956 | + QCOMPARE(userActionRequired.count(), 0); |
957 | |
958 | if (!expectedResponse.isEmpty()) { |
959 | QTRY_COMPARE(result.count(), 1); |
960 | |
961 | === modified file 'signon-plugin/ubuntuone-plugin.cpp' |
962 | --- signon-plugin/ubuntuone-plugin.cpp 2016-05-27 15:36:40 +0000 |
963 | +++ signon-plugin/ubuntuone-plugin.cpp 2016-06-21 17:27:01 +0000 |
964 | @@ -69,62 +69,17 @@ |
965 | { |
966 | } |
967 | |
968 | - bool SignOnPlugin::respondWithStoredData() |
969 | + void SignOnPlugin::respondWithStoredData() |
970 | { |
971 | QVariantMap storedData = m_data.StoredData(); |
972 | |
973 | - /* When U1 was using the password plugin, it was storing the token data |
974 | - * in the password field. So, if we don't have any data stored in the |
975 | - * plugin's data, try to get a token from the password field. |
976 | - */ |
977 | - if (storedData.isEmpty() && !m_data.Secret().isEmpty()) { |
978 | - Token *token = Token::fromQuery(m_data.Secret()); |
979 | - if (token->isValid()) { |
980 | - PluginData tokenData; |
981 | - tokenData.setConsumerKey(token->consumerKey()); |
982 | - tokenData.setConsumerSecret(token->consumerSecret()); |
983 | - tokenData.setTokenKey(token->tokenKey()); |
984 | - tokenData.setTokenSecret(token->tokenSecret()); |
985 | - QDateTime time = token->updated(); |
986 | - if (time.isValid()) { |
987 | - tokenData.setDateUpdated(time.toString(Qt::ISODate)); |
988 | - } |
989 | - time = token->created(); |
990 | - if (time.isValid()) { |
991 | - tokenData.setDateCreated(time.toString(Qt::ISODate)); |
992 | - } |
993 | - storedData[token->name()] = tokenData.toMap(); |
994 | - PluginData pluginData; |
995 | - pluginData.setStoredData(storedData); |
996 | - Q_EMIT store(pluginData); |
997 | - |
998 | - /* We know that the given secret is a valid token, so it cannot |
999 | - * be a valid password as well: let's clear it out now, so that |
1000 | - * if it turns out that the token is no longer valid and that |
1001 | - * we need to create a new one, we won't make a useless attempt |
1002 | - * to create one with a wrong password. |
1003 | - */ |
1004 | - m_data.setSecret(QString()); |
1005 | - } |
1006 | - delete token; |
1007 | - } else { |
1008 | - /* Always use the same token name for now */ |
1009 | - m_data.setTokenName(Token::buildTokenName()); |
1010 | - } |
1011 | + /* Always use the same token name for now */ |
1012 | + m_data.setTokenName(Token::buildTokenName()); |
1013 | |
1014 | /* Check if we have stored data for this token name */ |
1015 | PluginData tokenData(storedData[m_data.TokenName()].toMap()); |
1016 | - Token token(tokenData.TokenKey(), tokenData.TokenSecret(), |
1017 | - tokenData.ConsumerKey(), tokenData.ConsumerSecret(), |
1018 | - tokenData.DateCreated(), tokenData.DateUpdated()); |
1019 | - if (!token.isValid()) { |
1020 | - return false; |
1021 | - } |
1022 | - qDebug() << "Token is valid!" << tokenData.TokenKey(); |
1023 | - |
1024 | tokenData.setTokenName(m_data.TokenName()); |
1025 | Q_EMIT result(tokenData); |
1026 | - return true; |
1027 | } |
1028 | |
1029 | void SignOnPlugin::emitErrorFromReply(QNetworkReply *reply) |
1030 | @@ -145,6 +100,13 @@ |
1031 | Q_EMIT error(SignOn::Error(type, reply->errorString())); |
1032 | } |
1033 | |
1034 | + void SignOnPlugin::clearToken() { |
1035 | + qDebug() << "Clearing stored token"; |
1036 | + PluginData pluginData; |
1037 | + pluginData.setStoredData(QVariantMap()); |
1038 | + Q_EMIT store(pluginData); |
1039 | + } |
1040 | + |
1041 | void SignOnPlugin::process(const SignOn::SessionData &inData, |
1042 | const QString &mechanism) |
1043 | { |
1044 | @@ -158,15 +120,23 @@ |
1045 | PluginData response; |
1046 | m_data = inData.data<PluginData>(); |
1047 | |
1048 | - /* It may be that the stored token is valid; however, do the check only |
1049 | - * if no OTP was provided (since the presence of an OTP is a clear |
1050 | - * signal that the caller wants to get a new token). */ |
1051 | - if (m_data.OneTimePassword().isEmpty() && |
1052 | - respondWithStoredData()) { |
1053 | - return; |
1054 | + if (!m_data.UserName().isEmpty() && !m_data.Secret().isEmpty()) { |
1055 | + createNewToken(); |
1056 | + } else if (m_data.InvalidateToken()) { |
1057 | + clearToken(); |
1058 | + } else if (m_data.StoredData().isEmpty() && m_data.UserName().isEmpty() && !m_data.Secret().isEmpty()) { |
1059 | + /* If there's a secret stored in password field, clear it, and |
1060 | + * respond with the empty token result, as if it's invalid. |
1061 | + */ |
1062 | + m_data.setSecret(QString()); |
1063 | + clearToken(); |
1064 | + respondWithStoredData(); |
1065 | + } else if (!m_data.StoredData().isEmpty()) { |
1066 | + respondWithStoredData(); |
1067 | + } else { |
1068 | + Q_EMIT error(SignOn::Error(SignOn::Error::InvalidQuery, |
1069 | + "invalid query parameters")); |
1070 | } |
1071 | - |
1072 | - getCredentialsAndCreateNewToken(); |
1073 | } |
1074 | |
1075 | void SignOnPlugin::onCreationFinished() |
1076 | @@ -180,8 +150,6 @@ |
1077 | QJsonDocument json = QJsonDocument::fromJson(data); |
1078 | QJsonObject object = json.object(); |
1079 | |
1080 | - QString error = object.value("code").toString(); |
1081 | - |
1082 | int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
1083 | qDebug() << "Status code:" << statusCode; |
1084 | if (statusCode == 200 || statusCode == 201) { |
1085 | @@ -203,27 +171,22 @@ |
1086 | Q_EMIT store(pluginData); |
1087 | |
1088 | Q_EMIT result(token); |
1089 | - } else if (statusCode == 401 && error == ERR_INVALID_CREDENTIALS) { |
1090 | - m_data.setSecret(QString()); |
1091 | - m_data.setOneTimePassword(QString()); |
1092 | - getCredentialsAndCreateNewToken(); |
1093 | - } else if (statusCode == 401 && error == ERR_TWOFACTOR_REQUIRED) { |
1094 | - m_needsOtp = true; |
1095 | - getCredentialsAndCreateNewToken(); |
1096 | - } else if (statusCode == 403 && error == ERR_TWOFACTOR_FAILURE) { |
1097 | - m_data.setOneTimePassword(QString()); |
1098 | - getCredentialsAndCreateNewToken(); |
1099 | - } else if (statusCode == 403 && error == ERR_PASSWORD_POLICY_ERROR) { |
1100 | - QVariantMap data; |
1101 | - QJsonObject extra = object.value("extra").toObject(); |
1102 | - data[SSOUI_KEY_OPENURL] = extra.value("location").toString(); |
1103 | - Q_EMIT userActionRequired(data); |
1104 | - } else if (error == ERR_INVALID_DATA) { |
1105 | - // This error is received when the email address is invalid |
1106 | - m_data.setUserName(QString()); |
1107 | - m_data.setSecret(QString()); |
1108 | - m_data.setOneTimePassword(QString()); |
1109 | - getCredentialsAndCreateNewToken(); |
1110 | + } else if (statusCode == 401 || statusCode == 403) { |
1111 | + QString error = object.value("code").toString(); |
1112 | + QString message = object.value("message").toString(); |
1113 | + |
1114 | + PluginData errorData; |
1115 | + if (error == ERR_TWOFACTOR_REQUIRED) { |
1116 | + errorData.setU1ErrorCode(PluginData::OneTimePasswordRequired); |
1117 | + } else { |
1118 | + errorData.setU1ErrorCode(PluginData::InvalidPassword); |
1119 | + } |
1120 | + errorData.setU1ErrorMessage(message); |
1121 | + |
1122 | + // Emit a result with error data since this seems to be the |
1123 | + // only feasible way to get error messages from server back up |
1124 | + // to the UI. |
1125 | + Q_EMIT result(errorData); |
1126 | } else { |
1127 | emitErrorFromReply(reply); |
1128 | } |
1129 | @@ -250,94 +213,5 @@ |
1130 | this, SLOT(onCreationFinished())); |
1131 | } |
1132 | |
1133 | - void SignOnPlugin::getCredentialsAndCreateNewToken() |
1134 | - { |
1135 | - if (!m_data.Secret().isEmpty() && |
1136 | - (!m_needsOtp || !m_data.OneTimePassword().isEmpty())) { |
1137 | - createNewToken(); |
1138 | - } else if (m_data.Secret().isEmpty()) { |
1139 | - QVariantMap data; |
1140 | - data[SSOUI_KEY_TITLE] = |
1141 | - QString::fromUtf8(_("Sign in to your Ubuntu One account")); |
1142 | - if (m_data.UserName().isEmpty()) { |
1143 | - data[SSOUI_KEY_QUERYUSERNAME] = true; |
1144 | - } |
1145 | - data[SSOUI_KEY_USERNAME] = m_data.UserName(); |
1146 | - data[SSOUI_KEY_QUERYPASSWORD] = true; |
1147 | - m_didAskForPassword = true; |
1148 | - Q_EMIT userActionRequired(data); |
1149 | - } else { |
1150 | - QVariantMap data; |
1151 | - data[SSOUI_KEY_TITLE] = |
1152 | - QString::fromUtf8(_("Sign in to your Ubuntu One account")); |
1153 | - data[SSOUI_KEY_USERNAME] = m_data.UserName(); |
1154 | - data[SSOUI_KEY_PASSWORD] = m_data.Secret(); |
1155 | - data[SSOUI_KEY_QUERY2FA] = true; |
1156 | - data[SSOUI_KEY_2FA_TEXT] = |
1157 | - QString::fromUtf8(_("2-factor device code")); |
1158 | - Q_EMIT userActionRequired(data); |
1159 | - } |
1160 | - } |
1161 | - |
1162 | - bool SignOnPlugin::handleUiError(const SignOn::UiSessionData &data) |
1163 | - { |
1164 | - using namespace SignOn; |
1165 | - |
1166 | - int code = data.QueryErrorCode(); |
1167 | - if (code == QUERY_ERROR_NONE) { |
1168 | - return false; |
1169 | - } |
1170 | - |
1171 | - qDebug() << "userActionFinished with error: " << code; |
1172 | - if (code == QUERY_ERROR_CANCELED) { |
1173 | - Q_EMIT error(Error(Error::SessionCanceled, |
1174 | - QLatin1String("Cancelled by user"))); |
1175 | - } else if (code == QUERY_ERROR_NETWORK) { |
1176 | - Q_EMIT error(Error(Error::Network, QLatin1String("Network error"))); |
1177 | - } else if (code == QUERY_ERROR_SSL) { |
1178 | - Q_EMIT error(Error(Error::Ssl, QLatin1String("SSL error"))); |
1179 | - } else { |
1180 | - QVariantMap map = data.toMap(); |
1181 | - if (map.contains(SSOUI_KEY_QUERY2FA)) { |
1182 | - PluginData reply; |
1183 | - reply.setU1ErrorCode(PluginData::OneTimePasswordRequired); |
1184 | - Q_EMIT result(reply); |
1185 | - } else if (map.contains(SSOUI_KEY_QUERYPASSWORD)) { |
1186 | - PluginData reply; |
1187 | - reply.setU1ErrorCode(PluginData::InvalidPassword); |
1188 | - Q_EMIT result(reply); |
1189 | - } else { |
1190 | - Q_EMIT error(Error(Error::UserInteraction, |
1191 | - QString("userActionFinished error: ") |
1192 | - + QString::number(data.QueryErrorCode()))); |
1193 | - } |
1194 | - } |
1195 | - return true; |
1196 | - } |
1197 | - |
1198 | - void SignOnPlugin::userActionFinished(const SignOn::UiSessionData &data) |
1199 | - { |
1200 | - if (handleUiError(data)) { |
1201 | - return; |
1202 | - } |
1203 | - |
1204 | - PluginData uiData = data.data<PluginData>(); |
1205 | - if (!uiData.UserName().isEmpty()) { |
1206 | - m_data.setUserName(uiData.UserName()); |
1207 | - } |
1208 | - |
1209 | - if (!uiData.Secret().isEmpty()) { |
1210 | - m_data.setSecret(uiData.Secret()); |
1211 | - } |
1212 | - |
1213 | - QVariantMap map = data.toMap(); |
1214 | - QString oneTimePassword = map.value(SSOUI_KEY_2FA).toString(); |
1215 | - if (!oneTimePassword.isEmpty()) { |
1216 | - m_data.setOneTimePassword(oneTimePassword); |
1217 | - } |
1218 | - |
1219 | - getCredentialsAndCreateNewToken(); |
1220 | - } |
1221 | - |
1222 | SIGNON_DECL_AUTH_PLUGIN(SignOnPlugin) |
1223 | } // namespace UbuntuOne |
1224 | |
1225 | === modified file 'signon-plugin/ubuntuone-plugin.h' |
1226 | --- signon-plugin/ubuntuone-plugin.h 2016-05-24 20:57:27 +0000 |
1227 | +++ signon-plugin/ubuntuone-plugin.h 2016-06-21 17:27:01 +0000 |
1228 | @@ -51,14 +51,12 @@ |
1229 | void cancel() Q_DECL_OVERRIDE; |
1230 | void process(const SignOn::SessionData &inData, |
1231 | const QString &mechanism = 0) Q_DECL_OVERRIDE; |
1232 | - void userActionFinished(const SignOn::UiSessionData &data) Q_DECL_OVERRIDE; |
1233 | |
1234 | private: |
1235 | - bool respondWithStoredData(); |
1236 | + void respondWithStoredData(); |
1237 | void emitErrorFromReply(QNetworkReply *reply); |
1238 | + void clearToken(); |
1239 | void createNewToken(); |
1240 | - void getCredentialsAndCreateNewToken(); |
1241 | - bool handleUiError(const SignOn::UiSessionData &data); |
1242 | |
1243 | private Q_SLOTS: |
1244 | void onCreationFinished(); |
1245 | |
1246 | === modified file 'signon-plugin/ubuntuonedata.h' |
1247 | --- signon-plugin/ubuntuonedata.h 2016-04-27 08:36:19 +0000 |
1248 | +++ signon-plugin/ubuntuonedata.h 2016-06-21 17:27:01 +0000 |
1249 | @@ -46,6 +46,10 @@ |
1250 | SIGNON_SESSION_DECLARE_PROPERTY(QString, DateCreated); |
1251 | SIGNON_SESSION_DECLARE_PROPERTY(QString, DateUpdated); |
1252 | |
1253 | + // Set this to true if the token returned by the previous |
1254 | + // authentication is invalid. |
1255 | + SIGNON_SESSION_DECLARE_PROPERTY(bool, InvalidateToken); |
1256 | + |
1257 | // Error code |
1258 | enum ErrorCode { |
1259 | NoError = 0, |
1260 | @@ -53,6 +57,7 @@ |
1261 | InvalidPassword, |
1262 | }; |
1263 | SIGNON_SESSION_DECLARE_PROPERTY(int, U1ErrorCode); |
1264 | + SIGNON_SESSION_DECLARE_PROPERTY(QString, U1ErrorMessage); |
1265 | |
1266 | // Data which the plugin has stored into signond |
1267 | SIGNON_SESSION_DECLARE_PROPERTY(QVariantMap, StoredData); |
Couple of inline commits.