Merge lp:~online-accounts/ubuntu-system-settings-online-accounts/master into lp:ubuntu-system-settings-online-accounts

Proposed by Alberto Mardegan
Status: Merged
Approved by: David Barth
Approved revision: no longer in the source branch.
Merged at revision: 220
Proposed branch: lp:~online-accounts/ubuntu-system-settings-online-accounts/master
Merge into: lp:ubuntu-system-settings-online-accounts
Diff against target: 2361 lines (+1245/-359)
37 files modified
.bzrignore (+1/-0)
common-vars.pri (+1/-1)
debian/changelog (+9/-0)
online-accounts-service/libaccounts-service.cpp (+257/-0)
online-accounts-service/libaccounts-service.h (+69/-0)
online-accounts-service/main.cpp (+9/-0)
online-accounts-service/online-accounts-service.pro (+7/-2)
online-accounts-service/request-manager.cpp (+5/-3)
online-accounts-service/request.cpp (+12/-32)
online-accounts-service/request.h (+1/-0)
online-accounts-service/signonui-service.cpp (+19/-2)
online-accounts-service/ui-proxy.cpp (+116/-26)
online-accounts-service/ui-proxy.h (+4/-0)
online-accounts-service/utils.cpp (+57/-0)
online-accounts-service/utils.h (+34/-0)
online-accounts-ui/browser-request.cpp (+5/-4)
online-accounts-ui/globals.h (+2/-0)
online-accounts-ui/main.cpp (+17/-3)
online-accounts-ui/online-accounts-ui.pro (+0/-2)
online-accounts-ui/signonui-request.cpp (+17/-107)
online-accounts-ui/signonui-request.h (+1/-0)
plugins/module/OAuth.qml (+1/-0)
plugins/module/ServiceItem.qml (+1/-2)
plugins/plugins.pro (+2/-1)
tests/online-accounts-service/mock/request-mock.cpp (+10/-0)
tests/online-accounts-service/mock/request-mock.h (+2/-0)
tests/online-accounts-service/online-accounts-service.pro (+1/-0)
tests/online-accounts-service/tst_libaccounts_service.cpp (+522/-0)
tests/online-accounts-service/tst_libaccounts_service.pro (+38/-0)
tests/online-accounts-service/tst_service.pro (+2/-0)
tests/online-accounts-service/tst_signonui_service.pro (+2/-0)
tests/online-accounts-service/tst_ui_proxy.pro (+1/-0)
tests/online-accounts-ui/mock/signonui-request-mock.cpp (+6/-0)
tests/online-accounts-ui/mock/signonui-request-mock.h (+3/-0)
tests/online-accounts-ui/tst_browser_request.cpp (+11/-3)
tests/online-accounts-ui/tst_signonui_request.cpp (+0/-168)
tests/online-accounts-ui/tst_signonui_request.pro (+0/-3)
To merge this branch: bzr merge lp:~online-accounts/ubuntu-system-settings-online-accounts/master
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Online Accounts Pending
Review via email: mp+246305@code.launchpad.net

Commit message

New upstream release

- Make sure app items are not overlaid on top of each other (LP: #1384314)
- Remove snap decision fallback
- Make account plugins confinable (LP: #1219644)

Description of the change

New upstream release

- Make sure app items are not overlaid on top of each other (LP: #1384314)
- Remove snap decision fallback
- Make account plugins confinable

Testing instructions for plugin confinement:
1) Install http://mardy.it/archivos/com.ubuntu.reminders_0.5.latest_armhf.click (it's reminders app with its manifest modified to include an empty apparmor policy file for the evernote account plugin)
2) copy http://paste.ubuntu.com/9729995/ as /var/lib/apparmor/profiles/click_com.ubuntu.reminders_reminders_0.5.latest
3) run
   cd /var/lib/apparmor/profiles/
   sudo apparmor_parser -r \
     click_com.ubuntu.reminders_evernote-account-plugin_0.5.latest
4) Then try creating an Evernote account; it shoudl succeed.

Some apparmor denials (probably from oxide/chromium) appear in the syslog, they'll need to be investigated.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
220. By Alberto Mardegan

New upstream release

