Merge lp:~dobey/ubuntuone-credentials/signon-plugin-part2 into lp:ubuntuone-credentials

Proposed by dobey on 2016-06-07
Status: Rejected
Rejected by: Natalia Bidart on 2017-05-19
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
Reviewer Review Type Date Requested Status
unity-api-1-bot continuous-integration Approve on 2016-06-30
Alberto Mardegan (community) 2016-06-07 Needs Fixing on 2016-06-17
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.

To post a comment you must log in.
Alberto Mardegan (mardy) wrote :

Couple of inline commits.

review: Needs Fixing
dobey (dobey) wrote :

Replied inline.

Alberto Mardegan (mardy) wrote :

Replied inline, added one comment.

266. By dobey on 2016-06-17

Refactor so createSession/process are called after identity is stored.
Fix invalidateCredentials to actually invalidate the credentials.

267. By dobey on 2016-06-17

Emit tokenNotFound immediately if no account is found.

268. By dobey on 2016-06-20

Need to depend on signond, since apparently nothing else does.

269. By dobey on 2016-06-20

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 :-)

270. By dobey on 2016-06-21

Set the m_invalidate flag properly.

271. By dobey on 2016-06-21

Refactor token clearing to separate method.
Handle existing secret separately from invalidation request.

unity-api-1-bot (unity-api-1-bot) wrote :

PASSED: Continuous integration, rev:271
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntuone-credentials-ci/8/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build/99
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/106
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=vivid+overlay/63
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=xenial/63
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/40
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/40/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial/40
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial/40/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/40
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/40/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial/40
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial/40/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/40
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/40/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial/40
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial/40/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntuone-credentials-ci/8/rebuild

review: Approve (continuous-integration)
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 on 2016-06-21

Refactor token clearing to separate method.
Handle existing secret separately from invalidation request.

270. By dobey on 2016-06-21

Set the m_invalidate flag properly.

269. By dobey on 2016-06-20

Try to handle process() calls better.
Fix a few small typos.

268. By dobey on 2016-06-20

Need to depend on signond, since apparently nothing else does.

267. By dobey on 2016-06-17

Emit tokenNotFound immediately if no account is found.

266. By dobey on 2016-06-17

Refactor so createSession/process are called after identity is stored.
Fix invalidateCredentials to actually invalidate the credentials.

265. By dobey on 2016-06-16

The login() call should never emit credentialsNotFound.
Don't emit an error in existingCredentialsId if there is no account.

264. By dobey on 2016-06-16

Must use 'unsigned int' in symbols file for quint32.

263. By dobey on 2016-06-16

Hopefully store the token correctly now too.

262. By dobey on 2016-06-13

Try to store the credentials a different way.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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);

Subscribers

People subscribed via source and target branches

to all changes: