def get_record(params,record_uid): """Return the referenced record""" record_uid = record_uid.strip() if not record_uid: print('No record UID provided') return if not params.record_cache: print('No record cache. Sync down first.') return if not record_uid in params.record_cache: print('Record UID not found.') return cached_rec = params.record_cache[record_uid] if params.debug: print('Cached Rec: ' + str(cached_rec)) data = json.loads(cached_rec['data'].decode('utf-8')) rec = Record(record_uid) rec.load(data,cached_rec['revision']) return rec
def parse_record_json(json): record = Record() record.folder = json['folder'] record.title = json['title'] record.login = json['login'] record.password = json['password'] record.login_url = json['login_url'] record.notes = json['notes'] record.custom_fields = json['custom_fields'] return record
def parse_line(line): fields = line.split('\t') record = Record() record.folder = fields[0] record.title = fields[1] record.login = fields[2] record.password = fields[3] record.login_url = fields[4] record.notes = fields[5].replace('\\\\n', '\n') record.custom_fields = [{'name': fields[i], 'value': fields[i + 1], 'type': 'text'} for i in range(6, len(fields) - 1, 2)] return record
def test_audit_log_splunk_properties_success(self): splunk = aram.AuditLogSplunkExport() props = {} record = Record() with mock.patch('builtins.print'), mock.patch( 'builtins.input') as mock_input, mock.patch( 'requests.post') as mock_post: resp1 = mock.Mock() resp1.status_code = 401 resp1.json.return_value = {'code': 2} resp2 = mock.Mock() resp2.status_code = 400 resp2.json.return_value = {'code': 6} mock_input.side_effect = [ 'www.splunk.com', 'Splunk Token', KeyboardInterrupt() ] mock_post.side_effect = [resp1, resp2, Exception()] splunk.get_properties(record, props) self.assertIn('hec_url', props) self.assertIn('token', props) self.assertEqual(props['hec_url'], record.login_url) self.assertEqual(props['token'], record.password) self.assertTrue(splunk.store_record)
def parse_json(json): record = Record() record.folder = json['folder'] record.title = json['title'] record.login = json['login'] record.password = json['password'] record.login_url = json['login_url'] record.notes = json['notes'] record.custom_fields = json['custom_fields'] return record
def add_record(params): """ Create a new record with passed-in data or interactively. The shared folder UID is also optional """ record = Record() if not record.title: while not record.title: record.title = input("... Title (req'd): ") record.folder = input("... Folder: ") record.login = input("... Login: "******"... Password: "******"... Login URL: ") record.notes = input("... Notes: ") while True: custom_dict = {} custom_dict['name'] = input("... Custom Field Name : ") if not custom_dict['name']: break custom_dict['value'] = input("... Custom Field Value : ") custom_dict['type'] = 'text' record.custom_fields.append(custom_dict) new_record = prepare_record(params, record) request = make_request(params, 'record_update') request['add_records'] = [new_record] response_json = communicate(params, request) if response_json['result'] == 'success': new_revision = 0 if 'add_records' in response_json: for info in response_json['add_records']: if info['record_uid'] == record.record_uid: if info['status'] == 'success': # all records in the transaction get the # same revision. this just checks 100% success new_revision = response_json['revision'] if new_revision == 0: print('Error: Revision not updated') return False if new_revision == new_record['revision']: print('Error: Revision did not change') return False print('New record successful for record_uid=' + \ str(new_record['record_uid']) + ', revision=' + \ str(new_record['revision']), ', new_revision=' + \ str(new_revision)) new_record['revision'] = new_revision # sync down the data which updates the caches sync_down(params) return True
def test_audit_log_splunk_properties_cancel(self): splunk = enterprise.AuditLogSplunkExport() props = {} record = Record() with mock.patch('builtins.print'), mock.patch('builtins.input') as mock_input, mock.patch('requests.post') as mock_post: resp1 = mock.Mock() resp1.status_code = 404 mock_input.side_effect = ['www.splunk.com', KeyboardInterrupt()] mock_post.side_effect = [resp1, Exception()] with self.assertRaises(KeyboardInterrupt): splunk.get_properties(record, props)
def sample_record(): record = Record() record.folder = "folder" record.title = "title" record.login = "******" record.password = "******" record.login_url = "login_url" record.notes = "line1\nline2\nline3" record.custom_fields = [ {"name": "cf1", "value": "cf1val", "type": "text"}, {"name": "cf2", "value": "cf2val", "type": "text"}, ] return record
def sample_record(): record = Record() record.folder = 'folder' record.title = 'title' record.login = '******' record.password = '******' record.login_url = 'login_url' record.notes = 'line1\nline2\nline3' record.custom_fields = [{ 'name': 'cf1', 'value': 'cf1val', 'type': 'text' }, { 'name': 'cf2', 'value': 'cf2val', 'type': 'text' }] return record
from keepercommander.record import Record from keepercommander.params import KeeperParams from keepercommander import display, api my_params = KeeperParams() while not my_params.user: my_params.user = getpass.getpass(prompt='User(Email): ', stream=None) while not my_params.password: my_params.password = getpass.getpass(prompt='Master Password: '******'Test Record' r.login = '******' # generate a 32-char random password r.password = ''.join(random.SystemRandom().choice(string.printable) for _ in range(32)) if api.add_record(my_params, r): print('Added record UID='+r.record_uid) # Delete the record if r.record_uid: api.delete_record(my_params, r.record_uid)
from keepercommander.params import KeeperParams from keepercommander.shared_folder import SharedFolder from keepercommander import display, api params = KeeperParams() # Inputs - hard coded for demo purposes params.user = '******' params.password = '******' shared_folder_uid = 'your_shared_folder_uid' # Login and sync api.sync_down(params) # Create a new record with some random password record = Record() record.title = 'Test Record' record.login = '******' record.login_url = 'https://google.com' record.notes = 'Here are some notes.' record.password = ''.join(random.SystemRandom().choice(string.printable) for _ in range(32)) # Add the record to your vault if api.add_record(params, record): print('Added record UID=' + record.record_uid) else: print('Error: Unable to add record') sys.exit() # Get existing shared folder from the cache
def rotate_password(params, record_uid): """ Rotate the password for the specified record UID """ record_uid = record_uid.strip() if not record_uid: print('No record UID provided') return False if not params.record_cache: print('No record cache. Sync down first.') return False if not record_uid in params.record_cache: print('Record UID not found.') return False # get the record object cached_rec = params.record_cache[record_uid] # extract data and extra from record if 'data' in cached_rec: data = json.loads(cached_rec['data'].decode('utf-8')) else: data = {} if 'extra' in cached_rec: extra = json.loads(cached_rec['extra'].decode('utf-8')) else: extra = {} # check for edit permissions can_edit = False if 'can_edit' in cached_rec: if params.debug: print('Edit permissions found in record') can_edit = True # If record permission not there, check shared folders found_shared_folder_uid = '' if can_edit == False: for shared_folder_uid in params.shared_folder_cache: shared_folder = params.shared_folder_cache[shared_folder_uid] sf_key = shared_folder['shared_folder_key'] if 'records' in shared_folder: sf_records = shared_folder['records'] for sf_record in sf_records: if 'record_uid' in sf_record: if sf_record['record_uid'] == record_uid: found_shared_folder_uid = shared_folder_uid if 'can_edit' in sf_record: can_edit = True if params.debug: print('Edit permissions found in folder') break if not can_edit: print('You do not have permissions to edit this record.') return False if not params.server: raise CommunicationError('No server provided') if not params.user: raise CommunicationError('No username provided') # save previous password if params.debug: print('Data: ' + str(data)) if params.debug: print('Extra: ' + str(extra)) # generate friendly datestamp modified_time = int(round(time.time())) modified_time_milli = modified_time * 1000 datestamp = datetime.datetime.fromtimestamp( modified_time).strftime('%Y-%m-%d %H:%M:%S') # Backup old password in a custom field custom_dict = {} custom_dict['name'] = 'cmdr:Rotation @ '+str(datestamp) custom_dict['value'] = data['secret2'] custom_dict['type'] = 'text' # serialize this dict serialized = json.dumps(custom_dict) # load as json custom_dict_json = json.loads(serialized) # Append to the current structure data['custom'].append(custom_dict_json) if params.debug: print('Old password: '******'secret2'])) # load the data into a record object for convenience record_object = Record() record_object.load(data) new_password = generator.generate() # generate a new password with any specified rules rules = record_object.get("cmdr:rules") if rules: new_password = generator.generateFromRules(rules) # execute rotation plugin associated with this record plugin_name = record_object.get("cmdr:plugin") if plugin_name: print("Rotating with plugin " + str(plugin_name)) plugin = plugin_manager.get_plugin(plugin_name) if plugin: success = plugin.rotate(record_object, new_password) if success: if params.debug: print('Password rotation on target system is successful.') else: print('Password rotation failed') return False else: return False # Proceed with Keeper password rotation data['secret2'] = new_password if params.debug: print('New password: '******'secret2'])) if params.debug: print('New record data: ' + str(data)) # Update the record cache with the cleartext data if params.debug: print('data is ' + str(isinstance(data, dict))) if params.debug: print('params.record_cache is ' + \ str(isinstance(params.record_cache, dict))) # convert dict back to json then encode it params.record_cache[record_uid]['data'] = json.dumps(data).encode() if params.debug: print('New record: ' + str(params.record_cache[record_uid])) print('Data: ' + str(data)) print('Extra: ' + str(extra)) # Convert the data and extra dictionary to string object # with double quotes instead of single quotes data_serialized = json.dumps(data) extra_serialized = json.dumps(extra) if params.debug: print('data_serialized: ' + str(data_serialized)) if params.debug: print('extra_serialized: ' + str(extra_serialized)) # encrypt data and extra if not 'record_key_unencrypted' in params.record_cache[record_uid]: if plugin_name: print('Plugin updated password to: ' + new_password) raise CryptoError('No record_key_unencrypted found for ' + record_uid) if not 'record_key' in params.record_cache[record_uid]: if plugin_name: print('Plugin updated password to: ' + new_password) raise CryptoError('No record_key found for ' + record_uid) record_key_unencrypted = \ params.record_cache[record_uid]['record_key_unencrypted'] iv = os.urandom(16) cipher = AES.new(record_key_unencrypted, AES.MODE_CBC, iv) encrypted_data = iv + cipher.encrypt(pad(data_serialized)) iv = os.urandom(16) cipher = AES.new(record_key_unencrypted, AES.MODE_CBC, iv) encrypted_extra = iv + cipher.encrypt(pad(extra_serialized)) if params.debug: print('encrypted_data: ' + str(encrypted_data)) if params.debug: print('encrypted_extra: ' + str(encrypted_extra)) # note: decode() converts bytestream (b') to string encoded_data = base64.urlsafe_b64encode(encrypted_data).decode() encoded_extra = base64.urlsafe_b64encode(encrypted_extra).decode() if params.debug: print('encoded_data: ' + str(encoded_data)) if params.debug: print('encoded_extra: ' + str(encoded_extra)) # build a record object new_record = {} new_record['record_uid'] = record_uid new_record['version'] = 2 new_record['data'] = encoded_data new_record['extra'] = encoded_extra new_record['client_modified_time'] = modified_time_milli new_record['revision'] = params.record_cache[record_uid]['revision'] if found_shared_folder_uid: new_record['shared_folder_uid'] = found_shared_folder_uid if 'udata' in params.record_cache[record_uid]: new_record['udata'] = params.record_cache[record_uid]['udata'] if params.debug: print('new_record: ' + str(new_record)) # create updated records update_records = [] update_records.append(new_record) def make_json(params, update_records): return { 'client_time':current_milli_time(), 'device_id':'Commander', 'device_name':'Commander', 'command':'record_update', 'update_records':update_records, 'protocol_version':1, 'client_version':CLIENT_VERSION, '2fa_token':params.mfa_token, '2fa_type':params.mfa_type, 'session_token':params.session_token, 'username':params.user } if not params.session_token: try: login(params) except: if plugin_name: print('Plugin updated password to: ' + new_password) raise payload = make_json(params, update_records) if params.debug: print('payload: ' + str(payload)) try: r = requests.post(params.server, json=payload) except: if plugin_name: print('Plugin updated password to: ' + new_password) raise CommunicationError(sys.exc_info()[0]) response_json = r.json() if params.debug: debug_response(params, payload, r) if response_json['result_code'] == 'auth_failed': if params.debug: print('Re-authorizing.') try: login(params) except: if plugin_name: print('Plugin updated password to: ' + new_password) raise payload = make_json(params, update_records) try: r = requests.post(params.server, json=payload) except: print('Comm error during re-auth') if plugin_name: print('Plugin updated password to: ' + new_password) raise CommunicationError(sys.exc_info()[0]) response_json = r.json() if params.debug: debug_response(params, payload, r) if response_json['result'] == 'success': new_revision = 0 if 'update_records' in response_json: for info in response_json['update_records']: if info['record_uid'] == record_uid: if info['status'] == 'success': # all records in the transaction get the # same revision. this just checks 100% success new_revision = response_json['revision'] if new_revision == 0: print('Error: Revision not updated') if plugin_name: print('Plugin updated password to: ' + new_password) return False if new_revision == new_record['revision']: print('Error: Revision did not change') if plugin_name: print('Plugin updated password to: ' + new_password) return False print('Rotation successful for record_uid=' + \ str(new_record['record_uid']) + ', revision=' + \ str(new_record['revision']), ', new_revision=' + \ str(new_revision)) # update local cache params.record_cache[record_uid]['revision'] = new_revision else : if response_json['result_code']: if plugin_name: print('Plugin updated password to: ' + new_password) raise CommunicationError('Unexpected problem: ' + \ response_json['result_code']) return True
def do_command(params): if (params.command == 'q'): return False elif (params.command == 'l'): if (len(params.record_cache) == 0): print('No records') else: results = api.search_records(params, '') display.formatted_records(results) elif (params.command == 'lsf'): if (len(params.shared_folder_cache) == 0): print('No shared folders') else: results = api.search_shared_folders(params, '') display.formatted_shared_folders(results) elif (params.command == 'lt'): if (len(params.team_cache) == 0): print('No teams') else: results = api.search_teams(params, '') display.formatted_teams(results) elif (params.command[:2] == 'g '): if (api.is_shared_folder(params, params.command[2:])): sf = api.get_shared_folder(params, params.command[2:]) if sf: sf.display() elif (api.is_team(params, params.command[2:])): team = api.get_team(params, params.command[2:]) if team: team.display() else: r = api.get_record(params, params.command[2:]) if r: r.display() elif (params.command[:2] == 'r '): api.rotate_password(params, params.command[2:]) elif (params.command[:2] == 'd '): api.delete_record(params, params.command[2:]) elif (params.command == 'c'): print(chr(27) + "[2J") elif (params.command[:2] == 's '): results = api.search_records(params, params.command[2:]) display.formatted_records(results) elif (params.command[:2] == 'b '): results = api.search_records(params, params.command[2:]) for r in results: api.rotate_password(params, r.record_uid) elif (params.command[:3] == 'an '): api.append_notes(params, params.command[3:]) elif (params.command == 'd'): api.sync_down(params) elif (params.command == 'a'): record = Record() while not record.title: record.title = input("... Title (req'd): ") record.folder = input("... Folder: ") record.login = input("... Login: "******"... Password: "******"... Login URL: ") record.notes = input("... Notes: ") while True: custom_dict = {} custom_dict['name'] = input("... Custom Field Name : ") if not custom_dict['name']: break custom_dict['value'] = input("... Custom Field Value : ") custom_dict['type'] = 'text' record.custom_fields.append(custom_dict) api.add_record(params, record) elif (params.command == 'h'): display.formatted_history(stack) elif (params.command == 'debug'): if params.debug: params.debug = False print('Debug OFF') else: params.debug = True print('Debug ON') elif params.command == '': pass else: print('\n\nShell Commands:\n') print(' d ... download & decrypt data') print(' l ... list folders and record titles') print(' lsf ... list shared folders') print(' lt ... list teams') print(' s <regex> ... search with regular expression') print(' g <uid> ... get record or shared folder details for uid') print(' r <uid> ... rotate password for uid') print( ' b <regex> ... rotate password for matches of regular expression' ) print(' a ... add a new record interactively') print(' an <uid> ... append some notes to the specified record') print(' c ... clear the screen') print(' h ... show command history') print(' q ... quit') print('') if params.command: if params.command != 'h': stack.append(params.command) stack.reverse() return True