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