move auth_state encryption outside the ORM

privy is used for encryption

- db only has blob column, no knowledge of encryption
- add CryptKeeper for handling encryption
- use privy for encryption, so we have fewer choices to make
- storing/loading encrypted auth_state runs in a ThreadPool
This commit is contained in:
Min RK
2017-07-28 13:44:37 +02:00
parent 32a9b38d26
commit 90e8e1a8aa
8 changed files with 256 additions and 176 deletions

View File

@@ -15,6 +15,7 @@ from . import orm
from ._version import _check_version, __version__
from traitlets import HasTraits, Any, Dict, observe, default
from .spawner import LocalProcessSpawner
from .crypto import encrypt, decrypt, CryptKeeper, EncryptionUnavailable
class UserDict(dict):
"""Like defaultdict, but for users
@@ -82,6 +83,7 @@ class _SpawnerDict(dict):
self[key] = self.spawner_factory(key)
return super().__getitem__(key)
class User(HasTraits):
@default('log')
@@ -100,6 +102,35 @@ class User(HasTraits):
orm_user = Any(allow_none=True)
@gen.coroutine
def save_auth_state(self, auth_state):
"""Encrypt and store auth_state"""
if auth_state is None:
self.encrypted_auth_state = None
else:
self.encrypted_auth_state = yield encrypt(auth_state)
self.db.commit()
@gen.coroutine
def get_auth_state(self):
"""Retrieve and decrypt auth_state for the user"""
encrypted = self.encrypted_auth_state
if encrypted is None:
return None
try:
auth_state = yield decrypt(encrypted)
except (ValueError, EncryptionUnavailable) as e:
self.log.warning("Failed to retrieve encrypted auth_state for %s because %s",
self.name, e,
)
return
# loading auth_state
if auth_state:
# Crypt has multiple keys, store again with new key for rotation.
if len(CryptKeeper.instance().keys) > 1:
yield self.save_auth_state(auth_state)
return auth_state
@property
def authenticator(self):
return self.settings.get('authenticator', None)