Merge lp:~kissiel/checkbox/confined-permission-control into lp:checkbox

Proposed by Maciej Kisielewski
Status: Merged
Approved by: Maciej Kisielewski
Approved revision: 4124
Merged at revision: 4114
Proposed branch: lp:~kissiel/checkbox/confined-permission-control
Merge into: lp:checkbox
Diff against target: 316 lines (+129/-24)
8 files modified
checkbox-touch/build-me (+47/-0)
checkbox-touch/checkbox-touch.qml (+1/-1)
checkbox-touch/components/CheckboxTouchApplication.qml (+6/-0)
checkbox-touch/components/QmlConfinedPage.qml (+28/-4)
checkbox-touch/confinement/generate.py (+20/-16)
checkbox-touch/manifest.json (+2/-2)
checkbox-touch/py/checkbox_touch.py (+24/-1)
plainbox/plainbox/data/plainbox-qml-modules/Plainbox/QmlJob.qml (+1/-0)
To merge this branch: bzr merge lp:~kissiel/checkbox/confined-permission-control
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Approve
Review via email: mp+278066@code.launchpad.net

Description of the change

This MR brings a possibility of dropping specified permissions for confined tests in checkbox-converged.

bf3ffb4 checkbox-touch: fix missing test information for native jobs
eb2efd7 checkbox-touch: bump version in py backend
57cd0ed checkbox-touch: correct version in manifest to match PB format
0926cde checkbox-touch: add dropPersmission function
ac4d6a3 plainbox:data:plainbox-qml-modules: add 'clearedPermissions' field
4c7a6ff checkbox-touch: add logic for dropping permissions for confined tests
3367259 checkbox-touch: rename generate-confinement to make it importable
3656f0b checkbox-touch:confinement: make generate_confinement return the hook
c7a4f37 checkbox-touch:confinement: do not jsonize by default
7ddda80 checkbox-touch:confinement: fix PEP-8 issues
ccd2c6a checkbox-touch:build-me: add building confinement

To post a comment you must log in.
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

Most things are okay but I'd like to see something better for the database handling

review: Needs Fixing
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

> Most things are okay but I'd like to see something better for the database
> handling

As for DBs being locked, if the timeout hits, this will get propagated to UI.
For proper cleanup, I addded try, finally.

Todo for XDG stuff added.

4119. By Maciej Kisielewski

checkbox-touch: add logic for dropping permissions for confined tests

This patch adds the logic that pulls information from the test's qml file about
which permissions to clear, and calls appropriate bits.
To extract this info we first instantiate the QmlJob object in a fake container
Item, so te field is initiated as test author specified, then immedietaly (and
synchronously) destroyed, so all resources are (not sure about that :)) freed.
Signed-off-by: Maciej Kisielewski <email address hidden>

4120. By Maciej Kisielewski

checkbox-touch: rename generate-confinement to make it importable

Signed-off-by: Maciej Kisielewski <email address hidden>

4121. By Maciej Kisielewski

checkbox-touch:confinement: make generate_confinement return the hook

Signed-off-by: Maciej Kisielewski <email address hidden>

4122. By Maciej Kisielewski

checkbox-touch:confinement: do not jsonize by default

This patch makes generate result hook object instead of strigified json. This
makes easier to handle marshalling everything together in a calling function.

Signed-off-by: Maciej Kisielewski <email address hidden>

4123. By Maciej Kisielewski

checkbox-touch:confinement: fix PEP-8 issues

Signed-off-by: Maciej Kisielewski <email address hidden>

4124. By Maciej Kisielewski

checkbox-touch:build-me: add building confinement

This patch runs the task of building files necessary for confined qml tests to
work while building click package.

From now on, after build-me successfully validates all the included providers,
it loads job units from them, checks if there's any qml confined job, and
generates appropriate files necessary to run those. It also overwrites the
manifest.json file, to include the new hooks.

Signed-off-by: Maciej Kisielewski <email address hidden>

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

