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
from collections import deque
import ljson
from Crypto.Cipher import AES
from .files.tarfile import RewriteableTarFile
from .files.bunkeredfile import BunkeredFile
from .backends import components
class Bunker(object):
def __init__(self, path):
self._tarfile = RewriteableTarFile.open(path)
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
def open(cls, path):
@ -52,6 +55,8 @@ class Bunker(object):
while(True):
chunk = file_.read(chunk_size)
length_read += len(chunk)
if(not chunk):
break
if(length_read == length):
decrypted.write(key.decrypt(chunk))
break
@ -87,7 +92,7 @@ class Bunker(object):
self._components.save(self._tarfile.get_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
if(not {"component": name} in self._components):
raise ValueError("unknown component: {}".format(name))
@ -102,6 +107,7 @@ class Bunker(object):
file_out = self._tarfile.get_file(file_name)
file_out.truncate(0)
file_out.seek(0, 0)
file_.seek(0, 0)
chunk = file_.read(chunk_size)
@ -117,4 +123,33 @@ class Bunker(object):
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