diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2013-06-02 07:07:33 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2013-06-02 07:07:33 (GMT) |
commit | d1fdc48085e63202c0c4a6a4005bd41b9e0ac90f (patch) | |
tree | 7ca85707a492240f7d223322d89283cb2c9bfd10 | |
parent | a2181bd9f4cade2b4e1d401e9fd1b643a0b6e030 (diff) |
Keep author property all time empty for local db
-rw-r--r-- | sugar_network/client/__init__.py | 14 | ||||
-rw-r--r-- | sugar_network/client/commands.py | 72 | ||||
-rw-r--r-- | sugar_network/node/commands.py | 9 | ||||
-rw-r--r-- | sugar_network/node/slave.py | 54 | ||||
-rw-r--r-- | tests/__init__.py | 9 | ||||
-rwxr-xr-x | tests/integration/master_personal.py | 9 | ||||
-rwxr-xr-x | tests/units/client/commands.py | 13 | ||||
-rwxr-xr-x | tests/units/client/offline_commands.py | 7 | ||||
-rwxr-xr-x | tests/units/resources/implementation.py | 16 | ||||
-rwxr-xr-x | tests/units/resources/volume.py | 9 |
10 files changed, 122 insertions, 90 deletions
diff --git a/sugar_network/client/__init__.py b/sugar_network/client/__init__.py index 109c6a8..62d9128 100644 --- a/sugar_network/client/__init__.py +++ b/sugar_network/client/__init__.py @@ -30,6 +30,7 @@ _XO_SERIAL_PATH = ['/ofw/mfg-data/SN', '/proc/device-tree/mfg-data/SN'] _XO_UUID_PATH = ['/ofw/mfg-data/U#', '/proc/device-tree/mfg-data/U#'] _logger = logging.getLogger('client') +_sugar_uid = None def profile_path(*args): @@ -154,7 +155,7 @@ def Client(url=None): creds = None if not anonymous.value: if exists(key_path()): - creds = (sugar_uid(), key_path(), _profile) + creds = (sugar_uid(), key_path(), sugar_profile) else: _logger.warning('Sugar session was never started (no DSA key),' 'fallback to anonymous mode') @@ -211,12 +212,15 @@ def key_path(): def sugar_uid(): - import hashlib - pubkey = util.pubkey(key_path()).split()[1] - return str(hashlib.sha1(pubkey).hexdigest()) + global _sugar_uid + if _sugar_uid is None: + import hashlib + pubkey = util.pubkey(key_path()).split()[1] + _sugar_uid = str(hashlib.sha1(pubkey).hexdigest()) + return _sugar_uid -def _profile(): +def sugar_profile(): import gconf conf = gconf.client_get_default() return {'name': conf.get_string(_NICKNAME_GCONF) or '', diff --git a/sugar_network/client/commands.py b/sugar_network/client/commands.py index 9b3093f..92cf488 100644 --- a/sugar_network/client/commands.py +++ b/sugar_network/client/commands.py @@ -23,7 +23,7 @@ from sugar_network.toolkit import netlink, mountpoints from sugar_network.client import journal, clones, injector from sugar_network.client.spec import Spec from sugar_network.resources.volume import Volume, Commands -from sugar_network.node.slave import PersonalCommands +from sugar_network.node.slave import SlaveCommands from sugar_network.toolkit import zeroconf, coroutine, util, http from sugar_network.toolkit import exception, enforce @@ -32,6 +32,9 @@ from sugar_network.toolkit import exception, enforce _SN_DIRNAME = 'sugar-network' _LOCAL_PROPS = frozenset(['favorite', 'clone']) +# Flag file to recognize a directory as a synchronization directory +_SYNC_DIRNAME = 'sugar-network-sync' + _RECONNECT_TIMEOUT = 3 _RECONNECT_TIMEOUT_MAX = 60 * 15 @@ -138,7 +141,7 @@ class ClientCommands(db.CommandsProcessor, Commands, journal.Commands): try: result = self._node_call(request, response) except db.CommandNotFound: - result = {'roles': [], 'guid': request.principal} + result = {'roles': [], 'guid': client.sugar_uid()} return result @db.directory_command(method='GET', @@ -425,7 +428,7 @@ class ClientCommands(db.CommandsProcessor, Commands, journal.Commands): node.stats_root.value = join(root, _SN_DIRNAME, 'stats') node.files_root.value = join(root, _SN_DIRNAME, 'files') - volume = Volume(db_path, lazy_open=client.lazy_open.value) + volume = Volume(db_path) self._node = _PersonalCommands(join(db_path, 'node'), volume, self.broadcast) self._jobs.spawn(volume.populate) @@ -678,13 +681,36 @@ class _VolumeCommands(db.VolumeCommands): db.VolumeCommands.before_create(self, request, props) -class _PersonalCommands(PersonalCommands): +class _PersonalCommands(SlaveCommands): def __init__(self, key_path, volume, localcast): - PersonalCommands.__init__(self, key_path, volume, localcast) + SlaveCommands.__init__(self, key_path, volume) + self.api_url = 'http://127.0.0.1:%s' % node.port.value + self._localcast = localcast + self._mounts = util.Pool() + self._jobs = coroutine.Pool() + + users = volume['user'] + if not users.exists(client.sugar_uid()): + users.create(guid=client.sugar_uid(), **client.sugar_profile()) + + mountpoints.connect(_SYNC_DIRNAME, + self.__found_mountcb, self.__lost_mount_cb) volume.connect(localcast) + @db.volume_command(method='GET', cmd='whoami', + mime_type='application/json') + def whoami(self, request): + return {'roles': [], 'guid': client.sugar_uid()} + + def validate(self, *args): + return True + + def call(self, request, response=None): + request.principal = client.sugar_uid() + return SlaveCommands.call(self, request, response) + def close(self): self.volume.disconnect(self._localcast) self.volume.close() @@ -693,6 +719,42 @@ class _PersonalCommands(PersonalCommands): return '<LocalNode path=%s api_url=%s>' % \ (self.volume.root, self.api_url) + def _sync_mounts(self): + self._localcast({'event': 'sync_start'}) + + for mountpoint in self._mounts: + self._localcast({'event': 'sync_next', 'path': mountpoint}) + try: + self._offline_session = self._offline_sync( + join(mountpoint, _SYNC_DIRNAME), + **(self._offline_session or {})) + except Exception, error: + exception(_logger, 'Failed to complete synchronization') + self._localcast({'event': 'sync_abort', 'error': str(error)}) + self._offline_session = None + raise + + if self._offline_session is None: + _logger.debug('Synchronization completed') + self._localcast({'event': 'sync_complete'}) + else: + _logger.debug('Postpone synchronization with %r session', + self._offline_session) + self._localcast({'event': 'sync_paused'}) + + def __found_mountcb(self, path): + self._mounts.add(path) + if self._jobs: + _logger.debug('Found %r sync mount, pool it', path) + else: + _logger.debug('Found %r sync mount, start synchronization', path) + self._jobs.spawn(self._sync_mounts) + + def __lost_mount_cb(self, path): + if self._mounts.remove(path) == util.Pool.ACTIVE: + _logger.warning('%r was unmounted, break synchronization', path) + self._jobs.kill() + class _ResponseStream(object): diff --git a/sugar_network/node/commands.py b/sugar_network/node/commands.py index 9a9fc6d..0ef07a7 100644 --- a/sugar_network/node/commands.py +++ b/sugar_network/node/commands.py @@ -165,7 +165,7 @@ class NodeCommands(db.VolumeCommands, Commands): mime_type='application/json') def whoami(self, request): roles = [] - if auth.try_validate(request, 'root'): + if self.validate(request, 'root'): roles.append('root') return {'roles': roles, 'guid': request.principal} @@ -252,6 +252,9 @@ class NodeCommands(db.VolumeCommands, Commands): return result + def validate(self, *args): + return auth.try_validate(*args) + def call(self, request, response=None): if node.static_url.value: request.static_prefix = node.static_url.value @@ -272,7 +275,7 @@ class NodeCommands(db.VolumeCommands, Commands): return if cmd.permissions & db.ACCESS_AUTH: - enforce(auth.try_validate(request, 'user'), http.Unauthorized, + enforce(self.validate(request, 'user'), http.Unauthorized, 'User is not authenticated') if cmd.permissions & db.ACCESS_AUTHOR and 'guid' in request: @@ -281,7 +284,7 @@ class NodeCommands(db.VolumeCommands, Commands): else: doc = self.volume[request['document']].get(request['guid']) allowed = (request.principal in doc['author']) - enforce(allowed or auth.try_validate(request, 'root'), + enforce(allowed or self.validate(request, 'root'), http.Forbidden, 'Operation is permitted only for authors') return cmd diff --git a/sugar_network/node/slave.py b/sugar_network/node/slave.py index 762f468..2accd9e 100644 --- a/sugar_network/node/slave.py +++ b/sugar_network/node/slave.py @@ -25,13 +25,10 @@ from sugar_network import db, node, toolkit from sugar_network.client import api_url from sugar_network.node import sync, stats_user, files, volume from sugar_network.node.commands import NodeCommands -from sugar_network.toolkit import mountpoints, coroutine, util, http +from sugar_network.toolkit import util, http from sugar_network.toolkit import exception, enforce -# Flag file to recognize a directory as a synchronization directory -_SYNC_DIRNAME = 'sugar-network-sync' - _logger = logging.getLogger('node.slave') @@ -189,52 +186,3 @@ class SlaveCommands(NodeCommands): if seq: self._files_seq.exclude(seq) self._files_seq.commit() - - -class PersonalCommands(SlaveCommands): - - def __init__(self, key_path, volume_, localcast): - SlaveCommands.__init__(self, key_path, volume_) - - self._localcast = localcast - self._mounts = util.Pool() - self._jobs = coroutine.Pool() - - mountpoints.connect(_SYNC_DIRNAME, - self.__found_mountcb, self.__lost_mount_cb) - - def _sync_mounts(self): - self._localcast({'event': 'sync_start'}) - - for mountpoint in self._mounts: - self._localcast({'event': 'sync_next', 'path': mountpoint}) - try: - self._offline_session = self._offline_sync( - join(mountpoint, _SYNC_DIRNAME), - **(self._offline_session or {})) - except Exception, error: - exception(_logger, 'Failed to complete synchronization') - self._localcast({'event': 'sync_abort', 'error': str(error)}) - self._offline_session = None - raise - - if self._offline_session is None: - _logger.debug('Synchronization completed') - self._localcast({'event': 'sync_complete'}) - else: - _logger.debug('Postpone synchronization with %r session', - self._offline_session) - self._localcast({'event': 'sync_paused'}) - - def __found_mountcb(self, path): - self._mounts.add(path) - if self._jobs: - _logger.debug('Found %r sync mount, pool it', path) - else: - _logger.debug('Found %r sync mount, start synchronization', path) - self._jobs.spawn(self._sync_mounts) - - def __lost_mount_cb(self, path): - if self._mounts.remove(path) == util.Pool.ACTIVE: - _logger.warning('%r was unmounted, break synchronization', path) - self._jobs.kill() diff --git a/tests/__init__.py b/tests/__init__.py index b99433f..2bef872 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -116,6 +116,15 @@ class Test(unittest.TestCase): 'sugar_network.resources.report', ] + if tmp_root is None: + self.override(client, 'sugar_profile', lambda: { + 'name': 'test', + 'color': '#000000,#000000', + 'machine_sn': '', + 'machine_uuid': '', + 'pubkey': PUBKEY, + }) + os.makedirs('tmp') self.node = None diff --git a/tests/integration/master_personal.py b/tests/integration/master_personal.py index 03739b0..718659b 100755 --- a/tests/integration/master_personal.py +++ b/tests/integration/master_personal.py @@ -13,7 +13,7 @@ import rrdtool from __init__ import tests, src_root -from sugar_network.client import Client +from sugar_network.client import Client, sugar_uid from sugar_network.toolkit.rrd import Rrd from sugar_network.toolkit import util, coroutine @@ -63,6 +63,7 @@ class MasterPersonalTest(tests.Test): def test_SyncMounts(self): master = Client('http://127.0.0.1:8100') client = Client('http://127.0.0.1:8102') + uid = sugar_uid() # Create shared files on master self.touch(('master/files/1/1', '1')) @@ -104,7 +105,7 @@ class MasterPersonalTest(tests.Test): 'layer': 'pilot', }) stats_timestamp = int(time.time()) - client.post(['user', tests.UID], { + client.post(['user', uid], { 'name': 'db', 'values': [(stats_timestamp, {'f': 1}), (stats_timestamp + 1, {'f': 2})], }, cmd='stats-upload') @@ -195,12 +196,12 @@ class MasterPersonalTest(tests.Test): self.assertEqual('1', file('client/mnt/disk/sugar-network/files/1/1').read()) self.assertEqual('2', file('client/mnt/disk/sugar-network/files/2/2').read()) - rrd = Rrd('master/stats/user/%s/%s' % (tests.UID[:2], tests.UID), 1) + rrd = Rrd('master/stats/user/%s/%s' % (uid[:2], uid), 1) self.assertEqual([ [('db', stats_timestamp, {'f': 1.0}), ('db', stats_timestamp + 1, {'f': 2.0})], ], [[(db.name,) + i for i in db.get(db.first, db.last)] for db in rrd]) - rrd = Rrd('client/mnt/disk/sugar-network/stats/user/%s/%s' % (tests.UID[:2], tests.UID), 1) + rrd = Rrd('client/mnt/disk/sugar-network/stats/user/%s/%s' % (uid[:2], uid), 1) self.assertEqual([ [('db', stats_timestamp, {'f': 1.0}), ('db', stats_timestamp + 1, {'f': 2.0})], ], diff --git a/tests/units/client/commands.py b/tests/units/client/commands.py index 0713e04..691eeea 100755 --- a/tests/units/client/commands.py +++ b/tests/units/client/commands.py @@ -210,8 +210,14 @@ class CommandsTest(tests.Test): self.assertEqual([[3, None]], json.load(file('client/push.sequence'))) self.assertEqual({'en-us': 'title'}, volume['context'].get(guid1)['title']) self.assertEqual({'en-us': 'title'}, self.node_volume['context'].get(guid1)['title']) + self.assertEqual( + {tests.UID: {'role': 3, 'name': 'test', 'order': 0}}, + self.node_volume['context'].get(guid1)['author']) self.assertEqual({'en-us': 'title'}, volume['context'].get(guid2)['title']) self.assertEqual({'en-us': 'title'}, self.node_volume['context'].get(guid2)['title']) + self.assertEqual( + {tests.UID: {'role': 3, 'name': 'test', 'order': 0}}, + self.node_volume['context'].get(guid2)['author']) trigger = self.wait_for_events(cp, event='inline', state='offline') self.node.stop() @@ -229,8 +235,14 @@ class CommandsTest(tests.Test): self.assertEqual([[4, None]], json.load(file('client/push.sequence'))) self.assertEqual({'en-us': 'title_'}, volume['context'].get(guid1)['title']) self.assertEqual({'en-us': 'title_'}, self.node_volume['context'].get(guid1)['title']) + self.assertEqual( + {tests.UID: {'role': 3, 'name': 'test', 'order': 0}}, + self.node_volume['context'].get(guid1)['author']) assert not volume['context'].exists(guid2) self.assertEqual({'en-us': 'title'}, self.node_volume['context'].get(guid2)['title']) + self.assertEqual( + {tests.UID: {'role': 3, 'name': 'test', 'order': 0}}, + self.node_volume['context'].get(guid2)['author']) def test_CachedClientCommands_WipeReports(self): volume = Volume('client') @@ -240,7 +252,6 @@ class CommandsTest(tests.Test): post.content_type = 'application/json' post.content = { 'context': 'context', - 'description': 'description', 'error': 'error', } guid = cp.call(post) diff --git a/tests/units/client/offline_commands.py b/tests/units/client/offline_commands.py index c81dd66..8879c81 100755 --- a/tests/units/client/offline_commands.py +++ b/tests/units/client/offline_commands.py @@ -23,7 +23,7 @@ class OfflineCommandsTest(tests.Test): coroutine.spawn(server.serve_forever) coroutine.dispatch() - def test_SetUser(self): + def test_NoAuthors(self): ipc = IPCClient() guid = ipc.post(['context'], { @@ -33,7 +33,10 @@ class OfflineCommandsTest(tests.Test): 'description': 'description', }) self.assertEqual( - [{'name': tests.UID, 'role': 2}], + {}, + self.home_volume['context'].get(guid)['author']) + self.assertEqual( + [], ipc.get(['context', guid, 'author'])) def test_HandleDeletes(self): diff --git a/tests/units/resources/implementation.py b/tests/units/resources/implementation.py index 834e0d2..46c9768 100755 --- a/tests/units/resources/implementation.py +++ b/tests/units/resources/implementation.py @@ -63,7 +63,7 @@ class ImplementationTest(tests.Test): _encode_version('1-post1.2-3')) def test_SetMimeTypeForActivities(self): - home_volume = self.start_offline_client() + self.start_online_client() client = IPCClient() context = client.post(['context'], { @@ -80,17 +80,17 @@ class ImplementationTest(tests.Test): 'notes': '', }) client.request('PUT', ['implementation', impl, 'data'], 'blob', {'Content-Type': 'image/png'}) - self.assertEqual('image/png', home_volume['implementation'].get(impl).meta('data')['mime_type']) + self.assertEqual('image/png', self.node_volume['implementation'].get(impl).meta('data')['mime_type']) client.put(['context', context, 'type'], 'activity') client.request('PUT', ['implementation', impl, 'data'], 'blob', {'Content-Type': 'image/png'}) - self.assertEqual('application/vnd.olpc-sugar', home_volume['implementation'].get(impl).meta('data')['mime_type']) + self.assertEqual('application/vnd.olpc-sugar', self.node_volume['implementation'].get(impl).meta('data')['mime_type']) def test_WrongAuthor(self): - home_volume = self.start_offline_client() + self.start_online_client() client = IPCClient() - home_volume['context'].create( + self.node_volume['context'].create( guid='context', type='content', title='title', @@ -106,11 +106,11 @@ class ImplementationTest(tests.Test): 'notes': '', } self.assertRaises(http.Forbidden, client.post, ['implementation'], impl) - self.assertEqual(0, home_volume['implementation'].find()[1]) + self.assertEqual(0, self.node_volume['implementation'].find()[1]) - home_volume['context'].update('context', author={tests.UID: None}) + self.node_volume['context'].update('context', author={tests.UID: None}) guid = client.post(['implementation'], impl) - assert home_volume['implementation'].exists(guid) + assert self.node_volume['implementation'].exists(guid) if __name__ == '__main__': diff --git a/tests/units/resources/volume.py b/tests/units/resources/volume.py index 1e5b6ca..e07858f 100755 --- a/tests/units/resources/volume.py +++ b/tests/units/resources/volume.py @@ -140,15 +140,6 @@ class VolumeTest(tests.Test): volume = Volume('db') cp = TestCommands(volume) - assert not exists('db/context/index') - - self.assertEqual( - [], - call(cp, method='GET', document='context')['result']) - coroutine.dispatch() - self.assertEqual( - [{'guid': '1'}], - call(cp, method='GET', document='context')['result']) assert exists('db/context/index') def test_DefaultAuthor(self): |