async def reset(self, vault: Vault): with store.session() as session: session.add(vault) vault.revision_count = 0 vault.file_count = 0 vault.user_count = 0 session.commit()
async def clone(self, vault_id, local_directory, async_init: bool = False): backend = await self.open_backend() logger.info('Retrieving encrypted key for vault %s (Fingerprint: %s)', vault_id, format_fingerprint(self.identity.get_fingerprint())) auth_token, package_info = await \ backend.get_user_vault_key(self.identity.get_fingerprint(), vault_id) # decrypt package export_pipe = Once(package_info) \ >> DecryptRSA_PKCS1_OAEP(self.identity.private_key) decrypted_package_info = await export_pipe.readall() original_vault = self.get_vault_by_path(local_directory) if original_vault: if original_vault.config.id == vault_id: logger.warning('Same vault already exists in the given location, continuing...') vault = original_vault else: raise VaultAlreadyExists(original_vault.folder) else: # There is no vault present, but we want to make sure that this folder is nonexistent or # empty: if os.path.exists(local_directory) and not is_empty(local_directory): raise FolderExistsAndIsNotEmpty(local_directory) vault = Vault.from_package_info(decrypted_package_info, local_directory, auth_token) if not async_init: await self.pull_vault(vault, full=True) await self.add_vault(vault, async_init=async_init) return vault
async def post_obj(self, request): async def pull_and_watch(vault): await self.app.pull_vault(vault) # TODO No wait here! #await self.app.watch(vault) async def init_and_push(vault): await self.app.open_or_init(vault) await self.app.pull_vault(vault) await self.app.push_vault(vault) content = await request.content.read() request_dict = json.loads(content.decode()) if not 'folder' in request_dict or not request_dict['folder']: raise ValueError("Invalid value for parameter: 'folder'") if 'id' in request_dict: vault = await self.app.clone(request_dict['id'], request_dict['folder'], async_init=True) elif 'import_package' in request_dict: raise NotImplementedError() #vault = await self.app.import_package( # request_dict['import_package'], request_dict['folder']) #self.app.save_vault_dir_in_config(vault) #asyncio.get_event_loop().create_task(pull_and_watch(vault)) else: vault = await self.app.add_vault(Vault(request_dict['folder']), async_init=True, async_push=True) return vault
async def empty_vault(working_dir): "Return an uninitialized, empty vault" vault_folder = os.path.join(working_dir, "testvault") if os.path.exists(vault_folder): shutil.rmtree(vault_folder) os.makedirs(vault_folder) return Vault(vault_folder)
async def test_vault(working_dir): "Return a vault that is a clone of the test vault" source_folder = os.path.join('tests', 'testlocalvault/') vault_folder = os.path.join(working_dir, "testvault") if os.path.exists(vault_folder): shutil.rmtree(vault_folder) shutil.copytree(source_folder, vault_folder) return Vault(vault_folder)
async def test_remove_file(local_app, local_vault, working_dir): other_vault_path = os.path.join(working_dir, "othervault") # remove "other vault" folder first if os.path.exists(other_vault_path): shutil.rmtree(other_vault_path) app = local_app await app.initialize() await app.open_or_init(local_vault) await app.push() # init all vaults pre_rev = local_vault.revision await app.remove_file(local_vault, os.path.join(local_vault.folder, "hello.txt")) assert pre_rev != local_vault.revision await app.remove_file(local_vault, os.path.join(local_vault.folder, "random250k")) assert pre_rev != local_vault.revision # now we will clone the initialized vault by copying the vault config shutil.copytree( os.path.join(local_vault.folder, ".vault"), os.path.join(other_vault_path, ".vault"), ) other_vault = Vault(other_vault_path) with other_vault.config.update_context(): other_vault.config.unset("vault.revision") await app.open_or_init(other_vault) await app.add_vault(other_vault) await app.pull_vault(other_vault) files_in_new_vault = len(glob(os.path.join(other_vault_path, "*"))) assert files_in_new_vault == 6 assertSameFilesInFolder(local_vault.folder, other_vault_path) keys = UserVaultKeyManager(app) # We have one valid key for both vaults assert len(keys.list_for_vault(other_vault)) == 1 assert len(keys.list_for_vault(local_vault)) == 1 key = keys.list_for_vault(local_vault)[0] other_key = keys.list_for_vault(other_vault)[0] assert key.fingerprint == other_key.fingerprint assert key.fingerprint != local_vault.identity.get_fingerprint() assert key.fingerprint == app.identity.get_fingerprint()
async def test_local_metadata(local_app, local_vault, working_dir): other_vault_path = os.path.join(working_dir, "othervault") # remove "other vault" folder first if os.path.exists(other_vault_path): shutil.rmtree(other_vault_path) app = local_app await app.initialize() await app.open_or_init(local_vault) await trio.sleep(1) assert local_vault.revision_count == 3 # now we will clone the initialized vault by copying the vault config shutil.copytree( os.path.join(local_vault.folder, ".vault"), os.path.join(other_vault_path, ".vault"), ) other_vault = Vault(other_vault_path) with other_vault.config.update_context(): other_vault.config.unset("vault.revision") await app.open_or_init(other_vault) await app.add_vault(other_vault) await app.pull_vault(other_vault) assert other_vault.revision_count == 3 files_in_new_vault = len(glob(os.path.join(other_vault_path, "*"))) assert files_in_new_vault == 0 # Now we change the name of the original vault with local_vault.config.update_context(): other_vault.config.set("vault.name", "abc") # Upload metadata with the new name to the server revision = await local_vault.backend.set_vault_metadata(app.identity) await app.revisions.apply(revision, local_vault) original_count = other_vault.revision_count # Pull the new vault again to retrieve the change in metadata await app.pull_vault(other_vault) assert other_vault.revision_count == original_count + 1 assert local_vault.config.get("vault.name") == other_vault.config.get( "vault.name")
async def import_package(self, filename, target_folder, pull_vault=False): if os.path.exists(target_folder) and not is_empty(target_folder): raise FolderExistsAndIsNotEmpty(target_folder) with ZipFile(filename, 'r') as myzip: myzip.extractall(target_folder) logger.info('Importing vault into "%s"', target_folder) vault = Vault(target_folder) if not vault.config.id: raise InvalidVaultPackage() await self.open_or_init(vault) if pull_vault: await self.pull_vault(vault) return vault
async def initialize(self): await self.identity.init() if self.vault_dirs is None: self.vault_dirs = self.config.vault_dirs # Load cached vault information from config file config_vaults = [] with store.session() as session: for vault_dir in self.vault_dirs: abs_vault_dir = os.path.normpath(os.path.abspath(vault_dir)) try: vault = session.query(Vault).filter(Vault.folder==abs_vault_dir).one() except NoResultFound: vault = Vault(abs_vault_dir) session.add(vault) config_vaults.append(vault) for vault in config_vaults: await self.start_vault(vault)
async def clone_local(self, clone_target): import shutil import os vault = self.vaults[0] await self.push() if not os.path.exists(clone_target): os.makedirs(clone_target) vault_cfg = os.path.join(clone_target, '.vault') if not os.path.exists(vault_cfg): os.makedirs(vault_cfg) for f in ('config', 'id_rsa', 'id_rsa.pub'): shutil.copyfile(os.path.join(vault.folder, '.vault', f), os.path.join(vault_cfg, f)) logger.info("Cloned %s to %s" % (vault.folder, os.path.abspath(clone_target))) await self.add_vault(Vault(clone_target)) await self.pull()
async def clone(self, vault_id, local_directory): backend = await self.open_backend() logger.info('Retrieving encrypted key for vault %s (Fingerprint: %s)', vault_id, format_fingerprint(self.identity.get_fingerprint())) auth_token, package_info = await \ backend.get_user_vault_key(self.identity.get_fingerprint(), vault_id) await backend.close() # decrypt package export_pipe = Once(package_info) \ >> DecryptRSA_PKCS1_OAEP(self.identity.private_key) decrypted_package_info = await export_pipe.readall() original_vault = self.get_vault_by_path(local_directory) if original_vault: if original_vault.config.id == vault_id: logger.warning('Same vault already exists in the given location, continuing...') vault = original_vault else: raise VaultAlreadyExists(original_vault.folder) else: # There is no vault present, but we want to make sure that this folder is nonexistent or # empty: if os.path.exists(local_directory) and not is_empty(local_directory): raise FolderExistsAndIsNotEmpty(local_directory) vault = Vault.from_package_info(decrypted_package_info, local_directory, auth_token) await self.add_vault(vault) await self.pull_vault(vault, full=True) return vault
def get_vault_by_path(self, path): vault = Vault(path) if os.path.exists(vault.config_path): return vault return None
async def apply(self, revision: Revision, vault: Vault): if inspect(vault).session: raise ValueError('Vault object is bound to a session') revision.assert_valid() # 1. Check preconditions for this to be a valid revision (current revision must be parent) if vault.revision != revision.parent_id: raise UnexpectedParentInRevision("Expected parent to be {0}, but is {1}"\ .format(revision.parent_id, vault.revision)) smokesignal.emit('pre_apply_revision', vault=vault, revision=revision) with store.session() as session: # 2. Check if signing user's key is in the user vault key list if revision.operation != RevisionOp.CreateVault: signer_key = self.app.user_vault_keys.find_key( vault, revision.user_fingerprint) if not signer_key: raise InvalidRevision( "Key {0} is not allowed to generate revisions for vault {1}" .format(revision.user_fingerprint, vault)) else: # CreateVault is the only operation that is allowed to provide its own key signer_key = UserVaultKey( vault_id=vault.id, user_id=revision.user_id, fingerprint=revision.user_fingerprint, public_key=revision.user_public_key, ) # 3. Verify revision signature revision.verify(signer_key.get_identity(self.app.config)) # 4. Based on the revision type, perform an action to our state of the vault logger.debug( "Applying %s (%s) to %s", revision.operation, revision.revision_id, vault.id, ) if revision.operation == RevisionOp.CreateVault: session.add(vault) session.add(signer_key) session.add( VaultUser(vault_id=vault.id, user_id=revision.user_id)) session.commit() elif revision.operation == RevisionOp.Upload: try: bundle = await self.app.bundles.get_bundle_by_hash( vault, revision.file_hash) session.delete(bundle) except FileNotFoundError: pass bundle = await self.create_bundle_from_revision( revision, vault) session.add(bundle) session.commit() revision.path = bundle.relpath elif revision.operation == RevisionOp.SetMetadata: await vault.write_encrypted_metadata( Once(revision.revision_metadata)) elif revision.operation == RevisionOp.RemoveFile: bundle = await self.app.bundles.get_bundle_by_hash( vault, revision.file_hash) session.delete(bundle) session.commit() revision.path = bundle.relpath elif revision.operation == RevisionOp.AddUser: self.app.vault_users.add(vault, revision.user_id) elif revision.operation == RevisionOp.RemoveUser: self.app.vault_users.remove(vault, revision.user_id) elif revision.operation == RevisionOp.AddUserKey: new_identity = Identity.from_key(revision.user_public_key, self.app.config) self.app.user_vault_keys.add(vault, revision.user_id, new_identity) elif revision.operation == RevisionOp.RemoveUserKey: new_identity = Identity.from_key(revision.user_public_key, self.app.config) self.app.user_vault_keys.remove(vault, revision.user_id, new_identity) else: raise NotImplementedError(revision.operation) # 5. Store the revision in config and db revision.local_vault_id = vault.id revision.creator_id = signer_key.user_id session.add(revision) session.commit() vault.revision_count = (session.query(Revision).filter( Revision.local_vault_id == vault.id).count()) if revision.operation in (RevisionOp.Upload, RevisionOp.RemoveFile): vault.file_count = (session.query(Bundle).filter( Bundle.vault_id == vault.id).count()) if revision.operation in (RevisionOp.CreateVault, RevisionOp.AddUser, RevisionOp.RemoveUser): vault.user_count = (session.query(VaultUser).filter( VaultUser.vault_id == vault.id).count()) vault.modification_date = revision.created_at logger.debug( "Vault state revision_count=%s file_count=%s user_count=%s", vault.revision_count, vault.file_count, vault.user_count) # vault.revision = revision.id session.add(vault) vault.update_revision(revision) session.commit() smokesignal.emit('post_apply_revision', vault=vault, revision=revision)
async def apply(self, revision: Revision, vault: Vault): if inspect(vault).session: raise ValueError('Vault object is bound to a session') revision.assert_valid() # 1. Check preconditions for this to be a valid revision (current revision must be parent) if vault.revision != revision.parent_id: raise UnexpectedParentInRevision("Expected parent to be {0}, but is {1}"\ .format(revision.parent_id, vault.revision)) smokesignal.emit('pre_apply_revision', vault=vault, revision=revision) with store.session() as session: # 2. Check if signing user's key is in the user vault key list if revision.operation != RevisionOp.CreateVault: signer_key = self.app.user_vault_keys.find_key( vault, revision.user_fingerprint ) if not signer_key: raise InvalidRevision( "Key {0} is not allowed to generate revisions for vault {1}" .format(revision.user_fingerprint, vault) ) else: # CreateVault is the only operation that is allowed to provide its own key signer_key = UserVaultKey( vault_id=vault.id, user_id=revision.user_id, fingerprint=revision.user_fingerprint, public_key=revision.user_public_key, ) # 3. Verify revision signature revision.verify(signer_key.get_identity(self.app.config)) # 4. Based on the revision type, perform an action to our state of the vault logger.debug( "Applying %s (%s) to %s", revision.operation, revision.revision_id, vault.id, ) if revision.operation == RevisionOp.CreateVault: session.add(vault) session.add(signer_key) session.add(VaultUser(vault_id=vault.id, user_id=revision.user_id)) session.commit() elif revision.operation == RevisionOp.Upload: try: bundle = await self.app.bundles.get_bundle_by_hash(vault, revision.file_hash) session.delete(bundle) except FileNotFoundError: pass bundle = await self.create_bundle_from_revision(revision, vault) session.add(bundle) session.commit() revision.path = bundle.relpath elif revision.operation == RevisionOp.SetMetadata: await vault.write_encrypted_metadata(Once(revision.revision_metadata)) elif revision.operation == RevisionOp.RemoveFile: bundle = await self.app.bundles.get_bundle_by_hash(vault, revision.file_hash) session.delete(bundle) session.commit() revision.path = bundle.relpath elif revision.operation == RevisionOp.AddUser: self.app.vault_users.add(vault, revision.user_id) elif revision.operation == RevisionOp.RemoveUser: self.app.vault_users.remove(vault, revision.user_id) elif revision.operation == RevisionOp.AddUserKey: new_identity = Identity.from_key(revision.user_public_key, self.app.config) self.app.user_vault_keys.add(vault, revision.user_id, new_identity) elif revision.operation == RevisionOp.RemoveUserKey: new_identity = Identity.from_key(revision.user_public_key, self.app.config) self.app.user_vault_keys.remove(vault, revision.user_id, new_identity) else: raise NotImplementedError(revision.operation) # 5. Store the revision in config and db revision.local_vault_id = vault.id revision.creator_id = signer_key.user_id session.add(revision) session.commit() vault.revision_count = ( session.query(Revision) .filter(Revision.local_vault_id == vault.id) .count() ) if revision.operation in (RevisionOp.Upload, RevisionOp.RemoveFile): vault.file_count = ( session.query(Bundle) .filter(Bundle.vault_id == vault.id) .count() ) if revision.operation in (RevisionOp.CreateVault, RevisionOp.AddUser, RevisionOp.RemoveUser): vault.user_count = ( session.query(VaultUser) .filter(VaultUser.vault_id == vault.id) .count() ) vault.modification_date = revision.created_at logger.debug("Vault state revision_count=%s file_count=%s user_count=%s", vault.revision_count, vault.file_count, vault.user_count) # vault.revision = revision.id session.add(vault) vault.update_revision(revision) session.commit() smokesignal.emit('post_apply_revision', vault=vault, revision=revision)
async def test_non_existent_vault(local_app): app = local_app await app.add_vault(Vault('non-existent')) assert len(app.vaults) == 1 assert app.vaults[0].state == VaultState.FAILURE