Skip to content

Commit 76e8fc0

Browse files
authored
Add support for rotate on expire to share folder for pam user type records
1 parent 3ebb49b commit 76e8fc0

15 files changed

Lines changed: 344 additions & 85 deletions

File tree

keepercli-package/src/keepercli/commands/pam/discovery/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,10 @@ def from_gateway(vault: vault_online.VaultOnline, gateway: str, configuration_ui
200200
application_id = utils.base64_url_encode(found_gateway.applicationUid)
201201
application = vault.vault_data.load_record(application_id)
202202
if application is None:
203-
logger.debug(f"cannot find application for gateway {gateway}, skipping.")
203+
logger.warning(
204+
f"KSM application for gateway {gateway} is not in the vault "
205+
f"(record {application_id}); discovery may still work via the router."
206+
)
204207

205208
if (utils.base64_url_encode(found_gateway.controllerUid) == gateway or
206209
found_gateway.controllerName.lower() == gateway.lower()):

keepercli-package/src/keepercli/commands/pam/discovery/discover.py

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
from keepersdk.helpers.pam_user_record_facade import PamUserRecordFacade
2020
from keepersdk.helpers.keeper_dag.jobs import Jobs
21-
from keepersdk.helpers.keeper_dag.dag_types import (CredentialBase, DiscoveryDelta, DiscoveryObject, JobItem, UserAcl, DirectoryInfo,
21+
from keepersdk.helpers.keeper_dag.dag_types import (CredentialBase, DiscoveryDelta, DiscoveryObject, JobItem, Settings, UserAcl, DirectoryInfo,
2222
BulkRecordConvert, BulkRecordAdd, BulkRecordSuccess, BulkProcessResults, NormalizedRecord, BulkRecordFail, PromptResult,
2323
PromptActionEnum, RecordField)
2424
from keepersdk.helpers.keeper_dag.dag_vertex import DAGVertex
@@ -152,7 +152,7 @@ def print_job_detail(vault: vault_online.VaultOnline,
152152
job_id: str):
153153

154154
def _find_job(configuration_record) -> Optional[Dict]:
155-
jobs_obj = Jobs(record=configuration_record)
155+
jobs_obj = Jobs(record=configuration_record, vault=vault)
156156
job_item = jobs_obj.get_job(job_id)
157157
if job_item is not None:
158158
return {
@@ -167,7 +167,7 @@ def _find_job(configuration_record) -> Optional[Dict]:
167167
if gateway_context is not None:
168168
jobs = payload["jobs"]
169169
job = jobs.get_job(job_id)
170-
infra = Infrastructure(record=gateway_context.configuration)
170+
infra = Infrastructure(record=gateway_context.configuration, vault=vault)
171171

172172
status = "RUNNING"
173173
if job.end_ts is not None and not job.error:
@@ -296,7 +296,7 @@ def execute(self, context: KeeperParams, **kwargs):
296296
if len(gateway_context.gateway_name) > max_gateway_name:
297297
max_gateway_name = len(gateway_context.gateway_name)
298298

299-
jobs = Jobs(record=configuration_record)
299+
jobs = Jobs(record=configuration_record, vault=vault)
300300
if show_history is True:
301301
job_list = reversed(jobs.history)
302302
else:
@@ -391,7 +391,7 @@ def execute(self, context: KeeperParams, **kwargs):
391391
multi_conf_msg(gateway, err)
392392
return
393393

394-
jobs = Jobs(record=gateway_context.configuration)
394+
jobs = Jobs(record=gateway_context.configuration, vault=vault)
395395
current_job_item = jobs.current_job
396396
removed_prior_job = None
397397
if current_job_item is not None:
@@ -467,15 +467,20 @@ def execute(self, context: KeeperParams, **kwargs):
467467
setattr(c, key, obj[key])
468468
credentials.append(c.model_dump())
469469

470+
user_map_entries = self.make_protobuf_user_map(
471+
context=context,
472+
gateway_context=gateway_context
473+
)
474+
if len(user_map_entries) == 0:
475+
logger.info(
476+
"No pamUser records are linked to this configuration; "
477+
"discovery will run without an existing user map."
478+
)
479+
470480
action_inputs = GatewayActionDiscoverJobStartInputs(
471481
configuration_uid=gateway_context.configuration_uid,
472482
resource_uid=kwargs.get('resource_uid'),
473-
user_map=gateway_context.encrypt(
474-
self.make_protobuf_user_map(
475-
context=context,
476-
gateway_context=gateway_context
477-
)[0]
478-
),
483+
user_map=gateway_context.encrypt({"users": user_map_entries}),
479484

480485
shared_folder_uid=gateway_context.default_shared_folder_uid,
481486
languages=[kwargs.get('language')],
@@ -507,16 +512,39 @@ def execute(self, context: KeeperParams, **kwargs):
507512
logger.error(f"The router returned a failure.")
508513
return
509514

515+
discovery_settings = Settings(
516+
credentials=[CredentialBase(**c) for c in credentials],
517+
default_shared_folder_uid=gateway_context.default_shared_folder_uid,
518+
include_azure_aadds=kwargs.get('include_azure_aadds', False),
519+
skip_rules=kwargs.get('skip_rules', False),
520+
skip_machines=kwargs.get('skip_machines', False),
521+
skip_databases=kwargs.get('skip_databases', False),
522+
skip_directories=kwargs.get('skip_directories', False),
523+
skip_cloud_users=kwargs.get('skip_cloud_users', False),
524+
user_map=user_map_entries or None,
525+
)
526+
job_id = jobs.start(
527+
settings=discovery_settings,
528+
resource_uid=kwargs.get('resource_uid'),
529+
conversation_id=conversation_id,
530+
)
531+
jobs.close()
532+
510533
if "has been queued" in data.get("Response", ""):
511534

512535
if removed_prior_job is None:
513-
logger.info("The discovery job is currently running.")
536+
logger.info(f"Discovery job {job_id} is running.")
514537
else:
515-
logger.info(f"Active discovery job {removed_prior_job} has been removed and new discovery job is running.")
538+
logger.info(
539+
f"Active discovery job {removed_prior_job} has been removed; "
540+
f"discovery job {job_id} is running."
541+
)
516542
logger.info(f"To check the status, use the command 'pam action discover status'.")
517-
logger.info(f"To stop and remove the current job, use the command 'pam action discover remove -j <Job ID>'.")
543+
logger.info(f"To stop and remove the current job, use the command 'pam action discover remove -j {job_id}'.")
518544
else:
519545
router_utils.print_router_response(router_response, "job_info", conversation_id, gateway_uid=gateway_context.gateway_uid)
546+
logger.info(f"Discovery job {job_id} was recorded on the configuration.")
547+
logger.info(f"To check the status, use the command 'pam action discover status -j {job_id}'.")
520548

521549
@staticmethod
522550
def make_protobuf_user_map(context: KeeperParams, gateway_context: GatewayContext) -> List[dict]:
@@ -580,7 +608,7 @@ def execute(self, context: KeeperParams, **kwargs):
580608
all_gateways = GatewayContext.all_gateways(vault)
581609

582610
def _find_job(configuration_record) -> Optional[Dict]:
583-
jobs_obj = Jobs(record=configuration_record)
611+
jobs_obj = Jobs(record=configuration_record, vault=vault)
584612
job_item = jobs_obj.get_job(job_id)
585613
if job_item is not None:
586614
return {
@@ -1775,7 +1803,7 @@ def _get_directory_info(domain: str,
17751803
def remove_job(context: KeeperParams, configuration_record: vault_record.KeeperRecord, job_id: str):
17761804

17771805
try:
1778-
jobs = Jobs(record=configuration_record, context=context)
1806+
jobs = Jobs(record=configuration_record, vault=context.vault)
17791807
jobs.cancel(job_id)
17801808
logger.info(f"No items left to process. Removing completed discovery job.")
17811809
except Exception as err:
@@ -1786,7 +1814,7 @@ def preview(self, job_item: JobItem, context: KeeperParams, gateway_context: Gat
17861814

17871815
sync_point = job_item.sync_point
17881816
infra = Infrastructure(record=gateway_context.configuration,
1789-
context=context,
1817+
vault=context.vault,
17901818
logger=logger,
17911819
debug_level=debug_level)
17921820
infra.load(sync_point)
@@ -1941,7 +1969,7 @@ def execute(self, context: KeeperParams, **kwargs):
19411969

19421970
# Get the current job.
19431971
# There can only be one active job.
1944-
jobs = Jobs(record=configuration_record, context=context, logger=logger, debug_level=debug_level)
1972+
jobs = Jobs(record=configuration_record, vault=vault, logger=logger, debug_level=debug_level)
19451973
job_item = jobs.current_job
19461974
if job_item is None:
19471975
continue

keepercli-package/src/keepercli/commands/pam/pam_gateway_action.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,10 @@ def record_rotate(self, context: KeeperParams, record_uid, slient:bool = False):
255255
config_uid = facade.controller_uid
256256

257257
if not resource_uid:
258-
tmp_dag = tunnel_graph.TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, record.record_uid)
258+
tmp_dag = tunnel_graph.TunnelDAG(
259+
vault, encrypted_session_token, encrypted_transmission_key, record.record_uid,
260+
transmission_key=transmission_key,
261+
)
259262
resource_uid = tmp_dag.get_resource_uid(record_uid)
260263
if not resource_uid:
261264
is_noop = False

keepercli-package/src/keepercli/commands/pam/pam_rotation.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ def execute(self, context: KeeperParams, **kwargs):
295295
def config_resource(_dag, target_record, target_config_uid, silent=None):
296296
if not _dag.linking_dag.has_graph:
297297
if target_config_uid:
298-
_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_config_uid)
298+
_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_config_uid,
299+
transmission_key=transmission_key)
299300
_dag.edit_tunneling_config(rotation=True)
300301
else:
301302
raise base.CommandError(f'Resource "{target_record.record_uid}" is not associated '
@@ -305,7 +306,7 @@ def config_resource(_dag, target_record, target_config_uid, silent=None):
305306
resource_dag = None
306307
if not _dag.resource_belongs_to_config(target_record.record_uid):
307308
resource_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key,
308-
target_record.record_uid)
309+
target_record.record_uid, transmission_key=transmission_key)
309310
_dag.link_resource_to_config(target_record.record_uid)
310311

311312
admin = kwargs.get('admin')
@@ -401,10 +402,12 @@ def config_iam_aad_user(_dag, target_record, target_iam_aad_config_uid):
401402
return
402403

403404
if _dag and not _dag.linking_dag.has_graph:
404-
_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_iam_aad_config_uid)
405+
_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_iam_aad_config_uid,
406+
transmission_key=transmission_key)
405407
if not _dag or not _dag.linking_dag.has_graph:
406408
_dag.edit_tunneling_config(rotation=True)
407-
old_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_record.record_uid)
409+
old_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_record.record_uid,
410+
transmission_key=transmission_key)
408411
if old_dag.linking_dag.has_graph and old_dag.record.record_uid != target_iam_aad_config_uid:
409412
old_dag.remove_from_dag(target_record.record_uid)
410413

