Merge lp:~mardy/ubuntu-system-settings/other-app-access into lp:ubuntu-system-settings

Proposed by Alberto Mardegan
Status: Merged
Approved by: Ken VanDine
Approved revision: 823
Merged at revision: 907
Proposed branch: lp:~mardy/ubuntu-system-settings/other-app-access
Merge into: lp:ubuntu-system-settings
Diff against target: 1079 lines (+965/-2)
12 files modified
debian/control (+1/-0)
plugins/security-privacy/AppAccess.qml (+74/-0)
plugins/security-privacy/AppAccessControl.qml (+53/-0)
plugins/security-privacy/CMakeLists.txt (+20/-1)
plugins/security-privacy/PageComponent.qml (+1/-1)
plugins/security-privacy/plugin.cpp (+2/-0)
plugins/security-privacy/trust-store-model.cpp (+333/-0)
plugins/security-privacy/trust-store-model.h (+74/-0)
po/CMakeLists.txt (+1/-0)
tests/plugins/CMakeLists.txt (+1/-0)
tests/plugins/security-privacy/CMakeLists.txt (+11/-0)
tests/plugins/security-privacy/tst_trust_store_model.cpp (+394/-0)
To merge this branch: bzr merge lp:~mardy/ubuntu-system-settings/other-app-access
Reviewer Review Type Date Requested Status
Ken VanDine Approve
Thomas Voß (community) Approve
PS Jenkins bot continuous-integration Approve
Sebastien Bacher (community) Needs Information
Review via email: mp+228299@code.launchpad.net

Commit message

Implement the "Other App Access" screen

As per https://wiki.ubuntu.com/AccountPrivileges#Phone

Description of the change

Implement the "Other App Access" screen

As per https://wiki.ubuntu.com/AccountPrivileges#Phone

The UI strings needs to be confirmed with design, before landing this.

Testing instructions
====================

At the moment, the CameraService and PulseAudio helper services are not implemented, so the UI will always show "0" applications using them. However, it's possible to test the feature by using a couple of command line utilities from this branch: https://code.launchpad.net/~thomas-voss/trust-store/add-trust-stored/+merge/227685

(replace "x86_64-linux-gnu" with your architecture)

1) Run this on a terminal:
    /usr/lib/x86_64-linux-gnu/trust-stored-stub --remote-agent UnixDomainSocketRemoteAgent --endpoint=/tmp/endpoint.for.testing --for-service CameraService
2) Run this on another terminal:
    /usr/lib/x86_64-linux-gnu/trust-stored-skeleton --local-agent TerminalAgent --remote-agent UnixDomainSocketRemoteAgent --endpoint=/tmp/endpoint.for.testing --for-service CameraService
3) Launch an application which has is registered in "~/.local/share/applications/" (if you are on the desktop and don't have any, copy the .desktop file from the system location), take a note of its pid and on the terminal where you launched the command at step #1 type (replace 1000 with your user id):
    <pid> 1000 0
4) Go to the terminal from step #2, and either accept or deny the request
5) (desktop only) Fire up "sqlitebrowser ~/.local/share/CameraService/trust.db", and change the "undefined" string to the application ID of your app. Remember to save the changes!

Repeat steps 3-5 as many times as you want (with different apps).

6) Start system-settings, go to "Security & Privacy" -> "Other App Access"; play with the UI, and verify that it behaves as expected. From time to time, close it and re-open it, and make sure that it preserves the last state.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:818
http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-ci/1065/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/2508
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/2024
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-amd64-ci/257
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-armhf-ci/257
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-armhf-ci/257/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-i386-ci/257
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/2659
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/3751
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/3751/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/10436
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/1693
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/2277
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/2277/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-system-settings-ci/1065/rebuild

review: Approve (continuous-integration)
Revision history for this message
Sebastien Bacher (seb128) wrote :

Thanks, should the listmodel values be translated? Or do you plan to do that when used in an U.I (btw are the title/caption used somewhere, they are pushed to the appaccesscontrol page but don't seem used there)

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

I'll push one more commit to add translations for those strings (I first wanted to read the designers' feedback, which just came).
As for the properties,
"title" is set to the ItemPage's title
"caption" is set as the captionLabel.text
so it seems that they are all used.

Revision history for this message
Sebastien Bacher (seb128) wrote :

Where did you get feedback from the designerS?

> "title" is set to the ItemPage's title

I didn't see it used in the other files, I guess pushing with a title property is enough to make it work by magic, right?

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

> Where did you get feedback from the designerS?

JohnLea on IRC :-)

> > "title" is set to the ItemPage's title
>
> I didn't see it used in the other files, I guess pushing with a title property
> is enough to make it work by magic, right?

Yes, ItemPage has a "title" property, and we are specifying that when calling pageStack.push()

819. By Alberto Mardegan

Translate all texts

820. By Alberto Mardegan

Merge trunk

[ Ken VanDine ]
* [sound] Added volume slider to the sound pane
[ jonas-drange ]
* Cellular panel changes: new carrier design. Carrier selection for
  two sims (individual selection). Using either sim1 or sim2 for
  cellular data. Data technology preference selection refactor to work
  for dual sim scenario as well.