- Make sure app items are not overlaid on top of each other (LP: #1384314)
- Remove snap decision fallback
- Make account plugins confinable (LP: #1219644) Fixes: #1219644, #1376445, #1380914, #1384314
Approved by: PS Jenkins bot

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2014-10-08 07:12:09 +0000
3+++ .bzrignore 2015-01-16 17:10:14 +0000
4@@ -47,6 +47,7 @@
5 /tests/client/tst_client
6 /tests/client/tst_qml_client
7 /tests/online-accounts-service/tst_inactivity_timer
8+/tests/online-accounts-service/tst_libaccounts_service
9 /tests/online-accounts-service/tst_service
10 /tests/online-accounts-service/tst_signonui_service
11 /tests/online-accounts-service/tst_ui_proxy
12
13=== modified file 'common-vars.pri'
14--- common-vars.pri 2014-03-19 12:14:14 +0000
15+++ common-vars.pri 2015-01-16 17:10:14 +0000
16@@ -13,6 +13,6 @@
17 # Project version
18 # remember to update debian/* files if you changes this
19 #-----------------------------------------------------------------------------
20-PROJECT_VERSION = 0.3
21+PROJECT_VERSION = 0.6
22
23 # End of File
24
25=== modified file 'debian/changelog'
26--- debian/changelog 2014-12-09 13:45:40 +0000
27+++ debian/changelog 2015-01-16 17:10:14 +0000
28@@ -1,3 +1,12 @@
29+ubuntu-system-settings-online-accounts (0.6) UNRELEASED; urgency=medium
30+
31+ * New upstream release
32+ - Make sure app items are not overlaid on top of each other (LP: #1384314)
33+ - Remove snap decision fallback
34+ - Make account plugins confinable (LP: #1219644)
35+
36+ -- Alberto Mardegan <alberto.mardegan@canonical.com> Tue, 13 Jan 2015 14:56:51 +0100
37+
38 ubuntu-system-settings-online-accounts (0.5+15.04.20141209-0ubuntu1) vivid; urgency=low
39
40 [ Ubuntu daily release ]
41
42=== added file 'online-accounts-service/libaccounts-service.cpp'
43--- online-accounts-service/libaccounts-service.cpp 1970-01-01 00:00:00 +0000
44+++ online-accounts-service/libaccounts-service.cpp 2015-01-16 17:10:14 +0000
45@@ -0,0 +1,257 @@
46+/*
47+ * Copyright (C) 2014 Canonical Ltd.
48+ *
49+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
50+ *
51+ * This file is part of online-accounts-ui
52+ *
53+ * This program is free software: you can redistribute it and/or modify it
54+ * under the terms of the GNU General Public License version 3, as published
55+ * by the Free Software Foundation.
56+ *
57+ * This program is distributed in the hope that it will be useful, but
58+ * WITHOUT ANY WARRANTY; without even the implied warranties of
59+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
60+ * PURPOSE. See the GNU General Public License for more details.
61+ *
62+ * You should have received a copy of the GNU General Public License along
63+ * with this program. If not, see <http://www.gnu.org/licenses/>.
64+ */
65+
66+#include "debug.h"
67+#include "libaccounts-service.h"
68+#include "utils.h"
69+
70+#include <Accounts/Account>
71+#include <Accounts/Manager>
72+#include <Accounts/Service>
73+#include <QDBusArgument>
74+#include <QDBusConnection>
75+#include <QVariantMap>
76+
77+using namespace OnlineAccountsUi;
78+
79+static QString stripVersion(const QString &appId)
80+{
81+ QStringList components = appId.split('_');
82+ if (components.count() != 3) return appId;
83+
84+ /* Click packages have a profile of the form
85+ * $name_$application_$version
86+ * (see https://wiki.ubuntu.com/SecurityTeam/Specifications/ApplicationConfinement/Manifest#Click)
87+ *
88+ * We assume that this is a click package, and strip out the last part.
89+ */
90+ components.removeLast();
91+ return components.join('_');
92+}
93+
94+namespace OnlineAccountsUi {
95+
96+struct ServiceChanges {
97+ QString service;
98+ QString serviceType;
99+ quint32 serviceId;
100+ QVariantMap settings;
101+ QStringList removedKeys;
102+};
103+
104+struct AccountChanges {
105+ quint32 accountId;
106+ bool created;
107+ bool deleted;
108+ QString provider;
109+ QList<ServiceChanges> serviceChanges;
110+};
111+
112+struct PendingWrite {
113+ PendingWrite(const QDBusConnection &c, const QDBusMessage &m):
114+ message(m), connection(c) {}
115+ QDBusMessage message;
116+ QDBusConnection connection;
117+};
118+
119+class LibaccountsServicePrivate: public QObject
120+{
121+ Q_OBJECT
122+ Q_DECLARE_PUBLIC(LibaccountsService)
123+
124+public:
125+ LibaccountsServicePrivate(LibaccountsService *q);
126+ ~LibaccountsServicePrivate() {};
127+
128+ void writeChanges(const AccountChanges &changes);
129+
130+private Q_SLOTS:
131+ void onAccountSynced();
132+ void onAccountError(Accounts::Error error);
133+
134+private:
135+ Accounts::Manager m_manager;
136+ QHash<Accounts::Account *,PendingWrite> m_pendingWrites;
137+ mutable LibaccountsService *q_ptr;
138+};
139+
140+} // namespace
141+
142+LibaccountsServicePrivate::LibaccountsServicePrivate(LibaccountsService *q):
143+ QObject(q),
144+ m_manager(new Accounts::Manager(this)),
145+ q_ptr(q)
146+{
147+}
148+
149+void LibaccountsServicePrivate::writeChanges(const AccountChanges &changes)
150+{
151+ Q_Q(LibaccountsService);
152+
153+ Accounts::Account *account;
154+
155+ if (changes.created) {
156+ account = m_manager.createAccount(changes.provider);
157+ } else {
158+ account = m_manager.account(changes.accountId);
159+ if (Q_UNLIKELY(!account)) {
160+ qWarning() << "Couldn't load account" << changes.accountId;
161+ return;
162+ }
163+ }
164+
165+ Q_ASSERT(account);
166+
167+ if (changes.deleted) {
168+ account->remove();
169+ } else {
170+ Q_FOREACH(const ServiceChanges &sc, changes.serviceChanges) {
171+ if (sc.service == "global") {
172+ account->selectService();
173+ } else {
174+ Accounts::Service service = m_manager.service(sc.service);
175+ if (Q_UNLIKELY(!service.isValid())) {
176+ qWarning() << "Invalid service" << sc.service;
177+ continue;
178+ }
179+
180+ account->selectService(service);
181+ }
182+
183+ QMapIterator<QString, QVariant> it(sc.settings);
184+ while (it.hasNext()) {
185+ it.next();
186+ account->setValue(it.key(), it.value());
187+ }
188+
189+ Q_FOREACH(const QString &key, sc.removedKeys) {
190+ account->remove(key);
191+ }
192+ }
193+ }
194+
195+ m_pendingWrites.insert(account,
196+ PendingWrite(q->connection(), q->message()));
197+ QObject::connect(account, SIGNAL(synced()),
198+ this, SLOT(onAccountSynced()));
199+ QObject::connect(account, SIGNAL(error(Accounts::Error)),
200+ this, SLOT(onAccountError(Accounts::Error)));
201+ account->sync();
202+}
203+
204+void LibaccountsServicePrivate::onAccountSynced()
205+{
206+ Q_Q(LibaccountsService);
207+
208+ Accounts::Account *account = qobject_cast<Accounts::Account*>(sender());
209+ uint accountId = account->id();
210+ account->deleteLater();
211+
212+ QHash<Accounts::Account*,PendingWrite>::iterator i =
213+ m_pendingWrites.find(account);
214+ if (Q_LIKELY(i != m_pendingWrites.end())) {
215+ PendingWrite &w = i.value();
216+ w.connection.send(w.message.createReply(accountId));
217+ m_pendingWrites.erase(i);
218+ }
219+}
220+
221+void LibaccountsServicePrivate::onAccountError(Accounts::Error error)
222+{
223+ Q_Q(LibaccountsService);
224+
225+ Accounts::Account *account = qobject_cast<Accounts::Account*>(sender());
226+ account->deleteLater();
227+
228+ QHash<Accounts::Account*,PendingWrite>::iterator i =
229+ m_pendingWrites.find(account);
230+ if (Q_LIKELY(i != m_pendingWrites.end())) {
231+ PendingWrite &w = i.value();
232+ QDBusMessage reply =
233+ w.message.createErrorReply(QDBusError::InternalError,
234+ error.message());
235+ w.connection.send(reply);
236+ m_pendingWrites.erase(i);
237+ }
238+}
239+
240+LibaccountsService::LibaccountsService(QObject *parent):
241+ QObject(parent),
242+ d_ptr(new LibaccountsServicePrivate(this))
243+{
244+}
245+
246+LibaccountsService::~LibaccountsService()
247+{
248+ delete d_ptr;
249+}
250+
251+void LibaccountsService::store(const QDBusMessage &msg)
252+{
253+ Q_D(LibaccountsService);
254+
255+ DEBUG() << "Got request:" << msg;
256+
257+ /* The following line tells QtDBus not to generate a reply now */
258+ setDelayedReply(true);
259+
260+ AccountChanges changes;
261+
262+ // signature: "ubbsa(ssua{sv}as)"
263+ QList<QVariant> args = msg.arguments();
264+ int n = 0;
265+ changes.accountId = args.value(n++).toUInt();
266+ changes.created = args.value(n++).toBool();
267+ changes.deleted = args.value(n++).toBool();
268+ changes.provider = args.value(n++).toString();
269+
270+ /* before continuing demarshalling the arguments, check if the provider ID
271+ * matches the apparmor label of the peer; if it doesn't, we shouldn't
272+ * honour this request. */
273+ QString profile = apparmorProfileOfPeer(msg);
274+ if (stripVersion(profile) != changes.provider) {
275+ DEBUG() << "Declining AccountManager store request to" << profile <<
276+ "for provider" << changes.provider;
277+ QDBusMessage reply = msg.createErrorReply(QDBusError::AccessDenied,
278+ "Profile/provider mismatch");
279+ connection().send(reply);
280+ return;
281+ }
282+
283+ const QDBusArgument dbusChanges = args.value(n++).value<QDBusArgument>();
284+ dbusChanges.beginArray();
285+ while (!dbusChanges.atEnd()) {
286+ ServiceChanges sc;
287+ dbusChanges.beginStructure();
288+ dbusChanges >> sc.service;
289+ dbusChanges >> sc.serviceType;
290+ dbusChanges >> sc.serviceId;
291+ dbusChanges >> sc.settings;
292+ dbusChanges >> sc.removedKeys;
293+ dbusChanges.endStructure();
294+
295+ changes.serviceChanges.append(sc);
296+ }
297+ dbusChanges.endArray();
298+
299+ d->writeChanges(changes);
300+}
301+
302+#include "libaccounts-service.moc"
303
304=== added file 'online-accounts-service/libaccounts-service.h'
305--- online-accounts-service/libaccounts-service.h 1970-01-01 00:00:00 +0000
306+++ online-accounts-service/libaccounts-service.h 2015-01-16 17:10:14 +0000
307@@ -0,0 +1,69 @@
308+/*
309+ * Copyright (C) 2014 Canonical Ltd.
310+ *
311+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
312+ *
313+ * This file is part of online-accounts-ui
314+ *
315+ * This program is free software: you can redistribute it and/or modify it
316+ * under the terms of the GNU General Public License version 3, as published
317+ * by the Free Software Foundation.
318+ *
319+ * This program is distributed in the hope that it will be useful, but
320+ * WITHOUT ANY WARRANTY; without even the implied warranties of
321+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
322+ * PURPOSE. See the GNU General Public License for more details.
323+ *
324+ * You should have received a copy of the GNU General Public License along
325+ * with this program. If not, see <http://www.gnu.org/licenses/>.
326+ */
327+
328+#ifndef OAU_LIBACCOUNTS_SERVICE_H
329+#define OAU_LIBACCOUNTS_SERVICE_H
330+
331+#include <QDBusContext>
332+#include <QDBusMessage>
333+#include <QObject>
334+
335+namespace OnlineAccountsUi {
336+
337+#define LIBACCOUNTS_OBJECT_PATH \
338+ QStringLiteral("/com/google/code/AccountsSSO/Accounts/Manager")
339+#define LIBACCOUNTS_BUS_NAME \
340+ QStringLiteral("com.google.code.AccountsSSO.Accounts.Manager")
341+
342+class LibaccountsServicePrivate;
343+
344+class LibaccountsService: public QObject, protected QDBusContext
345+{
346+ Q_OBJECT
347+ Q_CLASSINFO("D-Bus Interface",
348+ "com.google.code.AccountsSSO.Accounts.Manager")
349+ Q_CLASSINFO("D-Bus Introspection", ""
350+" <interface name=\"com.google.code.AccountsSSO.Accounts.Manager\">\n"
351+" <method name=\"store\">\n"
352+" <arg direction=\"in\" type=\"u\" name=\"account_id\"/>\n"
353+" <arg direction=\"in\" type=\"b\"/>\n"
354+" <arg direction=\"in\" type=\"b\"/>\n"
355+" <arg direction=\"in\" type=\"s\"/>\n"
356+" <arg direction=\"in\" type=\"a(ssua{sv}as)\"/>\n"
357+" <arg direction=\"out\" type=\"u\" name=\"account_id\"/>\n"
358+" </method>\n"
359+" </interface>\n"
360+ "")
361+
362+public:
363+ explicit LibaccountsService(QObject *parent = 0);
364+ ~LibaccountsService();
365+
366+public Q_SLOTS:
367+ void store(const QDBusMessage &msg);
368+
369+private:
370+ LibaccountsServicePrivate *d_ptr;
371+ Q_DECLARE_PRIVATE(LibaccountsService)
372+};
373+
374+} // namespace
375+
376+#endif // OAU_LIBACCOUNTS_SERVICE_H
377
378=== modified file 'online-accounts-service/main.cpp'
379--- online-accounts-service/main.cpp 2014-08-19 12:11:24 +0000
380+++ online-accounts-service/main.cpp 2015-01-16 17:10:14 +0000
381@@ -22,6 +22,7 @@
382 #include "globals.h"
383 #include "inactivity-timer.h"
384 #include "indicator-service.h"
385+#include "libaccounts-service.h"
386 #include "request-manager.h"
387 #include "service.h"
388 #include "signonui-service.h"
389@@ -79,6 +80,10 @@
390 connection.registerObject(WEBCREDENTIALS_OBJECT_PATH,
391 indicatorService->serviceObject());
392
393+ LibaccountsService *libaccountsService = new LibaccountsService();
394+ connection.registerService(LIBACCOUNTS_BUS_NAME);
395+ connection.registerObject(LIBACCOUNTS_OBJECT_PATH, libaccountsService,
396+ QDBusConnection::ExportAllContents);
397
398 InactivityTimer *inactivityTimer = 0;
399 if (daemonTimeout > 0) {
400@@ -91,6 +96,10 @@
401
402 int ret = app.exec();
403
404+ connection.unregisterService(LIBACCOUNTS_BUS_NAME);
405+ connection.unregisterObject(LIBACCOUNTS_OBJECT_PATH);
406+ delete libaccountsService;
407+
408 connection.unregisterService(WEBCREDENTIALS_BUS_NAME);
409 connection.unregisterObject(WEBCREDENTIALS_OBJECT_PATH);
410 delete indicatorService;
411
412=== modified file 'online-accounts-service/online-accounts-service.pro'
413--- online-accounts-service/online-accounts-service.pro 2014-08-26 08:39:20 +0000
414+++ online-accounts-service/online-accounts-service.pro 2015-01-16 17:10:14 +0000
415@@ -14,6 +14,7 @@
416 network
417
418 PKGCONFIG += \
419+ accounts-qt5 \
420 libnotify \
421 libsignon-qt5 \
422 signon-plugins-common
423@@ -46,13 +47,15 @@
424 $${COMMON_SRC}/notification.cpp \
425 inactivity-timer.cpp \
426 indicator-service.cpp \
427+ libaccounts-service.cpp \
428 main.cpp \
429 reauthenticator.cpp \
430 request.cpp \
431 request-manager.cpp \
432 service.cpp \
433 signonui-service.cpp \
434- ui-proxy.cpp
435+ ui-proxy.cpp \
436+ utils.cpp
437
438 HEADERS += \
439 $${COMMON_SRC}/debug.h \
440@@ -61,13 +64,15 @@
441 $${COMMON_SRC}/notification.h \
442 inactivity-timer.h \
443 indicator-service.h \
444+ libaccounts-service.h \
445 mir-helper.h \
446 reauthenticator.h \
447 request.h \
448 request-manager.h \
449 service.h \
450 signonui-service.h \
451- ui-proxy.h
452+ ui-proxy.h \
453+ utils.h
454
455 QMAKE_SUBSTITUTES += \
456 com.ubuntu.OnlineAccountsUi.service.in
457
458=== modified file 'online-accounts-service/request-manager.cpp'
459--- online-accounts-service/request-manager.cpp 2014-10-13 10:48:54 +0000
460+++ online-accounts-service/request-manager.cpp 2015-01-16 17:10:14 +0000
461@@ -117,14 +117,16 @@
462 return; // Nothing to do
463 }
464
465+ QObject::connect(request, SIGNAL(completed()),
466+ this, SLOT(onRequestCompleted()));
467+
468 UiProxy *proxy = new UiProxy(request->clientPid(), this);
469 if (Q_UNLIKELY(!proxy->init())) {
470 qWarning() << "UiProxy initialization failed!";
471- runQueue(queue);
472+ request->fail(OAU_ERROR_PROMPT_SESSION,
473+ "Could not create a prompt session");
474 return;
475 }
476- QObject::connect(request, SIGNAL(completed()),
477- this, SLOT(onRequestCompleted()));
478 QObject::connect(proxy, SIGNAL(finished()),
479 this, SLOT(onProxyFinished()));
480 m_proxies.append(proxy);
481
482=== modified file 'online-accounts-service/request.cpp'
483--- online-accounts-service/request.cpp 2014-11-21 13:50:15 +0000
484+++ online-accounts-service/request.cpp 2015-01-16 17:10:14 +0000
485@@ -21,6 +21,7 @@
486 #include "debug.h"
487 #include "globals.h"
488 #include "request.h"
489+#include "utils.h"
490
491 #include <SignOn/uisessiondata_priv.h>
492
493@@ -58,9 +59,6 @@
494 }
495
496 private:
497- QString findClientApparmorProfile();
498-
499-private:
500 mutable Request *q_ptr;
501 QDBusConnection m_connection;
502 QDBusMessage m_message;
503@@ -84,41 +82,13 @@
504 m_inProgress(false),
505 m_delay(0)
506 {
507- m_clientApparmorProfile = findClientApparmorProfile();
508+ m_clientApparmorProfile = apparmorProfileOfPeer(message);
509 }
510
511 RequestPrivate::~RequestPrivate()
512 {
513 }
514
515-QString RequestPrivate::findClientApparmorProfile()
516-{
517- QString uniqueConnectionId = m_message.service();
518- /* This is mainly for unit tests: real messages on the session bus always
519- * have a service name. */
520- if (uniqueConnectionId.isEmpty()) return QString();
521-
522- QString appId;
523-
524- QDBusMessage msg =
525- QDBusMessage::createMethodCall("org.freedesktop.DBus",
526- "/org/freedesktop/DBus",
527- "org.freedesktop.DBus",
528- "GetConnectionAppArmorSecurityContext");
529- QVariantList args;
530- args << uniqueConnectionId;
531- msg.setArguments(args);
532- QDBusMessage reply = QDBusConnection::sessionBus().call(msg, QDBus::Block);
533- if (reply.type() == QDBusMessage::ReplyMessage) {
534- appId = reply.arguments().value(0, QString()).toString();
535- DEBUG() << "App ID:" << appId;
536- } else {
537- qWarning() << "Error getting app ID:" << reply.errorName() <<
538- reply.errorMessage();
539- }
540- return appId;
541-}
542-
543 Request::Request(const QDBusConnection &connection,
544 const QDBusMessage &message,
545 const QVariantMap &parameters,
546@@ -192,6 +162,16 @@
547 return d->m_message.interface();
548 }
549
550+QString Request::providerId() const
551+{
552+ Q_D(const Request);
553+ if (interface() == OAU_INTERFACE) {
554+ return d->m_parameters.value(OAU_KEY_PROVIDER, 0).toString();
555+ } else {
556+ return QString();
557+ }
558+}
559+
560 void Request::setDelay(int delay)
561 {
562 Q_D(Request);
563
564=== modified file 'online-accounts-service/request.h'
565--- online-accounts-service/request.h 2014-11-21 13:50:15 +0000
566+++ online-accounts-service/request.h 2015-01-16 17:10:14 +0000
567@@ -49,6 +49,7 @@
568 const QVariantMap &parameters() const;
569 QString clientApparmorProfile() const;
570 QString interface() const;
571+ QString providerId() const;
572
573 void setDelay(int delay);
574 int delay() const;
575
576=== modified file 'online-accounts-service/signonui-service.cpp'
577--- online-accounts-service/signonui-service.cpp 2014-08-22 11:31:24 +0000
578+++ online-accounts-service/signonui-service.cpp 2015-01-16 17:10:14 +0000
579@@ -156,15 +156,32 @@
580
581 QString ServicePrivate::rootDirForIdentity(quint32 id)
582 {
583+ /* the BrowserRequest class instructs the webview to store its cookies and
584+ * local data into
585+ * ~/.cache/online-accounts-ui/id-<signon-id>-<provider-id>/; while we
586+ * don't normally expect to find more than one entry for a given signon-id,
587+ * it's a possibility we cannot completely discard, since in older versions
588+ * we were not appending the "-<provider-id>" suffix. Besides, unless we
589+ * look up into the accounts DB, we don't know the value for the provider
590+ * ID.
591+ * Because of both of these reasons, we list all the directories whose name
592+ * starts with "id-<signon-id>" and pick the most recent one.
593+ */
594 QString cachePath =
595 QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
596- return cachePath + QString("/online-accounts-ui/id-%1").arg(id);
597+ QDir cacheDir(cachePath + QStringLiteral("/online-accounts-ui"));
598+ QString nameFilter = QString("id-%1*").arg(id);
599+ QStringList rootDirs = cacheDir.entryList(QStringList() << nameFilter,
600+ QDir::Dirs, QDir::Time);
601+ return rootDirs.count() > 0 ? cacheDir.filePath(rootDirs.at(0)) : QString();
602 }
603
604 void ServicePrivate::removeIdentityData(quint32 id)
605 {
606 /* Remove any data associated with the given identity. */
607- QDir rootDir(ServicePrivate::rootDirForIdentity(id));
608+ QString rootDirName = rootDirForIdentity(id);
609+ if (rootDirName.isEmpty()) return;
610+ QDir rootDir(rootDirName);
611 rootDir.removeRecursively();
612 }
613
614
615=== modified file 'online-accounts-service/ui-proxy.cpp'
616--- online-accounts-service/ui-proxy.cpp 2014-11-21 13:50:15 +0000
617+++ online-accounts-service/ui-proxy.cpp 2015-01-16 17:10:14 +0000
618@@ -24,7 +24,11 @@
619 #include "request.h"
620 #include "ui-proxy.h"
621
622+#include <Accounts/Manager>
623+#include <Accounts/Provider>
624 #include <QDir>
625+#include <QDomDocument>
626+#include <QDomElement>
627 #include <QFileInfo>
628 #include <QLocalServer>
629 #include <QLocalSocket>
630@@ -49,11 +53,14 @@
631 inline UiProxyPrivate(pid_t clientPid, UiProxy *pluginProxy);
632 inline ~UiProxyPrivate();
633
634+ void setStatus(UiProxy::Status status);
635 bool setupSocket();
636 bool init();
637 void sendOperation(const QVariantMap &data);
638 void sendRequest(int requestId, Request *request);
639- void setupPromptSession();
640+ bool setupPromptSession();
641+ QString findAppArmorProfile();
642+ void startProcess();
643
644 private Q_SLOTS:
645 void onNewConnection();
646@@ -63,6 +70,7 @@
647
648 private:
649 QProcess m_process;
650+ UiProxy::Status m_status;
651 QLocalServer m_server;
652 QLocalSocket *m_socket;
653 OnlineAccountsUi::Ipc m_ipc;
654@@ -71,7 +79,9 @@
655 QMap<int,Request*> m_requests;
656 QStringList m_handlers;
657 pid_t m_clientPid;
658+ QString m_providerId;
659 PromptSessionP m_promptSession;
660+ QStringList m_arguments;
661 mutable UiProxy *q_ptr;
662 };
663
664@@ -79,6 +89,7 @@
665
666 UiProxyPrivate::UiProxyPrivate(pid_t clientPid, UiProxy *uiProxy):
667 QObject(uiProxy),
668+ m_status(UiProxy::Null),
669 m_socket(0),
670 m_nextRequestId(0),
671 m_clientPid(clientPid),
672@@ -109,6 +120,14 @@
673 m_server.close();
674 }
675
676+void UiProxyPrivate::setStatus(UiProxy::Status status)
677+{
678+ Q_Q(UiProxy);
679+ if (m_status == status) return;
680+ m_status = status;
681+ Q_EMIT q->statusChanged();
682+}
683+
684 void UiProxyPrivate::sendOperation(const QVariantMap &data)
685 {
686 QByteArray ba;
687@@ -136,6 +155,8 @@
688 m_ipc.setChannels(socket, socket);
689 m_server.close(); // stop listening
690
691+ setStatus(UiProxy::Ready);
692+
693 /* Execute any pending requests */
694 QMapIterator<int, Request*> it(m_requests);
695 while (it.hasNext()) {
696@@ -178,7 +199,7 @@
697 QDir socketDir(runtimeDir + "/online-accounts-ui");
698 if (!socketDir.exists()) socketDir.mkpath(".");
699
700- QString uniqueName = QString("ui-%1").arg(socketCounter++);
701+ QString uniqueName = QString("ui-%1-%2").arg(socketCounter++).arg(m_providerId);
702
703 /* If the file exists, it's a stale file: online-accounts-ui is a single
704 * instance process, and the only one creating files in this directory. */
705@@ -190,57 +211,114 @@
706 return m_server.listen(socketDir.filePath(uniqueName));
707 }
708
709-void UiProxyPrivate::setupPromptSession()
710+bool UiProxyPrivate::setupPromptSession()
711 {
712 Q_Q(UiProxy);
713
714- QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
715- if (!env.value("QT_QPA_PLATFORM").startsWith("ubuntu")) return;
716-
717 PromptSessionP session =
718 MirHelper::instance()->createPromptSession(m_clientPid);
719- if (!session) return;
720+ if (!session) return false;
721
722 QString mirSocket = session->requestSocket();
723 if (mirSocket.isEmpty()) {
724- return;
725+ return false;
726 }
727
728 m_promptSession = session;
729 QObject::connect(m_promptSession.data(), SIGNAL(finished()),
730 q, SIGNAL(finished()));
731
732+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
733 env.insert("MIR_SOCKET", mirSocket);
734 m_process.setProcessEnvironment(env);
735+ return true;
736 }
737
738 bool UiProxyPrivate::init()
739 {
740- if (Q_UNLIKELY(!setupSocket())) return false;
741+ m_arguments.clear();
742+ if (!m_promptSession) {
743+ /* the first argument is required to be the desktop file */
744+ m_arguments.append("--desktop_file_hint=/usr/share/applications/online-accounts-ui.desktop");
745+ }
746+
747+ if (m_clientPid) {
748+ setupPromptSession();
749+ }
750+
751+ return true;
752+}
753+
754+QString UiProxyPrivate::findAppArmorProfile()
755+{
756+ if (Q_UNLIKELY(m_providerId.isEmpty())) return QString();
757+
758+ /* Load the provider XML file */
759+ Accounts::Manager manager;
760+ Accounts::Provider provider = manager.provider(m_providerId);
761+ if (Q_UNLIKELY(!provider.isValid())) {
762+ qWarning() << "Provider not found:" << m_providerId;
763+ return QString();
764+ }
765+
766+ const QDomDocument doc = provider.domDocument();
767+ QDomElement root = doc.documentElement();
768+ return root.firstChildElement("profile").text();
769+}
770+
771+void UiProxyPrivate::startProcess()
772+{
773+ if (Q_UNLIKELY(!setupSocket())) {
774+ qWarning() << "Couldn't setup IPC socket";
775+ setStatus(UiProxy::Error);
776+ return;
777+ }
778+ m_arguments.append("--socket");
779+ m_arguments.append(m_server.fullServerName());
780+
781+ QString profile = findAppArmorProfile();
782+ if (profile.isEmpty()) {
783+ profile = "unconfined";
784+ } else {
785+ /* Set TMPDIR to a location which the confined process can actually
786+ * use */
787+ QString tmpdir =
788+ QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) +
789+ "/" + profile.split('_')[0];
790+ QProcessEnvironment env = m_process.processEnvironment();
791+ env.insert("TMPDIR", tmpdir);
792+ m_process.setProcessEnvironment(env);
793+ }
794+
795+ m_arguments.append("--profile");
796+ m_arguments.append(profile);
797
798 QString processName;
799- QStringList arguments;
800 QString wrapper = QString::fromUtf8(qgetenv("OAU_WRAPPER"));
801 QString accountsUi = QStringLiteral(INSTALL_BIN_DIR "/online-accounts-ui");
802 if (wrapper.isEmpty()) {
803 processName = accountsUi;
804 } else {
805 processName = wrapper;
806- arguments.append(accountsUi);
807- }
808- if (!m_promptSession) {
809- /* the first argument is required to be the desktop file */
810- arguments.append("--desktop_file_hint=/usr/share/applications/online-accounts-ui.desktop");
811- }
812- arguments.append("--socket");
813- arguments.append(m_server.fullServerName());
814-
815- if (m_clientPid) {
816- setupPromptSession();
817- }
818-
819- m_process.start(processName, arguments);
820- return m_process.waitForStarted();
821+ m_arguments.prepend(accountsUi);
822+ }
823+
824+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
825+ if (env.value("QT_QPA_PLATFORM").startsWith("ubuntu")) {
826+ if (!setupPromptSession()) {
827+ qWarning() << "Couldn't setup prompt session";
828+ setStatus(UiProxy::Error);
829+ return;
830+ }
831+ }
832+
833+ setStatus(UiProxy::Loading);
834+ m_process.start(processName, m_arguments);
835+ if (Q_UNLIKELY(!m_process.waitForStarted())) {
836+ qWarning() << "Couldn't start account plugin process";
837+ setStatus(UiProxy::Error);
838+ return;
839+ }
840 }
841
842 void UiProxyPrivate::sendRequest(int requestId, Request *request)
843@@ -285,6 +363,13 @@
844
845 UiProxy::~UiProxy()
846 {
847+ DEBUG();
848+}
849+
850+UiProxy::Status UiProxy::status() const
851+{
852+ Q_D(const UiProxy);
853+ return d->m_status;
854 }
855
856 bool UiProxy::init()
857@@ -297,14 +382,19 @@
858 {
859 Q_D(UiProxy);
860
861+ if (d->m_providerId.isEmpty()) {
862+ d->m_providerId = request->providerId();
863+ }
864 int requestId = d->m_nextRequestId++;
865 d->m_requests.insert(requestId, request);
866 QObject::connect(request, SIGNAL(completed()),
867 d, SLOT(onRequestCompleted()));
868 request->setInProgress(true);
869
870- if (d->m_socket && d->m_socket->isValid()) {
871+ if (d->m_status == UiProxy::Ready) {
872 d->sendRequest(requestId, request);
873+ } else if (d->m_status == UiProxy::Null) {
874+ d->startProcess();
875 }
876 }
877
878
879=== modified file 'online-accounts-service/ui-proxy.h'
880--- online-accounts-service/ui-proxy.h 2014-08-12 12:33:10 +0000
881+++ online-accounts-service/ui-proxy.h 2015-01-16 17:10:14 +0000
882@@ -37,11 +37,15 @@
883 explicit UiProxy(pid_t clientPid, QObject *parent = 0);
884 ~UiProxy();
885
886+ enum Status { Null, Ready, Loading, Error };
887+ Status status() const;
888+
889 bool init();
890 void handleRequest(Request *request);
891 bool hasHandlerFor(const QVariantMap &parameters);
892
893 Q_SIGNALS:
894+ void statusChanged();
895 void finished();
896
897 private:
898
899=== added file 'online-accounts-service/utils.cpp'
900--- online-accounts-service/utils.cpp 1970-01-01 00:00:00 +0000
901+++ online-accounts-service/utils.cpp 2015-01-16 17:10:14 +0000
902@@ -0,0 +1,57 @@
903+/*
904+ * Copyright (C) 2014 Canonical Ltd.
905+ *
906+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
907+ *
908+ * This file is part of online-accounts-ui
909+ *
910+ * This program is free software: you can redistribute it and/or modify it
911+ * under the terms of the GNU General Public License version 3, as published
912+ * by the Free Software Foundation.
913+ *
914+ * This program is distributed in the hope that it will be useful, but
915+ * WITHOUT ANY WARRANTY; without even the implied warranties of
916+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
917+ * PURPOSE. See the GNU General Public License for more details.
918+ *
919+ * You should have received a copy of the GNU General Public License along
920+ * with this program. If not, see <http://www.gnu.org/licenses/>.
921+ */
922+
923+#include "debug.h"
924+#include "utils.h"
925+
926+#include <QDBusConnection>
927+#include <QDBusMessage>
928+
929+namespace OnlineAccountsUi {
930+
931+QString apparmorProfileOfPeer(const QDBusMessage &message)
932+{
933+ QString uniqueConnectionId = message.service();
934+ /* This is mainly for unit tests: real messages on the session bus always
935+ * have a service name. */
936+ if (uniqueConnectionId.isEmpty()) return QString();
937+
938+ QString appId;
939+
940+ QDBusMessage msg =
941+ QDBusMessage::createMethodCall("org.freedesktop.DBus",
942+ "/org/freedesktop/DBus",
943+ "org.freedesktop.DBus",
944+ "GetConnectionAppArmorSecurityContext");
945+ QVariantList args;
946+ args << uniqueConnectionId;
947+ msg.setArguments(args);
948+ QDBusMessage reply = QDBusConnection::sessionBus().call(msg, QDBus::Block);
949+ if (reply.type() == QDBusMessage::ReplyMessage) {
950+ appId = reply.arguments().value(0, QString()).toString();
951+ DEBUG() << "App ID:" << appId;
952+ } else {
953+ qWarning() << "Error getting app ID:" << reply.errorName() <<
954+ reply.errorMessage();
955+ }
956+ return appId;
957+}
958+
959+} // namespace
960
961=== added file 'online-accounts-service/utils.h'
962--- online-accounts-service/utils.h 1970-01-01 00:00:00 +0000
963+++ online-accounts-service/utils.h 2015-01-16 17:10:14 +0000
964@@ -0,0 +1,34 @@
965+/*
966+ * Copyright (C) 2014 Canonical Ltd.
967+ *
968+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
969+ *
970+ * This file is part of online-accounts-ui
971+ *
972+ * This program is free software: you can redistribute it and/or modify it
973+ * under the terms of the GNU General Public License version 3, as published
974+ * by the Free Software Foundation.
975+ *
976+ * This program is distributed in the hope that it will be useful, but
977+ * WITHOUT ANY WARRANTY; without even the implied warranties of
978+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
979+ * PURPOSE. See the GNU General Public License for more details.
980+ *
981+ * You should have received a copy of the GNU General Public License along
982+ * with this program. If not, see <http://www.gnu.org/licenses/>.
983+ */
984+
985+#ifndef OAU_UTILS_H
986+#define OAU_UTILS_H
987+
988+#include <QString>
989+
990+class QDBusMessage;
991+
992+namespace OnlineAccountsUi {
993+
994+QString apparmorProfileOfPeer(const QDBusMessage &message);
995+
996+} // namespace
997+
998+#endif // OAU_UTILS_H
999
1000=== modified file 'online-accounts-ui/browser-request.cpp'
1001--- online-accounts-ui/browser-request.cpp 2014-10-09 12:24:45 +0000
1002+++ online-accounts-ui/browser-request.cpp 2015-01-16 17:10:14 +0000
1003@@ -70,7 +70,7 @@
1004 QUrl responseUrl() const { return m_responseUrl; }
1005 QUrl rootDir() const { return QUrl::fromLocalFile(m_rootDir); }
1006
1007- static QString rootDirForIdentity(quint32 id);
1008+ QString rootDirForIdentity() const;
1009
1010 public Q_SLOTS:
1011 void setCookies(const QVariant &cookies);
1012@@ -120,11 +120,12 @@
1013 delete m_dialog;
1014 }
1015
1016-QString BrowserRequestPrivate::rootDirForIdentity(quint32 id)
1017+QString BrowserRequestPrivate::rootDirForIdentity() const
1018 {
1019+ Q_Q(const BrowserRequest);
1020 QString cachePath =
1021 QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
1022- return cachePath + QString("/id-%1").arg(id);
1023+ return cachePath + QString("/id-%1-%2").arg(q->identity()).arg(q->providerId());
1024 }
1025
1026 void BrowserRequestPrivate::start()
1027@@ -134,7 +135,7 @@
1028 const QVariantMap &params = q->parameters();
1029 DEBUG() << params;
1030
1031- QDir rootDir(rootDirForIdentity(q->identity()));
1032+ QDir rootDir(rootDirForIdentity());
1033 if (!rootDir.exists()) {
1034 rootDir.mkpath(".");
1035 }
1036
1037=== modified file 'online-accounts-ui/globals.h'
1038--- online-accounts-ui/globals.h 2014-09-15 12:16:50 +0000
1039+++ online-accounts-ui/globals.h 2015-01-16 17:10:14 +0000
1040@@ -40,6 +40,8 @@
1041 QStringLiteral(OAU_ERROR_PREFIX "InvalidParameters")
1042 #define OAU_ERROR_INVALID_APPLICATION \
1043 QStringLiteral(OAU_ERROR_PREFIX "InvalidApplication")
1044+#define OAU_ERROR_PROMPT_SESSION \
1045+ QStringLiteral(OAU_ERROR_PREFIX "NoPromptSession")
1046
1047 /* SignOnUi service */
1048 #define SIGNONUI_SERVICE_NAME QStringLiteral("com.nokia.singlesignonui")
1049
1050=== modified file 'online-accounts-ui/main.cpp'
1051--- online-accounts-ui/main.cpp 2014-12-09 10:10:25 +0000
1052+++ online-accounts-ui/main.cpp 2015-01-16 17:10:14 +0000
1053@@ -32,6 +32,7 @@
1054 #else
1055 #include <QtGui/private/qopenglcontext_p.h>
1056 #endif
1057+#include <sys/apparmor.h>
1058
1059 using namespace OnlineAccountsUi;
1060
1061@@ -81,14 +82,27 @@
1062 QOpenGLContextPrivate::setGlobalShareContext(glcontext);
1063 #endif
1064
1065+ QString socket;
1066+ QString profile;
1067 QStringList arguments = app.arguments();
1068- int i = arguments.indexOf("--socket");
1069- if (i < 0 || i + 1 >= arguments.count()) {
1070+ for (int i = 0; i < arguments.count(); i++) {
1071+ const QString &arg = arguments[i];
1072+ if (arg == "--socket") {
1073+ socket = arguments.value(++i);
1074+ } else if (arg == "--profile") {
1075+ profile = arguments.value(++i);
1076+ }
1077+ }
1078+ if (Q_UNLIKELY(socket.isEmpty())) {
1079 qWarning() << "Missing --socket argument";
1080 return EXIT_FAILURE;
1081 }
1082
1083- UiServer server(arguments[i + 1]);
1084+ if (!profile.isEmpty()) {
1085+ aa_change_profile(profile.toUtf8().constData());
1086+ }
1087+
1088+ UiServer server(socket);
1089 QObject::connect(&server, SIGNAL(finished()),
1090 &app, SLOT(quit()));
1091 if (Q_UNLIKELY(!server.init())) {
1092
1093=== modified file 'online-accounts-ui/online-accounts-ui.pro'
1094--- online-accounts-ui/online-accounts-ui.pro 2014-10-31 12:05:37 +0000
1095+++ online-accounts-ui/online-accounts-ui.pro 2015-01-16 17:10:14 +0000
1096@@ -52,7 +52,6 @@
1097 i18n.cpp \
1098 ipc.cpp \
1099 main.cpp \
1100- notification.cpp \
1101 panel-request.cpp \
1102 provider-request.cpp \
1103 request.cpp \
1104@@ -67,7 +66,6 @@
1105 dialog-request.h \
1106 i18n.h \
1107 ipc.h \
1108- notification.h \
1109 panel-request.h \
1110 provider-request.h \
1111 request.h \
1112
1113=== modified file 'online-accounts-ui/signonui-request.cpp'
1114--- online-accounts-ui/signonui-request.cpp 2014-10-14 09:05:52 +0000
1115+++ online-accounts-ui/signonui-request.cpp 2015-01-16 17:10:14 +0000
1116@@ -24,13 +24,9 @@
1117 #include "debug.h"
1118 #include "dialog-request.h"
1119 #include "globals.h"
1120-#include "notification.h"
1121
1122 #include <Accounts/Account>
1123-#include <Accounts/Application>
1124-#include <Accounts/Provider>
1125 #include <OnlineAccountsPlugin/account-manager.h>
1126-#include <OnlineAccountsPlugin/application-manager.h>
1127 #include <OnlineAccountsPlugin/request-handler.h>
1128 #include <QDBusArgument>
1129 #include <QPointer>
1130@@ -52,19 +48,13 @@
1131 ~RequestPrivate();
1132
1133 private:
1134- void setWindow(QWindow *window);
1135 Accounts::Account *findAccount();
1136
1137-private Q_SLOTS:
1138- void onActionInvoked(const QString &action);
1139- void onNotificationClosed();
1140-
1141 private:
1142 mutable Request *q_ptr;
1143 QVariantMap m_clientData;
1144 QPointer<RequestHandler> m_handler;
1145- OnlineAccountsUi::Notification *m_notification;
1146- QWindow *m_window;
1147+ Accounts::Account *m_account;
1148 };
1149
1150 } // namespace
1151@@ -72,9 +62,7 @@
1152 RequestPrivate::RequestPrivate(Request *request):
1153 QObject(request),
1154 q_ptr(request),
1155- m_handler(0),
1156- m_notification(0),
1157- m_window(0)
1158+ m_handler(0)
1159 {
1160 const QVariantMap &parameters = request->parameters();
1161 if (parameters.contains(SSOUI_KEY_CLIENT_DATA)) {
1162@@ -83,55 +71,12 @@
1163 variant.toMap() :
1164 qdbus_cast<QVariantMap>(variant.value<QDBusArgument>());
1165 }
1166+
1167+ m_account = findAccount();
1168 }
1169
1170 RequestPrivate::~RequestPrivate()
1171 {
1172- delete m_notification;
1173- m_notification = 0;
1174-}
1175-
1176-void RequestPrivate::setWindow(QWindow *window)
1177-{
1178- Q_Q(Request);
1179-
1180- /* Don't show the window yet: the user must be presented with a
1181- * snap-decision, and we'll show the window only if he decides to
1182- * authenticate. */
1183- Accounts::Account *account = findAccount();
1184- if (Q_UNLIKELY(!account)) {
1185- QVariantMap result;
1186- result[SSOUI_KEY_ERROR] = SignOn::QUERY_ERROR_FORBIDDEN;
1187- q->setResult(result);
1188- return;
1189- }
1190-
1191- OnlineAccountsUi::ApplicationManager *appManager =
1192- OnlineAccountsUi::ApplicationManager::instance();
1193- Accounts::Application application =
1194- appManager->applicationFromProfile(q->clientApparmorProfile());
1195-
1196- OnlineAccountsUi::AccountManager *accountManager =
1197- OnlineAccountsUi::AccountManager::instance();
1198- Accounts::Provider provider =
1199- accountManager->provider(account->providerName());
1200-
1201- QString summary =
1202- QString("Please authorize %1 to access your %2 account %3").
1203- arg(application.isValid() ? application.displayName() : "Ubuntu").
1204- arg(provider.displayName()).
1205- arg(account->displayName());
1206- m_notification =
1207- new OnlineAccountsUi::Notification("Authentication request", summary);
1208- m_notification->addAction("cancel", "Cancel");
1209- m_notification->addAction("continue", "Authorize...");
1210- m_notification->setSnapDecision(true);
1211- QObject::connect(m_notification, SIGNAL(actionInvoked(const QString &)),
1212- this, SLOT(onActionInvoked(const QString &)));
1213- QObject::connect(m_notification, SIGNAL(closed()),
1214- this, SLOT(onNotificationClosed()));
1215- m_notification->show();
1216- m_window = window;
1217 }
1218
1219 Accounts::Account *RequestPrivate::findAccount()
1220@@ -162,39 +107,6 @@
1221 return 0;
1222 }
1223
1224-void RequestPrivate::onActionInvoked(const QString &action)
1225-{
1226- Q_Q(Request);
1227-
1228- DEBUG() << action;
1229-
1230- QObject::disconnect(m_notification, 0, this, 0);
1231- m_notification->deleteLater();
1232- m_notification = 0;
1233-
1234- if (action == QStringLiteral("continue")) {
1235- q->setWindow(m_window);
1236- } else {
1237- q->cancel();
1238- }
1239-}
1240-
1241-void RequestPrivate::onNotificationClosed()
1242-{
1243- Q_Q(Request);
1244-
1245- DEBUG();
1246-
1247- /* setResult() should have been called by onActionInvoked(), but calling it
1248- * twice won't harm because only the first invocation counts. */
1249- QVariantMap result;
1250- result[SSOUI_KEY_ERROR] = SignOn::QUERY_ERROR_FORBIDDEN;
1251- q->setResult(result);
1252-
1253- m_notification->deleteLater();
1254- m_notification = 0;
1255-}
1256-
1257 #ifndef NO_REQUEST_FACTORY
1258 Request *Request::newRequest(int id,
1259 const QString &clientProfile,
1260@@ -266,22 +178,13 @@
1261 {
1262 Q_D(Request);
1263
1264- /* While a notification is shown, ignore any further calls to
1265- * setWindow(). */
1266- if (d->m_notification) return;
1267-
1268- /* The first time that this method is called, we handle it by presenting a
1269- * snap decision to the user.
1270- * Then, if this is called again with the same QWindow, it means that the
1271- * snap decision was accepted, and we show the window.
1272- */
1273- if (window == d->m_window ||
1274- /* If we are part of a prompt session, just show the window */
1275- !qgetenv("MIR_SOCKET").isEmpty()) {
1276+ /* Show the window only if we are in a prompt session */
1277+ if (qgetenv("MIR_SOCKET").isEmpty()) {
1278+ QVariantMap result;
1279+ result[SSOUI_KEY_ERROR] = SignOn::QUERY_ERROR_FORBIDDEN;
1280+ setResult(result);
1281+ } else {
1282 OnlineAccountsUi::Request::setWindow(window);
1283- d->m_window = 0;
1284- } else {
1285- d->setWindow(window);
1286 }
1287 }
1288
1289@@ -300,6 +203,13 @@
1290 return parameters().value(SSOUI_KEY_MECHANISM).toString();
1291 }
1292
1293+QString Request::providerId() const
1294+{
1295+ Q_D(const Request);
1296+ return d->m_account ? d->m_account->providerName() :
1297+ d->m_clientData.value("providerId").toString();
1298+}
1299+
1300 const QVariantMap &Request::clientData() const
1301 {
1302 Q_D(const Request);
1303
1304=== modified file 'online-accounts-ui/signonui-request.h'
1305--- online-accounts-ui/signonui-request.h 2014-08-08 11:37:56 +0000
1306+++ online-accounts-ui/signonui-request.h 2015-01-16 17:10:14 +0000
1307@@ -45,6 +45,7 @@
1308 uint identity() const;
1309 QString method() const;
1310 QString mechanism() const;
1311+ QString providerId() const;
1312
1313 const QVariantMap &clientData() const;
1314
1315
1316=== modified file 'plugins/module/OAuth.qml'
1317--- plugins/module/OAuth.qml 2014-06-10 14:48:22 +0000
1318+++ plugins/module/OAuth.qml 2015-01-16 17:10:14 +0000
1319@@ -169,6 +169,7 @@
1320 if (creds.credentialsId == 0) return
1321 var parameters = {}
1322 parameters[requestHandler.matchKey] = requestHandler.matchId
1323+ parameters["providerId"] = account.provider.id
1324 for (var p in authenticationParameters) {
1325 parameters[p] = authenticationParameters[p]
1326 }
1327
1328=== modified file 'plugins/module/ServiceItem.qml'
1329--- plugins/module/ServiceItem.qml 2014-09-30 13:30:39 +0000
1330+++ plugins/module/ServiceItem.qml 2015-01-16 17:10:14 +0000
1331@@ -21,7 +21,7 @@
1332 import Ubuntu.Components.ListItems 0.1 as ListItem
1333 import Ubuntu.OnlineAccounts 0.1
1334
1335-Item {
1336+Column {
1337 property variant accountServiceHandle
1338
1339 signal applicationAdded(string applicationId)
1340@@ -29,7 +29,6 @@
1341
1342 anchors.left: parent.left
1343 anchors.right: parent.right
1344- height: childrenRect.height
1345
1346 Repeater {
1347 resources: AccountService {
1348
1349=== modified file 'plugins/plugins.pro'
1350--- plugins/plugins.pro 2014-10-03 14:56:11 +0000
1351+++ plugins/plugins.pro 2015-01-16 17:10:14 +0000
1352@@ -1,6 +1,7 @@
1353 TEMPLATE = subdirs
1354-CONFIG += ordered
1355 SUBDIRS = \
1356 OnlineAccountsPlugin \
1357 module \
1358 example
1359+
1360+module.depends = OnlineAccountsPlugin
1361
1362=== modified file 'tests/online-accounts-service/mock/request-mock.cpp'
1363--- tests/online-accounts-service/mock/request-mock.cpp 2014-11-25 13:34:16 +0000
1364+++ tests/online-accounts-service/mock/request-mock.cpp 2015-01-16 17:10:14 +0000
1365@@ -48,6 +48,11 @@
1366 m_clientApparmorProfile = profile;
1367 }
1368
1369+void RequestPrivate::setProviderId(const QString &provider)
1370+{
1371+ m_providerId = provider;
1372+}
1373+
1374 Request::Request(const QDBusConnection &connection,
1375 const QDBusMessage &message,
1376 const QVariantMap &parameters,
1377@@ -96,6 +101,11 @@
1378 return d->m_message.interface();
1379 }
1380
1381+QString Request::providerId() const {
1382+ Q_D(const Request);
1383+ return d->m_providerId;
1384+}
1385+
1386 void Request::setDelay(int delay)
1387 {
1388 Q_D(Request);
1389
1390=== modified file 'tests/online-accounts-service/mock/request-mock.h'
1391--- tests/online-accounts-service/mock/request-mock.h 2014-11-25 13:34:16 +0000
1392+++ tests/online-accounts-service/mock/request-mock.h 2015-01-16 17:10:14 +0000
1393@@ -43,6 +43,7 @@
1394 static RequestPrivate *mocked(Request *r) { return r->d_ptr; }
1395
1396 void setClientApparmorProfile(const QString &profile);
1397+ void setProviderId(const QString &provider);
1398
1399 Q_SIGNALS:
1400 void cancelCalled();
1401@@ -54,6 +55,7 @@
1402 QDBusMessage m_message;
1403 QVariantMap m_parameters;
1404 QString m_clientApparmorProfile;
1405+ QString m_providerId;
1406 bool m_inProgress;
1407 int m_delay;
1408 mutable Request *q_ptr;
1409
1410=== modified file 'tests/online-accounts-service/online-accounts-service.pro'
1411--- tests/online-accounts-service/online-accounts-service.pro 2014-08-25 14:54:38 +0000
1412+++ tests/online-accounts-service/online-accounts-service.pro 2015-01-16 17:10:14 +0000
1413@@ -1,6 +1,7 @@
1414 TEMPLATE = subdirs
1415 SUBDIRS = \
1416 tst_inactivity_timer.pro \
1417+ tst_libaccounts_service.pro \
1418 tst_service.pro \
1419 tst_signonui_service.pro \
1420 tst_ui_proxy.pro
1421
1422=== added file 'tests/online-accounts-service/tst_libaccounts_service.cpp'
1423--- tests/online-accounts-service/tst_libaccounts_service.cpp 1970-01-01 00:00:00 +0000
1424+++ tests/online-accounts-service/tst_libaccounts_service.cpp 2015-01-16 17:10:14 +0000
1425@@ -0,0 +1,522 @@
1426+/*
1427+ * Copyright (C) 2013 Canonical Ltd.
1428+ *
1429+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
1430+ *
1431+ * This file is part of online-accounts-ui
1432+ *
1433+ * This program is free software: you can redistribute it and/or modify it
1434+ * under the terms of the GNU General Public License version 3, as published
1435+ * by the Free Software Foundation.
1436+ *
1437+ * This program is distributed in the hope that it will be useful, but
1438+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1439+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1440+ * PURPOSE. See the GNU General Public License for more details.
1441+ *
1442+ * You should have received a copy of the GNU General Public License along
1443+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1444+ */
1445+
1446+#include "debug.h"
1447+#include "libaccounts-service.h"
1448+
1449+#include <Accounts/Account>
1450+#include <Accounts/Manager>
1451+#include <Accounts/Service>
1452+#include <QDBusConnection>
1453+#include <QDebug>
1454+#include <QProcess>
1455+#include <QSignalSpy>
1456+#include <QString>
1457+#include <QTest>
1458+
1459+using namespace OnlineAccountsUi;
1460+
1461+#define TEST_SERVICE_NAME \
1462+ "com.ubuntu.OnlineAccountsUi.LibaccountsService.Test"
1463+#define TEST_OBJECT_PATH "/"
1464+
1465+class LibaccountsServiceTest: public QObject
1466+{
1467+ Q_OBJECT
1468+
1469+public:
1470+ LibaccountsServiceTest();
1471+
1472+private:
1473+ QProcess *requestStore(const QString &args, bool showError = false) {
1474+ QString command = QStringLiteral("gdbus call --session "
1475+ "--dest " TEST_SERVICE_NAME " "
1476+ "--object-path " TEST_OBJECT_PATH " "
1477+ "--method com.google.code.AccountsSSO.Accounts.Manager.store ");
1478+ QProcess *process = new QProcess(this);
1479+ if (showError) {
1480+ process->setProcessChannelMode(QProcess::ForwardedErrorChannel);
1481+ }
1482+ process->start(command + args);
1483+ process->waitForStarted();
1484+ return process;
1485+ }
1486+
1487+private Q_SLOTS:
1488+ void init();
1489+ void testProfile_data();
1490+ void testProfile();
1491+ void testFailure();
1492+ void testAccount_data();
1493+ void testAccount();
1494+ void testSettings_data();
1495+ void testSettings();
1496+
1497+private:
1498+ LibaccountsService m_service;
1499+};
1500+
1501+/* Mocking libaccounts-qt { */
1502+class ManagerController {
1503+public:
1504+ ManagerController(): lastLoadedAccount(0) { m_instance = this; }
1505+ ~ManagerController() { m_instance = 0; }
1506+ static ManagerController *instance() { return m_instance; }
1507+
1508+ void setServices(const QStringList &services) { m_services = services; }
1509+
1510+public:
1511+ Accounts::Account *lastLoadedAccount;
1512+
1513+private:
1514+ friend class Accounts::Manager;
1515+ QStringList m_services;
1516+ static ManagerController *m_instance;
1517+};
1518+
1519+ManagerController *ManagerController::m_instance = 0;
1520+
1521+typedef QHash<QString,QVariantMap> ServiceSettings;
1522+typedef QHash<QString,QSet<QString> > RemovedKeys;
1523+
1524+class AccountController: public QObject {
1525+ Q_OBJECT
1526+public:
1527+ AccountController(Accounts::Account *account):
1528+ QObject(account),
1529+ m_id(0),
1530+ m_wasDeleted(false),
1531+ m_syncWasCalled(false),
1532+ m_account(account) {
1533+ m_controllers[account] = this;
1534+ }
1535+
1536+ static AccountController *mock(Accounts::Account *account) {
1537+ return m_controllers[account];
1538+ }
1539+
1540+ bool syncWasCalled() const { return m_syncWasCalled; }
1541+ void doSync(Accounts::Error error = Accounts::Error()) {
1542+ if (error.type() == Accounts::Error::NoError) {
1543+ QMetaObject::invokeMethod(m_account, "synced", Qt::QueuedConnection);
1544+ } else {
1545+ QMetaObject::invokeMethod(m_account, "error", Qt::QueuedConnection,
1546+ Q_ARG(Accounts::Error, error));
1547+ }
1548+ }
1549+
1550+protected:
1551+ void syncCalled() { m_syncWasCalled = true; }
1552+
1553+public:
1554+ quint32 m_id;
1555+ QString m_provider;
1556+ ServiceSettings m_serviceSettings;
1557+ RemovedKeys m_removedKeys;
1558+ bool m_wasDeleted;
1559+ bool m_syncWasCalled;
1560+private:
1561+ friend class Accounts::Account;
1562+ static QHash<Accounts::Account *,AccountController*> m_controllers;
1563+ Accounts::Account *m_account;
1564+};
1565+
1566+QHash<Accounts::Account *,AccountController*> AccountController::m_controllers;
1567+
1568+struct _AgService {
1569+ _AgService(const QString &name): name(name) {}
1570+ QString name;
1571+};
1572+
1573+namespace Accounts {
1574+
1575+Service::Service():
1576+ m_service(0),
1577+ m_tags(0)
1578+{
1579+}
1580+
1581+Service::Service(AgService *service, ReferenceMode):
1582+ m_service(service),
1583+ m_tags(0)
1584+{
1585+}
1586+
1587+Service::Service(const Service &other):
1588+ m_service(other.m_service ? new _AgService(other.m_service->name) : 0),
1589+ m_tags(0)
1590+{
1591+}
1592+
1593+Service &Service::operator=(const Service &other)
1594+{
1595+ delete m_service;
1596+ m_service = other.m_service ? new _AgService(other.m_service->name) : 0;
1597+ return *this;
1598+}
1599+
1600+Service::~Service()
1601+{
1602+ delete m_service;
1603+}
1604+
1605+bool Service::isValid() const
1606+{
1607+ return m_service != 0;
1608+}
1609+
1610+class Account::Private {
1611+public:
1612+ Private() {}
1613+
1614+ QString m_selectedService;
1615+ AccountController *m_controller;
1616+};
1617+
1618+Account::Account(Private *d, QObject *parent):
1619+ QObject(parent),
1620+ d(d)
1621+{
1622+ d->m_controller = new AccountController(this);
1623+}
1624+
1625+Account::~Account()
1626+{
1627+ delete d;
1628+}
1629+
1630+AccountId Account::id() const
1631+{
1632+ return d->m_controller->m_id;
1633+}
1634+
1635+void Account::selectService(const Service &service)
1636+{
1637+ d->m_selectedService = service.m_service ?
1638+ service.m_service->name : QString();
1639+}
1640+
1641+void Account::setValue(const QString &key, const QVariant &value)
1642+{
1643+ QVariantMap &settings =
1644+ d->m_controller->m_serviceSettings[d->m_selectedService];
1645+ settings.insert(key, value);
1646+}
1647+
1648+void Account::remove(const QString &key)
1649+{
1650+ QSet<QString> &removedKeys =
1651+ d->m_controller->m_removedKeys[d->m_selectedService];
1652+ removedKeys.insert(key);
1653+}
1654+
1655+void Account::sync()
1656+{
1657+ d->m_controller->syncCalled();
1658+}
1659+
1660+void Account::remove()
1661+{
1662+ d->m_controller->m_wasDeleted = true;
1663+}
1664+
1665+Watch::~Watch()
1666+{
1667+}
1668+
1669+class Manager::Private {
1670+public:
1671+ Private() {}
1672+
1673+ ManagerController m_controller;
1674+};
1675+
1676+Manager::Manager(QObject *parent):
1677+ QObject(parent),
1678+ d(new Private())
1679+{
1680+}
1681+
1682+Manager::~Manager()
1683+{
1684+ delete d;
1685+}
1686+
1687+Account *Manager::account(const AccountId &id) const
1688+{
1689+ Account::Private *accountD = new Account::Private();
1690+ d->m_controller.lastLoadedAccount =
1691+ new Account(accountD, const_cast<Manager*>(this));
1692+ accountD->m_controller->m_id = id;
1693+ return d->m_controller.lastLoadedAccount;
1694+}
1695+
1696+Account *Manager::createAccount(const QString &providerName)
1697+{
1698+ Account::Private *accountD = new Account::Private();
1699+ d->m_controller.lastLoadedAccount = new Account(accountD, this);
1700+ accountD->m_controller->m_provider = providerName;
1701+ return d->m_controller.lastLoadedAccount;
1702+}
1703+
1704+Service Manager::service(const QString &serviceName) const
1705+{
1706+ if (d->m_controller.m_services.contains(serviceName)) {
1707+ return Service(new _AgService(serviceName));
1708+ } else {
1709+ return Service();
1710+ }
1711+}
1712+
1713+} // namespace
1714+
1715+/* } mocking libaccounts-qt */
1716+
1717+/* mocking utils.cpp { */
1718+namespace OnlineAccountsUi {
1719+
1720+static QString staticApparmorProfile;
1721+
1722+QString apparmorProfileOfPeer(const QDBusMessage &)
1723+{
1724+ return staticApparmorProfile;
1725+}
1726+
1727+void setApparmorProfile(const QString &profile)
1728+{
1729+ staticApparmorProfile = profile;
1730+}
1731+
1732+} // namespace
1733+/* } mocking utils.cpp */
1734+
1735+Q_DECLARE_METATYPE(QProcess::ExitStatus)
1736+
1737+LibaccountsServiceTest::LibaccountsServiceTest():
1738+ QObject(0)
1739+{
1740+ QDBusConnection conn = QDBusConnection::sessionBus();
1741+ conn.registerService(TEST_SERVICE_NAME);
1742+ conn.registerObject(TEST_OBJECT_PATH, &m_service,
1743+ QDBusConnection::ExportAllContents);
1744+
1745+ qRegisterMetaType<QProcess::ExitStatus>();
1746+ setLoggingLevel(2);
1747+}
1748+
1749+void LibaccountsServiceTest::init()
1750+{
1751+ ManagerController *mc = ManagerController::instance();
1752+ mc->lastLoadedAccount = 0;
1753+}
1754+
1755+void LibaccountsServiceTest::testProfile_data()
1756+{
1757+ QTest::addColumn<QString>("profile");
1758+ QTest::addColumn<QString>("provider");
1759+ QTest::addColumn<bool>("mustPass");
1760+
1761+ QTest::newRow("non click, mismatch") <<
1762+ "appProfile" << "OneProvider" << false;
1763+
1764+ QTest::newRow("non click, match") <<
1765+ "theProfile" << "theProfile" << true;
1766+
1767+ QTest::newRow("click, mismatch") <<
1768+ "com.ubuntu.package_app_0.1" << "com.ubuntu.package_other" << false;
1769+
1770+ QTest::newRow("click, match") <<
1771+ "com.ubuntu.package_app_0.2" << "com.ubuntu.package_app" << true;
1772+}
1773+
1774+void LibaccountsServiceTest::testProfile()
1775+{
1776+ QFETCH(QString, profile);
1777+ QFETCH(QString, provider);
1778+ QFETCH(bool, mustPass);
1779+
1780+ setApparmorProfile(profile);
1781+ QString params = QString::fromUtf8("0 true false %1 []").arg(provider);
1782+ QProcess *client = requestStore(params);
1783+ QSignalSpy finished(client, SIGNAL(finished(int,QProcess::ExitStatus)));
1784+
1785+ if (mustPass) {
1786+ ManagerController *mc = ManagerController::instance();
1787+ QTRY_VERIFY(mc->lastLoadedAccount != 0);
1788+ AccountController *ac = AccountController::mock(mc->lastLoadedAccount);
1789+ QTRY_COMPARE(ac->syncWasCalled(), true);
1790+ ac->doSync();
1791+ }
1792+
1793+ finished.wait();
1794+
1795+ QByteArray stdErr = client->readAllStandardError();
1796+ QCOMPARE(stdErr.contains("Profile/provider mismatch"), !mustPass);
1797+}
1798+
1799+void LibaccountsServiceTest::testFailure()
1800+{
1801+ setApparmorProfile("MyProvider");
1802+ QProcess *client = requestStore("0 true false MyProvider []");
1803+ QSignalSpy finished(client, SIGNAL(finished(int,QProcess::ExitStatus)));
1804+
1805+ ManagerController *mc = ManagerController::instance();
1806+ QTRY_VERIFY(mc->lastLoadedAccount != 0);
1807+ AccountController *ac = AccountController::mock(mc->lastLoadedAccount);
1808+ QTRY_COMPARE(ac->syncWasCalled(), true);
1809+
1810+ // return an error
1811+ Accounts::Error error(Accounts::Error::Database, "hi there");
1812+ ac->doSync(error);
1813+ finished.wait();
1814+
1815+ QVERIFY(client->readAllStandardError().contains("hi there"));
1816+}
1817+
1818+void LibaccountsServiceTest::testAccount_data()
1819+{
1820+ QTest::addColumn<QString>("clientSettings");
1821+ QTest::addColumn<QString>("provider");
1822+ QTest::addColumn<quint32>("accountId");
1823+ QTest::addColumn<bool>("deleted");
1824+
1825+ QTest::newRow("new account") <<
1826+ "0 true false Cool" <<
1827+ "Cool" << quint32(0) << false;
1828+
1829+ QTest::newRow("existing account") <<
1830+ "5 false false Bad" <<
1831+ "Bad" << quint32(5) << false;
1832+
1833+ QTest::newRow("deleting account") <<
1834+ "7 false true Die" <<
1835+ "Die" << quint32(7) << true;
1836+}
1837+
1838+void LibaccountsServiceTest::testAccount()
1839+{
1840+ QFETCH(QString, clientSettings);
1841+ QFETCH(QString, provider);
1842+ QFETCH(quint32, accountId);
1843+ QFETCH(bool, deleted);
1844+
1845+ setApparmorProfile(provider);
1846+
1847+ ManagerController *mc = ManagerController::instance();
1848+
1849+ QString params = QString::fromUtf8("%1 []").arg(clientSettings);
1850+ QProcess *client = requestStore(params, true);
1851+ QSignalSpy finished(client, SIGNAL(finished(int,QProcess::ExitStatus)));
1852+
1853+ QTRY_VERIFY(mc->lastLoadedAccount != 0);
1854+ AccountController *ac = AccountController::mock(mc->lastLoadedAccount);
1855+ QTRY_COMPARE(ac->syncWasCalled(), true);
1856+
1857+ QCOMPARE(ac->m_id, accountId);
1858+ if (accountId == 0) {
1859+ QCOMPARE(ac->m_provider, provider);
1860+ }
1861+ QCOMPARE(ac->m_wasDeleted, deleted);
1862+
1863+ ac->doSync();
1864+
1865+ finished.wait();
1866+}
1867+
1868+void LibaccountsServiceTest::testSettings_data()
1869+{
1870+ QTest::addColumn<QString>("clientSettings");
1871+ QTest::addColumn<ServiceSettings>("settings");
1872+ QTest::addColumn<RemovedKeys>("removedKeys");
1873+
1874+ QHash<QString,QVariantMap> settings;
1875+ QHash<QString,QSet<QString> > removedKeys;
1876+
1877+ QTest::newRow("no services") <<
1878+ "\"[]\"" <<
1879+ settings << removedKeys;
1880+ QTest::newRow("no settings") <<
1881+ "\"[('cool', 'type', 3, {}, [])]\"" <<
1882+ settings << removedKeys;
1883+
1884+ settings["cool"].insert("enabled", false);
1885+ settings["cool"].insert("name", QString("Bob"));
1886+ QTest::newRow("some keys changed") <<
1887+ "\"[('cool', 'type', 3, {'enabled': <false>, 'name': <'Bob'>}, [])]\"" <<
1888+ settings << removedKeys;
1889+ settings.clear();
1890+
1891+ removedKeys["cool"].insert("enabled");
1892+ removedKeys["cool"].insert("id");
1893+ QTest::newRow("some keys removed") <<
1894+ "\"[('cool', 'type', 3, {}, ['enabled','id'])]\"" <<
1895+ settings << removedKeys;
1896+ removedKeys.clear();
1897+
1898+ settings["cool"].insert("enabled", true);
1899+ settings["cool"].insert("name", QString("Tom"));
1900+ removedKeys["cool"].insert("id");
1901+ settings["bad"].insert("port", quint32(4000));
1902+ removedKeys["bad"].insert("name");
1903+ QTest::newRow("two services, lots of changes") <<
1904+ "\"["
1905+ "('cool', 'type', 3, {'enabled': <true>, 'name': <'Tom'>}, ['id']),"
1906+ "('bad', 'btype', 2, {'port': <uint32 4000>}, ['name'])"
1907+ "]\"" <<
1908+ settings << removedKeys;
1909+ settings.clear();
1910+ removedKeys.clear();
1911+
1912+ QTest::newRow("invalid service") <<
1913+ "\"[('findme', 'type', 3, {}, ['enabled','id'])]\"" <<
1914+ settings << removedKeys;
1915+}
1916+
1917+void LibaccountsServiceTest::testSettings()
1918+{
1919+ QFETCH(QString, clientSettings);
1920+ QFETCH(ServiceSettings, settings);
1921+ QFETCH(RemovedKeys, removedKeys);
1922+
1923+ setApparmorProfile("MyProvider");
1924+
1925+ ManagerController *mc = ManagerController::instance();
1926+ mc->setServices(QStringList() << "cool" << "bad");
1927+
1928+ QString params =
1929+ QString::fromUtf8("0 true false MyProvider %1").arg(clientSettings);
1930+ QProcess *client = requestStore(params, true);
1931+ QSignalSpy finished(client, SIGNAL(finished(int,QProcess::ExitStatus)));
1932+
1933+ QTRY_VERIFY(mc->lastLoadedAccount != 0);
1934+ AccountController *ac = AccountController::mock(mc->lastLoadedAccount);
1935+ QTRY_COMPARE(ac->syncWasCalled(), true);
1936+
1937+ QCOMPARE(ac->m_serviceSettings, settings);
1938+ QCOMPARE(ac->m_removedKeys, removedKeys);
1939+
1940+ ac->doSync();
1941+
1942+ finished.wait();
1943+}
1944+
1945+QTEST_MAIN(LibaccountsServiceTest);
1946+
1947+#include "tst_libaccounts_service.moc"
1948
1949=== added file 'tests/online-accounts-service/tst_libaccounts_service.pro'
1950--- tests/online-accounts-service/tst_libaccounts_service.pro 1970-01-01 00:00:00 +0000
1951+++ tests/online-accounts-service/tst_libaccounts_service.pro 2015-01-16 17:10:14 +0000
1952@@ -0,0 +1,38 @@
1953+include(../../common-project-config.pri)
1954+
1955+TARGET = tst_libaccounts_service
1956+
1957+CONFIG += \
1958+ debug \
1959+ link_pkgconfig
1960+
1961+QT += \
1962+ core \
1963+ dbus \
1964+ testlib \
1965+ xml
1966+
1967+ONLINE_ACCOUNTS_SERVICE_DIR = $${TOP_SRC_DIR}/online-accounts-service
1968+COMMON_SRC_DIR = $${TOP_SRC_DIR}/online-accounts-ui
1969+LIBACCOUNTS_QT_DIR = $$system(pkg-config --variable=includedir accounts-qt5)/Accounts
1970+
1971+SOURCES += \
1972+ $${COMMON_SRC_DIR}/debug.cpp \
1973+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/libaccounts-service.cpp \
1974+ tst_libaccounts_service.cpp
1975+
1976+HEADERS += \
1977+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/libaccounts-service.h \
1978+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/utils.h \
1979+ $${LIBACCOUNTS_QT_DIR}/account.h \
1980+ $${LIBACCOUNTS_QT_DIR}/manager.h
1981+
1982+INCLUDEPATH += \
1983+ $${ONLINE_ACCOUNTS_SERVICE_DIR} \
1984+ $${COMMON_SRC_DIR} \
1985+ $$system(pkg-config --variable=includedir accounts-qt5)
1986+
1987+check.commands = "xvfb-run -s '-screen 0 640x480x24' -a dbus-test-runner -t ./$${TARGET}"
1988+check.depends = $${TARGET}
1989+QMAKE_EXTRA_TARGETS += check
1990+
1991
1992=== modified file 'tests/online-accounts-service/tst_service.pro'
1993--- tests/online-accounts-service/tst_service.pro 2014-10-13 11:25:27 +0000
1994+++ tests/online-accounts-service/tst_service.pro 2015-01-16 17:10:14 +0000
1995@@ -25,6 +25,7 @@
1996 $${ONLINE_ACCOUNTS_SERVICE_DIR}/request.cpp \
1997 $${ONLINE_ACCOUNTS_SERVICE_DIR}/request-manager.cpp \
1998 $${ONLINE_ACCOUNTS_SERVICE_DIR}/service.cpp \
1999+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/utils.cpp \
2000 tst_service.cpp
2001
2002 HEADERS += \
2003@@ -33,6 +34,7 @@
2004 $${ONLINE_ACCOUNTS_SERVICE_DIR}/request-manager.h \
2005 $${ONLINE_ACCOUNTS_SERVICE_DIR}/service.h \
2006 $${ONLINE_ACCOUNTS_SERVICE_DIR}/ui-proxy.h \
2007+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/utils.h \
2008
2009 INCLUDEPATH += \
2010 $${ONLINE_ACCOUNTS_SERVICE_DIR} \
2011
2012=== modified file 'tests/online-accounts-service/tst_signonui_service.pro'
2013--- tests/online-accounts-service/tst_signonui_service.pro 2014-08-19 11:14:34 +0000
2014+++ tests/online-accounts-service/tst_signonui_service.pro 2015-01-16 17:10:14 +0000
2015@@ -24,6 +24,7 @@
2016 SOURCES += \
2017 $${ONLINE_ACCOUNTS_SERVICE_DIR}/request.cpp \
2018 $${ONLINE_ACCOUNTS_SERVICE_DIR}/signonui-service.cpp \
2019+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/utils.cpp \
2020 mock/request-manager-mock.cpp \
2021 tst_signonui_service.cpp
2022
2023@@ -31,6 +32,7 @@
2024 $${ONLINE_ACCOUNTS_SERVICE_DIR}/request.h \
2025 $${ONLINE_ACCOUNTS_SERVICE_DIR}/request-manager.h \
2026 $${ONLINE_ACCOUNTS_SERVICE_DIR}/signonui-service.h \
2027+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/utils.h \
2028 mock/request-manager-mock.h
2029
2030 INCLUDEPATH += \
2031
2032=== modified file 'tests/online-accounts-service/tst_ui_proxy.pro'
2033--- tests/online-accounts-service/tst_ui_proxy.pro 2014-08-26 08:39:20 +0000
2034+++ tests/online-accounts-service/tst_ui_proxy.pro 2015-01-16 17:10:14 +0000
2035@@ -16,6 +16,7 @@
2036 INSTALL_BIN_DIR=\\\"$${INSTALL_PREFIX}/bin\\\"
2037
2038 PKGCONFIG += \
2039+ accounts-qt5 \
2040 signon-plugins-common
2041
2042 ONLINE_ACCOUNTS_SERVICE_DIR = $${TOP_SRC_DIR}/online-accounts-service
2043
2044=== modified file 'tests/online-accounts-ui/mock/signonui-request-mock.cpp'
2045--- tests/online-accounts-ui/mock/signonui-request-mock.cpp 2014-09-17 14:39:07 +0000
2046+++ tests/online-accounts-ui/mock/signonui-request-mock.cpp 2015-01-16 17:10:14 +0000
2047@@ -86,6 +86,12 @@
2048 return parameters().value(SSOUI_KEY_MECHANISM).toString();
2049 }
2050
2051+QString Request::providerId() const
2052+{
2053+ Q_D(const Request);
2054+ return d->m_providerId;
2055+}
2056+
2057 const QVariantMap &Request::clientData() const
2058 {
2059 Q_D(const Request);
2060
2061=== modified file 'tests/online-accounts-ui/mock/signonui-request-mock.h'
2062--- tests/online-accounts-ui/mock/signonui-request-mock.h 2014-10-09 12:24:45 +0000
2063+++ tests/online-accounts-ui/mock/signonui-request-mock.h 2015-01-16 17:10:14 +0000
2064@@ -39,10 +39,13 @@
2065 ~RequestPrivate();
2066 static RequestPrivate *mocked(Request *r) { return r->d_ptr; }
2067
2068+ void setProviderId(const QString &id) { m_providerId = id; }
2069+
2070 private:
2071 mutable Request *q_ptr;
2072 QVariantMap m_clientData;
2073 QPointer<RequestHandler> m_handler;
2074+ QString m_providerId;
2075 };
2076
2077 } // namespace
2078
2079=== modified file 'tests/online-accounts-ui/tst_browser_request.cpp'
2080--- tests/online-accounts-ui/tst_browser_request.cpp 2014-10-09 12:24:45 +0000
2081+++ tests/online-accounts-ui/tst_browser_request.cpp 2015-01-16 17:10:14 +0000
2082@@ -85,6 +85,7 @@
2083 void BrowserRequestTest::testParametersWithHandler_data()
2084 {
2085 QTest::addColumn<QVariantMap>("parameters");
2086+ QTest::addColumn<QString>("providerId");
2087 QTest::addColumn<QString>("pageComponentUrl");
2088 QTest::addColumn<QString>("startUrl");
2089 QTest::addColumn<QString>("finalUrl");
2090@@ -95,10 +96,11 @@
2091
2092 QTest::newRow("empty") <<
2093 QVariantMap() <<
2094+ QString() <<
2095 "DefaultPage.qml" <<
2096 QString() <<
2097 QString() <<
2098- baseCacheDir + "/id-0";
2099+ baseCacheDir + "/id-0-";
2100
2101 QVariantMap parameters;
2102 QVariantMap clientData;
2103@@ -107,10 +109,11 @@
2104 parameters.insert(SSOUI_KEY_IDENTITY, uint(4));
2105 QTest::newRow("with URLs and ID") <<
2106 parameters <<
2107+ "google" <<
2108 "DefaultPage.qml" <<
2109 "http://localhost/start.html" <<
2110 "http://localhost/end.html" <<
2111- baseCacheDir + "/id-4";
2112+ baseCacheDir + "/id-4-google";
2113 parameters.clear();
2114
2115 clientData.insert("X-PageComponent",
2116@@ -121,16 +124,18 @@
2117 parameters.insert(SSOUI_KEY_IDENTITY, uint(4));
2118 QTest::newRow("with page component") <<
2119 parameters <<
2120+ "com.ubuntu.app_plugin" <<
2121 "file:///usr/share/signon-ui/MyPage.qml" <<
2122 "http://localhost/start.html" <<
2123 "http://localhost/end.html" <<
2124- baseCacheDir + "/id-4";
2125+ baseCacheDir + "/id-4-com.ubuntu.app_plugin";
2126 parameters.clear();
2127 }
2128
2129 void BrowserRequestTest::testParametersWithHandler()
2130 {
2131 QFETCH(QVariantMap, parameters);
2132+ QFETCH(QString, providerId);
2133 QFETCH(QString, pageComponentUrl);
2134 QFETCH(QString, startUrl);
2135 QFETCH(QString, finalUrl);
2136@@ -140,6 +145,9 @@
2137 QSignalSpy requestChanged(&handler, SIGNAL(requestChanged()));
2138
2139 TestRequest request(parameters);
2140+ SignOnUi::RequestPrivate *mockedRequest =
2141+ SignOnUi::RequestPrivate::mocked(&request);
2142+ mockedRequest->setProviderId(providerId);
2143 request.setHandler(&handler);
2144
2145 request.start();
2146
2147=== modified file 'tests/online-accounts-ui/tst_signonui_request.cpp'
2148--- tests/online-accounts-ui/tst_signonui_request.cpp 2014-10-09 12:24:45 +0000
2149+++ tests/online-accounts-ui/tst_signonui_request.cpp 2015-01-16 17:10:14 +0000
2150@@ -20,7 +20,6 @@
2151
2152 #include "globals.h"
2153 #include "signonui-request.h"
2154-#include "mock/notification-mock.h"
2155 #include "mock/request-mock.h"
2156 #include "mock/ui-server-mock.h"
2157
2158@@ -68,8 +67,6 @@
2159 void testParameters_data();
2160 void testParameters();
2161 void testHandler();
2162- void testSnapDecision_data();
2163- void testSnapDecision();
2164
2165 private:
2166 bool mustCreateAccount(uint credentialsId) { return credentialsId > 10; }
2167@@ -167,171 +164,6 @@
2168 delete handler;
2169 }
2170
2171-void SignonuiRequestTest::testSnapDecision_data()
2172-{
2173- QTest::addColumn<uint>("credentialsId");
2174- QTest::addColumn<QString>("accountName");
2175- QTest::addColumn<QString>("clientProfile");
2176- QTest::addColumn<QString>("applicationName");
2177- QTest::addColumn<bool>("mustAccept");
2178- QTest::addColumn<QVariantMap>("result");
2179-
2180- QVariantMap acceptedResult;
2181- acceptedResult.insert("some key", QString("some value"));
2182-
2183- QVariantMap declinedResult;
2184- declinedResult.insert(SSOUI_KEY_ERROR, SignOn::QUERY_ERROR_CANCELED);
2185-
2186- QVariantMap errorResult;
2187- errorResult.insert(SSOUI_KEY_ERROR, SignOn::QUERY_ERROR_FORBIDDEN);
2188-
2189- QTest::newRow("no account") <<
2190- uint(0) <<
2191- "tom@example.com" <<
2192- "com.ubuntu.tests_application_0.3" <<
2193- QString() <<
2194- false <<
2195- errorResult;
2196-
2197- QTest::newRow("invalid account") <<
2198- uint(1) <<
2199- "tom@example.com" <<
2200- "com.ubuntu.tests_application_0.3" <<
2201- QString() <<
2202- false <<
2203- errorResult;
2204-
2205- QTest::newRow("valid application, accepted") <<
2206- uint(14231) <<
2207- "tom@example.com" <<
2208- "com.ubuntu.tests_application_0.3" <<
2209- "Easy Mailer" <<
2210- true <<
2211- acceptedResult;
2212-
2213- QTest::newRow("valid application, declined") <<
2214- uint(14231) <<
2215- "tom@example.com" <<
2216- "com.ubuntu.tests_application_0.3" <<
2217- "Easy Mailer" <<
2218- false <<
2219- declinedResult;
2220-
2221- QTest::newRow("unconfined application, accepted") <<
2222- uint(14235) <<
2223- "tom@example.com" <<
2224- "unconfined" <<
2225- "Ubuntu" <<
2226- true <<
2227- acceptedResult;
2228-
2229- QTest::newRow("unconfined application, declined") <<
2230- uint(14235) <<
2231- "tom@example.com" <<
2232- "unconfined" <<
2233- "Ubuntu" <<
2234- false <<
2235- declinedResult;
2236-}
2237-
2238-void SignonuiRequestTest::testSnapDecision()
2239-{
2240- QString providerId("cool");
2241- QFETCH(uint, credentialsId);
2242- QFETCH(QString, accountName);
2243- QFETCH(QString, clientProfile);
2244- QFETCH(QString, applicationName);
2245- QFETCH(bool, mustAccept);
2246- QFETCH(QVariantMap, result);
2247-
2248- // First, create an account
2249- Accounts::Manager *manager = new Accounts::Manager(this);
2250- Accounts::Provider provider = manager->provider(providerId);
2251- QVERIFY(provider.isValid());
2252- if (mustCreateAccount(credentialsId)) {
2253- Accounts::Account *account = manager->createAccount(providerId);
2254- QVERIFY(account != 0);
2255- account->setEnabled(true);
2256- account->setDisplayName(accountName);
2257- account->setCredentialsId(credentialsId);
2258- account->syncAndBlock();
2259- }
2260-
2261- /* Then, create a request referring to the same credentials ID of the
2262- * created account. */
2263- QVariantMap parameters;
2264- parameters.insert(SSOUI_KEY_IDENTITY, credentialsId);
2265- parameters.insert(SSOUI_KEY_METHOD, "funnyMethod");
2266- parameters.insert(SSOUI_KEY_MECHANISM, "funnyMechanism");
2267- TestRequest request(clientProfile, parameters);
2268- OnlineAccountsUi::RequestPrivate *mockRequest =
2269- OnlineAccountsUi::RequestPrivate::mocked(&request);
2270- QSignalSpy failCalled(mockRequest,
2271- SIGNAL(failCalled(const QString&, const QString&)));
2272- QSignalSpy setResultCalled(mockRequest,
2273- SIGNAL(setResultCalled(const QVariantMap &)));
2274- request.start();
2275-
2276- /* Request to show a window; a snap decision should appear instead */
2277- QWindow *window = new QWindow;
2278- QSignalSpy setWindowCalled(mockRequest,
2279- SIGNAL(setWindowCalled(QWindow*)));
2280- request.setWindow(window);
2281- QCOMPARE(setWindowCalled.count(), 0);
2282- if (mustCreateAccount(credentialsId)) {
2283- QCOMPARE(NotificationPrivate::allNotifications.count(), 1);
2284- } else {
2285- /* If the account is not found, no notification should appear, and an
2286- * error returned to the app */
2287- QCOMPARE(NotificationPrivate::allNotifications.count(), 0);
2288- QCOMPARE(setResultCalled.count(), 1);
2289- QCOMPARE(setResultCalled.at(0).at(0).toMap(), result);
2290- return;
2291- }
2292-
2293- /* Inspect the snap decision contents */
2294- Notification *notification =
2295- NotificationPrivate::allNotifications.first();
2296- NotificationPrivate *mockNotification =
2297- NotificationPrivate::mocked(notification);
2298- QCOMPARE(mockNotification->m_summary, QString("Authentication request"));
2299- QCOMPARE(mockNotification->m_body,
2300- QString("Please authorize %1 to access your %2 account %3").
2301- arg(applicationName).arg(provider.displayName()).arg(accountName));
2302- QVERIFY(mockNotification->m_isSnapDecision);
2303-
2304- /* Invoke the action on the snap decision */
2305- QString action = mustAccept ? "continue" : "cancel";
2306- QSignalSpy actionInvoked(notification,
2307- SIGNAL(actionInvoked(const QString &)));
2308- mockNotification->invokeAction(action);
2309- QCOMPARE(actionInvoked.count(), 1);
2310- QCOMPARE(actionInvoked.at(0).at(0).toString(), action);
2311-
2312- /* Here we iterate the main loop because the notification object is
2313- * destroyed with deleteLater() */
2314- QTest::qWait(5);
2315-
2316- if (mustAccept) {
2317- QCOMPARE(setWindowCalled.count(), 1);
2318- QCOMPARE(setWindowCalled.at(0).at(0), QVariant::fromValue(window));
2319- QCOMPARE(failCalled.count(), 0);
2320- QCOMPARE(setResultCalled.count(), 0);
2321-
2322- /* deliver the result */
2323- request.sendResult(result);
2324- QCOMPARE(setResultCalled.count(), 1);
2325- QCOMPARE(setResultCalled.at(0).at(0).toMap(), result);
2326- } else {
2327- QCOMPARE(setWindowCalled.count(), 0);
2328- QCOMPARE(failCalled.count(), 0);
2329- QCOMPARE(setResultCalled.count(), 1);
2330- QCOMPARE(setResultCalled.at(0).at(0).toMap(), result);
2331- }
2332- delete window;
2333- delete manager;
2334-}
2335-
2336 QTEST_MAIN(SignonuiRequestTest);
2337
2338 #include "tst_signonui_request.moc"
2339
2340=== modified file 'tests/online-accounts-ui/tst_signonui_request.pro'
2341--- tests/online-accounts-ui/tst_signonui_request.pro 2014-10-14 11:19:19 +0000
2342+++ tests/online-accounts-ui/tst_signonui_request.pro 2015-01-16 17:10:14 +0000
2343@@ -27,18 +27,15 @@
2344 SOURCES += \
2345 $${ONLINE_ACCOUNTS_UI_DIR}/debug.cpp \
2346 $${ONLINE_ACCOUNTS_UI_DIR}/signonui-request.cpp \
2347- mock/notification-mock.cpp \
2348 mock/request-mock.cpp \
2349 mock/qwindow.cpp \
2350 mock/ui-server-mock.cpp \
2351 tst_signonui_request.cpp
2352
2353 HEADERS += \
2354- $${ONLINE_ACCOUNTS_UI_DIR}/notification.h \
2355 $${ONLINE_ACCOUNTS_UI_DIR}/request.h \
2356 $${ONLINE_ACCOUNTS_UI_DIR}/signonui-request.h \
2357 $${ONLINE_ACCOUNTS_UI_DIR}/ui-server.h \
2358- mock/notification-mock.h \
2359 mock/request-mock.h \
2360 mock/ui-server-mock.h \
2361 window-watcher.h

Subscribers

People subscribed via source and target branches

to all changes: