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