Skip to content

Commit

Permalink
pam rotation commands to work with multiple records. KC-779
Browse files Browse the repository at this point in the history
  • Loading branch information
sk-keeper committed Jun 17, 2024
1 parent e654be8 commit 92c350f
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 144 deletions.
381 changes: 265 additions & 116 deletions keepercommander/commands/discoveryrotation.py

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions keepercommander/commands/record_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,9 @@ def assign_typed_fields(self, record, fields):
record_field.value[0].update(value)
else:
record_field.value[0] = value
noneKeys = [k for k,v in record_field.value[0].items() if v is None]
for k in noneKeys:
del record_field.value[0][k]
else:
if is_field:
record_field.value.clear()
Expand Down
11 changes: 6 additions & 5 deletions keepercommander/commands/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,17 +250,18 @@ def get_parser(self):
return sync_down_parser

def execute(self, params, **kwargs):
force = kwargs.get('force')
if force is True:
force = kwargs.get('force') is True
if force:
params.revision = 0
params.sync_down_token = b''
if params.config:
if 'skip_records' in params.config:
del params.config['skip_records']

api.sync_down(params, record_types=force is True)
from keepercommander.loginv3 import LoginV3Flow
LoginV3Flow.populateAccountSummary(params)
api.sync_down(params, record_types=force)
if force:
from keepercommander.loginv3 import LoginV3Flow
LoginV3Flow.populateAccountSummary(params)

accepted = False
if len(params.pending_share_requests) > 0:
Expand Down
2 changes: 1 addition & 1 deletion keepercommander/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ def enforcement_list(): # type: () -> List[Tuple[str, str, str]]

ENFORCEMENTS = {e[0].lower(): e[2].lower() for e in [*_ENFORCEMENTS, *_COMPOUND_ENFORCEMENTS]}

week_days = ('SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY','THURSDAY', 'FRIDAY', 'SATURDAY')
week_days = ('SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY')
occurrences = ('FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST')
months = ('JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE', 'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER',
'NOVEMBER', 'DECEMBER')
Expand Down
90 changes: 68 additions & 22 deletions keepercommander/vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,12 +469,26 @@ def export_ssh_key_field(value): # type: (dict) -> Optional[str]
def export_schedule_field(value): # type: (dict) -> Optional[str]
if isinstance(value, dict):
schedule_type = value.get('type')
if schedule_type == 'RUN_ONCE':
return ''

if schedule_type == 'CRON':
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]
return ' '.join(comps)
return ''

hour = '0'
minute = '0'
day = '*'
month = '*'
week_day = '*'
utc_time = value.get('time') or ''
utc_time = value.get('time')
if not utc_time:
utc_time = value.get('utcTime')
if utc_time:
comps = utc_time.split(':')
if len(comps) >= 2:
Expand All @@ -500,6 +514,16 @@ def export_schedule_field(value): # type: (dict) -> Optional[str]
elif schedule_type == 'MONTHLY_BY_WEEKDAY':
wd = constants.get_cron_week_day(value.get('weekday')) or 1
occ = constants.get_cron_occurrence(value.get('occurrence'))
if occ == 'FIRST':
occ = 1
elif occ == 'SECOND':
occ = 2
elif occ == 'THIRD':
occ = 3
elif occ == 'FOURTH':
occ = 4
else:
occ = 1
week_day = f'{wd}#{occ}'
elif schedule_type == 'YEARLY':
month = constants.get_cron_month(value.get('month')) or 1
Expand Down Expand Up @@ -689,68 +713,90 @@ def import_schedule_field(value): # type: (str) -> Optional[dict]
minute = int(comps[0]) if comps[0].isnumeric() else 0
if minute < 0 or minute > 59:
minute = 0
hour = int(comps[1]) if comps[1].isnumeric() else 0
hour = 0
if comps[1].isnumeric():
hour = int(comps[1])
else:
hrs = comps[1].replace('-', ',').split(',')
if len(hrs) > 0 and hrs[0].isnumeric():
hour = int(hrs[0])
if hour < 0 or hour > 23:
hour = 0
time = f'{hour:02}:{minute:02}:00'
if comps[3] == '*' and comps[4] == '*': # daily
intervalCount = 1