@@ -621,7 +624,8 @@ def config_user(_dag, target_record, target_resource_uid, target_config_uid=None
621624
return
622625

623626
if isinstance(target_resource_uid, str) and len(target_resource_uid) > 0:
624-
_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_resource_uid)
627+
_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_resource_uid,
628+
transmission_key=transmission_key)
625629
if not _dag or not _dag.linking_dag.has_graph:
626630
if target_config_uid and target_resource_uid:
627631
config_resource(_dag, target_record, target_config_uid, silent=silent)
@@ -639,7 +643,8 @@ def config_user(_dag, target_record, target_resource_uid, target_config_uid=None
639643
current_record_rotation = context.get_record_rotation(target_record.record_uid)
640644

641645
if not _dag or not _dag.linking_dag.has_graph:
642-
_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_resource_uid)
646+
_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, target_resource_uid,
647+
transmission_key=transmission_key)
643648
if not _dag.linking_dag.has_graph:
644649
raise base.CommandError(f'Resource "{target_resource_uid}" is not associated '
645650
f'with any configuration. '
@@ -824,6 +829,8 @@ def config_user(_dag, target_record, target_resource_uid, target_config_uid=None
824829
if record_name:
825830
if record_name in vault.vault_data._records:
826831
record_uids.add(record_name)
832+
elif vault.vault_data.load_record(record_name):
833+
record_uids.add(record_name)
827834
else:
828835
rs = folder_utils.try_resolve_path(context, record_name)
829836
if rs is not None:
@@ -866,7 +873,10 @@ def add_folders(folder: vault_types.Folder):
866873
if folder_uids:
867874
regex = re.compile(fnmatch.translate(record_pattern), re.IGNORECASE).match if record_pattern else None
868875
for folder_uid in folder_uids:
869-
folder_records = vault.vault_data.get_folder(folder_uid).records
876+
folder = vault.vault_data.get_folder(folder_uid)
877+
if not folder:
878+
continue
879+
folder_records = folder.records
870880
if not folder_records:
871881
continue
872882
if record_pattern and record_pattern in folder_records:
@@ -957,7 +967,8 @@ def add_folders(folder: vault_types.Folder):
957967
r_requests = []
958968

959969
for _record in pam_records:
960-
tmp_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, _record.record_uid)
970+
tmp_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, _record.record_uid,
971+
transmission_key=transmission_key)
961972
if _record.record_type in ['pamMachine', 'pamDatabase', 'pamDirectory', 'pamRemoteBrowser']:
962973
config_resource(tmp_dag, _record, config_uid, silent=kwargs.get('silent'))
963974
elif _record.record_type == 'pamUser':
@@ -1108,7 +1119,7 @@ def is_resource_ok(resource_id, vault, configuration_uid):
11081119
logger.info(f"Is Rotation Disabled: {rri.disabled}")
11091120

11101121
rq = pam_pb2.PAMGenericUidsRequest()
1111-
schedules_proto = router_utils.router_get_rotation_schedules(context, rq)
1122+
schedules_proto = router_utils.router_get_rotation_schedules(vault, rq)
11121123
if schedules_proto:
11131124
schedules = list(schedules_proto.schedules)
11141125
for s in schedules:

0 commit comments

Comments
 (0)