added the KeyValueStore component

This commit is contained in:
Daniel Knüttel 2019-03-20 09:59:23 +01:00
parent 52220bffbd
commit 3e2b7d9014
4 changed files with 132 additions and 2 deletions

View File

@ -0,0 +1,3 @@
from .kvs import KeyValueStore
components = {KeyValueStore.component_type: KeyValueStore}

View File

@ -0,0 +1,38 @@
from abc import abstractmethod, abstractclassmethod, ABCMeta
class AbstractComponent(metaclass=ABCMeta):
component_type = "DO NOT INSTANTIATE abstract component"
def __init__(self, bunker, name, password):
self._bunker = bunker
self._name = name
self._password = password
@abstractclassmethod
def new(cls, bunker, name, password):
"""
Create a new component in the given bunker
using the given name and password.
"""
pass
@abstractmethod
def close(self, notify_bunker=True):
"""
This method should "close" the given component,
meaning it should write back the changes and bring
it in a state where it cannot be used anymore.
Such a state needs two important protections:
- It is protected against accidental usage, such that
a regular user that forgets that the component is already
closed cannot damage the data
- All sensitive data (i.e. the password) is destroyed.
If notify_bunker is True the parenting bunker will be
notified that the current component is already closed.
"""
pass
@abstractmethod
def write_back(self):
pass

54
bunker/backends/kvs.py Normal file
View File

@ -0,0 +1,54 @@
from ljson.slapdash.mem import Table
from ljson.slapdash import SlapdashHeader
from .component import AbstractComponent
class KeyValueStore(AbstractComponent):
component_type = "kvs"
def __init__(self, bunker, name, password):
AbstractComponent.__init__(self, bunker, name, password)
file_, type_ = bunker._load_component(name, password)
self._content = Table.from_file(file_)
self._open = True
@classmethod
def new(cls, bunker, name, password):
bunker._add_component(name, password, KeyValueStore.component_type)
file_, type_ = bunker._load_component(name, password)
table = Table(SlapdashHeader({}), [])
table.save(file_)
bunker._save_component(name, password, file_)
return cls(bunker, name, password)
def additem(self, key, value):
if(not self._open):
raise IOError("KeyValueStore is closed")
self._content.additem({"key": key, "value": value})
def getitem(self, key):
if(not self._open):
raise IOError("KeyValueStore is closed")
return self._content[{"key": key}]["value"][0]
def write_back(self):
if(not self._open):
raise IOError("KeyValueStore is closed")
file_, type_ = self._bunker._load_component(self._name, self._password)
self._content.save(file_)
self._bunker._save_component(self._name, self._password, file_)
@property
def closed(self):
return not self._open
def close(self, notify_bunker=True):
self.write_back()
self._open = False
self._name = ""
self._password = b""
del(self._content)
if(notify_bunker):
self._bunker.notify_component_is_closed(self)

View File

@ -1,15 +1,18 @@
import uuid import uuid
from collections import deque
import ljson import ljson
from Crypto.Cipher import AES from Crypto.Cipher import AES
from .files.tarfile import RewriteableTarFile from .files.tarfile import RewriteableTarFile
from .files.bunkeredfile import BunkeredFile from .files.bunkeredfile import BunkeredFile
from .backends import components
class Bunker(object): class Bunker(object):
def __init__(self, path): def __init__(self, path):
self._tarfile = RewriteableTarFile.open(path) self._tarfile = RewriteableTarFile.open(path)
self._components = ljson.Table.from_file(self._tarfile.get_file("__bunker_main__")) self._components = ljson.Table.from_file(self._tarfile.get_file("__bunker_main__"))
self._stack_of_open_components = deque()
self._stack_of_open_components.appendleft(deque())
@classmethod @classmethod
def open(cls, path): def open(cls, path):
@ -52,6 +55,8 @@ class Bunker(object):
while(True): while(True):
chunk = file_.read(chunk_size) chunk = file_.read(chunk_size)
length_read += len(chunk) length_read += len(chunk)
if(not chunk):
break
if(length_read == length): if(length_read == length):
decrypted.write(key.decrypt(chunk)) decrypted.write(key.decrypt(chunk))
break break
@ -87,7 +92,7 @@ class Bunker(object):
self._components.save(self._tarfile.get_file("__bunker_main__")) self._components.save(self._tarfile.get_file("__bunker_main__"))
self._tarfile.writeback_file("__bunker_main__") self._tarfile.writeback_file("__bunker_main__")
def _save_compontent(self, name, password, file_): def _save_component(self, name, password, file_):
chunk_size = 16 chunk_size = 16
if(not {"component": name} in self._components): if(not {"component": name} in self._components):
raise ValueError("unknown component: {}".format(name)) raise ValueError("unknown component: {}".format(name))
@ -102,6 +107,7 @@ class Bunker(object):
file_out = self._tarfile.get_file(file_name) file_out = self._tarfile.get_file(file_name)
file_out.truncate(0) file_out.truncate(0)
file_out.seek(0, 0)
file_.seek(0, 0) file_.seek(0, 0)
chunk = file_.read(chunk_size) chunk = file_.read(chunk_size)
@ -117,4 +123,33 @@ class Bunker(object):
file_out.close() file_out.close()
def __enter__(self):
self._stack_of_open_components.appendleft(deque())
return self
def __exit__(self):
open_components = self._stack_of_open_components.popleft()
for component in open_components:
component.close(notify_bunker=False)
return False
def add_component(self, type_, name, password):
if(not type_ in components):
raise ValueError("unknown component type: '{}'".format(type_))
component = components[type_].new(self, name, password)
self._stack_of_open_components[0].append(component)
return component
def get_component(self, name, password):
type_ = self._components[{"component": name}]["type"][0]
component = components[type_](self, name, password)
self._stack_of_open_components[0].append(component)
return component
def notify_component_is_closed(self, component):
for context in self._stack_of_open_components:
for open_component in context:
if(open_component == component):
context.remove(open_component)
break