if comps[3] in ('*', '?') and comps[4] in ('*', '?'): # daily
if comps[2].isnumeric():
schedule = {
'type': 'MONTHLY_BY_DAY',
'time': time,
'monthDay': int(comps[2])
}
else:
interval = 1
if comps[2].startswith('*/'):
intr = comps[2][2:]
if intr.isnumeric():
interval = int(intr)
elif comps[2].startswith('*'):
schedule = {
'type': 'DAILY',
'time': time,
'intervalCount': interval
}
elif comps[4] != '*': # day of week
if comps[2].startswith('*/'):
intr = comps[2][2:]
if intr.isnumeric():
schedule['occurrences'] = int(intr)
elif comps[4] not in ('*', '?'): # day of week
if comps[4].isnumeric():
wd = int(comps[4])
if wd < 0 or wd > len(constants.week_days):
wd = 1
schedule = {
'type': 'WEEKLY',
'time': time,
'weekday': constants.week_days[wd]
}
elif comps[4].startswith('*/'):
schedule = {
'type': 'DAILY',
}
intr = comps[4][2:]
if intr.isnumeric():
schedule['occurrences'] = int(intr)
else:
wd_comps = comps[4].split('#')
wd_comps = comps[4].replace('%', '#').split('#')
if len(wd_comps) == 2 and wd_comps[0].isnumeric() and wd_comps[1].isnumeric():
wd = int(wd_comps[0])
if wd < 0 or wd > len(constants.week_days):
wd = 1
occ = int(wd_comps[1])
occ = int(wd_comps[1]) - 1
if occ < 0 or occ >= len(constants.occurrences):
occ = 0
occurrence = constants.occurrences[occ]
schedule = {
'type': 'MONTHLY_BY_WEEKDAY',
'time': time,
'weekday': constants.week_days[wd],
'occurrence': constants.occurrences[occ]
'occurrence': occurrence,
}
elif comps[2].isnumeric() and comps[3].isnumeric(): # day of year
mm = int(comps[4])
elif comps[3].isnumeric(): # day of month
mm = int(comps[3])
if mm > 0:
mm -= 1
if mm >= len(constants.months):
mm = len(constants.months) - 1
else:
mm = 0

dd = 1
if comps[2].isnumeric():
dd = int(comps[2])

schedule = {
'type': 'YEARLY',
'time': time,
'month': constants.months[mm],
'monthday': int(comps[3])
'monthDay': dd,
}
else:
schedule = {
'type': 'RUN_ONCE',
}
time = '2000-01-01T00:00:00'

schedule['tz'] = 'Etc/UTC'
schedule['time'] = time
schedule['intervalCount'] = intervalCount
return schedule

@staticmethod
Expand Down
30 changes: 30 additions & 0 deletions unit-tests/test_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,33 @@ def test_bank_account_serialization(self):
self.assertIsNotNone(str_value)
dict_value1 = vault.TypedField.import_account_field(str_value)
self.assertEqual(dict_value, dict_value1)

def test_schedule_parser(self):
sc = vault.TypedField.import_schedule_field('1 1 * * *')
self.assertEqual(sc.get('type'), 'DAILY')

sc = vault.TypedField.import_schedule_field('1 1 */5 * *')
self.assertEqual(sc.get('type'), 'DAILY')
self.assertEqual(sc.get('occurrences'), 5)

sc = vault.TypedField.import_schedule_field('1 1 5 * *')
self.assertEqual(sc.get('type'), 'MONTHLY_BY_DAY')
self.assertEqual(sc.get('monthDay'), 5)

sc = vault.TypedField.import_schedule_field('1 1 20 5 ?')
self.assertEqual(sc.get('type'), 'YEARLY')
self.assertEqual(sc.get('monthDay'), 20)
self.assertEqual(sc.get('month'), 'MAY')

sc = vault.TypedField.import_schedule_field('1 1 * * */3')
self.assertEqual(sc.get('type'), 'DAILY')
self.assertEqual(sc.get('occurrences'), 3)

sc = vault.TypedField.import_schedule_field('1 1 * * 3')
self.assertEqual(sc.get('type'), 'WEEKLY')
self.assertEqual(sc.get('weekday'), 'WEDNESDAY')

sc = vault.TypedField.import_schedule_field('1 1 * * 3#2')
self.assertEqual(sc.get('type'), 'MONTHLY_BY_WEEKDAY')
self.assertEqual(sc.get('weekday'), 'WEDNESDAY')
self.assertEqual(sc.get('occurrence'), 'SECOND')

0 comments on commit 92c350f

Please sign in to comment.