[ Ken VanDine ]
* [phone] Call forwarding implementation
[ CI bot ]
* Resync trunk
[ Oliver Grawert ]
* Add DeveloperMode page to "About this device" (see: .
  https://wiki.ubuntu.com/AboutThisDevice#developer-mode) Enable
  toggling of the adb usb gadget driver in DeveloperMode page through
  the dbus-property-service interface. Add shortcut from DeveloperMode
  page to Lock screen settings (since the final dev mode will require
  that you can lock before enabling it). Depend on suru-icon-theme
  since we are using the security alert icon on the Developer Mode
  page. Depend on dbus-property-service on the arches where it is
  available since we use it in Developer Mode .
[ jonas-drange ]
* Hi, this branch seeks to address most of the design changes to the
  Background panel in System Settings (post Malta) The current spec
  and designs are here:
  https://wiki.ubuntu.com/Appearance#Phone.2BAC8-tablet Related bugs:
  https://bugs.launchpad.net/ubuntu/+source/ubuntu-system-
  settings/+bug/1297418 https://bugs.launchpad.net/ubuntu-autopilot-
  tests/+bug/1322074 Changes: Removed all functionality and code
  related to changing scope background. Added autopilot tests. New
  header design that collapses/expands. Not part of this branch:
  Scaling in preview has not been implemented. It should have its own
  branch. After rotating the phone in system settings, the default
  background will appear to have been deselected. This is because the
  current background is now the default tablet background. Confirmed
  on mako. Will file bug. Thank you. (LP: #1322074)
[ Iain Lane ]
* Add 1 gu of spacing between rows in the main screen's grid
[ Pawel Stolowski ]
* Added X-Ubuntu-Default-Department-ID key to the desktop file. This
  is required by click scope to support departments for preinstalled
  applications.
[ Ubuntu daily release ]
* debian/ubuntu-system-settings.maintscript: auto-update to released
  version
[ Iain Lane ]
* Remove leftover /etc/apparmor.d/usr.bin.system-settings conffile.
* Use the last updated date returned by 'Info' on system-image's D-Bus
  interface instead of reading a file on the fs, which is an
  implementation detail.
* Clean up AP tests to try to fix test failures. Run ofono and system-
  image tests on the about page under mocks.

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

I marked the strings as translatable.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:820
http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-ci/1074/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/2572
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/2055
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-amd64-ci/266
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-armhf-ci/266
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-armhf-ci/266/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-i386-ci/266
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/2713
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/3815
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/3815/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/10523
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/1721
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/2308
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/2308/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-system-settings-ci/1074/rebuild

review: Approve (continuous-integration)
Revision history for this message
Ken VanDine (ken-vandine) wrote :

I suspect the author line in the new QML files should be mardy instead of seb128.

Would there be any value in providing (in the future) TrustStoreModel bindings separately, or is use outside of system-settings unlikely?

review: Needs Information
821. By Alberto Mardegan

Fix author line

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

I fixed the author lines.

As for providing the TrustStoreModel bindings separately, I don't think it will be needed, since I'd thing that most trusted helpers would use the C++ bindings. But anyway this is something that can be changed later, should the need arise.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Thomas Voß (thomas-voss) wrote :

I'm not sure about the general system settings policy, but build-time tests for the model are missing. Having standalone tests in place that we can use to exercise the model should help a lot in tracking down errors.

review: Needs Fixing
822. By Alberto Mardegan

Added unit tests

823. By Alberto Mardegan

merge from trunk

[ Michael Terry ]
* Only turn the wizard off if the user actively presses the Finished
  button. That is, don't disable the wizard if we crash or the user
  turns off the phone while still in the wizard. (LP: #1348577)
* Avoid making a method call that can return undefined results,
  potentially causing password or language setting to not work
  correctly.
[ jonas-drange ]
* [reset] bring back reset and hook it up to the backend. Added happy
  path tests.
[ CI bot ]
* Resync trunk
[ Oliver Grawert ]
* Tie activity of the developer mode toggle to the selected password
  type (only make the toggle switch active if the password type is not
  set to "swipe")
[ Michael Terry ]
* Stop the wizard from crashing on exit by not redefining a property
  that blew up when the wifi list was emptied. (LP: #1335298)
[ John R. Lenton ]
* Fix the depends to be on the right gsettings schema version.
[ Ken VanDine ]
* handle qtdeclarative5-ubuntu-content1 package rename
[ John R. Lenton ]
* This makes the category grid a little bit looser. (LP: #1352866)
[ Iain Lane ]
* Add a 'reset' API so that plugins can reset their settings to
  default. They can do this in one of two ways: by providing a
  function 'bool reset()' in their 'plugin' which returns true or by
  providing a top-level javascript function 'reset' in their page
  component.
[ Diego Sarmentero ]
* Add a plugin for enabling/disabling push notification per-app.
[ Roberto Alsina ]
* Add a plugin for enabling/disabling push notification per-app.
[ Mathieu Trudel-Lapierre ]
* Bluetooth UI redesign:
  - Redesign the UI to follow the specification.
  - Create devices only when attempting to connect.
  - Bugfixes for displaying icons for devices.
  - Show new icons for common device types.
  - Support more device types.
  - Show Trusted (known) devices in a separate list from the devices detected
    by Discovery.
  - Allow for a way to forget (unpair) a known device. (LP: #1336197)
  - Follow the state of the Bluetooth adapter for whether Bluetooth
    is enabled. (LP: #1272317)
  - Miscellaneous bug fixes.
* debian/control: add Build-Depends on libqtdbusmock1-dev and
  libqtdbustest1-dev for Bluetooth testing.
[ Ken VanDine ]
* [sound] exposed silentMode and vibrate settings
* Set anchorToKeyboard in the MainView, to fix issues found on screens
  with multiple input fields. Most noticeable is in the apneditor
  branch that is still being worked on.
[ CI bot ]
* Resync trunk
[ Ubuntu daily release ]
* New rebuild forced
[ Michael Terry ]
* Make the buttons in the password change dialog conform to the color
  scheme preferred by design (green for accept buttons, grey for
  neutral buttons).
[ jonas-drange ]
* [cellular] add sim name editor for dual sim scenario
[ CI bot ]
* Resync trunk
[ Iain Lane ]
* Move the arch-restricted Depends on qtmir to the wizard binary
  package which uses it, and lower it to Recommends since there's a
  non-mir path too.
[ Adolfo Jayme Barrientos ]
* Fix typo in user-facing strings: user → use (LP: #1349855)
[ Michael Terry ]
* Stop storing user's password in plaintext. Instead, store it in PAM
  the same way the Desktop images do. Also, expose the UI to set it.
  (LP: #1234983)
[ Gerry Boland ]
* Use QtMir instead of the older Unity-Mir Need for Qt Compositor.
[ Iain Lane ]
* Update pot
[ Michał Sawicz ]
* Use QtMir instead of the older Unity-Mir Need for Qt Compositor.
[ Robert Bruce Park ]
* Add a build-dep on qtmir to avoid building on inappropriate arches.
[ Ubuntu daily release ]
* New rebuild forced

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alberto Mardegan (mardy) wrote :
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:823
http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-ci/1214/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/3409
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/2679
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-amd64-ci/407
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-armhf-ci/403
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-armhf-ci/403/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-i386-ci/406
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/3329
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/4656
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/4656/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/11333
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2167
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/2956
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/2956/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-system-settings-ci/1214/rebuild

review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:823
http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-ci/1215/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/3410
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/2680
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-amd64-ci/408
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-armhf-ci/404
        deb: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-armhf-ci/404/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/ubuntu-system-settings-utopic-i386-ci/407
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/3330
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/4657
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/4657/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/11334
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/2168
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/2957
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/2957/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/ubuntu-system-settings-ci/1215/rebuild

review: Approve (continuous-integration)
Revision history for this message
Thomas Voß (thomas-voss) wrote :

LGTM.

review: Approve
Revision history for this message
Ken VanDine (ken-vandine) wrote :

Thanks, looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2014-08-06 05:57:42 +0000
3+++ debian/control 2014-08-11 11:06:25 +0000
4@@ -17,6 +17,7 @@
5 libpolkit-agent-1-dev,
6 libqmenumodel-dev,
7 libtimezonemap1-dev (>= 0.4.1),
8+ libtrust-store-dev,
9 libunity-api-dev,
10 libupower-glib-dev,
11 pkg-config,
12
13=== added file 'plugins/security-privacy/AppAccess.qml'
14--- plugins/security-privacy/AppAccess.qml 1970-01-01 00:00:00 +0000
15+++ plugins/security-privacy/AppAccess.qml 2014-08-11 11:06:25 +0000
16@@ -0,0 +1,74 @@
17+/*
18+ * Copyright (C) 2013 Canonical Ltd
19+ *
20+ * This program is free software: you can redistribute it and/or modify
21+ * it under the terms of the GNU General Public License version 3 as
22+ * published by the Free Software Foundation.
23+ *
24+ * This program is distributed in the hope that it will be useful,
25+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
26+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27+ * GNU General Public License for more details.
28+ *
29+ * You should have received a copy of the GNU General Public License
30+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
31+ *
32+ * Authors:
33+ * Alberto Mardegan <alberto.mardegan@canonical.com>
34+ */
35+
36+import QtQuick 2.0
37+import Ubuntu.Components 0.1
38+import Ubuntu.Components.ListItems 0.1 as ListItem
39+import Ubuntu.SystemSettings.SecurityPrivacy 1.0
40+import SystemSettings 1.0
41+
42+ItemPage {
43+ id: root
44+ title: i18n.tr("Other app access")
45+
46+ Column {
47+ anchors.left: parent.left
48+ anchors.right: parent.right
49+
50+ ListItem.Caption {
51+ text: i18n.tr("Apps that you have granted and have requested access to:")
52+ }
53+
54+ ListModel {
55+ id: appsModel
56+ ListElement {
57+ name: QT_TR_NOOP("Camera")
58+ caption: QT_TR_NOOP("Apps that have requested access to your camera")
59+ trustStoreService: "CameraService"
60+ }
61+ ListElement {
62+ name: QT_TR_NOOP("Mic")
63+ caption: QT_TR_NOOP("Apps that have requested access to your mic")
64+ trustStoreService: "PulseAudio"
65+ }
66+ }
67+
68+ Repeater {
69+ model: appsModel
70+
71+ ListItem.SingleValue {
72+ text: model.name
73+ enabled: trustStoreModel.count > 0
74+ value: trustStoreModel.count > 0 ?
75+ i18n.tr("%1/%2").arg(trustStoreModel.grantedCount).arg(trustStoreModel.count) :
76+ i18n.tr("0")
77+ onClicked: pageStack.push(Qt.resolvedUrl("AppAccessControl.qml"), {
78+ "title": i18n.tr(model.name),
79+ "caption": i18n.tr(model.caption),
80+ "model": trustStoreModel,
81+ })
82+
83+ TrustStoreModel {
84+ id: trustStoreModel
85+ serviceName: model.trustStoreService
86+ }
87+ }
88+ }
89+ }
90+}
91
92=== added file 'plugins/security-privacy/AppAccessControl.qml'
93--- plugins/security-privacy/AppAccessControl.qml 1970-01-01 00:00:00 +0000
94+++ plugins/security-privacy/AppAccessControl.qml 2014-08-11 11:06:25 +0000
95@@ -0,0 +1,53 @@
96+/*
97+ * Copyright (C) 2013 Canonical Ltd
98+ *
99+ * This program is free software: you can redistribute it and/or modify
100+ * it under the terms of the GNU General Public License version 3 as
101+ * published by the Free Software Foundation.
102+ *
103+ * This program is distributed in the hope that it will be useful,
104+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
105+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
106+ * GNU General Public License for more details.
107+ *
108+ * You should have received a copy of the GNU General Public License
109+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
110+ *
111+ * Authors:
112+ * Alberto Mardegan <alberto.mardegan@canonical.com>
113+ */
114+
115+import QtQuick 2.0
116+import Ubuntu.Components 0.1
117+import Ubuntu.Components.ListItems 0.1 as ListItem
118+import SystemSettings 1.0
119+
120+ItemPage {
121+ id: root
122+
123+ property alias model: repeater.model
124+ property alias caption: captionLabel.text
125+
126+ Column {
127+ anchors.left: parent.left
128+ anchors.right: parent.right
129+
130+ ListItem.Caption {
131+ id: captionLabel
132+ }
133+
134+ Repeater {
135+ id: repeater
136+
137+ ListItem.Standard {
138+ text: model.applicationName
139+ iconSource: "image://theme/" + model.iconName
140+ control: Switch {
141+ id: welcomeStatsSwitch
142+ checked: model.granted
143+ onClicked: root.model.setEnabled(index, !model.granted)
144+ }
145+ }
146+ }
147+ }
148+}
149
150=== modified file 'plugins/security-privacy/CMakeLists.txt'
151--- plugins/security-privacy/CMakeLists.txt 2014-07-13 23:57:50 +0000
152+++ plugins/security-privacy/CMakeLists.txt 2014-08-11 11:06:25 +0000
153@@ -17,6 +17,8 @@
154 add_definitions(-DHELPER_EXEC="${PLUG_DIR}/UbuntuSecurityPrivacyHelper" -DQT_NO_KEYWORDS)
155
156 set(QML_SOURCES
157+ AppAccess.qml
158+ AppAccessControl.qml
159 Dash.qml
160 Location.qml
161 LockSecurity.qml
162@@ -26,13 +28,30 @@
163
164 set(PANEL_SOURCES
165 plugin.cpp
166+ plugin.h
167 securityprivacy.cpp
168+ securityprivacy.h
169+ trust-store-model.cpp
170+ trust-store-model.h
171+ ${QML_SOURCES}
172 )
173
174 add_library(UbuntuSecurityPrivacyPanel MODULE ${PANEL_SOURCES} ${QML_SOURCES})
175 qt5_use_modules(UbuntuSecurityPrivacyPanel Qml Quick DBus)
176
177-target_link_libraries (UbuntuSecurityPrivacyPanel uss-accountsservice ${ACCOUNTSSERVICE_LDFLAGS})
178+pkg_check_modules(TRUST_STORE REQUIRED trust-store)
179+
180+include_directories(
181+ ${CMAKE_CURRENT_BINARY_DIR}
182+ ${TRUST_STORE_INCLUDE_DIRS}
183+)
184+
185+set(PLUG_DIR ${PLUGIN_PRIVATE_MODULE_DIR}/Ubuntu/SystemSettings/SecurityPrivacy)
186+target_link_libraries (UbuntuSecurityPrivacyPanel
187+ uss-accountsservice
188+ ${ACCOUNTSSERVICE_LDFLAGS}
189+ ${TRUST_STORE_LDFLAGS}
190+)
191 install(TARGETS UbuntuSecurityPrivacyPanel UbuntuSecurityPrivacyHelper DESTINATION ${PLUG_DIR})
192 install(FILES qmldir DESTINATION ${PLUG_DIR})
193 install(FILES ${QML_SOURCES} DESTINATION ${PLUGIN_QML_DIR}/security-privacy)
194
195=== modified file 'plugins/security-privacy/PageComponent.qml'
196--- plugins/security-privacy/PageComponent.qml 2014-07-13 23:57:50 +0000
197+++ plugins/security-privacy/PageComponent.qml 2014-08-11 11:06:25 +0000
198@@ -173,7 +173,7 @@
199 ListItem.SingleValue {
200 text: i18n.tr("Other app access")
201 progression: true
202- visible: showAllUI
203+ onClicked: pageStack.push(Qt.resolvedUrl("AppAccess.qml"))
204 }
205 ListItem.SingleValue {
206 text: i18n.tr("Diagnostics")
207
208=== modified file 'plugins/security-privacy/plugin.cpp'
209--- plugins/security-privacy/plugin.cpp 2013-08-29 14:20:50 +0000
210+++ plugins/security-privacy/plugin.cpp 2014-08-11 11:06:25 +0000
211@@ -18,6 +18,7 @@
212
213 #include "plugin.h"
214 #include "securityprivacy.h"
215+#include "trust-store-model.h"
216
217 #include <QtQml/QtQml>
218
219@@ -25,6 +26,7 @@
220 {
221 Q_ASSERT(uri == QLatin1String("Ubuntu.SystemSettings.SecurityPrivacy"));
222 qmlRegisterType<SecurityPrivacy>(uri, 1, 0, "UbuntuSecurityPrivacyPanel");
223+ qmlRegisterType<TrustStoreModel>(uri, 1, 0, "TrustStoreModel");
224 }
225
226 void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
227
228=== added file 'plugins/security-privacy/trust-store-model.cpp'
229--- plugins/security-privacy/trust-store-model.cpp 1970-01-01 00:00:00 +0000
230+++ plugins/security-privacy/trust-store-model.cpp 2014-08-11 11:06:25 +0000
231@@ -0,0 +1,333 @@
232+/*
233+ * Copyright (C) 2014 Canonical, Ltd.
234+ *
235+ * This program is free software; you can redistribute it and/or modify
236+ * it under the terms of the GNU General Public License as published by
237+ * the Free Software Foundation; version 3.
238+ *
239+ * This program is distributed in the hope that it will be useful,
240+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
241+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
242+ * GNU General Public License for more details.
243+ *
244+ * You should have received a copy of the GNU General Public License
245+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
246+ *
247+ * Authors: Alberto Mardegan <alberto.mardegan@canonical.com>
248+ */
249+
250+#include "trust-store-model.h"
251+
252+#include <QDebug>
253+#include <QList>
254+#include <QMap>
255+#include <QSet>
256+#include <QSettings>
257+#include <QStandardPaths>
258+
259+#include <core/trust/resolve.h>
260+#include <core/trust/store.h>
261+
262+class Application
263+{
264+public:
265+ Application() {}
266+
267+ void setId(const QString &id) {
268+ this->id = id;
269+
270+ QString localShare =
271+ QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
272+ QSettings desktopFile(QString("%1/applications/%2.desktop").
273+ arg(localShare).arg(id),
274+ QSettings::IniFormat);
275+ desktopFile.beginGroup("Desktop Entry");
276+ displayName = desktopFile.value("Name").toString();
277+ iconName = desktopFile.value("Icon").toString();
278+ }
279+
280+ void addRequest(const core::trust::Request &request) {
281+ if (request.answer == core::trust::Request::Answer::granted) {
282+ grantedFeatures.insert(request.feature.value);
283+ } else {
284+ grantedFeatures.remove(request.feature.value);
285+ }
286+ }
287+
288+ bool hasGrants() const { return !grantedFeatures.isEmpty(); }
289+
290+ QString id;
291+ QString displayName;
292+ QString iconName;
293+ QSet<std::uint64_t> grantedFeatures;
294+};
295+
296+class TrustStoreModelPrivate: public QObject
297+{
298+ Q_OBJECT
299+ Q_DECLARE_PUBLIC(TrustStoreModel)
300+
301+public:
302+ TrustStoreModelPrivate(TrustStoreModel *model);
303+ ~TrustStoreModelPrivate();
304+
305+ void update();
306+ void updateRow(int row);
307+ void updateGrantedCount();
308+
309+private:
310+ QHash<int, QByteArray> roleNames;
311+ bool componentCompleted;
312+ QString serviceName;
313+ int grantedCount;
314+ std::shared_ptr<core::trust::Store> trustStore;
315+ QList<Application> applications;
316+ mutable TrustStoreModel *q_ptr;
317+};
318+
319+TrustStoreModelPrivate::TrustStoreModelPrivate(TrustStoreModel *model):
320+ QObject(model),
321+ componentCompleted(false),
322+ grantedCount(0),
323+ q_ptr(model)
324+{
325+}
326+
327+TrustStoreModelPrivate::~TrustStoreModelPrivate()
328+{
329+}
330+
331+void TrustStoreModelPrivate::update()
332+{
333+ Q_Q(TrustStoreModel);
334+
335+ if (!componentCompleted) return;
336+
337+ q->beginResetModel();
338+
339+ if (trustStore) {
340+ trustStore.reset();
341+ }
342+
343+ trustStore = core::trust::resolve_store_in_session_with_name(
344+ serviceName.toStdString());
345+
346+ /* Test is the trustStore is valid; ideally, there should be an API on the
347+ * trust-store to check this. See
348+ * https://bugs.launchpad.net/bugs/1348215 */
349+ try {
350+ auto query = trustStore->query();
351+ } catch (std::exception &e) {
352+ qWarning() << "Exception " << e.what();
353+ trustStore.reset();
354+ }
355+
356+ QMap<QString,Application> appMap;
357+
358+ if (trustStore) {
359+ auto query = trustStore->query();
360+ query->execute();
361+
362+ while (query->status() != core::trust::Store::Query::Status::eor) {
363+ auto r = query->current();
364+
365+ QString applicationId = QString::fromStdString(r.from);
366+
367+ Application &app = appMap[applicationId];
368+ app.setId(applicationId);
369+ app.addRequest(r);
370+
371+ query->next();
372+ }
373+ }
374+
375+ applications = appMap.values();
376+ updateGrantedCount();
377+
378+ q->endResetModel();
379+}
380+
381+void TrustStoreModelPrivate::updateRow(int row)
382+{
383+ Q_Q(TrustStoreModel);
384+
385+ Q_ASSERT(trustStore);
386+ Q_ASSERT(row >= 0 && row < d->applications.count());
387+
388+ Application &app = applications[row];
389+ app.grantedFeatures.clear();
390+
391+ auto query = trustStore->query();
392+ query->for_application_id(app.id.toStdString());
393+ query->execute();
394+
395+ while (query->status() != core::trust::Store::Query::Status::eor) {
396+ auto r = query->current();
397+
398+ app.addRequest(r);
399+
400+ query->next();
401+ }
402+
403+ updateGrantedCount();
404+
405+ /* Let the model emit the change notification */
406+ QModelIndex index = q->index(row);
407+ q->dataChanged(index, index);
408+}
409+
410+void TrustStoreModelPrivate::updateGrantedCount()
411+{
412+ Q_Q(TrustStoreModel);
413+
414+ int count = 0;
415+
416+ Q_FOREACH(const Application &app, applications) {
417+ if (app.hasGrants()) count++;
418+ }
419+
420+ if (count != grantedCount) {
421+ grantedCount = count;
422+ Q_EMIT q->grantedCountChanged();
423+ }
424+}
425+
426+TrustStoreModel::TrustStoreModel(QObject *parent):
427+ QAbstractListModel(parent),
428+ d_ptr(new TrustStoreModelPrivate(this))
429+{
430+ Q_D(TrustStoreModel);
431+ d->roleNames[Qt::DisplayRole] = "applicationName";
432+ d->roleNames[ApplicationIdRole] = "applicationId";
433+ d->roleNames[IconNameRole] = "iconName";
434+ d->roleNames[GrantedRole] = "granted";
435+
436+ QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &,int,int)),
437+ this, SIGNAL(countChanged()));
438+ QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &,int,int)),
439+ this, SIGNAL(countChanged()));
440+ QObject::connect(this, SIGNAL(modelReset()),
441+ this, SIGNAL(countChanged()));
442+}
443+
444+TrustStoreModel::~TrustStoreModel()
445+{
446+}
447+
448+void TrustStoreModel::classBegin()
449+{
450+}
451+
452+void TrustStoreModel::componentComplete()
453+{
454+ Q_D(TrustStoreModel);
455+ d->componentCompleted = true;
456+ d->update();
457+}
458+
459+void TrustStoreModel::setServiceName(const QString &serviceName)
460+{
461+ Q_D(TrustStoreModel);
462+
463+ if (serviceName == d->serviceName) return;
464+ d->serviceName = serviceName;
465+ d->update();
466+ Q_EMIT serviceNameChanged();
467+}
468+
469+QString TrustStoreModel::serviceName() const
470+{
471+ Q_D(const TrustStoreModel);
472+ return d->serviceName;
473+}
474+
475+int TrustStoreModel::grantedCount() const
476+{
477+ Q_D(const TrustStoreModel);
478+ return d->grantedCount;
479+}
480+
481+void TrustStoreModel::setEnabled(int row, bool enabled)
482+{
483+ Q_D(TrustStoreModel);
484+
485+ if (Q_UNLIKELY(!d->trustStore)) {
486+ qWarning() << "Trust store is NULL on setEnabled call";
487+ return;
488+ }
489+
490+ if (Q_UNLIKELY(row >= d->applications.count())) return;
491+
492+ const Application &app = d->applications.at(row);
493+
494+ core::trust::Request r;
495+ r.from = app.id.toStdString();
496+ r.feature = core::trust::Feature(core::trust::Request::default_feature);
497+ r.answer = enabled ?
498+ core::trust::Request::Answer::granted : core::trust::Request::Answer::denied;
499+ r.when = std::chrono::system_clock::now();
500+
501+ d->trustStore->add(r);
502+
503+ /* When disabling, we must disable all the features */
504+ if (!enabled) {
505+ Q_FOREACH(std::int64_t feature, app.grantedFeatures) {
506+ /* Skip the default feature, we already disabled it */
507+ if (feature == core::trust::Request::default_feature) continue;
508+
509+ r.feature = core::trust::Feature(feature);
510+ d->trustStore->add(r);
511+ }
512+ }
513+
514+ /* Reload the application from the trust store */
515+ d->updateRow(row);
516+}
517+
518+QVariant TrustStoreModel::get(int row, const QString &roleName) const
519+{
520+ int role = roleNames().key(roleName.toLatin1(), -1);
521+ return data(index(row), role);
522+}
523+
524+int TrustStoreModel::rowCount(const QModelIndex &parent) const
525+{
526+ Q_D(const TrustStoreModel);
527+ Q_UNUSED(parent);
528+ return d->applications.count();
529+}
530+
531+QVariant TrustStoreModel::data(const QModelIndex &index, int role) const
532+{
533+ Q_D(const TrustStoreModel);
534+
535+ if (index.row() >= d->applications.count()) return QVariant();
536+
537+ const Application &app = d->applications.at(index.row());
538+ QVariant ret;
539+
540+ switch (role) {
541+ case Qt::DisplayRole:
542+ ret = app.displayName;
543+ break;
544+ case IconNameRole:
545+ ret = app.iconName;
546+ break;
547+ case ApplicationIdRole:
548+ ret = app.id;
549+ break;
550+ case GrantedRole:
551+ ret = app.hasGrants();
552+ break;
553+ }
554+
555+ return ret;
556+}
557+
558+QHash<int, QByteArray> TrustStoreModel::roleNames() const
559+{
560+ Q_D(const TrustStoreModel);
561+ return d->roleNames;
562+}
563+
564+#include "trust-store-model.moc"
565
566=== added file 'plugins/security-privacy/trust-store-model.h'
567--- plugins/security-privacy/trust-store-model.h 1970-01-01 00:00:00 +0000
568+++ plugins/security-privacy/trust-store-model.h 2014-08-11 11:06:25 +0000
569@@ -0,0 +1,74 @@
570+/*
571+ * Copyright (C) 2014 Canonical, Ltd.
572+ *
573+ * This program is free software; you can redistribute it and/or modify
574+ * it under the terms of the GNU General Public License as published by
575+ * the Free Software Foundation; version 3.
576+ *
577+ * This program is distributed in the hope that it will be useful,
578+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
579+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
580+ * GNU General Public License for more details.
581+ *
582+ * You should have received a copy of the GNU General Public License
583+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
584+ *
585+ * Authors: Alberto Mardegan <alberto.mardegan@canonical.com>
586+ */
587+
588+#ifndef TRUST_STORE_MODEL_H
589+#define TRUST_STORE_MODEL_H
590+
591+#include <QAbstractListModel>
592+#include <QQmlParserStatus>
593+#include <QString>
594+
595+class TrustStoreModelPrivate;
596+
597+class TrustStoreModel: public QAbstractListModel, public QQmlParserStatus
598+{
599+ Q_OBJECT
600+ Q_INTERFACES(QQmlParserStatus)
601+ Q_PROPERTY(QString serviceName READ serviceName WRITE setServiceName
602+ NOTIFY serviceNameChanged)
603+ Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
604+ Q_PROPERTY(int grantedCount READ grantedCount NOTIFY grantedCountChanged)
605+
606+public:
607+ explicit TrustStoreModel(QObject *parent = 0);
608+ ~TrustStoreModel();
609+
610+ enum Roles {
611+ ApplicationIdRole = Qt::UserRole + 1,
612+ IconNameRole,
613+ GrantedRole,
614+ };
615+
616+ void setServiceName(const QString &serviceName);
617+ QString serviceName() const;
618+
619+ int grantedCount() const;
620+
621+ Q_INVOKABLE void setEnabled(int row, bool enabled);
622+ Q_INVOKABLE QVariant get(int row, const QString &roleName) const;
623+
624+ // reimplemented virtual methods
625+ int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
626+ QVariant data(const QModelIndex &index,
627+ int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
628+ QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
629+
630+ void classBegin() Q_DECL_OVERRIDE;
631+ void componentComplete() Q_DECL_OVERRIDE;
632+
633+Q_SIGNALS:
634+ void serviceNameChanged();
635+ void countChanged();
636+ void grantedCountChanged();
637+
638+private:
639+ TrustStoreModelPrivate *d_ptr;
640+ Q_DECLARE_PRIVATE(TrustStoreModel)
641+};
642+
643+#endif // TRUST_STORE_MODEL_H
644
645=== modified file 'po/CMakeLists.txt'
646--- po/CMakeLists.txt 2013-12-31 19:30:03 +0000
647+++ po/CMakeLists.txt 2014-08-11 11:06:25 +0000
648@@ -23,6 +23,7 @@
649 --copyright=\"Canonical Ltd.\"
650 --package-name ubuntu-system-settings
651 --qt --c++ --add-comments=TRANSLATORS
652+ --keyword=QT_TR_NOOP
653 --keyword=tr --keyword=tr:1,2 --from-code=UTF-8
654 ${QMLFILES} "${CMAKE_CURRENT_BINARY_DIR}/settings.js"
655 WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
656
657=== modified file 'tests/plugins/CMakeLists.txt'
658--- tests/plugins/CMakeLists.txt 2014-07-31 13:43:09 +0000
659+++ tests/plugins/CMakeLists.txt 2014-08-11 11:06:25 +0000
660@@ -1,2 +1,3 @@
661+add_subdirectory(security-privacy)
662 add_subdirectory(system-update)
663 add_subdirectory(bluetooth)
664
665=== added directory 'tests/plugins/security-privacy'
666=== added file 'tests/plugins/security-privacy/CMakeLists.txt'
667--- tests/plugins/security-privacy/CMakeLists.txt 1970-01-01 00:00:00 +0000
668+++ tests/plugins/security-privacy/CMakeLists.txt 2014-08-11 11:06:25 +0000
669@@ -0,0 +1,11 @@
670+set(XVFB_CMD xvfb-run -a -s "-screen 0 640x480x24")
671+include_directories(${CMAKE_CURRENT_BINARY_DIR} ../../../plugins/security-privacy)
672+add_definitions(-DTESTS)
673+
674+add_executable(tst-trust-store-model
675+ tst_trust_store_model.cpp
676+ ../../../plugins/security-privacy/trust-store-model.cpp
677+)
678+
679+qt5_use_modules(tst-trust-store-model Core DBus Qml Test)
680+add_test(NAME tst-trust-store-model COMMAND ${XVFB_CMD} ${CMAKE_CURRENT_BINARY_DIR}/tst-trust-store-model)
681
682=== added file 'tests/plugins/security-privacy/tst_trust_store_model.cpp'
683--- tests/plugins/security-privacy/tst_trust_store_model.cpp 1970-01-01 00:00:00 +0000
684+++ tests/plugins/security-privacy/tst_trust_store_model.cpp 2014-08-11 11:06:25 +0000
685@@ -0,0 +1,394 @@
686+/*
687+ * Copyright 2013 Canonical Ltd.
688+ *
689+ * This library is free software; you can redistribute it and/or
690+ * modify it under the terms of version 3 of the GNU Lesser General Public
691+ * License as published by the Free Software Foundation.
692+ *
693+ * This program is distributed in the hope that it will be useful,
694+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
695+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
696+ * General Public License for more details.
697+ *
698+ * You should have received a copy of the GNU Lesser General Public
699+ * License along with this library; if not, write to the
700+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
701+ * Boston, MA 02110-1301, USA.
702+ */
703+
704+#include <QTest>
705+#include <QSignalSpy>
706+#include <QtQml>
707+
708+#include <core/trust/resolve.h>
709+#include <core/trust/store.h>
710+
711+#include "trust-store-model.h"
712+
713+/* mocking trust-store { */
714+namespace
715+{
716+namespace mock
717+{
718+struct Store: public core::trust::Store
719+{
720+ std::string m_name;
721+ std::list<core::trust::Request> m_allRequests;
722+ static std::shared_ptr<mock::Store> m_instance;
723+
724+ Store()
725+ {
726+ }
727+
728+ ~Store()
729+ {
730+ }
731+
732+ static void setInstance(std::shared_ptr<mock::Store> store) {
733+ m_instance = store;
734+ }
735+
736+ struct Query: public core::trust::Store::Query
737+ {
738+ mock::Store *m_store;
739+ std::list<core::trust::Request> m_requests;
740+ std::list<core::trust::Request>::iterator m_it;
741+ std::string m_applicationFilter;
742+ core::trust::Store::Query::Status m_status;
743+
744+ Query(mock::Store *store):
745+ m_store(store)
746+ {
747+ }
748+
749+ ~Query()
750+ {
751+ }
752+
753+ void all()
754+ {
755+ m_applicationFilter.clear();
756+ }
757+
758+ core::trust::Request current()
759+ {
760+ if (m_it == m_requests.end())
761+ {
762+ throw core::trust::Store::Query::Errors::NoCurrentResult{};
763+ }
764+
765+ return *m_it;
766+ }
767+
768+ void erase()
769+ {
770+ }
771+
772+ void execute()
773+ {
774+ if (m_applicationFilter.empty()) {
775+ m_requests = m_store->m_allRequests;
776+ } else {
777+ m_requests.clear();
778+ for (auto it = m_store->m_allRequests.begin();
779+ it != m_store->m_allRequests.end();
780+ it++) {
781+ if (it->from == m_applicationFilter) {
782+ m_requests.push_back(*it);
783+ }
784+ }
785+ }
786+ m_it = m_requests.begin();
787+ m_status = (m_it == m_requests.end()) ?
788+ core::trust::Store::Query::Status::eor :
789+ core::trust::Store::Query::Status::has_more_results;
790+ }
791+
792+ void for_answer(core::trust::Request::Answer)
793+ {
794+ }
795+
796+ void for_application_id(const std::string &id)
797+ {
798+ m_applicationFilter = id;
799+ }
800+
801+ void for_feature(core::trust::Feature)
802+ {
803+ }
804+
805+ void for_interval(const core::trust::Request::Timestamp &,
806+ const core::trust::Request::Timestamp &)
807+ {
808+ }
809+
810+ void next()
811+ {
812+ m_it++;
813+ m_status = (m_it == m_requests.end()) ?
814+ core::trust::Store::Query::Status::eor :
815+ core::trust::Store::Query::Status::has_more_results;
816+ }
817+
818+ core::trust::Store::Query::Status status() const
819+ {
820+ return m_status;
821+ }
822+ };
823+
824+ void add(const core::trust::Request &r)
825+ {
826+ m_allRequests.push_back(r);
827+ }
828+
829+ void reset()
830+ {
831+ }
832+
833+ std::shared_ptr<core::trust::Store::Query> query()
834+ {
835+ auto query =
836+ std::shared_ptr<core::trust::Store::Query>(new mock::Store::Query(
837+ this));
838+
839+ return query;
840+ }
841+};
842+}
843+}
844+
845+std::shared_ptr<mock::Store> mock::Store::m_instance;
846+
847+std::shared_ptr<core::trust::Store> core::trust::resolve_store_in_session_with_name(
848+ const std::string &name)
849+{
850+ if (name.empty())
851+ throw Errors::ServiceNameMustNotBeEmpty{};
852+
853+ mock::Store::m_instance->m_name = name;
854+ return mock::Store::m_instance;
855+}
856+/* } mocking trust-store */
857+
858+class TrustStoreModelTest: public QObject
859+{
860+ Q_OBJECT
861+
862+private Q_SLOTS:
863+ void initTestCase();
864+ void init();
865+ void cleanup();
866+ void testEmpty();
867+ void testList_data();
868+ void testList();
869+ void testAppEnabling_data();
870+ void testAppEnabling();
871+
872+private:
873+ QVariant get(const QAbstractListModel *model, int row, QString roleName);
874+
875+private:
876+ std::shared_ptr<mock::Store> m_store;
877+};
878+
879+QVariant TrustStoreModelTest::get(const QAbstractListModel *model, int row,
880+ QString roleName)
881+{
882+ QHash<int, QByteArray> roleNames = model->roleNames();
883+
884+ int role = roleNames.key(roleName.toLatin1(), -1);
885+ return model->data(model->index(row), role);
886+}
887+
888+void TrustStoreModelTest::initTestCase()
889+{
890+ qmlRegisterType<TrustStoreModel>("TrustStore.Test", 0, 1,
891+ "TrustStoreModel");
892+}
893+
894+void TrustStoreModelTest::init()
895+{
896+ m_store = std::shared_ptr<mock::Store>(new mock::Store);
897+ mock::Store::setInstance(m_store);
898+}
899+
900+void TrustStoreModelTest::cleanup()
901+{
902+ m_store.reset();
903+}
904+
905+void TrustStoreModelTest::testEmpty()
906+{
907+ QQmlEngine engine;
908+ QQmlComponent component(&engine);
909+ component.setData("import TrustStore.Test 0.1\n"
910+ "TrustStoreModel {\n"
911+ " serviceName: \"storeTest\"\n"
912+ "}",
913+ QUrl());
914+ QObject *object = component.create();
915+ QVERIFY(object != 0);
916+ QAbstractListModel *model = qobject_cast<QAbstractListModel*>(object);
917+ QVERIFY(model != 0);
918+
919+ QCOMPARE(model->rowCount(), 0);
920+
921+ QCOMPARE(m_store->m_name, std::string("storeTest"));
922+}
923+
924+void TrustStoreModelTest::testList_data()
925+{
926+ QTest::addColumn<QStringList>("appIds");
927+ QTest::addColumn<QList<bool> >("appGrants");
928+ QTest::addColumn<QStringList>("expectedAppIds");
929+ QTest::addColumn<QList<bool> >("expectedAppGrants");
930+
931+ QTest::newRow("No repetitions") <<
932+ (QStringList() << "Calendar" << "Gallery" << "MyApp") <<
933+ (QList<bool>() << true << false << true) <<
934+ (QStringList() << "Calendar" << "Gallery" << "MyApp") <<
935+ (QList<bool>() << true << false << true);
936+
937+ QTest::newRow("With repetitions") <<
938+ (QStringList() << "Calendar" << "Gallery" << "MyApp" << "Calendar") <<
939+ (QList<bool>() << true << false << true << false) <<
940+ (QStringList() << "Calendar" << "Gallery" << "MyApp") <<
941+ (QList<bool>() << false << false << true);
942+}
943+
944+void TrustStoreModelTest::testList()
945+{
946+ QFETCH(QStringList, appIds);
947+ QFETCH(QList<bool>, appGrants);
948+ QFETCH(QStringList, expectedAppIds);
949+ QFETCH(QList<bool>, expectedAppGrants);
950+
951+ for (int i = 0; i < appIds.count(); i++) {
952+ core::trust::Request r;
953+ r.from = appIds[i].toStdString();
954+ r.feature = core::trust::Feature(core::trust::Request::default_feature);
955+ r.answer = appGrants[i] ?
956+ core::trust::Request::Answer::granted : core::trust::Request::Answer::denied;
957+ r.when = std::chrono::system_clock::now();
958+ m_store->add(r);
959+ }
960+
961+ QQmlEngine engine;
962+ QQmlComponent component(&engine);
963+ component.setData("import TrustStore.Test 0.1\n"
964+ "TrustStoreModel {\n"
965+ " serviceName: \"storeTest\"\n"
966+ "}",
967+ QUrl());
968+ QObject *object = component.create();
969+ QVERIFY(object != 0);
970+ QAbstractListModel *model = qobject_cast<QAbstractListModel*>(object);
971+ QVERIFY(model != 0);
972+
973+ QCOMPARE(model->rowCount(), expectedAppIds.count());
974+
975+ for (int i = 0; i < model->rowCount(); i++) {
976+ QCOMPARE(get(model, i, "applicationId").toString(), expectedAppIds[i]);
977+ QCOMPARE(get(model, i, "granted").toBool(), expectedAppGrants[i]);
978+ }
979+}
980+
981+void TrustStoreModelTest::testAppEnabling_data()
982+{
983+ /* In this test the model will be pre-populated with three applications:
984+ * - Calendar: enabled
985+ * - Gallery: disabled
986+ * - MyApp: enabled
987+ * We change the enabled status of each one of them at a time, and we check
988+ * that the results are consistent. */
989+ QTest::addColumn<int>("row");
990+ QTest::addColumn<bool>("mustEnable");
991+ QTest::addColumn<QStringList>("expectedEnabledApps");
992+
993+ QTest::newRow("Enabling Calendar") <<
994+ 0 << true <<
995+ (QStringList() << "Calendar" << "MyApp");
996+
997+ QTest::newRow("Disabling Calendar") <<
998+ 0 << false <<
999+ (QStringList() << "MyApp");
1000+
1001+ QTest::newRow("Enabling Gallery") <<
1002+ 1 << true <<
1003+ (QStringList() << "Calendar" << "Gallery" << "MyApp");
1004+
1005+ QTest::newRow("Disabling Gallery") <<
1006+ 1 << false <<
1007+ (QStringList() << "Calendar" << "MyApp");
1008+
1009+ QTest::newRow("Enabling MyApp") <<
1010+ 2 << true <<
1011+ (QStringList() << "Calendar" << "MyApp");
1012+
1013+ QTest::newRow("Disabling MyApp") <<
1014+ 2 << false <<
1015+ (QStringList() << "Calendar");
1016+}
1017+
1018+void TrustStoreModelTest::testAppEnabling()
1019+{
1020+ QFETCH(int, row);
1021+ QFETCH(bool, mustEnable);
1022+ QFETCH(QStringList, expectedEnabledApps);
1023+
1024+ /* Pre-populate the model (update the comment on top of
1025+ * testAppEnabling_data() if you change this). */
1026+ QStringList apps;
1027+ apps << "Calendar" << "Gallery" << "MyApp";
1028+ QList<bool> appStatuses;
1029+ appStatuses << true << false << true;
1030+ for (int i = 0; i < apps.count(); i++) {
1031+ core::trust::Request r;
1032+ r.from = apps[i].toStdString();
1033+ r.feature = core::trust::Feature(core::trust::Request::default_feature);
1034+ r.answer = appStatuses[i] ?
1035+ core::trust::Request::Answer::granted : core::trust::Request::Answer::denied;
1036+ r.when = std::chrono::system_clock::now();
1037+ m_store->add(r);
1038+ }
1039+
1040+ QQmlEngine engine;
1041+ QQmlComponent component(&engine);
1042+ component.setData("import TrustStore.Test 0.1\n"
1043+ "TrustStoreModel {\n"
1044+ " serviceName: \"storeTest\"\n"
1045+ "}",
1046+ QUrl());
1047+ QObject *object = component.create();
1048+ QVERIFY(object != 0);
1049+ QAbstractListModel *model = qobject_cast<QAbstractListModel*>(object);
1050+ QVERIFY(model != 0);
1051+
1052+ QCOMPARE(model->rowCount(), apps.count());
1053+
1054+ QSignalSpy dataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
1055+
1056+ /* Enable or disable an application */
1057+ bool ok;
1058+ ok = QMetaObject::invokeMethod(object, "setEnabled",
1059+ Q_ARG(int, row),
1060+ Q_ARG(bool, mustEnable));
1061+ QVERIFY(ok);
1062+
1063+ QTRY_COMPARE(dataChanged.count(), 1);
1064+
1065+ QStringList enabledApps;
1066+ for (int i = 0; i < model->rowCount(); i++) {
1067+ if (get(model, i, "granted").toBool()) {
1068+ enabledApps.append(get(model, i, "applicationId").toString());
1069+ }
1070+ }
1071+
1072+ QCOMPARE(enabledApps, expectedEnabledApps);
1073+ QCOMPARE(object->property("grantedCount").toInt(),
1074+ expectedEnabledApps.count());
1075+}
1076+
1077+QTEST_MAIN(TrustStoreModelTest)
1078+
1079+#include "tst_trust_store_model.moc"

Subscribers

People subscribed via source and target branches