diff --git a/keepercommander/commands/pam/pam_dto.py b/keepercommander/commands/pam/pam_dto.py index eb1c16165..948d280c2 100644 --- a/keepercommander/commands/pam/pam_dto.py +++ b/keepercommander/commands/pam/pam_dto.py @@ -292,3 +292,12 @@ def __init__(self, inputs: GatewayActionIdpInputs, conversation_id=None): def toJSON(self): return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) + + +class GatewayActionIdpValidateDomain(GatewayAction): + + def __init__(self, inputs: GatewayActionIdpInputs, conversation_id=None): + super().__init__('rm-validate-domain', inputs=inputs, conversation_id=conversation_id, is_scheduled=False) + + def toJSON(self): + return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) diff --git a/keepercommander/commands/pam_cloud/pam_request_access.py b/keepercommander/commands/pam_cloud/pam_request_access.py index 494823f87..d4e79d006 100644 --- a/keepercommander/commands/pam_cloud/pam_request_access.py +++ b/keepercommander/commands/pam_cloud/pam_request_access.py @@ -1,12 +1,24 @@ import argparse +import json import logging from keeper_secrets_manager_core.utils import url_safe_str_to_bytes from keepercommander.commands.base import Command, RecordMixin +from keepercommander.commands.pam.pam_dto import ( + GatewayAction, + GatewayActionIdpInputs, + GatewayActionIdpValidateDomain, +) +from keepercommander.commands.pam.router_helper import router_send_action_to_gateway +from keepercommander.commands.pam_cloud.pam_idp import resolve_idp_config +from keepercommander.commands.tunnel.port_forward.tunnel_helpers import ( + get_config_uid_from_record, + get_gateway_uid_from_record, +) from keepercommander.error import CommandError from keepercommander import api, vault -from keepercommander.proto import NotificationCenter_pb2, GraphSync_pb2 +from keepercommander.proto import NotificationCenter_pb2, GraphSync_pb2, pam_pb2 ELIGIBLE_RECORD_TYPES = {'pamRemoteBrowser', 'pamDatabase', 'pamMachine'} @@ -54,7 +66,48 @@ def execute(self, params, **kwargs): if owner == params.user: raise CommandError('pam-request-access', 'You are the owner of this record.') - # Build notification + # Resolve PAM config and IdP config for this resource + config_uid = get_config_uid_from_record(params, vault, record.record_uid) + if not config_uid: + raise CommandError('pam-request-access', 'Could not resolve PAM configuration for this resource.') + + gateway_uid = get_gateway_uid_from_record(params, vault, record.record_uid) + + # Validate the requesting user's domain against the IdP + try: + idp_config_uid = resolve_idp_config(params, config_uid) + except CommandError: + idp_config_uid = config_uid + + inputs = GatewayActionIdpInputs( + configuration_uid=config_uid, + idp_config_uid=idp_config_uid, + user=params.user, + resourceUid=record.record_uid, + ) + action = GatewayActionIdpValidateDomain(inputs=inputs) + conversation_id = GatewayAction.generate_conversation_id() + action.conversationId = conversation_id + + router_response = router_send_action_to_gateway( + params=params, + gateway_action=action, + message_type=pam_pb2.CMT_GENERAL, + is_streaming=False, + destination_gateway_uid_str=gateway_uid, + ) + + if router_response: + response = router_response.get('response', {}) + payload_str = response.get('payload') + if payload_str: + payload = json.loads(payload_str) + data = payload.get('data', {}) + if isinstance(data, dict) and not data.get('success', True): + error_msg = data.get('error', 'Domain validation failed') + raise CommandError('pam-request-access', error_msg) + + # Domain validated — send approval notification to record owner record_uid_bytes = url_safe_str_to_bytes(record.record_uid) record_ref = GraphSync_pb2.GraphSyncRef() @@ -81,8 +134,6 @@ def execute(self, params, **kwargs): batch_rq = NotificationCenter_pb2.NotificationsSendRequest() batch_rq.notifications.append(send_rq) - #api.communicate_rest(params, batch_rq, 'vault/notifications_send') + api.communicate_rest(params, batch_rq, 'vault/notifications_send') - print(batch_rq) - - logging.info(f'Credential request sent to {owner} for record "{record.title}".') + logging.info(f'Access request sent to {owner} for record "{record.title}".')