diff --git a/perfact/zodbsync/tests/test_sync.py b/perfact/zodbsync/tests/test_sync.py index 04d5936..177a60b 100644 --- a/perfact/zodbsync/tests/test_sync.py +++ b/perfact/zodbsync/tests/test_sync.py @@ -2023,3 +2023,86 @@ def test_layer_update(self): self.run('layer-hash', layer) self.run('layer-update', ident) assert self.app.Test.title == 'Changed' + + def test_keep_acl(self): + ''' + Make sure deletions on top level acl_users are NOT synced into + Data.fs + + User folders living somewhere else in the application may be + deleted though. + ''' + acl_path = os.path.join( + self.repo.path, + '__root__', + 'acl_users', + ) + shutil.rmtree(acl_path) + self.run('playback', '/') + + # this playback will fail horribly if acl_users is gone! + self.run('playback', '/') + + # make sure acl_users in toplevel is still present + assert 'acl_users' in self.app.objectIds() + + # now create dummy module with its own acl_users folder + with self.runner.sync.tm: + self.app.manage_addFolder(id='some_module') + self.app.some_module.manage_addUserFolder() + + self.run('record', '/') + + assert 'acl_users' in self.app.some_module.objectIds() + + module_acl = os.path.join( + self.repo.path, + '__root__', + 'some_module', + 'acl_users', + ) + shutil.rmtree(module_acl) + self.run('playback', '/') + assert 'acl_users' not in self.app.some_module.objectIds() + + def test_keep_acl_norecurse(self): + ''' + test_keep_acl but slightly altered for norecurse, + aka playing back single objects instead of the whole + object tree + ''' + acl_path = os.path.join( + self.repo.path, + '__root__', + 'acl_users', + ) + shutil.rmtree(acl_path) + self.run('playback', '--no-recurse', '/acl_users') + + # make sure acl_users in toplevel is still present + assert 'acl_users' in self.app.objectIds() + + # now create dummy module with its own acl_users folder + with self.runner.sync.tm: + self.app.manage_addFolder(id='some_module') + self.app.some_module.manage_addFolder(id='something') + self.app.some_module.manage_addUserFolder() + + self.run('record', '/') + + assert 'acl_users' in self.app.some_module.objectIds() + + module_acl = os.path.join( + self.repo.path, + '__root__', + 'some_module', + 'acl_users', + ) + shutil.rmtree(module_acl) + self.run( + 'playback', + '--no-recurse', + '/some_module', + '/some_module/acl_users', + ) + assert 'acl_users' not in self.app.some_module.objectIds() diff --git a/perfact/zodbsync/zodbsync.py b/perfact/zodbsync/zodbsync.py index 05a1181..6babedb 100644 --- a/perfact/zodbsync/zodbsync.py +++ b/perfact/zodbsync/zodbsync.py @@ -766,6 +766,8 @@ def _playback_path(self, pathinfo): return if fs_data is None: + if obj_id == 'acl_users' and path.startswith('/acl_users'): + return self.logger.info('Removing object ' + path) parent_obj.manage_delObjects(ids=[obj_id]) return @@ -784,7 +786,11 @@ def _playback_path(self, pathinfo): srv_contents = obj_contents(obj) if obj else [] # Find IDs in Data.fs object not present in file system - del_ids = [a for a in srv_contents if a not in contents] + del_ids = [ + a for a in srv_contents + if a not in contents and + not (obj == self.app and a == 'acl_users') + ] if del_ids: self.logger.warning('Deleting objects ' + repr(del_ids)) obj.manage_delObjects(ids=del_ids)