Merge lp:~brian.curtin/ubuntuone-client/824252-validate-path-export into lp:ubuntuone-client

Proposed by Brian Curtin
Status: Merged
Approved by: Natalia Bidart
Approved revision: 1189
Merged at revision: 1193
Proposed branch: lp:~brian.curtin/ubuntuone-client/824252-validate-path-export
Merge into: lp:ubuntuone-client
Diff against target: 303 lines (+176/-9)
9 files modified
tests/platform/test_external_interface.py (+10/-0)
tests/syncdaemon/test_interaction_interfaces.py (+10/-0)
tests/syncdaemon/test_vm.py (+107/-1)
ubuntuone/platform/linux/dbus_interface.py (+6/-0)
ubuntuone/platform/tools/__init__.py (+8/-0)
ubuntuone/platform/windows/ipc.py (+5/-0)
ubuntuone/platform/windows/ipc_client.py (+4/-0)
ubuntuone/syncdaemon/interaction_interfaces.py (+7/-0)
ubuntuone/syncdaemon/volume_manager.py (+19/-8)
To merge this branch: bzr merge lp:~brian.curtin/ubuntuone-client/824252-validate-path-export
Reviewer Review Type Date Requested Status
Natalia Bidart (community) Approve
Roberto Alsina (community) Approve
Review via email: mp+91443@code.launchpad.net

Commit message

- Exported validate_path_from_folder from volume_manager instead of ubuntuone-control-panel's backend (lp:824252)

Description of the change

Fix lp:824252 by moving validate_path_for_folder inside volume_manager.VolumeManager and exposing it via the dbus and IPC interfaces. Corrects an earlier None/False issue that was failing on Linux.

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) wrote :

+1

review: Approve
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Branch looks good!

Two minor nitpicks:

* Any reason to have this:

from ubuntuone.syncdaemon import config, event_queue, tritcask
from ubuntuone.syncdaemon import volume_manager

instead of:

from ubuntuone.syncdaemon import config, event_queue, tritcask, volume_manager

?

* Can you please add the docstring for this method?

def validate_path_for_folder(self, path):

* Shouldn't we call normpath(path) before checking if the path is inside the user home?

