Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion keepercommander/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
# Contact: commander@keepersecurity.com
#

__version__ = '18.0.10'
__version__ = '18.0.11'
6 changes: 5 additions & 1 deletion keepercommander/auth/console_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,11 @@ def on_sso_redirect(self, step):
token = pyperclip.paste()
except:
token = ''
logging.warning('Failed to paste from clipboard')
if len(token) < 10:
try:
token = input(f'{Fore.GREEN}Paste SSO Token: {Fore.RESET}').strip()
except KeyboardInterrupt:
token = ''
else:
if len(token) < 10:
logging.warning(f'Unsupported menu option: {token}')
Expand Down
43 changes: 30 additions & 13 deletions keepercommander/importer/imp_exp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,7 @@ def value_to_token(value): # type: (any) -> str
return str(value)


def tokenize_record_key(record): # type: (ImportRecord) -> Iterator[str]
def tokenize_record_key(record, folder): # type: (ImportRecord, str) -> Iterator[str]
"""
Turn a record-to-import into an iterable of str's for hashing. This is really about import --update.

Expand All @@ -1807,6 +1807,7 @@ def tokenize_record_key(record): # type: (ImportRecord) -> Iterator[str]
yield f'$title:{record.title or ""}'
yield f'$login:{record.login or ""}'
yield f'$url:{record.login_url or ""}'
yield f'$folder:{folder}'

if record_type in {'', 'login'}:
return
Expand Down Expand Up @@ -2071,7 +2072,7 @@ def prepare_record_add_or_update(update_flag, params, records):
If a 100% match is found for a record, then just skip requesting anything; it doesn't need to be changed.
Otherwise import the record, risking creating an almost-duplicate.
If update_flag is True:
if a unique field match (on title, login, and url) is found, then request a change in password only.
if a unique field match (on title, login, url, and folder) is found, then request a change in password only.
"""
preexisting_entire_record_hash = {}
preexisting_partial_record_hash = {}
Expand All @@ -2081,8 +2082,13 @@ def prepare_record_add_or_update(update_flag, params, records):
record_hash = build_record_hash(tokenize_full_import_record(import_record))
preexisting_entire_record_hash[record_hash] = record_uid
if update_flag:
record_hash = build_record_hash(tokenize_record_key(import_record))
preexisting_partial_record_hash[record_hash] = record_uid
folders = [get_folder_path(params, x) for x in find_folders(params, record_uid)]
folders = [x for x in folders if x]
if len(folders) == 0:
folders.append(record_uid)
for folder in folders:
record_hash = build_record_hash(tokenize_record_key(import_record, folder))
preexisting_partial_record_hash[record_hash] = record_uid
else:
pass

Expand Down Expand Up @@ -2128,15 +2134,26 @@ def prepare_record_add_or_update(update_flag, params, records):
record_to_import.append(import_record)
continue
elif update_flag:
record_hash = build_record_hash(tokenize_record_key(import_record))
if record_hash in preexisting_partial_record_hash:
record_uid = preexisting_partial_record_hash[record_hash]
if import_record.uid:
external_lookup[import_record.uid] = record_uid
if record_uid not in record_uid_to_update:
record_uid_to_update.add(record_uid)
import_record.uid = record_uid
record_to_import.append(import_record)
folders = []
if import_record.folders:
folders.extend([x.get_folder_path() for x in import_record.folders])
folders = [x for x in folders if x]
if len(folders) == 0:
folders.append('')
found = False
for folder in folders:
record_hash = build_record_hash(tokenize_record_key(import_record, folder))
if record_hash in preexisting_partial_record_hash:
record_uid = preexisting_partial_record_hash[record_hash]
if import_record.uid:
external_lookup[import_record.uid] = record_uid
if record_uid not in record_uid_to_update:
record_uid_to_update.add(record_uid)
import_record.uid = record_uid
record_to_import.append(import_record)
found = True
break
if found:
continue

record_uid = utils.generate_uid()
Expand Down
3 changes: 1 addition & 2 deletions keepercommander/vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,8 +475,7 @@ def export_schedule_field(value): # type: (dict) -> Optional[str]
cron = value.get('cron')
if isinstance(cron, str):
comps = [x for x in cron.split(' ') if x]
if len(comps) >= 6:
comps = comps[1:6]
if comps:
return ' '.join(comps)
return ''

Expand Down
10 changes: 10 additions & 0 deletions unit-tests/test_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,13 @@ def test_schedule_parser(self):
self.assertEqual(sc.get('type'), 'MONTHLY_BY_WEEKDAY')
self.assertEqual(sc.get('weekday'), 'WEDNESDAY')
self.assertEqual(sc.get('occurrence'), 'SECOND')

def test_export_schedule_field_preserves_quartz_cron_seconds(self):
schedule = {"type": "CRON", "cron": "1 5 1 * * ?", "tz": "Etc/UTC"}
self.assertEqual(vault.TypedField.export_schedule_field(schedule), '1 5 1 * * ?')

schedule = {"type": "CRON", "cron": "0 5 1 * * ?"}
self.assertEqual(vault.TypedField.export_schedule_field(schedule), '0 5 1 * * ?')

schedule = {"type": "CRON", "cron": "5 1 * * ?"}
self.assertEqual(vault.TypedField.export_schedule_field(schedule), '5 1 * * ?')
Loading