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

Proposed by dobey
Status: Rejected
Rejected by: Natalia Bidart
Proposed branch: lp:~dobey/ubuntuone-credentials/signon-plugin-part2
Merge into: lp:ubuntuone-credentials
Prerequisite: lp:~dobey/ubuntuone-credentials/default-token-name
Diff against target: 1267 lines (+501/-370)
15 files modified
data/ubuntuone.provider (+2/-2)
debian/control (+2/-1)
debian/libubuntuoneauth-2.0-0.symbols (+1/-0)
libubuntuoneauth/CMakeLists.txt (+4/-0)
libubuntuoneauth/authenticator.cpp (+211/-0)
libubuntuoneauth/authenticator.h (+78/-0)
libubuntuoneauth/keyring.cpp (+50/-44)
libubuntuoneauth/keyring.h (+7/-5)
libubuntuoneauth/libubuntuoneauth.symbols (+2/-1)
libubuntuoneauth/ssoservice.cpp (+53/-18)
libubuntuoneauth/ssoservice.h (+2/-2)
signon-plugin/tests/test_plugin.cpp (+40/-125)
signon-plugin/ubuntuone-plugin.cpp (+42/-168)
signon-plugin/ubuntuone-plugin.h (+2/-4)
signon-plugin/ubuntuonedata.h (+5/-0)
To merge this branch: bzr merge lp:~dobey/ubuntuone-credentials/signon-plugin-part2
Reviewer Review Type Date Requested Status
unity-api-1-bot continuous-integration Approve
Alberto Mardegan (community) Needs Fixing
PS Jenkins bot continuous-integration Pending
Review via email: mp+296716@code.launchpad.net

Commit message

Final pieces for switching to the new signon plug-in.

To post a comment you must log in.
Revision history for this message
Alberto Mardegan (mardy) wrote :

Couple of inline commits.

review: Needs Fixing
Revision history for this message
dobey (dobey) wrote :

Replied inline.

Revision history for this message
Alberto Mardegan (mardy) wrote :

Replied inline, added one comment.

266. By dobey

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

267. By dobey

Emit tokenNotFound immediately if no account is found.

268. By dobey

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

269. By dobey

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

Revision history for this message
Alberto Mardegan (mardy) wrote :

A couple of inline comments, it looks 99.9% good :-)

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.

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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)
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Started a massive cleanup of old MPs, closing this given its age, please update and re-open if still valid.

Unmerged revisions

271. By dobey

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

270. By dobey

Set the m_invalidate flag properly.

269. By dobey

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

268. By dobey

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

267. By dobey

Emit tokenNotFound immediately if no account is found.

266. By dobey

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

265. By dobey

The login() call should never emit credentialsNotFound.
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

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