+1, looks good to me

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'checkbox-touch/build-me'
2--- checkbox-touch/build-me 2015-10-15 09:54:02 +0000
3+++ checkbox-touch/build-me 2015-11-25 11:37:27 +0000
4@@ -1,5 +1,6 @@
5 #!/usr/bin/env python3
6 import argparse
7+from contextlib import contextmanager
8 from distutils.dir_util import copy_tree
9 from datetime import datetime
10 import json
11@@ -9,6 +10,9 @@
12 import sys
13 import textwrap
14
15+from confinement.generate import generate_confinement
16+from py.embedded_providers import EmbeddedProvider1PlugInCollection
17+
18 # DEFAULT_PROVIDERS is a list of providers directory names as listed in
19 # ../providers. NOTE: those are directory names, not provider names as reported
20 # by manage.py info
21@@ -19,6 +23,24 @@
22 ]
23
24
25+@contextmanager
26+def chdir(path):
27+ previous_path = os.getcwd()
28+ os.chdir(path)
29+ yield
30+ os.chdir(previous_path)
31+
32+
33+def get_confined_jobs(path):
34+ collection = EmbeddedProvider1PlugInCollection(path)
35+ # this should be run within provider's directory, so there should be
36+ # only one provider loaded
37+ provider = collection.get_all_plugin_objects()[0]
38+ for job in provider.job_list:
39+ if job.plugin == 'qml' and 'confined' in job.get_flag_set():
40+ yield job
41+
42+
43 def check_libs_present():
44 """
45 Check if paths listed in NECESSARY_PATHS are present.
46@@ -117,6 +139,16 @@
47 if not validate_providers():
48 sys.exit('Provider validation failed.')
49
50+ current_manifest = json.loads(open('manifest.json', 'rt').read())
51+ hooks = {
52+ 'checkbox-touch': current_manifest['hooks']['checkbox-touch']
53+ }
54+ for hook in build_confinement(current_manifest['version']):
55+ hooks.update(hook)
56+ new_manifest = current_manifest.copy()
57+ new_manifest['hooks'] = hooks
58+ with open('manifest.json', 'wt') as f:
59+ f.write(json.dumps(new_manifest, sort_keys=True, indent=4))
60 build_i18n()
61
62 generate_desktop_file()
63@@ -159,6 +191,21 @@
64 return providers_valid
65
66
67+def build_confinement(checkbox_version):
68+ print("Generating confinement")
69+ checkbox_name = "com.ubuntu.checkbox_checkbox-touch_" + checkbox_version
70+ new_hooks = []
71+ for provider_dir in os.listdir('providers'):
72+ with chdir(os.path.join('providers', provider_dir)):
73+ for job in get_confined_jobs('.'):
74+ print(" job: {}".format(job.id))
75+ qml_file = os.path.relpath(
76+ job.qml_file, 'data')
77+ new_hooks.append(generate_confinement(
78+ provider_dir, job.partial_id, checkbox_name, qml_file))
79+ return new_hooks
80+
81+
82 def build_i18n():
83 print("Building i18n")
84 for provider_dir in os.listdir('providers'):
85
86=== modified file 'checkbox-touch/checkbox-touch.qml'
87--- checkbox-touch/checkbox-touch.qml 2015-11-18 09:00:54 +0000
88+++ checkbox-touch/checkbox-touch.qml 2015-11-25 11:37:27 +0000
89@@ -658,7 +658,7 @@
90 }
91 function performConfinedQmlTest(test) {
92 runTestActivity(test, function(test) {
93- var qmlNativePage = createPage("components/QmlConfinedPage.qml");
94+ var qmlNativePage = createPage("components/QmlConfinedPage.qml", test);
95 qmlNativePage.applicationVersion = app.applicationVersion;
96 qmlNativePage.testDone.connect(completeTest);
97 pageStack.push(qmlNativePage);
98
99=== modified file 'checkbox-touch/components/CheckboxTouchApplication.qml'
100--- checkbox-touch/components/CheckboxTouchApplication.qml 2015-10-15 22:31:52 +0000
101+++ checkbox-touch/components/CheckboxTouchApplication.qml 2015-11-25 11:37:27 +0000
102@@ -169,6 +169,12 @@
103 continuation_error(error);
104 });
105 }
106+ function dropPermissions(appId, services, continuation, continuation_error) {
107+ request("drop_permissions", [appId, services], continuation, function(error) {
108+ console.error("Unable to remove permissions");
109+ if (continuation_error) continuation_error(error);
110+ });
111+ }
112
113 function rememberPassword(password, continuation) {
114 // using low-level py.call() to 'silently' pass password string through pyotherside
115
116=== modified file 'checkbox-touch/components/QmlConfinedPage.qml'
117--- checkbox-touch/components/QmlConfinedPage.qml 2015-11-17 14:38:41 +0000
118+++ checkbox-touch/components/QmlConfinedPage.qml 2015-11-25 11:37:27 +0000
119@@ -107,11 +107,35 @@
120 Layout.fillWidth: true
121 text: i18n.tr("Start the test")
122 onClicked: {
123- busyIndicatorGroup.visible = true;
124- var appName = "com.ubuntu.checkbox_" + test["partial_id"] + "_" + applicationVersion + ".desktop";
125- console.log("Launching " + appName);
126- Qt.openUrlExternally("application:///" + appName);
127+ var appName = "com.ubuntu.checkbox_" + test["partial_id"];
128+ // load the test qml file to check which permissions should be cleared
129+ var testItemComponent = Qt.createComponent(Qt.resolvedUrl(test['qml_file']));
130+ if (testItemComponent.status == Component.Error) {
131+ console.error("Error creating testItemComponent. Possible cause: Problem with job's qml file. Error:", testItemComponent.errorString());
132+ test['outcome'] = 'fail';
133+ testDone(test);
134+ return;
135+ }
136+ var testItem = testItemComponent.createObject(dummyContainer, {"testingShell": testingShell});
137+ var clearedPermissions = testItem.clearedPermissions;
138+ testItem.destroy();
139+ var runConfinedTestApp = function() {
140+ busyIndicatorGroup.visible = true;
141+ Qt.openUrlExternally("application:///" + appName + "_" + applicationVersion+ ".desktop");
142+ }
143+ if (clearedPermissions) {
144+ app.dropPermissions(appName, clearedPermissions, function() {
145+ runConfinedTestApp();
146+ });
147+ } else {
148+ runConfinedTestApp();
149+ }
150+
151 }
152 }
153+ Item {
154+ id: dummyContainer
155+ visible: false
156+ }
157 }
158 }
159
160=== renamed file 'checkbox-touch/confinement/generate-confinement.py' => 'checkbox-touch/confinement/generate.py'
161--- checkbox-touch/confinement/generate-confinement.py 2015-07-23 10:54:18 +0000
162+++ checkbox-touch/confinement/generate.py 2015-11-25 11:37:27 +0000
163@@ -17,6 +17,7 @@
164 # You should have received a copy of the GNU General Public License
165 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
166 import argparse
167+import json
168 import os
169 import string
170 import sys
171@@ -49,15 +50,9 @@
172 X-Ubuntu-Touch=true
173 """
174
175-HOOK = """
176-"${partial_id}": {
177- "apparmor": "providers/${provider_name}/data/confined/${partial_id}.apparmor",
178- "desktop": "providers/${provider_name}/data/confined/${partial_id}.desktop",
179- "content-hub": "providers/${provider_name}/data/confined/${partial_id}-ch.json"
180-}
181-"""
182
183-def generate_confinement(provider_name, partial_id, full_checkbox_name, qml_file):
184+def generate_confinement(provider_name, partial_id, full_checkbox_name,
185+ qml_file):
186 # generate content-hub file
187 target_dir = os.path.join('data', 'confined')
188 if not os.path.exists(target_dir):
189@@ -72,17 +67,24 @@
190 with open(apparmor_path, "wt") as f:
191 f.write(APPARMOR)
192
193-
194 # generate desktop file
195 desktop_path = os.path.join(target_dir, partial_id + '.desktop')
196 template = string.Template(DESKTOP)
197 with open(desktop_path, "wt") as f:
198- f.write(template.substitute(partial_id=partial_id, provider_name=provider_name, full_checkbox_name=full_checkbox_name, qml_file=qml_file))
199+ f.write(template.substitute(
200+ partial_id=partial_id, provider_name=provider_name,
201+ full_checkbox_name=full_checkbox_name, qml_file=qml_file))
202
203-def generate_hook(provider_name, partial_id):
204- return string.Template(HOOK).substitute(
205+ base = 'providers/{provider_name}/data/confined/{partial_id}'.format(
206 provider_name=provider_name, partial_id=partial_id)
207-
208+ hook = {
209+ partial_id: {
210+ 'apparmor': base + '.apparmor',
211+ 'desktop': base + '.desktop',
212+ 'content-hub': base + '-ch.json',
213+ }
214+ }
215+ return hook
216
217
218 def main():
219@@ -91,7 +93,8 @@
220 parser.add_argument('--checkbox_version', action='store', type=str)
221 parser.add_argument('job_ids', nargs='+')
222 args = parser.parse_args()
223- checkbox_name = "com.ubuntu.checkbox_checkbox-touch_" + args.checkbox_version
224+ checkbox_name = ("com.ubuntu.checkbox_checkbox-touch_" +
225+ args.checkbox_version)
226
227 # check if current dir looks like a provider - very dumb heuristic
228 if not os.path.exists('manage.py'):
229@@ -100,8 +103,9 @@
230
231 hooks = ''
232 for job in args.job_ids:
233- generate_confinement(provider_name, job, checkbox_name, job + '.qml')
234- hooks += generate_hook(provider_name, job)
235+ hook = generate_confinement(
236+ provider_name, job, checkbox_name, job + '.qml')
237+ hooks += json.dumps(hook, sort_keys=True, indent=4)[1:-1]
238
239 print("Add following hooks to your checkbox-touch.manifest:")
240 print(hooks)
241
242=== modified file 'checkbox-touch/manifest.json'
243--- checkbox-touch/manifest.json 2015-11-16 09:17:41 +0000
244+++ checkbox-touch/manifest.json 2015-11-25 11:37:27 +0000
245@@ -11,9 +11,9 @@
246 "maintainer": "Zygmunt Krynicki <zkrynicki@ubuntu.com>",
247 "name": "com.ubuntu.checkbox",
248 "title": "Checkbox",
249- "version": "1.3.dev0",
250+ "version": "1.3.dev",
251 "x-source": {
252 "vcs-bzr": "lp:checkbox",
253- "vcs-bzr-revno": "checkbox-touch-v1.3.dev0"
254+ "vcs-bzr-revno": "checkbox-touch-v1.3.dev"
255 }
256 }
257
258=== modified file 'checkbox-touch/py/checkbox_touch.py'
259--- checkbox-touch/py/checkbox_touch.py 2015-11-16 10:20:39 +0000
260+++ checkbox-touch/py/checkbox_touch.py 2015-11-25 11:37:27 +0000
261@@ -36,6 +36,7 @@
262 import logging
263 import os
264 import pyotherside
265+import sqlite3
266 import re
267 import time
268 import traceback
269@@ -133,7 +134,7 @@
270 response data to alter the user interface.
271 """
272
273- __version__ = (1, 2, 1, 'final', 0)
274+ __version__ = (1, 3, 0, 'dev', 0)
275
276 def __init__(self):
277 if plainbox.__version__ < (0, 22):
278@@ -459,6 +460,28 @@
279 return self.assistant.export_to_transport(
280 '2013.com.canonical.plainbox::hexr', transport)
281
282+ @view
283+ def drop_permissions(self, app_id, services):
284+ # TODO: use XDG once available
285+ trust_dbs = {
286+ 'camera': '~/.local/share/CameraService/trust.db',
287+ 'audio': '~/.local/share/PulseAudio/trust.db',
288+ 'location': '~/.local/share/UbuntuLocationServices/trust.db',
289+ }
290+ sql = 'delete from requests where ApplicationId = (?);'
291+
292+ for service in services:
293+ conn = None
294+ try:
295+ conn = sqlite3.connect(
296+ os.path.expanduser(trust_dbs[service]),
297+ isolation_level='EXCLUSIVE')
298+ conn.execute(sql, (app_id,))
299+ conn.commit()
300+ finally:
301+ if conn:
302+ conn.close()
303+
304 def _get_user_directory_documents(self):
305 xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or \
306 os.path.expanduser('~/.config')
307
308=== modified file 'plainbox/plainbox/data/plainbox-qml-modules/Plainbox/QmlJob.qml'
309--- plainbox/plainbox/data/plainbox-qml-modules/Plainbox/QmlJob.qml 2015-07-07 08:32:21 +0000
310+++ plainbox/plainbox/data/plainbox-qml-modules/Plainbox/QmlJob.qml 2015-11-25 11:37:27 +0000
311@@ -24,4 +24,5 @@
312 Item {
313 signal testDone(var testResult)
314 property var testingShell;
315+ property var clearedPermissions: [];
316 }

Subscribers

People subscribed via source and target branches