def encrypt_handler(args): if not get_server_setting('secret_keeping:enabled'): sys.exit('You cannot encrypt when secret-keeping is disabled.') db = get_db() selectors = get_selectors() spec = {'$or': [{s.plain_mongo: {'$exists': True}} for s in selectors]} for doc in db.clients.find(spec): doc, update = encrypt_document(doc) if update: db.clients.update({'_id': doc['_id']}, update) log.info('Encrypted data in client document {} (host {})', doc['_id'], doc['hostname']) print('Encrypted client document {} (host {})'.format( doc['_id'], doc['hostname'])) spec = {'key': {'$in': [s.plain_mongo for s in selectors]}} for doc in db.audit_trail.find(spec): doc, update = encrypt_document(doc, selectors=audit_trail_selectors) if update: update['$set']['key'] = next(s.enc_mongo for s in selectors if s.plain_mongo == doc['key']) db.audit_trail.update({'_id': doc['_id']}, update) log.info('Encrypted data in audit trail document {} (host {})', doc['_id'], doc['hostname']) print('Encrypted audit trail document {} (host {})'.format( doc['_id'], doc['hostname']))
def decrypt_iterator(document_iterator, document_keys=None, selectors=None, full_documents=False): """Decrypt documents and return decrypted data `selectors` defaults to `get_selectors()` Each yield is a tuple of a document containing the document keys, a document containing the decrypted data in the same structure as the original document, and a list of tuples of selectors that were decrypted and the decrypted datum for each selector. I.e., the same decrypted data is returned in two different forms, to simplify the use of the data by the caller. if `full_documents` is true, then the document in each yield will be the full document returned by the iterator with encrypted fields replaced, rather than a document containing just the decrypted fields. Assumes that the secret key has already been combined _before_ this function is called. Note: There is a yield for every document that is returned by document_iterator, even if nothing was decrypted. If that's the case, then the document keys in the yield will be populated, but the decrypted data document and list will be empty. """ if selectors is None: selectors = get_selectors() for doc in document_iterator: document_keys = {k: doc[k] for k in document_keys or ()} output_dict = doc if full_documents else {} output_tuples = [] for s in selectors: encrypted_data = get_setting(doc, s.enc_mem, check_defaults=False) if not encrypted_data: continue with NamedTemporaryFile('w+b') as unencrypted_file, \ NamedTemporaryFile('w+b') as encrypted_file: encrypted_file.write(b64decode(encrypted_data['data'])) encrypted_file.flush() gpg_command('--decrypt', '-o', unencrypted_file.name, encrypted_file.name) unencrypted_file.seek(0) unencrypted_data = json.loads( unencrypted_file.read().decode('utf-8')) # Order is important here. We unset before we set because the # selector key may be the same for both unencrypted and encrypted # fields. if full_documents: set_setting(output_dict, s.enc_mem, None) set_setting(output_dict, s.plain_mem, unencrypted_data) output_tuples.append((s, unencrypted_data)) yield (document_keys, output_dict if output_tuples else {}, output_tuples)
def access_handler(args): combine_secret_key() selectors = get_selectors() try: db = get_db() spec = {'$or': [{s.enc_mongo: {'$exists': True}} for s in selectors]} printed_header = not args.audit_trail for keys, dct, tuples in decrypt_iterator(db.clients.find(spec), ('_id', 'hostname'), full_documents=args.full, selectors=selectors): if dct: if not printed_header: print('Clients:\n') printed_header = True pprint.pprint({**keys, **dct}) log.info('Displayed encrypted data in document {} (host {})', keys['_id'], keys['hostname']) if args.audit_trail: if printed_header: print('') spec = {'key': {'$in': [s.enc_mongo for s in selectors]}} printed_header = False for keys, dct, tuples in decrypt_iterator( db.audit_trail.find(spec, sort=(('_id', DESCENDING), )), selectors=audit_trail_selectors, full_documents=True): if dct: if not printed_header: print('Audit trail:\n') printed_header = True dct['key'] = next(s.plain_mongo for s in selectors if s.enc_mongo == dct['key']) pprint.pprint(dct) log.info( 'Displayed encrypted audit trail in document {} ' '(host {})', dct['_id'], dct['hostname']) finally: delete_secret_key()
def decrypt_handler(args): combine_secret_key() selectors = get_selectors() try: db = get_db() spec = {'$or': [{s.enc_mongo: {'$exists': True}} for s in selectors]} for keys, dct, tuples in decrypt_iterator(db.clients.find(spec), ('_id', 'hostname'), selectors=selectors): if dct: spec = {'_id': keys['_id']} update = { '$set': {s.plain_mongo: u for s, u in tuples}, '$unset': {s.enc_mongo: True for s, u in tuples} } db.clients.update(spec, update) log.info('Decrypted data in client document {} (host {})', keys['_id'], keys['hostname']) print('Decrypted client document {} (host {})'.format( keys['_id'], keys['hostname'])) spec = {'key': {'$in': [s.enc_mongo for s in selectors]}} for keys, dct, tuples in decrypt_iterator( db.audit_trail.find(spec), selectors=audit_trail_selectors, full_documents=True): if dct: dct['key'] = next(s.plain_mongo for s in selectors if s.enc_mongo == dct['key']) spec = {'_id': dct['_id']} db.audit_trail.update(spec, dct) log.info('Decrypted data in audit trail document {} (host {})', dct['_id'], dct['hostname']) print('Decrypted audit trail document {} (host {})'.format( dct['_id'], dct['hostname'])) finally: delete_secret_key()