Tests are green in both OSes, yey! (except for bug #929546 which is not relevant to this branch)
Thanks!

review: Needs Fixing
1189. By Brian Curtin

Address Natalia's review comments.
Rearranged import, added docstring, moved normpath call to earlier.

Revision history for this message
Brian Curtin (brian.curtin) wrote :

> Branch looks good!

rev 1189 makes the changes you suggested.

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks great!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'tests/platform/test_external_interface.py'
--- tests/platform/test_external_interface.py 2012-01-02 17:53:12 +0000
+++ tests/platform/test_external_interface.py 2012-02-09 23:36:18 +0000
@@ -551,6 +551,16 @@
551 in_signature='s', out_signature=None)551 in_signature='s', out_signature=None)
552552
553 @defer.inlineCallbacks553 @defer.inlineCallbacks
554 def test_validate_path(self):
555 """Test validate_path."""
556 result = False
557 path = 'test'
558 yield self.assert_method_called(self.service.folders,
559 'validate_path', result, path)
560 self.assert_remote_method('validate_path',
561 in_signature='s', out_signature='b')
562
563 @defer.inlineCallbacks
554 def test_get_folders(self):564 def test_get_folders(self):
555 """Test get_folders."""565 """Test get_folders."""
556 result = [{'folder_id': '1'}, {'folder_id': '2'}]566 result = [{'folder_id': '1'}, {'folder_id': '2'}]
557567
=== modified file 'tests/syncdaemon/test_interaction_interfaces.py'
--- tests/syncdaemon/test_interaction_interfaces.py 2012-01-17 20:00:44 +0000
+++ tests/syncdaemon/test_interaction_interfaces.py 2012-02-09 23:36:18 +0000
@@ -1041,6 +1041,16 @@
10411041
1042 self.assertEqual(self._called, ((folder_id,), {}))1042 self.assertEqual(self._called, ((folder_id,), {}))
10431043
1044 def test_validate_path(self):
1045 """Test the validate_path method."""
1046 # Use a lambda instead of _set_called since we need to return a tuple.
1047 self.patch(self.main.vm, 'validate_path_for_folder',
1048 lambda arg: (True, arg))
1049 path = u'this/is/a/test'
1050 rv = self.sd_obj.validate_path(path)
1051
1052 self.assertTrue(rv)
1053
1044 @defer.inlineCallbacks1054 @defer.inlineCallbacks
1045 def test_get_folders(self):1055 def test_get_folders(self):
1046 """Test the get_folders method."""1056 """Test the get_folders method."""
10471057
=== modified file 'tests/syncdaemon/test_vm.py'
--- tests/syncdaemon/test_vm.py 2012-01-18 20:47:18 +0000
+++ tests/syncdaemon/test_vm.py 2012-02-09 23:36:18 +0000
@@ -44,7 +44,7 @@
44 FakeMain,44 FakeMain,
45)45)
46from ubuntuone import platform46from ubuntuone import platform
47from ubuntuone.syncdaemon import config, event_queue, tritcask47from ubuntuone.syncdaemon import config, event_queue, tritcask, volume_manager
48from ubuntuone.syncdaemon.volume_manager import (48from ubuntuone.syncdaemon.volume_manager import (
49 ACCESS_LEVEL_RO,49 ACCESS_LEVEL_RO,
50 ACCESS_LEVEL_RW,50 ACCESS_LEVEL_RW,
@@ -2713,6 +2713,112 @@
2713 self.assertEquals(str(root_volume.node_id), root_node_id)2713 self.assertEquals(str(root_volume.node_id), root_node_id)
2714 self.assertEquals(root_volume.free_bytes, free_bytes)2714 self.assertEquals(root_volume.free_bytes, free_bytes)
27152715
2716 def test_validate_UDF_path_inside_home(self):
2717 """Test proper validation of path for creating folders."""
2718 folder_path = os.path.join(self.home_dir, 'Test Me')
2719
2720 result, msg = self.vm.validate_path_for_folder(folder_path)
2721 self.assertTrue(result)
2722 self.assertIs(msg, "",
2723 '%r must be a valid path for creating a folder.' % folder_path)
2724
2725 def test_validate_UDF_path_if_folder_shares_a_prefix_with_an_udf(self):
2726 """Test proper validation of path for creating folders.
2727
2728 If the user chooses a folder with the same prefix as an UDF, but
2729 outside every UDF, the path is valid.
2730
2731 """
2732 tricky_path = self.root_dir
2733 assert not tricky_path.endswith(os.path.sep)
2734 tricky_path += ' Suffix'
2735 assert tricky_path.startswith(self.root_dir)
2736
2737 result, msg = self.vm.validate_path_for_folder(tricky_path)
2738 self.assertTrue(result)
2739 self.assertIs(msg, "",
2740 '%r must be a valid path for creating a folder.' % tricky_path)
2741
2742 def test_validate_UDF_path_not_valid_if_outside_home(self):
2743 """A folder outside ~ is not valid."""
2744 outside_home = os.path.abspath(os.path.join(self.home_dir, os.path.pardir))
2745
2746 result, msg = self.vm.validate_path_for_folder(outside_home)
2747 self.assertFalse(result)
2748 self.assertIsNot(msg, "",
2749 '%r must be an invalid path for creating a folder.' % outside_home)
2750
2751 def test_validate_UDF_not_valid_if_folder_inside_root(self):
2752 """A folder inside the root is not valid."""
2753 root_path = self.root_dir
2754 # create a valid path inside the root
2755 inside_root = os.path.abspath(os.path.join(root_path, 'test'))
2756
2757 result, msg = self.vm.validate_path_for_folder(inside_root)
2758 self.assertFalse(result)
2759 self.assertIsNot(msg, "",
2760 '%r must be an invalid path for creating a folder.' % inside_root)
2761
2762 @defer.inlineCallbacks
2763 def test_validate_UDF_not_valid_if_folder_inside_an_udf(self):
2764 """A folder inside an UDF is not valid."""
2765 udf = self._create_udf(subscribed=True)
2766 yield self.vm.add_udf(udf)
2767 # create a valid path inside an existing UDF
2768 inside_udf = os.path.abspath(os.path.join(udf.path, 'test'))
2769
2770 result, msg = self.vm.validate_path_for_folder(inside_udf)
2771 self.assertFalse(result)
2772 self.assertIsNot(msg, "",
2773 '%r must be an invalid path for creating a folder.' % inside_udf)
2774
2775 @defer.inlineCallbacks
2776 def test_validate_UDF_not_valid_if_folder_is_parent_of_an_udf(self):
2777 """A folder parent of an UDF is not valid."""
2778 udf = self._create_udf(subscribed=True)
2779 yield self.vm.add_udf(udf)
2780 # create a valid path that is parent from an existing UDF
2781 udf_parent = os.path.abspath(os.path.join(self.home_dir, os.path.pardir))
2782
2783 result, msg = self.vm.validate_path_for_folder(udf_parent)
2784 self.assertFalse(result)
2785 self.assertIsNot(msg, "",
2786 '%r must be an invalid path for creating a folder.' % udf_parent)
2787
2788 def test_not_valid_if_folder_is_link(self):
2789 """A link path is not valid."""
2790 self.patch(volume_manager, 'is_link', lambda p: True)
2791 path_link = os.path.join(self.home_dir, 'Test Me')
2792
2793 result, msg = self.vm.validate_path_for_folder(path_link)
2794 self.assertFalse(result)
2795 self.assertIsNot(msg, "",
2796 '%r must be an invalid path for creating a folder.' % path_link)
2797
2798 @defer.inlineCallbacks
2799 def test_no_UDFs_outside_home(self):
2800 udf = self._create_udf(subscribed=True)
2801 yield self.vm.add_udf(udf)
2802 # Use a directory surely outside home
2803 # Drive root on Windows, or root on linux
2804 outside_path = os.path.splitdrive(udf.path)[0] or "/"
2805 # patch FakeAQ
2806 def create_udf(path, name, marker):
2807 """Fake create_udf"""
2808 d = dict(volume_id=uuid.uuid4(), node_id=uuid.uuid4(),
2809 marker=marker)
2810 self.main.event_q.push("AQ_CREATE_UDF_OK", **d)
2811
2812 self.main.action_q.create_udf = create_udf
2813 d = defer.Deferred()
2814 self._listen_for('VM_UDF_CREATE_ERROR', d.callback)
2815 self._listen_for('VM_UDF_CREATED', lambda r: d.errback(Exception(r)))
2816 self.vm.create_udf(outside_path)
2817 result = yield d
2818 self.assertEqual(1, len(list(self.vm.udfs.keys())))
2819 self.assertEqual(result,
2820 dict(path=outside_path, error="UDFs must be within home"))
2821
2716 @defer.inlineCallbacks2822 @defer.inlineCallbacks
2717 def test_no_UDFs_inside_root(self):2823 def test_no_UDFs_inside_root(self):
2718 """Test that a UDF can't be created inside the root"""2824 """Test that a UDF can't be created inside the root"""
27192825
=== modified file 'ubuntuone/platform/linux/dbus_interface.py'
--- ubuntuone/platform/linux/dbus_interface.py 2012-01-02 20:24:31 +0000
+++ ubuntuone/platform/linux/dbus_interface.py 2012-02-09 23:36:18 +0000
@@ -699,6 +699,12 @@
699 """Delete the folder specified by folder_id"""699 """Delete the folder specified by folder_id"""
700 return self.service.folders.delete(folder_id)700 return self.service.folders.delete(folder_id)
701701
702 @dbus.service.method(DBUS_IFACE_FOLDERS_NAME,
703 in_signature='s', out_signature='b')
704 def validate_path(self, path):
705 """Return True if the path is valid for a folder"""
706 return self.service.folders.validate_path(path)
707
702 @dbus.service.method(DBUS_IFACE_FOLDERS_NAME, out_signature='aa{ss}')708 @dbus.service.method(DBUS_IFACE_FOLDERS_NAME, out_signature='aa{ss}')
703 def get_folders(self):709 def get_folders(self):
704 """Return the list of folders (a list of dicts)"""710 """Return the list of folders (a list of dicts)"""
705711
=== modified file 'ubuntuone/platform/tools/__init__.py'
--- ubuntuone/platform/tools/__init__.py 2011-12-19 13:39:46 +0000
+++ ubuntuone/platform/tools/__init__.py 2012-02-09 23:36:18 +0000
@@ -441,6 +441,14 @@
441441
442 @defer.inlineCallbacks442 @defer.inlineCallbacks
443 @log_call(logger.debug)443 @log_call(logger.debug)
444 def validate_path(self, path):
445 """Return True if the path is valid for a folder."""
446 result = yield self.proxy.call_method('folders', 'validate', path)
447 self.log.debug('valid: %r', result)
448 defer.returnValue(result)
449
450 @defer.inlineCallbacks
451 @log_call(logger.debug)
444 def get_folders(self):452 def get_folders(self):
445 """Return the list of folders (a list of dicts)."""453 """Return the list of folders (a list of dicts)."""
446 results = yield self.proxy.call_method('folders', 'get_folders')454 results = yield self.proxy.call_method('folders', 'get_folders')
447455
=== modified file 'ubuntuone/platform/windows/ipc.py'
--- ubuntuone/platform/windows/ipc.py 2012-01-03 12:40:04 +0000
+++ ubuntuone/platform/windows/ipc.py 2012-02-09 23:36:18 +0000
@@ -773,6 +773,7 @@
773 remote_calls = [773 remote_calls = [
774 'create',774 'create',
775 'delete',775 'delete',
776 'validate_path',
776 'get_folders',777 'get_folders',
777 'subscribe',778 'subscribe',
778 'unsubscribe',779 'unsubscribe',
@@ -799,6 +800,10 @@
799 """Delete the folder specified by folder_id"""800 """Delete the folder specified by folder_id"""
800 return self.service.folders.delete(folder_id)801 return self.service.folders.delete(folder_id)
801802
803 def validate_path(self, path):
804 """Return True if the path is valid for a folder."""
805 return self.service.folders.validate_path(path)
806
802 def get_folders(self):807 def get_folders(self):
803 """Return the list of folders (a list of dicts)"""808 """Return the list of folders (a list of dicts)"""
804 return self.service.folders.get_folders()809 return self.service.folders.get_folders()
805810
=== modified file 'ubuntuone/platform/windows/ipc_client.py'
--- ubuntuone/platform/windows/ipc_client.py 2012-01-03 12:40:04 +0000
+++ ubuntuone/platform/windows/ipc_client.py 2012-02-09 23:36:18 +0000
@@ -579,6 +579,10 @@
579 """Delete the folder specified by folder_id"""579 """Delete the folder specified by folder_id"""
580580
581 @remote581 @remote
582 def validate_path(self, path):
583 """Return True if the path is valid for a folder"""
584
585 @remote
582 def get_folders(self):586 def get_folders(self):
583 """Return the list of folders (a list of dicts)"""587 """Return the list of folders (a list of dicts)"""
584588
585589
=== modified file 'ubuntuone/syncdaemon/interaction_interfaces.py'
--- ubuntuone/syncdaemon/interaction_interfaces.py 2012-01-15 19:31:07 +0000
+++ ubuntuone/syncdaemon/interaction_interfaces.py 2012-02-09 23:36:18 +0000
@@ -674,6 +674,13 @@
674 """Delete the folder specified by folder_id."""674 """Delete the folder specified by folder_id."""
675 self.vm.delete_volume(folder_id)675 self.vm.delete_volume(folder_id)
676676
677 @unicode_to_bytes
678 @log_call(logger.debug)
679 def validate_path(self, path):
680 """Return True if the path is valid for a folder."""
681 # Returns (bool, str), but we only care about the bool from here on.
682 return self.vm.validate_path_for_folder(path)[0]
683
677 @log_call(logger.debug)684 @log_call(logger.debug)
678 def get_folders(self):685 def get_folders(self):
679 """Return the list of folders (a list of dicts)."""686 """Return the list of folders (a list of dicts)."""
680687
=== modified file 'ubuntuone/syncdaemon/volume_manager.py'
--- ubuntuone/syncdaemon/volume_manager.py 2011-12-20 15:35:51 +0000
+++ ubuntuone/syncdaemon/volume_manager.py 2012-02-09 23:36:18 +0000
@@ -1178,20 +1178,31 @@
1178 return True1178 return True
1179 return False1179 return False
11801180
1181 def create_udf(self, path):1181 def validate_path_for_folder(self, path):
1182 """Request the creation of a UDF to AQ."""1182 """Validate 'folder_path' for folder creation."""
1183 self.log.debug('create udf: %r', path)
1184 path = normpath(path)1183 path = normpath(path)
1184 user_home = expand_user('~')
1185 # handle folder_path not within '~'
1186 if not path.startswith(user_home):
1187 return (False, "UDFs must be within home")
1188
1185 # check if path is the realpath, bail out if not1189 # check if path is the realpath, bail out if not
1186 if is_link(path):1190 if is_link(path):
1187 self.m.event_q.push('VM_UDF_CREATE_ERROR', path=path,1191 return (False, "UDFs can not be a symlink")
1188 error="UDFs can not be a symlink")1192
1189 return
1190 # check if the path it's ok (outside root and1193 # check if the path it's ok (outside root and
1191 # isn't a ancestor or child of another UDF)1194 # isn't a ancestor or child of another UDF)
1192 if self._is_nested_udf(path):1195 if self._is_nested_udf(path):
1193 self.m.event_q.push('VM_UDF_CREATE_ERROR', path=path,1196 return (False, "UDFs can not be nested")
1194 error="UDFs can not be nested")1197
1198 return (True, "")
1199
1200 def create_udf(self, path):
1201 """Request the creation of a UDF to AQ."""
1202 self.log.debug('create udf: %r', path)
1203 valid, msg = self.validate_path_for_folder(path)
1204 if not valid:
1205 self.m.event_q.push('VM_UDF_CREATE_ERROR', path=path, error=msg)
1195 return1206 return
11961207
1197 try:1208 try:

Subscribers

People subscribed via source and target branches