From 95a41eae2fcb50a2bbd166099028f36dbd96c29d Mon Sep 17 00:00:00 2001 From: Nicolas Kruchten Date: Thu, 3 May 2018 22:29:07 -0400 Subject: [PATCH] first cut at encrypted_ props --- dash/dash.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/dash/dash.py b/dash/dash.py index 143d1e802b..8ace74dd67 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -20,6 +20,12 @@ from . import exceptions from ._utils import AttributeDict as _AttributeDict +from cryptography.fernet import Fernet +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.hkdf import HKDF +from cryptography.hazmat.backends import default_backend + + # pylint: disable=too-many-instance-attributes # pylint: disable=too-many-arguments @@ -31,6 +37,7 @@ def __init__( static_folder='static', url_base_pathname='/', compress=True, + encryption_keystring='', **kwargs): # pylint-disable: too-many-instance-attributes @@ -122,6 +129,23 @@ def add_url(name, view_func, methods=('GET',)): self._cached_layout = None self.routes = [] + self.fernet_key = None + + if encryption_keystring != '': + if len(encryption_keystring) < 30: + raise Exception( + '' + 'encryption_keystring must be at least 30 characters.') + else: + hkdf = HKDF( + algorithm=hashes.SHA256(), + length=32, + salt=None, + info=None, + backend=default_backend() + ) + self.fernet_key = hkdf.derive(encryption_keystring) + @property def layout(self): return self._layout @@ -513,6 +537,18 @@ def wrap_func(func): def add_context(*args, **kwargs): output_value = func(*args, **kwargs) + + if output.component_property.startswith("encrypted_"): + if not self.fernet_key: + raise Exception( + '' + 'You must provide an encryption_keystring ' + 'to use encrypted_* properties.') + f = Fernet(self.fernet_key) + output_value_str = json.dumps(output_value, + cls=plotly.utils.PlotlyJSONEncoder) + output_value = f.encrypt(bytes(output_value_str)) + response = { 'response': { 'props': { @@ -547,6 +583,14 @@ def dispatch(self): c['property'] == component_registration['property'] and c['id'] == component_registration['id'] ][0]) + if component_registration['property'].startswith("encrypted_"): + if not self.fernet_key: + raise Exception( + '' + 'You must provide an encryption_keystring ' + 'to use encrypted_* properties.') + f = Fernet(self.fernet_key) + args[-1] = json.loads(f.decrypt(bytes(args[-1]))) for component_registration in self.callback_map[target_id]['state']: args.append([