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
15 changes: 13 additions & 2 deletions keepercommander/commands/tunnel/port_forward/TunnelGraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,8 @@ def set_resource_allowed(self, resource_uid, tunneling=None, connections=None, r
session_recording=None, typescript_recording=None, remote_browser_isolation=None,
ai_enabled=None, ai_session_terminate=None,
allowed_settings_name='allowedSettings', is_config=False,
v_type: RefType=str(RefType.PAM_MACHINE), meta_version=None):
v_type: RefType=str(RefType.PAM_MACHINE), meta_version=None,
rotate_on_termination=None):
v_type = RefType(v_type)
allowed_ref_types = [RefType.PAM_MACHINE, RefType.PAM_DATABASE, RefType.PAM_DIRECTORY, RefType.PAM_BROWSER]
if v_type not in allowed_ref_types:
Expand Down Expand Up @@ -625,13 +626,23 @@ def set_resource_allowed(self, resource_uid, tunneling=None, connections=None, r
else:
settings["aiSessionTerminate"] = ai_session_terminate

if rotate_on_termination is not None:
if content is None:
content = {allowed_settings_name: {}}
dirty = True
current_rot = bool(content.get("rotateOnTermination", False))
if rotate_on_termination != current_rot:
dirty = True
content = ensure_resource_meta_v1(dict(content))
content["rotateOnTermination"] = bool(rotate_on_termination)

if dirty:
# Legacy: missing or meta_version=0 -> write content as-is (no version in meta)
if meta_version is not None and meta_version != 0:
meta_payload = build_resource_meta(
meta_version,
content.get(allowed_settings_name, {}),
rotate_on_termination=False,
rotate_on_termination=bool(content.get("rotateOnTermination", False)),
)
resource_vertex.add_data(content=meta_payload, path='meta', needs_encryption=False)
else:
Expand Down
42 changes: 35 additions & 7 deletions keepercommander/commands/tunnel_and_connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -1862,7 +1862,9 @@ class PAMConnectionEditCommand(Command):
'credential on the PAM Resource')
parser.add_argument('--launch-user', '-lu', required=False, dest='launch_user', action='store',
help='The record path or UID of the PAM User record to configure as the launch '
'credential on the PAM Resource')
'credential on the PAM Resource.')
parser.add_argument('--clear-launch-user', required=False, dest='clear_launch_user', action='store_true',
help='Remove the launch credential from the resource (clears is_launch_credential in the DAG)')
parser.add_argument('--protocol', '-p', dest='protocol', choices=protocols,
help='Set connection protocol')
parser.add_argument('--connections', '-cn', dest='connections', choices=choices,
Expand All @@ -1876,6 +1878,9 @@ class PAMConnectionEditCommand(Command):
'the port from the record will be used.')
parser.add_argument('--key-events', '-k', dest='key_events', choices=choices,
help='Toggle Key Events settings')
parser.add_argument('--rotate-on-termination', required=False, dest='rotate_on_termination',
choices=['on', 'off'],
help='Rotate launch credentials when the PAM session ends (DAG resource meta)')
parser.add_argument('--silent', '-s', required=False, dest='silent', action='store_true',
help='Silent mode - don\'t print PAM User, PAM Config etc.')

Expand Down Expand Up @@ -2072,24 +2077,47 @@ def execute(self, params, **kwargs):
if _typescript_recording is not None and tdag.check_if_resource_allowed(record_uid, "typescriptRecording") != _typescript_recording:
dirty = True

if dirty:
launch_credential_record_types = ("pamDatabase", "pamDirectory", "pamMachine")
rot_kw = kwargs.get('rotate_on_termination')
rot_bool = True if rot_kw == 'on' else False if rot_kw == 'off' else None
if rot_bool is not None and record_type not in launch_credential_record_types:
raise CommandError('pam connection edit',
f'{bcolors.FAIL}--rotate-on-termination is only supported for pamMachine, pamDatabase, and '
f'pamDirectory records. Record "{record_uid}" is of type "{record_type}" and does not support '
f'launch credentials.{bcolors.ENDC}')

if dirty or rot_bool is not None:
tdag.set_resource_allowed(resource_uid=record_uid,
allowed_settings_name=allowed_settings_name,
connections=kwargs.get('connections', None),
session_recording=kwargs.get('recording', None),
typescript_recording=kwargs.get('typescriptrecording', None))
typescript_recording=kwargs.get('typescriptrecording', None),
rotate_on_termination=rot_bool)

# admin parameter is optional yet if not set connections may fail
admin_name = kwargs.get('admin')
adm_rec = RecordMixin.resolve_single_record(params, admin_name)
admin_uid = adm_rec.record_uid if adm_rec else None
if admin_uid and record_type in ("pamDatabase", "pamDirectory", "pamMachine"):
if admin_uid and record_type in launch_credential_record_types:
tdag.link_user_to_resource(admin_uid, record_uid, is_admin=True, belongs_to=True)
# tdag.link_user_to_config(admin_uid) # is_iam_user=True

# launch-user parameter sets the launch credential on the resource
# launch-user parameter sets the launch credential; --clear-launch-user removes it
clear_launch_user = bool(kwargs.get('clear_launch_user'))
launch_user_name = kwargs.get('launch_user')
if launch_user_name:

if clear_launch_user and launch_user_name:
raise CommandError('pam connection edit',
f'{bcolors.FAIL}Use either --clear-launch-user or --launch-user, not both.{bcolors.ENDC}')
if clear_launch_user:
if record_type not in launch_credential_record_types:
raise CommandError('pam connection edit',
f'{bcolors.FAIL}--clear-launch-user is only supported for pamMachine, pamDatabase, and '
f'pamDirectory records. Record "{record_uid}" is of type "{record_type}" and does not '
f'support launch credentials.{bcolors.ENDC}')
tdag.clear_launch_credential_for_resource(record_uid)
tdag.upgrade_resource_meta_to_v1(record_uid)
elif launch_user_name:
launch_rec = RecordMixin.resolve_single_record(params, launch_user_name)
if not launch_rec:
raise CommandError('',
Expand All @@ -2098,7 +2126,7 @@ def execute(self, params, **kwargs):
raise CommandError('',
f'{bcolors.FAIL}Launch user record must be a pamUser record type.{bcolors.ENDC}')
launch_uid = launch_rec.record_uid
if record_type in ("pamDatabase", "pamDirectory", "pamMachine"):
if record_type in launch_credential_record_types:
tdag.clear_launch_credential_for_resource(record_uid, exclude_user_uid=launch_uid)
tdag.link_user_to_resource(launch_uid, record_uid, is_launch_credential=True, belongs_to=True)
tdag.upgrade_resource_meta_to_v1(record_uid)
Expand Down
Loading