"""Secure Document Manager.""" from dynamodb_encryption_sdk.encrypted import CryptoConfig from dynamodb_encryption_sdk.encrypted.client import EncryptedPaginator from dynamodb_encryption_sdk.encrypted.item import decrypt_dynamodb_item from dynamodb_encryption_sdk.encrypted.item import decrypt_python_item from dynamodb_encryption_sdk.encrypted.item import encrypt_python_item from dynamodb_encryption_sdk.identifiers import CryptoAction from dynamodb_encryption_sdk.material_providers.aws_kms import \ AwsKmsCryptographicMaterialsProvider from dynamodb_encryption_sdk.structures import AttributeActions from dynamodb_encryption_sdk.structures import EncryptionContext from dynamodb_encryption_sdk.transform import ddb_to_dict from dynamodb_encryption_sdk.transform import dict_to_ddb from secdoc.config import TABLE_NAME class SecureDocumentManager: """Secure Document Manager.""" primary_key = 'service-name' sort_key = 'group-name' unencrypted_fields = ['service-name', 'group-name', 'favorite_color'] def __init__(self, table, key_alias): """Create Secure Document Manager.""" self.table = table self.key_alias = key_alias self.cmp = AwsKmsCryptographicMaterialsProvider(self.key_alias) def save_item(self, item): """Save a dict.""" encrypted_item = self.encrypt_item(item) self.put_item(encrypted_item) def encrypt_item(self, item): """Encrypt a dict.""" return encrypt_python_item(item, self.get_crypto_config_for_save(item)) def put_item(self, encrypted_item): """Put an encrypted item.""" self.table.put_item(Item=encrypted_item) def get_item(self, index_key): """Retrieve a decrypted item.""" record = self.table.get_item(Key=index_key) item = record.get('Item') crypto_config = self.get_crypto_config_for_save(item) return decrypt_python_item(item, crypto_config) def get_crypto_config_for_read(self): """Get crypto config for read.""" return self.get_crypto_config(None) def get_crypto_config_for_page(self, **kwargs): """Get crypto config for paging.""" return self.get_crypto_config_for_read(), kwargs def get_crypto_config_for_save(self, item): """Create a crypto config for our table and data.""" return self.get_crypto_config(dict_to_ddb(self.get_item_index(item))) def get_crypto_config(self, attributes): """Get crypto config.""" encryption_context = EncryptionContext( table_name=TABLE_NAME, partition_key_name=self.primary_key, sort_key_name=self.sort_key, attributes=attributes) return CryptoConfig( materials_provider=self.cmp, encryption_context=encryption_context, attribute_actions=self.get_attribute_actions()) def select_all(self, paginator): """Iterate through all items.""" e_page = EncryptedPaginator( paginator=paginator, decrypt_method=decrypt_dynamodb_item, crypto_config_method=self.get_crypto_config_for_page) for items in e_page.paginate(TableName=TABLE_NAME): for item in items.get('Items'): yield ddb_to_dict(item) @classmethod def get_attribute_actions(cls): """Get attribute actions.""" # Declare the default attribute action as ENCRYPT & SIGN. # Omit unencrypted attributes from encryption. attribute_actions = dict( (attr, CryptoAction.DO_NOTHING) for attr in cls.unencrypted_fields ) return AttributeActions( default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions=attribute_actions) @classmethod def get_item_index(cls, item): """Get item index.""" return { cls.primary_key: item.get(cls.primary_key), cls.sort_key: item.get(cls.sort_key) }