diff --git a/keepercommander/cli.py b/keepercommander/cli.py index 64593fd26..b80961270 100644 --- a/keepercommander/cli.py +++ b/keepercommander/cli.py @@ -126,6 +126,7 @@ def clean_description(desc): ('pam rotation', 'Manage Rotations'), ('pam split', 'Split credentials from legacy PAM Machine'), ('pam tunnel', 'Manage Tunnels'), + ('pam workflow', 'Manage PAM Workflows'), ] domain_subcommands = [ ('domain list (dl)', 'List all reserved domains for the enterprise'), diff --git a/keepercommander/commands/discoveryrotation.py b/keepercommander/commands/discoveryrotation.py index b4db65add..15a789ae7 100644 --- a/keepercommander/commands/discoveryrotation.py +++ b/keepercommander/commands/discoveryrotation.py @@ -76,6 +76,7 @@ from .pam_debug.vertex import PAMDebugVertexCommand from .pam_import.commands import PAMProjectCommand from .pam_launch.launch import PAMLaunchCommand +from .workflow import PAMWorkflowCommand from .pam_service.list import PAMActionServiceListCommand from .pam_service.add import PAMActionServiceAddCommand from .pam_service.remove import PAMActionServiceRemoveCommand @@ -186,6 +187,7 @@ def __init__(self): self.register_command('rbi', PAMRbiCommand(), 'Manage Remote Browser Isolation', 'b') self.register_command('project', PAMProjectCommand(), 'PAM Project Import/Export', 'p') self.register_command('launch', PAMLaunchCommand(), 'Launch a connection to a PAM resource', 'l') + self.register_command('workflow', PAMWorkflowCommand(), 'Manage PAM Workflows', 'w') class PAMGatewayCommand(GroupCommand): diff --git a/keepercommander/commands/pam_launch/launch.py b/keepercommander/commands/pam_launch/launch.py index 3646be303..b4e0458ee 100644 --- a/keepercommander/commands/pam_launch/launch.py +++ b/keepercommander/commands/pam_launch/launch.py @@ -284,6 +284,17 @@ def execute(self, params: KeeperParams, **kwargs): logging.debug(f"Found record: {record_uid}") + # Workflow access check and 2FA prompt + try: + from ..workflow import check_workflow_and_prompt_2fa + should_proceed, two_factor_value = check_workflow_and_prompt_2fa(params, record_uid) + if not should_proceed: + return + if two_factor_value: + kwargs['two_factor_value'] = two_factor_value + except ImportError: + pass + # Validate --user and --host parameters against allowSupply flags # Note: cmdline options override record data when provided # launch_credential_uid = kwargs.get('launch_credential_uid') diff --git a/keepercommander/commands/pam_launch/terminal_connection.py b/keepercommander/commands/pam_launch/terminal_connection.py index 5e182b612..87be4dc55 100644 --- a/keepercommander/commands/pam_launch/terminal_connection.py +++ b/keepercommander/commands/pam_launch/terminal_connection.py @@ -1328,6 +1328,11 @@ def _open_terminal_webrtc_tunnel(params: KeeperParams, logging.debug("Using userSupplied credential type - user will provide credentials") # else: no credentialType - gateway uses pamMachine credentials directly (backward compatible) + # Add 2FA value if workflow requires MFA + two_factor_value = kwargs.get('two_factor_value') + if two_factor_value: + inputs['twoFactorValue'] = two_factor_value + # Router token is no longer extracted from cookies (removed in commit 338a9fda) # Router affinity is now handled server-side diff --git a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py index 0e67930e7..4ea09e81a 100644 --- a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py +++ b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py @@ -1966,7 +1966,7 @@ def cleanup(self): logging.debug("TunnelSignalHandler cleaned up") def start_rust_tunnel(params, record_uid, gateway_uid, host, port, - seed, target_host, target_port, socks, trickle_ice=True, record_title=None, allow_supply_host=False): + seed, target_host, target_port, socks, trickle_ice=True, record_title=None, allow_supply_host=False, two_factor_value=None): """ Start a tunnel using Rust WebRTC with trickle ICE via HTTP POST and WebSocket responses. @@ -2284,23 +2284,29 @@ def start_rust_tunnel(params, record_uid, gateway_uid, host, port, } if trickle_ice and http_session is not None: offer_kwargs["http_session"] = http_session + + # Build tunnel inputs + inputs = { + "recordUid": record_uid, + "tubeId": commander_tube_id, + 'kind': 'start', + 'base64Nonce': base64_nonce, + 'conversationType': 'tunnel', + "data": encrypted_data, + "trickleICE": trickle_ice, + } + if two_factor_value: + inputs['twoFactorValue'] = two_factor_value + router_response = router_send_action_to_gateway( params=params, destination_gateway_uid_str=gateway_uid, gateway_action=GatewayActionWebRTCSession( conversation_id = conversation_id_original, - inputs={ - "recordUid": record_uid, - "tubeId": commander_tube_id, - 'kind': 'start', - 'base64Nonce': base64_nonce, - 'conversationType': 'tunnel', - "data": encrypted_data, - "trickleICE": trickle_ice, - } + inputs=inputs ), message_type=pam_pb2.CMT_CONNECT, - is_streaming=trickle_ice, # Streaming only for trickle ICE + is_streaming=trickle_ice, gateway_timeout=GATEWAY_TIMEOUT, **offer_kwargs ) diff --git a/keepercommander/commands/tunnel_and_connections.py b/keepercommander/commands/tunnel_and_connections.py index 48cd8b2dd..9522a1dc4 100644 --- a/keepercommander/commands/tunnel_and_connections.py +++ b/keepercommander/commands/tunnel_and_connections.py @@ -411,6 +411,11 @@ def execute(self, params, **kwargs): if _remove_tunneling_override_port and pam_settings.value[0]['portForward'].get('port'): pam_settings.value[0]['portForward'].pop('port') dirty = True + # Persist the record changes (new pamSettings field or port modifications) + if dirty: + record_management.update_record(params, record) + api.sync_down(params) + dirty = False if not tmp_dag.is_tunneling_config_set_up(record_uid): print(f"{bcolors.FAIL}No PAM Configuration UID set. This must be set for tunneling to work. " f"This can be done by running " @@ -538,6 +543,16 @@ def execute(self, params, **kwargs): print(f"{bcolors.FAIL}Record {record_uid} not found.{bcolors.ENDC}") return + # Workflow access check and 2FA prompt + two_factor_value = None + try: + from .workflow import check_workflow_and_prompt_2fa + should_proceed, two_factor_value = check_workflow_and_prompt_2fa(params, record_uid) + if not should_proceed: + return + except ImportError: + pass + # Validate PAM settings pam_settings = record.get_typed_field('pamSettings') if not pam_settings: @@ -633,7 +648,7 @@ def execute(self, params, **kwargs): # Use Rust WebRTC implementation with configurable trickle ICE trickle_ice = not no_trickle_ice - result = start_rust_tunnel(params, record_uid, gateway_uid, host, port, seed, target_host, target_port, socks, trickle_ice, record.title, allow_supply_host=allow_supply_host) + result = start_rust_tunnel(params, record_uid, gateway_uid, host, port, seed, target_host, target_port, socks, trickle_ice, record.title, allow_supply_host=allow_supply_host, two_factor_value=two_factor_value) if result and result.get("success"): # The helper will show endpoint table when local socket is actually listening diff --git a/keepercommander/commands/workflow/__init__.py b/keepercommander/commands/workflow/__init__.py new file mode 100644 index 000000000..a25cc6909 --- /dev/null +++ b/keepercommander/commands/workflow/__init__.py @@ -0,0 +1,15 @@ +# _ __ +# | |/ /___ ___ _ __ ___ _ _ ® +# | ' {bcolors.ENDC}") + print() + + except Exception as e: + raise CommandError('', f'Failed to create workflow: {str(e)}') + + +class WorkflowReadCommand(Command): + parser = argparse.ArgumentParser( + prog='pam workflow read', + description='Read and display workflow configuration', + ) + parser.add_argument('record', help='Record UID or name') + parser.add_argument('--format', dest='format', action='store', + choices=['table', 'json'], default='table', help='Output format') + + def get_parser(self): + return WorkflowReadCommand.parser + + def execute(self, params: KeeperParams, **kwargs): + record_uid, record = RecordResolver.resolve(params, kwargs.get('record')) + record_uid_bytes = utils.base64_url_decode(record_uid) + ref = ProtobufRefBuilder.record_ref(record_uid_bytes, record.title) + + try: + response = _post_request_to_router( + params, 'read_workflow_config', + rq_proto=ref, rs_type=workflow_pb2.WorkflowConfig, + ) + + if not response: + if kwargs.get('format') == 'json': + print(json.dumps({'status': 'no_workflow', 'message': 'No workflow configured'}, indent=2)) + else: + print(f"\n{bcolors.WARNING}No workflow configured for this record{bcolors.ENDC}\n") + print(f"Record: {record.title} ({record_uid})") + print(f"\nTo create a workflow, run:") + print(f" pam workflow create {record_uid}") + print() + return + + if kwargs.get('format') == 'json': + self._print_json(params, response, record_uid) + else: + self._print_table(params, response, record_uid) + + except Exception as e: + raise CommandError('', f'Failed to read workflow: {str(e)}') + + @staticmethod + def _print_json(params, response, record_uid): + result = { + 'record_uid': record_uid, + 'record_name': RecordResolver.resolve_name(params, response.parameters.resource), + 'parameters': { + 'approvals_needed': response.parameters.approvalsNeeded, + 'checkout_needed': response.parameters.checkoutNeeded, + 'start_access_on_approval': response.parameters.startAccessOnApproval, + 'require_reason': response.parameters.requireReason, + 'require_ticket': response.parameters.requireTicket, + 'require_mfa': response.parameters.requireMFA, + 'access_duration': WorkflowFormatter.format_duration(response.parameters.accessLength), + }, + 'approvers': [], + } + + for approver in response.approvers: + approver_info = {'escalation': approver.escalation} + if approver.HasField('user'): + approver_info['type'] = 'user' + approver_info['email'] = approver.user + elif approver.HasField('userId'): + approver_info['type'] = 'user_id' + approver_info['user_id'] = approver.userId + elif approver.HasField('teamUid'): + approver_info['type'] = 'team' + approver_info['team_uid'] = utils.base64_url_encode(approver.teamUid) + result['approvers'].append(approver_info) + + print(json.dumps(result, indent=2)) + + @staticmethod + def _print_table(params, response, record_uid): + print(f"\n{bcolors.OKBLUE}Workflow Configuration{bcolors.ENDC}\n") + print(f"Record: {RecordResolver.resolve_name(params, response.parameters.resource)}") + print(f"Record UID: {record_uid}") + + if response.createdOn: + created_date = datetime.fromtimestamp(response.createdOn / 1000) + print(f"Created: {created_date.strftime('%Y-%m-%d %H:%M:%S')}") + + p = response.parameters + print(f"\n{bcolors.BOLD}Access Parameters:{bcolors.ENDC}") + print(f" Approvals needed: {p.approvalsNeeded}") + print(f" Check-in/out required: {'Yes' if p.checkoutNeeded else 'No'}") + print(f" Access duration: {WorkflowFormatter.format_duration(p.accessLength)}") + print(f" Timer starts: {'On approval' if p.startAccessOnApproval else 'On check-out'}") + + print(f"\n{bcolors.BOLD}Requirements:{bcolors.ENDC}") + print(f" Reason required: {'Yes' if p.requireReason else 'No'}") + print(f" Ticket required: {'Yes' if p.requireTicket else 'No'}") + print(f" MFA required: {'Yes' if p.requireMFA else 'No'}") + + if response.approvers: + print(f"\n{bcolors.BOLD}Approvers ({len(response.approvers)}):{bcolors.ENDC}") + for idx, approver in enumerate(response.approvers, 1): + escalation = ' (Escalation)' if approver.escalation else '' + if approver.HasField('user'): + print(f" {idx}. User: {approver.user}{escalation}") + elif approver.HasField('userId'): + print(f" {idx}. User: {RecordResolver.resolve_user(params, approver.userId)}{escalation}") + elif approver.HasField('teamUid'): + team_uid = utils.base64_url_encode(approver.teamUid) + team_data = params.team_cache.get(team_uid, {}) + team_name = team_data.get('name', '') + team_display = f"{team_name} ({team_uid})" if team_name else team_uid + print(f" {idx}. Team: {team_display}{escalation}") + else: + print(f"\n{bcolors.WARNING}⚠ No approvers configured!{bcolors.ENDC}") + print(f"Add approvers with: pam workflow add-approver {record_uid} --user ") + + print() + + +class WorkflowUpdateCommand(Command): + parser = argparse.ArgumentParser( + prog='pam workflow update', + description='Update existing workflow configuration. ' + 'Only specified fields are changed; unspecified fields retain their current values.', + ) + parser.add_argument('record', help='Record UID or name with workflow to update') + parser.add_argument('-n', '--approvals-needed', type=int, help='Number of approvals required') + parser.add_argument('-co', '--checkout', type=lambda x: x.lower() == 'true', + help='Enable/disable check-in/check-out (true/false)') + parser.add_argument('-sa', '--start-on-approval', type=lambda x: x.lower() == 'true', + help='Start timer on approval vs check-out (true/false)') + parser.add_argument('-rr', '--require-reason', type=lambda x: x.lower() == 'true', + help='Require reason (true/false)') + parser.add_argument('-rt', '--require-ticket', type=lambda x: x.lower() == 'true', + help='Require ticket (true/false)') + parser.add_argument('-rm', '--require-mfa', type=lambda x: x.lower() == 'true', + help='Require MFA (true/false)') + parser.add_argument('-d', '--duration', type=str, help='Access duration (e.g., "2h", "30m", "1d")') + parser.add_argument('--format', dest='format', action='store', + choices=['table', 'json'], default='table', help='Output format') + + def get_parser(self): + return WorkflowUpdateCommand.parser + + def execute(self, params: KeeperParams, **kwargs): + record_uid, record = RecordResolver.resolve(params, kwargs.get('record')) + record_uid_bytes = utils.base64_url_decode(record_uid) + + try: + ref = ProtobufRefBuilder.record_ref(record_uid_bytes, record.title) + current_config = _post_request_to_router( + params, 'read_workflow_config', + rq_proto=ref, rs_type=workflow_pb2.WorkflowConfig, + ) + + if not current_config: + raise CommandError('', 'No workflow found for record. Create one first with "pam workflow create"') + + parameters = workflow_pb2.WorkflowParameters() + parameters.CopyFrom(current_config.parameters) + + updatable_fields = { + 'approvals_needed': 'approvalsNeeded', + 'checkout': 'checkoutNeeded', + 'start_on_approval': 'startAccessOnApproval', + 'require_reason': 'requireReason', + 'require_ticket': 'requireTicket', + 'require_mfa': 'requireMFA', + } + + updates_provided = False + for kwarg_key, proto_field in updatable_fields.items(): + if kwargs.get(kwarg_key) is not None: + setattr(parameters, proto_field, kwargs[kwarg_key]) + updates_provided = True + + if kwargs.get('duration') is not None: + parameters.accessLength = WorkflowFormatter.parse_duration(kwargs['duration']) + updates_provided = True + + if not updates_provided: + raise CommandError( + '', 'No updates provided. Specify at least one option to update ' + '(e.g., --approvals-needed, --duration)', + ) + + _post_request_to_router(params, 'update_workflow_config', rq_proto=parameters) + + if kwargs.get('format') == 'json': + result = {'status': 'success', 'record_uid': record_uid, 'record_name': record.title} + print(json.dumps(result, indent=2)) + else: + print(f"\n{bcolors.OKGREEN}✓ Workflow updated successfully{bcolors.ENDC}\n") + print(f"Record: {record.title} ({record_uid})") + print() + + except CommandError: + raise + except Exception as e: + raise CommandError('', f'Failed to update workflow: {str(e)}') + + +class WorkflowDeleteCommand(Command): + parser = argparse.ArgumentParser( + prog='pam workflow delete', + description='Delete workflow configuration from a record', + ) + parser.add_argument('record', help='Record UID or name to remove workflow from') + parser.add_argument('--format', dest='format', action='store', + choices=['table', 'json'], default='table', help='Output format') + + def get_parser(self): + return WorkflowDeleteCommand.parser + + def execute(self, params: KeeperParams, **kwargs): + record_uid, record = RecordResolver.resolve(params, kwargs.get('record')) + record_uid_bytes = utils.base64_url_decode(record_uid) + ref = ProtobufRefBuilder.record_ref(record_uid_bytes, record.title) + + try: + _post_request_to_router(params, 'delete_workflow_config', rq_proto=ref) + + if kwargs.get('format') == 'json': + result = {'status': 'success', 'record_uid': record_uid, 'record_name': record.title} + print(json.dumps(result, indent=2)) + else: + print(f"\n{bcolors.OKGREEN}✓ Workflow deleted successfully{bcolors.ENDC}\n") + print(f"Record: {record.title} ({record_uid})") + print() + + except Exception as e: + raise CommandError('', f'Failed to delete workflow: {str(e)}') + + +class WorkflowAddApproversCommand(Command): + parser = argparse.ArgumentParser( + prog='pam workflow add-approver', + description='Add approvers to a workflow', + ) + parser.add_argument('record', help='Record UID or name') + parser.add_argument('-u', '--user', action='append', + help='User email to add as approver (can specify multiple times)') + parser.add_argument('-t', '--team', action='append', + help='Team name or UID to add as approver (can specify multiple times)') + parser.add_argument('-e', '--escalation', action='store_true', help='Mark as escalation approver') + parser.add_argument('--format', dest='format', action='store', + choices=['table', 'json'], default='table', help='Output format') + + def get_parser(self): + return WorkflowAddApproversCommand.parser + + def execute(self, params: KeeperParams, **kwargs): + users = kwargs.get('user') or [] + teams = kwargs.get('team') or [] + is_escalation = kwargs.get('escalation', False) + + if not users and not teams: + raise CommandError('', 'Must specify at least one --user or --team') + + record_uid, record = RecordResolver.resolve(params, kwargs.get('record')) + record_uid_bytes = utils.base64_url_decode(record_uid) + + config = workflow_pb2.WorkflowConfig() + config.parameters.resource.CopyFrom(ProtobufRefBuilder.record_ref(record_uid_bytes, record.title)) + + for user_email in users: + approver = workflow_pb2.WorkflowApprover() + approver.user = user_email + approver.escalation = is_escalation + config.approvers.append(approver) + + for team_input in teams: + resolved_team_uid = RecordResolver.validate_team(params, team_input) + approver = workflow_pb2.WorkflowApprover() + approver.teamUid = utils.base64_url_decode(resolved_team_uid) + approver.escalation = is_escalation + config.approvers.append(approver) + + try: + _post_request_to_router(params, 'add_workflow_approvers', rq_proto=config) + + total = len(users) + len(teams) + if kwargs.get('format') == 'json': + result = { + 'status': 'success', + 'record_uid': record_uid, + 'record_name': record.title, + 'approvers_added': total, + 'escalation': is_escalation, + } + print(json.dumps(result, indent=2)) + else: + print(f"\n{bcolors.OKGREEN}✓ Approvers added successfully{bcolors.ENDC}\n") + print(f"Record: {record.title} ({record_uid})") + print(f"Added {total} approver(s)") + if is_escalation: + print("Type: Escalation approver") + print() + + except Exception as e: + raise CommandError('', f'Failed to add approvers: {str(e)}') + + +class WorkflowDeleteApproversCommand(Command): + parser = argparse.ArgumentParser( + prog='pam workflow remove-approver', + description='Remove approvers from a workflow', + ) + parser.add_argument('record', help='Record UID or name') + parser.add_argument('-u', '--user', action='append', help='User email to remove as approver') + parser.add_argument('-t', '--team', action='append', help='Team name or UID to remove as approver') + parser.add_argument('--format', dest='format', action='store', + choices=['table', 'json'], default='table', help='Output format') + + def get_parser(self): + return WorkflowDeleteApproversCommand.parser + + def execute(self, params: KeeperParams, **kwargs): + users = kwargs.get('user') or [] + teams = kwargs.get('team') or [] + + if not users and not teams: + raise CommandError('', 'Must specify at least one --user or --team') + + record_uid, record = RecordResolver.resolve(params, kwargs.get('record')) + record_uid_bytes = utils.base64_url_decode(record_uid) + + config = workflow_pb2.WorkflowConfig() + config.parameters.resource.CopyFrom(ProtobufRefBuilder.record_ref(record_uid_bytes, record.title)) + + for user_email in users: + approver = workflow_pb2.WorkflowApprover() + approver.user = user_email + config.approvers.append(approver) + + for team_input in teams: + resolved_team_uid = RecordResolver.validate_team(params, team_input) + approver = workflow_pb2.WorkflowApprover() + approver.teamUid = utils.base64_url_decode(resolved_team_uid) + config.approvers.append(approver) + + try: + _post_request_to_router(params, 'delete_workflow_approvers', rq_proto=config) + + total = len(users) + len(teams) + if kwargs.get('format') == 'json': + result = { + 'status': 'success', + 'record_uid': record_uid, + 'record_name': record.title, + 'approvers_removed': total, + } + print(json.dumps(result, indent=2)) + else: + print(f"\n{bcolors.OKGREEN}✓ Approvers removed successfully{bcolors.ENDC}\n") + print(f"Record: {record.title} ({record_uid})") + print(f"Removed {total} approver(s)") + print() + + except Exception as e: + raise CommandError('', f'Failed to remove approvers: {str(e)}') diff --git a/keepercommander/commands/workflow/helpers.py b/keepercommander/commands/workflow/helpers.py new file mode 100644 index 000000000..5c34e471b --- /dev/null +++ b/keepercommander/commands/workflow/helpers.py @@ -0,0 +1,155 @@ +# _ __ +# | |/ /___ ___ _ __ ___ _ _ ® +# | ' bytes: + uid_bytes = utils.base64_url_decode(record_uid) + if record_uid not in params.record_cache: + raise CommandError('', f'Record {record_uid} not found') + return uid_bytes + + @staticmethod + def resolve_name(params, resource_ref) -> str: + if resource_ref.name: + return resource_ref.name + if resource_ref.value: + rec_uid = utils.base64_url_encode(resource_ref.value) + rec = vault.KeeperRecord.load(params, rec_uid) + return rec.title if rec else '' + return '' + + @staticmethod + def format_label(params, resource_ref) -> str: + rec_uid = utils.base64_url_encode(resource_ref.value) if resource_ref.value else '' + rec_name = RecordResolver.resolve_name(params, resource_ref) + if rec_name and rec_name != rec_uid: + return f"{rec_name} ({rec_uid})" + return rec_uid or 'Unknown' + + @staticmethod + def resolve_user(params: KeeperParams, user_id: int) -> str: + if params.enterprise and 'users' in params.enterprise: + for u in params.enterprise['users']: + if u.get('enterprise_user_id') == user_id: + return u.get('username', f'User ID {user_id}') + return f'User ID {user_id}' + + @staticmethod + def validate_team(params: KeeperParams, team_input: str) -> str: + if team_input in params.team_cache: + return team_input + for uid, team_data in params.team_cache.items(): + if team_data.get('name', '').casefold() == team_input.casefold(): + return uid + raise CommandError('', f'Team "{team_input}" not found. Use a valid team UID or team name.') + + +class ProtobufRefBuilder: + + @staticmethod + def record_ref(record_uid_bytes: bytes, record_name: str = '') -> GraphSync_pb2.GraphSyncRef: + ref = GraphSync_pb2.GraphSyncRef() + ref.type = GraphSync_pb2.RFT_REC + ref.value = record_uid_bytes + if record_name: + ref.name = record_name + return ref + + @staticmethod + def workflow_ref(flow_uid_bytes: bytes) -> GraphSync_pb2.GraphSyncRef: + ref = GraphSync_pb2.GraphSyncRef() + ref.type = GraphSync_pb2.RFT_WORKFLOW + ref.value = flow_uid_bytes + return ref + + +class WorkflowFormatter: + + STAGE_MAP = { + workflow_pb2.WS_READY_TO_START: 'Ready to Start', + workflow_pb2.WS_STARTED: 'Started', + workflow_pb2.WS_NEEDS_ACTION: 'Needs Action', + workflow_pb2.WS_WAITING: 'Waiting', + } + + CONDITION_MAP = { + workflow_pb2.AC_APPROVAL: 'Approval Required', + workflow_pb2.AC_CHECKIN: 'Check-in Required', + workflow_pb2.AC_MFA: 'MFA Required', + workflow_pb2.AC_TIME: 'Time Restriction', + workflow_pb2.AC_REASON: 'Reason Required', + workflow_pb2.AC_TICKET: 'Ticket Required', + } + + DURATION_MULTIPLIERS = {'d': 86_400_000, 'h': 3_600_000, 'm': 60_000} + + @staticmethod + def format_stage(stage: int) -> str: + return WorkflowFormatter.STAGE_MAP.get(stage, f'Unknown ({stage})') + + @staticmethod + def format_conditions(conditions: List[int]) -> str: + return ', '.join( + WorkflowFormatter.CONDITION_MAP.get(c, f'Unknown ({c})') + for c in conditions + ) + + @staticmethod + def parse_duration(duration_str: str) -> int: + duration_str = duration_str.lower().strip() + try: + for suffix, factor in WorkflowFormatter.DURATION_MULTIPLIERS.items(): + if duration_str.endswith(suffix): + return int(duration_str[:-1]) * factor + return int(duration_str) * 60_000 + except ValueError: + raise CommandError( + '', f'Invalid duration format: {duration_str}. ' + 'Use format like "2h", "30m", or "1d"', + ) + + @staticmethod + def format_duration(milliseconds: int) -> str: + seconds = milliseconds // 1000 + minutes = seconds // 60 + hours = minutes // 60 + days = hours // 24 + + if days > 0: + return f"{days} day{'s' if days != 1 else ''}" + if hours > 0: + return f"{hours} hour{'s' if hours != 1 else ''}" + if minutes > 0: + return f"{minutes} minute{'s' if minutes != 1 else ''}" + return f"{seconds} second{'s' if seconds != 1 else ''}" diff --git a/keepercommander/commands/workflow/mfa.py b/keepercommander/commands/workflow/mfa.py new file mode 100644 index 000000000..844592d77 --- /dev/null +++ b/keepercommander/commands/workflow/mfa.py @@ -0,0 +1,309 @@ +# _ __ +# | |/ /___ ___ _ __ ___ _ _ ® +# | ' dict: + config = self._read_workflow_config() + if config is None: + return dict(self._DEFAULT_RESULT) + + mfa_required = bool(config.parameters and config.parameters.requireMFA) + + workflow = self._find_active_workflow() + if workflow is None: + self._print_no_workflow() + return {'allowed': False, 'require_mfa': False} + + return self._evaluate_stage(workflow, mfa_required) + + def _read_workflow_config(self): + ref = ProtobufRefBuilder.record_ref(self.record_uid_bytes, self.record_name) + try: + return _post_request_to_router( + self.params, 'read_workflow_config', + rq_proto=ref, rs_type=workflow_pb2.WorkflowConfig, + ) + except Exception: + return None + + def _find_active_workflow(self): + try: + user_state = _post_request_to_router( + self.params, 'get_user_access_state', + rs_type=workflow_pb2.UserAccessState, + ) + except Exception: + return None + + if user_state and user_state.workflows: + for wf in user_state.workflows: + if wf.resource and wf.resource.value == self.record_uid_bytes: + return wf + return None + + def _evaluate_stage(self, workflow, mfa_required: bool) -> dict: + if not workflow.status: + self._print_no_workflow() + return {'allowed': False, 'require_mfa': False} + + stage = workflow.status.stage + + if stage == workflow_pb2.WS_STARTED: + return {'allowed': True, 'require_mfa': mfa_required} + + if stage == workflow_pb2.WS_READY_TO_START: + print(f"\n{bcolors.WARNING}Workflow access approved but not yet checked out.{bcolors.ENDC}") + print(f"Run: {bcolors.OKBLUE}pam workflow start {self.record_uid}{bcolors.ENDC} to check out the record.\n") + return {'allowed': False, 'require_mfa': False} + + if stage == workflow_pb2.WS_WAITING: + conditions = workflow.status.conditions + cond_str = WorkflowFormatter.format_conditions(conditions) if conditions else 'approval' + print(f"\n{bcolors.WARNING}Workflow access is pending: waiting for {cond_str}.{bcolors.ENDC}") + print("Your request is being processed. Please wait for approval.\n") + return {'allowed': False, 'require_mfa': False} + + if stage == workflow_pb2.WS_NEEDS_ACTION: + flow_uid_str = utils.base64_url_encode(workflow.flowUid) + print(f"\n{bcolors.WARNING}Workflow requires additional action before access is granted.{bcolors.ENDC}") + print(f"Run: {bcolors.OKBLUE}pam workflow state --flow-uid {flow_uid_str}{bcolors.ENDC} to see details.\n") + return {'allowed': False, 'require_mfa': False} + + self._print_no_workflow() + return {'allowed': False, 'require_mfa': False} + + def _print_no_workflow(self): + print(f"\n{bcolors.WARNING}This record is protected by a workflow.{bcolors.ENDC}") + print("You must request access before connecting.") + print(f"Run: {bcolors.OKBLUE}pam workflow request {self.record_uid}{bcolors.ENDC} to request access.\n") + + +class WorkflowMfaPrompt: + + def __init__(self, params: KeeperParams): + self.params = params + + def prompt(self): + import getpass + from ...proto import APIRequest_pb2 + from ... import api + + tfa_list = self._fetch_2fa_list(self.params, api, APIRequest_pb2, getpass) + if tfa_list is None: + return None + + supported_types = { + APIRequest_pb2.TWO_FA_CT_TOTP: 'TOTP (Authenticator App)', + APIRequest_pb2.TWO_FA_CT_SMS: 'SMS Text Message', + APIRequest_pb2.TWO_FA_CT_DUO: 'DUO Security', + APIRequest_pb2.TWO_FA_CT_WEBAUTHN: 'Security Key', + APIRequest_pb2.TWO_FA_CT_DNA: 'Keeper DNA (Watch)', + } + + channels = [ch for ch in tfa_list.channels if ch.channelType in supported_types] + + if not channels: + print(f"{bcolors.FAIL}No supported 2FA methods found. Supported: TOTP, SMS, DUO, Security Key.{bcolors.ENDC}") + return None + + selected = self._select_channel(channels, supported_types) + if selected is None: + return None + + return self._dispatch(selected.channelType, APIRequest_pb2) + + @staticmethod + def _fetch_2fa_list(params, api, APIRequest_pb2, getpass): + try: + tfa_list = api.communicate_rest( + params, None, 'authentication/2fa_list', + rs_type=APIRequest_pb2.TwoFactorListResponse, + ) + except Exception: + try: + code = getpass.getpass('2FA required. Enter TOTP code: ').strip() + return code if code else None + except (KeyboardInterrupt, EOFError): + return None + + if not tfa_list.channels: + print(f"\n{bcolors.FAIL}This workflow requires 2FA verification{bcolors.ENDC}") + print( + "Your account does not have any 2FA methods configured. " + f"For available methods, run: {bcolors.OKBLUE}2fa add -h{bcolors.ENDC}" + ) + return None + + return tfa_list + + @staticmethod + def _select_channel(channels, supported_types): + if len(channels) == 1: + return channels[0] + + print(f"\n{bcolors.OKBLUE}2FA required. Select authentication method:{bcolors.ENDC}") + for idx, ch in enumerate(channels, 1): + name = supported_types.get(ch.channelType, 'Unknown') + extra = f' ({ch.channelName})' if ch.channelName else '' + print(f" {idx}. {name}{extra}") + print(" q. Cancel") + + try: + answer = input('Selection: ').strip() + except (KeyboardInterrupt, EOFError): + return None + if answer.lower() == 'q': + return None + try: + idx = int(answer) - 1 + if 0 <= idx < len(channels): + return channels[idx] + except ValueError: + pass + + print(f"{bcolors.FAIL}Invalid selection.{bcolors.ENDC}") + return None + + def _dispatch(self, channel_type, APIRequest_pb2): + import getpass + + if channel_type == APIRequest_pb2.TWO_FA_CT_TOTP: + try: + code = getpass.getpass('Enter TOTP code: ').strip() + return code if code else None + except (KeyboardInterrupt, EOFError): + return None + + push_config = { + APIRequest_pb2.TWO_FA_CT_SMS: ( + APIRequest_pb2.TWO_FA_PUSH_SMS, + 'SMS sent.', 'SMS', + ), + APIRequest_pb2.TWO_FA_CT_DUO: ( + APIRequest_pb2.TWO_FA_PUSH_DUO_PUSH, + 'DUO push sent. Respond on your device, then enter the code.', 'DUO', + ), + APIRequest_pb2.TWO_FA_CT_DNA: ( + APIRequest_pb2.TWO_FA_PUSH_DNA, + 'Keeper DNA push sent. Approve on your watch, then enter the code.', 'DNA', + ), + } + + if channel_type in push_config: + push_type, sent_msg, label = push_config[channel_type] + return self._send_push_and_prompt(push_type, sent_msg, label) + + if channel_type == APIRequest_pb2.TWO_FA_CT_WEBAUTHN: + return self._handle_webauthn() + + return None + + def _send_push_and_prompt(self, push_type, sent_message, prompt_label): + import getpass + from ...proto import router_pb2 + + try: + push_rq = router_pb2.Router2FASendPushRequest() + push_rq.pushType = push_type + _post_request_to_router(self.params, '2fa_send_push', rq_proto=push_rq) + print(f"{bcolors.OKGREEN}{sent_message}{bcolors.ENDC}") + except Exception as e: + print(f"{bcolors.FAIL}Failed to send {prompt_label} push: {e}{bcolors.ENDC}") + return None + + try: + code = getpass.getpass(f'Enter {prompt_label} code: ').strip() + return code if code else None + except (KeyboardInterrupt, EOFError): + return None + + def _handle_webauthn(self): + import json as _json + from ...proto import router_pb2 + + try: + challenge_rq = router_pb2.Router2FAGetWebAuthnChallengeRequest() + challenge_rs = _post_request_to_router( + self.params, '2fa_get_webauthn_challenge', rq_proto=challenge_rq, + rs_type=router_pb2.Router2FAGetWebAuthnChallengeResponse, + ) + if not challenge_rs or not challenge_rs.challenge: + print(f"{bcolors.FAIL}Failed to get WebAuthn challenge from server.{bcolors.ENDC}") + return None + + challenge = _json.loads(challenge_rs.challenge) + + from ...yubikey.yubikey import yubikey_authenticate + response = yubikey_authenticate(challenge) + + if response: + signature = { + "id": response.id, + "rawId": utils.base64_url_encode(response.raw_id), + "response": { + "authenticatorData": utils.base64_url_encode(response.response.authenticator_data), + "clientDataJSON": response.response.client_data.b64, + "signature": utils.base64_url_encode(response.response.signature), + }, + "type": "public-key", + "clientExtensionResults": ( + dict(response.client_extension_results) + if response.client_extension_results else {} + ), + } + return _json.dumps(signature) + + print(f"{bcolors.FAIL}Security key authentication failed or was cancelled.{bcolors.ENDC}") + return None + + except ImportError: + from ...yubikey import display_fido2_warning + display_fido2_warning() + return None + except Exception as e: + print(f"{bcolors.FAIL}Security key error: {e}{bcolors.ENDC}") + return None + + +def check_workflow_access(params: KeeperParams, record_uid: str) -> dict: + return WorkflowAccessValidator(params, record_uid).validate() + + +def check_workflow_and_prompt_2fa(params: KeeperParams, record_uid: str): + result = check_workflow_access(params, record_uid) + if not result.get('allowed', True): + return (False, None) + if result.get('require_mfa', False): + value = WorkflowMfaPrompt(params).prompt() + if not value: + return (False, None) + return (True, value) + return (True, None) diff --git a/keepercommander/commands/workflow/registry.py b/keepercommander/commands/workflow/registry.py new file mode 100644 index 000000000..770c32700 --- /dev/null +++ b/keepercommander/commands/workflow/registry.py @@ -0,0 +1,82 @@ +# _ __ +# | |/ /___ ___ _ __ ___ _ _ ® +# | ' None: ... class GraphSyncActor(_message.Message): - __slots__ = ["type", "id", "name", "effectiveUserId"] + __slots__ = ("type", "id", "name", "effectiveUserId") TYPE_FIELD_NUMBER: _ClassVar[int] ID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] @@ -92,7 +93,7 @@ class GraphSyncActor(_message.Message): def __init__(self, type: _Optional[_Union[GraphSyncActorType, str]] = ..., id: _Optional[bytes] = ..., name: _Optional[str] = ..., effectiveUserId: _Optional[bytes] = ...) -> None: ... class GraphSyncData(_message.Message): - __slots__ = ["type", "ref", "parentRef", "content", "path"] + __slots__ = ("type", "ref", "parentRef", "content", "path") TYPE_FIELD_NUMBER: _ClassVar[int] REF_FIELD_NUMBER: _ClassVar[int] PARENTREF_FIELD_NUMBER: _ClassVar[int] @@ -106,7 +107,7 @@ class GraphSyncData(_message.Message): def __init__(self, type: _Optional[_Union[GraphSyncDataType, str]] = ..., ref: _Optional[_Union[GraphSyncRef, _Mapping]] = ..., parentRef: _Optional[_Union[GraphSyncRef, _Mapping]] = ..., content: _Optional[bytes] = ..., path: _Optional[str] = ...) -> None: ... class GraphSyncDataPlus(_message.Message): - __slots__ = ["data", "timestamp", "actor"] + __slots__ = ("data", "timestamp", "actor") DATA_FIELD_NUMBER: _ClassVar[int] TIMESTAMP_FIELD_NUMBER: _ClassVar[int] ACTOR_FIELD_NUMBER: _ClassVar[int] @@ -116,7 +117,7 @@ class GraphSyncDataPlus(_message.Message): def __init__(self, data: _Optional[_Union[GraphSyncData, _Mapping]] = ..., timestamp: _Optional[int] = ..., actor: _Optional[_Union[GraphSyncActor, _Mapping]] = ...) -> None: ... class GraphSyncQuery(_message.Message): - __slots__ = ["streamId", "origin", "syncPoint", "maxCount"] + __slots__ = ("streamId", "origin", "syncPoint", "maxCount") STREAMID_FIELD_NUMBER: _ClassVar[int] ORIGIN_FIELD_NUMBER: _ClassVar[int] SYNCPOINT_FIELD_NUMBER: _ClassVar[int] @@ -128,7 +129,7 @@ class GraphSyncQuery(_message.Message): def __init__(self, streamId: _Optional[bytes] = ..., origin: _Optional[bytes] = ..., syncPoint: _Optional[int] = ..., maxCount: _Optional[int] = ...) -> None: ... class GraphSyncResult(_message.Message): - __slots__ = ["streamId", "syncPoint", "data", "hasMore"] + __slots__ = ("streamId", "syncPoint", "data", "hasMore") STREAMID_FIELD_NUMBER: _ClassVar[int] SYNCPOINT_FIELD_NUMBER: _ClassVar[int] DATA_FIELD_NUMBER: _ClassVar[int] @@ -137,22 +138,22 @@ class GraphSyncResult(_message.Message): syncPoint: int data: _containers.RepeatedCompositeFieldContainer[GraphSyncDataPlus] hasMore: bool - def __init__(self, streamId: _Optional[bytes] = ..., syncPoint: _Optional[int] = ..., data: _Optional[_Iterable[_Union[GraphSyncDataPlus, _Mapping]]] = ..., hasMore: bool = ...) -> None: ... + def __init__(self, streamId: _Optional[bytes] = ..., syncPoint: _Optional[int] = ..., data: _Optional[_Iterable[_Union[GraphSyncDataPlus, _Mapping]]] = ..., hasMore: _Optional[bool] = ...) -> None: ... class GraphSyncMultiQuery(_message.Message): - __slots__ = ["queries"] + __slots__ = ("queries",) QUERIES_FIELD_NUMBER: _ClassVar[int] queries: _containers.RepeatedCompositeFieldContainer[GraphSyncQuery] def __init__(self, queries: _Optional[_Iterable[_Union[GraphSyncQuery, _Mapping]]] = ...) -> None: ... class GraphSyncMultiResult(_message.Message): - __slots__ = ["results"] + __slots__ = ("results",) RESULTS_FIELD_NUMBER: _ClassVar[int] results: _containers.RepeatedCompositeFieldContainer[GraphSyncResult] def __init__(self, results: _Optional[_Iterable[_Union[GraphSyncResult, _Mapping]]] = ...) -> None: ... class GraphSyncAddDataRequest(_message.Message): - __slots__ = ["origin", "data"] + __slots__ = ("origin", "data") ORIGIN_FIELD_NUMBER: _ClassVar[int] DATA_FIELD_NUMBER: _ClassVar[int] origin: GraphSyncRef @@ -160,13 +161,13 @@ class GraphSyncAddDataRequest(_message.Message): def __init__(self, origin: _Optional[_Union[GraphSyncRef, _Mapping]] = ..., data: _Optional[_Iterable[_Union[GraphSyncData, _Mapping]]] = ...) -> None: ... class GraphSyncLeafsQuery(_message.Message): - __slots__ = ["vertices"] + __slots__ = ("vertices",) VERTICES_FIELD_NUMBER: _ClassVar[int] vertices: _containers.RepeatedScalarFieldContainer[bytes] def __init__(self, vertices: _Optional[_Iterable[bytes]] = ...) -> None: ... class GraphSyncRefsResult(_message.Message): - __slots__ = ["refs"] + __slots__ = ("refs",) REFS_FIELD_NUMBER: _ClassVar[int] refs: _containers.RepeatedCompositeFieldContainer[GraphSyncRef] def __init__(self, refs: _Optional[_Iterable[_Union[GraphSyncRef, _Mapping]]] = ...) -> None: ... diff --git a/keepercommander/proto/router_pb2.py b/keepercommander/proto/router_pb2.py index eacef95b7..16b4301dc 100644 --- a/keepercommander/proto/router_pb2.py +++ b/keepercommander/proto/router_pb2.py @@ -14,68 +14,88 @@ from . import pam_pb2 as pam__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0crouter.proto\x12\x06Router\x1a\tpam.proto\"r\n\x0eRouterResponse\x12\x30\n\x0cresponseCode\x18\x01 \x01(\x0e\x32\x1a.Router.RouterResponseCode\x12\x14\n\x0c\x65rrorMessage\x18\x02 \x01(\t\x12\x18\n\x10\x65ncryptedPayload\x18\x03 \x01(\x0c\"\xaf\x01\n\x17RouterControllerMessage\x12/\n\x0bmessageType\x18\x01 \x01(\x0e\x32\x1a.PAM.ControllerMessageType\x12\x12\n\nmessageUid\x18\x02 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\x12\x16\n\x0estreamResponse\x18\x04 \x01(\x08\x12\x0f\n\x07payload\x18\x05 \x01(\x0c\x12\x0f\n\x07timeout\x18\x06 \x01(\x05\"\xec\x01\n\x0eRouterUserAuth\x12\x17\n\x0ftransmissionKey\x18\x01 \x01(\x0c\x12\x14\n\x0csessionToken\x18\x02 \x01(\x0c\x12\x0e\n\x06userId\x18\x03 \x01(\x05\x12\x18\n\x10\x65nterpriseUserId\x18\x04 \x01(\x03\x12\x12\n\ndeviceName\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65viceToken\x18\x06 \x01(\x0c\x12\x17\n\x0f\x63lientVersionId\x18\x07 \x01(\x05\x12\x14\n\x0cneedUsername\x18\x08 \x01(\x08\x12\x10\n\x08username\x18\t \x01(\t\x12\x17\n\x0fmspEnterpriseId\x18\n \x01(\x05\"\x83\x02\n\x10RouterDeviceAuth\x12\x10\n\x08\x63lientId\x18\x01 \x01(\t\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x11\n\tsignature\x18\x03 \x01(\x0c\x12\x14\n\x0c\x65nterpriseId\x18\x04 \x01(\x05\x12\x0e\n\x06nodeId\x18\x05 \x01(\x03\x12\x12\n\ndeviceName\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65viceToken\x18\x07 \x01(\x0c\x12\x16\n\x0e\x63ontrollerName\x18\x08 \x01(\t\x12\x15\n\rcontrollerUid\x18\t \x01(\x0c\x12\x11\n\townerUser\x18\n \x01(\t\x12\x11\n\tchallenge\x18\x0b \x01(\t\x12\x0f\n\x07ownerId\x18\x0c \x01(\x05\"\x83\x01\n\x14RouterRecordRotation\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x18\n\x10\x63onfigurationUid\x18\x02 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\x12\x13\n\x0bresourceUid\x18\x04 \x01(\x0c\x12\x12\n\nnoSchedule\x18\x05 \x01(\x08\"E\n\x1cRouterRecordRotationsRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x0f\n\x07records\x18\x02 \x03(\x0c\"a\n\x1dRouterRecordRotationsResponse\x12/\n\trotations\x18\x01 \x03(\x0b\x32\x1c.Router.RouterRecordRotation\x12\x0f\n\x07hasMore\x18\x02 \x01(\x08\"\xed\x01\n\x12RouterRotationInfo\x12,\n\x06status\x18\x01 \x01(\x0e\x32\x1c.Router.RouterRotationStatus\x12\x18\n\x10\x63onfigurationUid\x18\x02 \x01(\x0c\x12\x13\n\x0bresourceUid\x18\x03 \x01(\x0c\x12\x0e\n\x06nodeId\x18\x04 \x01(\x03\x12\x15\n\rcontrollerUid\x18\x05 \x01(\x0c\x12\x16\n\x0e\x63ontrollerName\x18\x06 \x01(\t\x12\x12\n\nscriptName\x18\x07 \x01(\t\x12\x15\n\rpwdComplexity\x18\x08 \x01(\t\x12\x10\n\x08\x64isabled\x18\t \x01(\x08\"\x84\x02\n\x1bRouterRecordRotationRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x18\n\x10\x63onfigurationUid\x18\x03 \x01(\x0c\x12\x13\n\x0bresourceUid\x18\x04 \x01(\x0c\x12\x10\n\x08schedule\x18\x05 \x01(\t\x12\x18\n\x10\x65nterpriseUserId\x18\x06 \x01(\x03\x12\x15\n\rpwdComplexity\x18\x07 \x01(\x0c\x12\x10\n\x08\x64isabled\x18\x08 \x01(\x08\x12\x15\n\rremoteAddress\x18\t \x01(\t\x12\x17\n\x0f\x63lientVersionId\x18\n \x01(\x05\x12\x0c\n\x04noop\x18\x0b \x01(\x08\"<\n\x17UserRecordAccessRequest\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\"a\n\x18UserRecordAccessResponse\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x32\n\x0b\x61\x63\x63\x65ssLevel\x18\x02 \x01(\x0e\x32\x1d.Router.UserRecordAccessLevel\"8\n\x10RotationSchedule\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x10\n\x08schedule\x18\x02 \x01(\t\"\x90\x01\n\x12\x41piCallbackRequest\x12\x13\n\x0bresourceUid\x18\x01 \x01(\x0c\x12.\n\tschedules\x18\x02 \x03(\x0b\x32\x1b.Router.ApiCallbackSchedule\x12\x0b\n\x03url\x18\x03 \x01(\t\x12(\n\x0bserviceType\x18\x04 \x01(\x0e\x32\x13.Router.ServiceType\"5\n\x13\x41piCallbackSchedule\x12\x10\n\x08schedule\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"@\n\x16RouterScheduledActions\x12\x10\n\x08schedule\x18\x01 \x01(\t\x12\x14\n\x0cresourceUids\x18\x02 \x03(\x0c\"Y\n\x1cRouterRecordsRotationRequest\x12\x39\n\x11rotationSchedules\x18\x01 \x03(\x0b\x32\x1e.Router.RouterScheduledActions\"\x85\x01\n\x14\x43onnectionParameters\x12\x15\n\rconnectionUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x0e\n\x06userId\x18\x03 \x01(\x05\x12\x15\n\rcontrollerUid\x18\x04 \x01(\x0c\x12\x1c\n\x14\x63redentialsRecordUid\x18\x05 \x01(\x0c\"O\n\x1aValidateConnectionsRequest\x12\x31\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x1c.Router.ConnectionParameters\"J\n\x1b\x43onnectionValidationFailure\x12\x15\n\rconnectionUid\x18\x01 \x01(\x0c\x12\x14\n\x0c\x65rrorMessage\x18\x02 \x01(\t\"]\n\x1bValidateConnectionsResponse\x12>\n\x11\x66\x61iledConnections\x18\x01 \x03(\x0b\x32#.Router.ConnectionValidationFailure\"1\n\x15GetEnforcementRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\";\n\x0f\x45nforcementType\x12\x19\n\x11\x65nforcementTypeId\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t\"p\n\x16GetEnforcementResponse\x12\x31\n\x10\x65nforcementTypes\x18\x01 \x03(\x0b\x32\x17.Router.EnforcementType\x12\x10\n\x08\x61\x64\x64OnIds\x18\x02 \x03(\x05\x12\x11\n\tisInTrial\x18\x03 \x01(\x08\"O\n\x17PEDMTOTPValidateRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x14\n\x0c\x65nterpriseId\x18\x02 \x01(\x05\x12\x0c\n\x04\x63ode\x18\x03 \x01(\x05*\x98\x02\n\x12RouterResponseCode\x12\n\n\x06RRC_OK\x10\x00\x12\x15\n\x11RRC_GENERAL_ERROR\x10\x01\x12\x13\n\x0fRRC_NOT_ALLOWED\x10\x02\x12\x13\n\x0fRRC_BAD_REQUEST\x10\x03\x12\x0f\n\x0bRRC_TIMEOUT\x10\x04\x12\x11\n\rRRC_BAD_STATE\x10\x05\x12\x17\n\x13RRC_CONTROLLER_DOWN\x10\x06\x12\x16\n\x12RRC_WRONG_INSTANCE\x10\x07\x12+\n\'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED\x10\x08\x12\x33\n/RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED\x10\t*k\n\x14RouterRotationStatus\x12\x0e\n\nRRS_ONLINE\x10\x00\x12\x13\n\x0fRRS_NO_ROTATION\x10\x01\x12\x15\n\x11RRS_NO_CONTROLLER\x10\x02\x12\x17\n\x13RRS_CONTROLLER_DOWN\x10\x03*}\n\x15UserRecordAccessLevel\x12\r\n\tRRAL_NONE\x10\x00\x12\r\n\tRRAL_READ\x10\x01\x12\x0e\n\nRRAL_SHARE\x10\x02\x12\r\n\tRRAL_EDIT\x10\x03\x12\x17\n\x13RRAL_EDIT_AND_SHARE\x10\x04\x12\x0e\n\nRRAL_OWNER\x10\x05*.\n\x0bServiceType\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x06\n\x02KA\x10\x01\x12\x06\n\x02\x42I\x10\x02\x42\"\n\x18\x63om.keepersecurity.protoB\x06Routerb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0crouter.proto\x12\x06Router\x1a\tpam.proto\x1a\x10\x41PIRequest.proto\"r\n\x0eRouterResponse\x12\x30\n\x0cresponseCode\x18\x01 \x01(\x0e\x32\x1a.Router.RouterResponseCode\x12\x14\n\x0c\x65rrorMessage\x18\x02 \x01(\t\x12\x18\n\x10\x65ncryptedPayload\x18\x03 \x01(\x0c\"\xaf\x01\n\x17RouterControllerMessage\x12/\n\x0bmessageType\x18\x01 \x01(\x0e\x32\x1a.PAM.ControllerMessageType\x12\x12\n\nmessageUid\x18\x02 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\x12\x16\n\x0estreamResponse\x18\x04 \x01(\x08\x12\x0f\n\x07payload\x18\x05 \x01(\x0c\x12\x0f\n\x07timeout\x18\x06 \x01(\x05\"\x99\x02\n\x0eRouterUserAuth\x12\x17\n\x0ftransmissionKey\x18\x01 \x01(\x0c\x12\x14\n\x0csessionToken\x18\x02 \x01(\x0c\x12\x0e\n\x06userId\x18\x03 \x01(\x05\x12\x18\n\x10\x65nterpriseUserId\x18\x04 \x01(\x03\x12\x12\n\ndeviceName\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x65viceToken\x18\x06 \x01(\x0c\x12\x17\n\x0f\x63lientVersionId\x18\x07 \x01(\x05\x12\x14\n\x0cneedUsername\x18\x08 \x01(\x08\x12\x10\n\x08username\x18\t \x01(\t\x12\x17\n\x0fmspEnterpriseId\x18\n \x01(\x05\x12\x13\n\x0bisPedmAdmin\x18\x0b \x01(\x08\x12\x16\n\x0emcEnterpriseId\x18\x0c \x01(\x05\"\x9d\x02\n\x10RouterDeviceAuth\x12\x10\n\x08\x63lientId\x18\x01 \x01(\t\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x11\n\tsignature\x18\x03 \x01(\x0c\x12\x14\n\x0c\x65nterpriseId\x18\x04 \x01(\x05\x12\x0e\n\x06nodeId\x18\x05 \x01(\x03\x12\x12\n\ndeviceName\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65viceToken\x18\x07 \x01(\x0c\x12\x16\n\x0e\x63ontrollerName\x18\x08 \x01(\t\x12\x15\n\rcontrollerUid\x18\t \x01(\x0c\x12\x11\n\townerUser\x18\n \x01(\t\x12\x11\n\tchallenge\x18\x0b \x01(\t\x12\x0f\n\x07ownerId\x18\x0c \x01(\x05\x12\x18\n\x10maxInstanceCount\x18\r \x01(\x05\"\x83\x01\n\x14RouterRecordRotation\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x18\n\x10\x63onfigurationUid\x18\x02 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\x12\x13\n\x0bresourceUid\x18\x04 \x01(\x0c\x12\x12\n\nnoSchedule\x18\x05 \x01(\x08\"E\n\x1cRouterRecordRotationsRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x0f\n\x07records\x18\x02 \x03(\x0c\"a\n\x1dRouterRecordRotationsResponse\x12/\n\trotations\x18\x01 \x03(\x0b\x32\x1c.Router.RouterRecordRotation\x12\x0f\n\x07hasMore\x18\x02 \x01(\x08\"\xed\x01\n\x12RouterRotationInfo\x12,\n\x06status\x18\x01 \x01(\x0e\x32\x1c.Router.RouterRotationStatus\x12\x18\n\x10\x63onfigurationUid\x18\x02 \x01(\x0c\x12\x13\n\x0bresourceUid\x18\x03 \x01(\x0c\x12\x0e\n\x06nodeId\x18\x04 \x01(\x03\x12\x15\n\rcontrollerUid\x18\x05 \x01(\x0c\x12\x16\n\x0e\x63ontrollerName\x18\x06 \x01(\t\x12\x12\n\nscriptName\x18\x07 \x01(\t\x12\x15\n\rpwdComplexity\x18\x08 \x01(\t\x12\x10\n\x08\x64isabled\x18\t \x01(\x08\"\xba\x02\n\x1bRouterRecordRotationRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x18\n\x10\x63onfigurationUid\x18\x03 \x01(\x0c\x12\x13\n\x0bresourceUid\x18\x04 \x01(\x0c\x12\x10\n\x08schedule\x18\x05 \x01(\t\x12\x18\n\x10\x65nterpriseUserId\x18\x06 \x01(\x03\x12\x15\n\rpwdComplexity\x18\x07 \x01(\x0c\x12\x10\n\x08\x64isabled\x18\x08 \x01(\x08\x12\x15\n\rremoteAddress\x18\t \x01(\t\x12\x17\n\x0f\x63lientVersionId\x18\n \x01(\x05\x12\x0c\n\x04noop\x18\x0b \x01(\x08\x12\x1e\n\x11saasConfiguration\x18\x0c \x01(\x0cH\x00\x88\x01\x01\x42\x14\n\x12_saasConfiguration\"<\n\x17UserRecordAccessRequest\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\"a\n\x18UserRecordAccessResponse\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x32\n\x0b\x61\x63\x63\x65ssLevel\x18\x02 \x01(\x0e\x32\x1d.Router.UserRecordAccessLevel\"M\n\x18UserRecordAccessRequests\x12\x31\n\x08requests\x18\x01 \x03(\x0b\x32\x1f.Router.UserRecordAccessRequest\"P\n\x19UserRecordAccessResponses\x12\x33\n\tresponses\x18\x01 \x03(\x0b\x32 .Router.UserRecordAccessResponse\"8\n\x10RotationSchedule\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x10\n\x08schedule\x18\x02 \x01(\t\"\x90\x01\n\x12\x41piCallbackRequest\x12\x13\n\x0bresourceUid\x18\x01 \x01(\x0c\x12.\n\tschedules\x18\x02 \x03(\x0b\x32\x1b.Router.ApiCallbackSchedule\x12\x0b\n\x03url\x18\x03 \x01(\t\x12(\n\x0bserviceType\x18\x04 \x01(\x0e\x32\x13.Router.ServiceType\"5\n\x13\x41piCallbackSchedule\x12\x10\n\x08schedule\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"@\n\x16RouterScheduledActions\x12\x10\n\x08schedule\x18\x01 \x01(\t\x12\x14\n\x0cresourceUids\x18\x02 \x03(\x0c\"Y\n\x1cRouterRecordsRotationRequest\x12\x39\n\x11rotationSchedules\x18\x01 \x03(\x0b\x32\x1e.Router.RouterScheduledActions\"\x85\x01\n\x14\x43onnectionParameters\x12\x15\n\rconnectionUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x0e\n\x06userId\x18\x03 \x01(\x05\x12\x15\n\rcontrollerUid\x18\x04 \x01(\x0c\x12\x1c\n\x14\x63redentialsRecordUid\x18\x05 \x01(\x0c\"O\n\x1aValidateConnectionsRequest\x12\x31\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x1c.Router.ConnectionParameters\"J\n\x1b\x43onnectionValidationFailure\x12\x15\n\rconnectionUid\x18\x01 \x01(\x0c\x12\x14\n\x0c\x65rrorMessage\x18\x02 \x01(\t\"]\n\x1bValidateConnectionsResponse\x12>\n\x11\x66\x61iledConnections\x18\x01 \x03(\x0b\x32#.Router.ConnectionValidationFailure\"1\n\x15GetEnforcementRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\";\n\x0f\x45nforcementType\x12\x19\n\x11\x65nforcementTypeId\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t\"p\n\x16GetEnforcementResponse\x12\x31\n\x10\x65nforcementTypes\x18\x01 \x03(\x0b\x32\x17.Router.EnforcementType\x12\x10\n\x08\x61\x64\x64OnIds\x18\x02 \x03(\x05\x12\x11\n\tisInTrial\x18\x03 \x01(\x08\"O\n\x17PEDMTOTPValidateRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x14\n\x0c\x65nterpriseId\x18\x02 \x01(\x05\x12\x0c\n\x04\x63ode\x18\x03 \x01(\x05\"H\n\x18GetPEDMAdminInfoResponse\x12\x13\n\x0bisPedmAdmin\x18\x01 \x01(\x08\x12\x17\n\x0fpedmAddonActive\x18\x02 \x01(\x08\"-\n\x12PAMNetworkSettings\x12\x17\n\x0f\x61llowedSettings\x18\x01 \x01(\x0c\"\xe4\x01\n\x1ePAMNetworkConfigurationRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x38\n\x0fnetworkSettings\x18\x02 \x01(\x0b\x32\x1a.Router.PAMNetworkSettingsH\x00\x88\x01\x01\x12)\n\tresources\x18\x03 \x03(\x0b\x32\x16.PAM.PAMResourceConfig\x12\x36\n\trotations\x18\x04 \x03(\x0b\x32#.Router.RouterRecordRotationRequestB\x12\n\x10_networkSettings\"R\n\x1bPAMDiscoveryRulesSetRequest\x12\x12\n\nnetworkUid\x18\x01 \x01(\x0c\x12\r\n\x05rules\x18\x02 \x01(\x0c\x12\x10\n\x08rulesKey\x18\x03 \x01(\x0c\"X\n\x18Router2FAValidateRequest\x12\x17\n\x0ftransmissionKey\x18\x01 \x01(\x0c\x12\x14\n\x0csessionToken\x18\x02 \x01(\x0c\x12\r\n\x05value\x18\x03 \x01(\t\"~\n\x18Router2FASendPushRequest\x12\x17\n\x0ftransmissionKey\x18\x01 \x01(\x0c\x12\x14\n\x0csessionToken\x18\x02 \x01(\x0c\x12\x33\n\x08pushType\x18\x03 \x01(\x0e\x32!.Authentication.TwoFactorPushType\"U\n$Router2FAGetWebAuthnChallengeRequest\x12\x17\n\x0ftransmissionKey\x18\x01 \x01(\x0c\x12\x14\n\x0csessionToken\x18\x02 \x01(\x0c\"P\n%Router2FAGetWebAuthnChallengeResponse\x12\x11\n\tchallenge\x18\x01 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x02 \x03(\t*\x98\x02\n\x12RouterResponseCode\x12\n\n\x06RRC_OK\x10\x00\x12\x15\n\x11RRC_GENERAL_ERROR\x10\x01\x12\x13\n\x0fRRC_NOT_ALLOWED\x10\x02\x12\x13\n\x0fRRC_BAD_REQUEST\x10\x03\x12\x0f\n\x0bRRC_TIMEOUT\x10\x04\x12\x11\n\rRRC_BAD_STATE\x10\x05\x12\x17\n\x13RRC_CONTROLLER_DOWN\x10\x06\x12\x16\n\x12RRC_WRONG_INSTANCE\x10\x07\x12+\n\'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED\x10\x08\x12\x33\n/RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED\x10\t*k\n\x14RouterRotationStatus\x12\x0e\n\nRRS_ONLINE\x10\x00\x12\x13\n\x0fRRS_NO_ROTATION\x10\x01\x12\x15\n\x11RRS_NO_CONTROLLER\x10\x02\x12\x17\n\x13RRS_CONTROLLER_DOWN\x10\x03*}\n\x15UserRecordAccessLevel\x12\r\n\tRRAL_NONE\x10\x00\x12\r\n\tRRAL_READ\x10\x01\x12\x0e\n\nRRAL_SHARE\x10\x02\x12\r\n\tRRAL_EDIT\x10\x03\x12\x17\n\x13RRAL_EDIT_AND_SHARE\x10\x04\x12\x0e\n\nRRAL_OWNER\x10\x05*.\n\x0bServiceType\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x06\n\x02KA\x10\x01\x12\x06\n\x02\x42I\x10\x02\x42\"\n\x18\x63om.keepersecurity.protoB\x06Routerb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'router_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\030com.keepersecurity.protoB\006Router' - _globals['_ROUTERRESPONSECODE']._serialized_start=2911 - _globals['_ROUTERRESPONSECODE']._serialized_end=3191 - _globals['_ROUTERROTATIONSTATUS']._serialized_start=3193 - _globals['_ROUTERROTATIONSTATUS']._serialized_end=3300 - _globals['_USERRECORDACCESSLEVEL']._serialized_start=3302 - _globals['_USERRECORDACCESSLEVEL']._serialized_end=3427 - _globals['_SERVICETYPE']._serialized_start=3429 - _globals['_SERVICETYPE']._serialized_end=3475 - _globals['_ROUTERRESPONSE']._serialized_start=35 - _globals['_ROUTERRESPONSE']._serialized_end=149 - _globals['_ROUTERCONTROLLERMESSAGE']._serialized_start=152 - _globals['_ROUTERCONTROLLERMESSAGE']._serialized_end=327 - _globals['_ROUTERUSERAUTH']._serialized_start=330 - _globals['_ROUTERUSERAUTH']._serialized_end=566 - _globals['_ROUTERDEVICEAUTH']._serialized_start=569 - _globals['_ROUTERDEVICEAUTH']._serialized_end=828 - _globals['_ROUTERRECORDROTATION']._serialized_start=831 - _globals['_ROUTERRECORDROTATION']._serialized_end=962 - _globals['_ROUTERRECORDROTATIONSREQUEST']._serialized_start=964 - _globals['_ROUTERRECORDROTATIONSREQUEST']._serialized_end=1033 - _globals['_ROUTERRECORDROTATIONSRESPONSE']._serialized_start=1035 - _globals['_ROUTERRECORDROTATIONSRESPONSE']._serialized_end=1132 - _globals['_ROUTERROTATIONINFO']._serialized_start=1135 - _globals['_ROUTERROTATIONINFO']._serialized_end=1372 - _globals['_ROUTERRECORDROTATIONREQUEST']._serialized_start=1375 - _globals['_ROUTERRECORDROTATIONREQUEST']._serialized_end=1635 - _globals['_USERRECORDACCESSREQUEST']._serialized_start=1637 - _globals['_USERRECORDACCESSREQUEST']._serialized_end=1697 - _globals['_USERRECORDACCESSRESPONSE']._serialized_start=1699 - _globals['_USERRECORDACCESSRESPONSE']._serialized_end=1796 - _globals['_ROTATIONSCHEDULE']._serialized_start=1798 - _globals['_ROTATIONSCHEDULE']._serialized_end=1854 - _globals['_APICALLBACKREQUEST']._serialized_start=1857 - _globals['_APICALLBACKREQUEST']._serialized_end=2001 - _globals['_APICALLBACKSCHEDULE']._serialized_start=2003 - _globals['_APICALLBACKSCHEDULE']._serialized_end=2056 - _globals['_ROUTERSCHEDULEDACTIONS']._serialized_start=2058 - _globals['_ROUTERSCHEDULEDACTIONS']._serialized_end=2122 - _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_start=2124 - _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_end=2213 - _globals['_CONNECTIONPARAMETERS']._serialized_start=2216 - _globals['_CONNECTIONPARAMETERS']._serialized_end=2349 - _globals['_VALIDATECONNECTIONSREQUEST']._serialized_start=2351 - _globals['_VALIDATECONNECTIONSREQUEST']._serialized_end=2430 - _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_start=2432 - _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_end=2506 - _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_start=2508 - _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_end=2601 - _globals['_GETENFORCEMENTREQUEST']._serialized_start=2603 - _globals['_GETENFORCEMENTREQUEST']._serialized_end=2652 - _globals['_ENFORCEMENTTYPE']._serialized_start=2654 - _globals['_ENFORCEMENTTYPE']._serialized_end=2713 - _globals['_GETENFORCEMENTRESPONSE']._serialized_start=2715 - _globals['_GETENFORCEMENTRESPONSE']._serialized_end=2827 - _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_start=2829 - _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_end=2908 + _globals['_ROUTERRESPONSECODE']._serialized_start=4038 + _globals['_ROUTERRESPONSECODE']._serialized_end=4318 + _globals['_ROUTERROTATIONSTATUS']._serialized_start=4320 + _globals['_ROUTERROTATIONSTATUS']._serialized_end=4427 + _globals['_USERRECORDACCESSLEVEL']._serialized_start=4429 + _globals['_USERRECORDACCESSLEVEL']._serialized_end=4554 + _globals['_SERVICETYPE']._serialized_start=4556 + _globals['_SERVICETYPE']._serialized_end=4602 + _globals['_ROUTERRESPONSE']._serialized_start=53 + _globals['_ROUTERRESPONSE']._serialized_end=167 + _globals['_ROUTERCONTROLLERMESSAGE']._serialized_start=170 + _globals['_ROUTERCONTROLLERMESSAGE']._serialized_end=345 + _globals['_ROUTERUSERAUTH']._serialized_start=348 + _globals['_ROUTERUSERAUTH']._serialized_end=629 + _globals['_ROUTERDEVICEAUTH']._serialized_start=632 + _globals['_ROUTERDEVICEAUTH']._serialized_end=917 + _globals['_ROUTERRECORDROTATION']._serialized_start=920 + _globals['_ROUTERRECORDROTATION']._serialized_end=1051 + _globals['_ROUTERRECORDROTATIONSREQUEST']._serialized_start=1053 + _globals['_ROUTERRECORDROTATIONSREQUEST']._serialized_end=1122 + _globals['_ROUTERRECORDROTATIONSRESPONSE']._serialized_start=1124 + _globals['_ROUTERRECORDROTATIONSRESPONSE']._serialized_end=1221 + _globals['_ROUTERROTATIONINFO']._serialized_start=1224 + _globals['_ROUTERROTATIONINFO']._serialized_end=1461 + _globals['_ROUTERRECORDROTATIONREQUEST']._serialized_start=1464 + _globals['_ROUTERRECORDROTATIONREQUEST']._serialized_end=1778 + _globals['_USERRECORDACCESSREQUEST']._serialized_start=1780 + _globals['_USERRECORDACCESSREQUEST']._serialized_end=1840 + _globals['_USERRECORDACCESSRESPONSE']._serialized_start=1842 + _globals['_USERRECORDACCESSRESPONSE']._serialized_end=1939 + _globals['_USERRECORDACCESSREQUESTS']._serialized_start=1941 + _globals['_USERRECORDACCESSREQUESTS']._serialized_end=2018 + _globals['_USERRECORDACCESSRESPONSES']._serialized_start=2020 + _globals['_USERRECORDACCESSRESPONSES']._serialized_end=2100 + _globals['_ROTATIONSCHEDULE']._serialized_start=2102 + _globals['_ROTATIONSCHEDULE']._serialized_end=2158 + _globals['_APICALLBACKREQUEST']._serialized_start=2161 + _globals['_APICALLBACKREQUEST']._serialized_end=2305 + _globals['_APICALLBACKSCHEDULE']._serialized_start=2307 + _globals['_APICALLBACKSCHEDULE']._serialized_end=2360 + _globals['_ROUTERSCHEDULEDACTIONS']._serialized_start=2362 + _globals['_ROUTERSCHEDULEDACTIONS']._serialized_end=2426 + _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_start=2428 + _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_end=2517 + _globals['_CONNECTIONPARAMETERS']._serialized_start=2520 + _globals['_CONNECTIONPARAMETERS']._serialized_end=2653 + _globals['_VALIDATECONNECTIONSREQUEST']._serialized_start=2655 + _globals['_VALIDATECONNECTIONSREQUEST']._serialized_end=2734 + _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_start=2736 + _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_end=2810 + _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_start=2812 + _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_end=2905 + _globals['_GETENFORCEMENTREQUEST']._serialized_start=2907 + _globals['_GETENFORCEMENTREQUEST']._serialized_end=2956 + _globals['_ENFORCEMENTTYPE']._serialized_start=2958 + _globals['_ENFORCEMENTTYPE']._serialized_end=3017 + _globals['_GETENFORCEMENTRESPONSE']._serialized_start=3019 + _globals['_GETENFORCEMENTRESPONSE']._serialized_end=3131 + _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_start=3133 + _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_end=3212 + _globals['_GETPEDMADMININFORESPONSE']._serialized_start=3214 + _globals['_GETPEDMADMININFORESPONSE']._serialized_end=3286 + _globals['_PAMNETWORKSETTINGS']._serialized_start=3288 + _globals['_PAMNETWORKSETTINGS']._serialized_end=3333 + _globals['_PAMNETWORKCONFIGURATIONREQUEST']._serialized_start=3336 + _globals['_PAMNETWORKCONFIGURATIONREQUEST']._serialized_end=3564 + _globals['_PAMDISCOVERYRULESSETREQUEST']._serialized_start=3566 + _globals['_PAMDISCOVERYRULESSETREQUEST']._serialized_end=3648 + _globals['_ROUTER2FAVALIDATEREQUEST']._serialized_start=3650 + _globals['_ROUTER2FAVALIDATEREQUEST']._serialized_end=3738 + _globals['_ROUTER2FASENDPUSHREQUEST']._serialized_start=3740 + _globals['_ROUTER2FASENDPUSHREQUEST']._serialized_end=3866 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGEREQUEST']._serialized_start=3868 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGEREQUEST']._serialized_end=3953 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGERESPONSE']._serialized_start=3955 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGERESPONSE']._serialized_end=4035 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/router_pb2.pyi b/keepercommander/proto/router_pb2.pyi index 17e9d9e07..486c4628c 100644 --- a/keepercommander/proto/router_pb2.pyi +++ b/keepercommander/proto/router_pb2.pyi @@ -1,14 +1,16 @@ import pam_pb2 as _pam_pb2 +import APIRequest_pb2 as _APIRequest_pb2 from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor class RouterResponseCode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () RRC_OK: _ClassVar[RouterResponseCode] RRC_GENERAL_ERROR: _ClassVar[RouterResponseCode] RRC_NOT_ALLOWED: _ClassVar[RouterResponseCode] @@ -21,14 +23,14 @@ class RouterResponseCode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED: _ClassVar[RouterResponseCode] class RouterRotationStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () RRS_ONLINE: _ClassVar[RouterRotationStatus] RRS_NO_ROTATION: _ClassVar[RouterRotationStatus] RRS_NO_CONTROLLER: _ClassVar[RouterRotationStatus] RRS_CONTROLLER_DOWN: _ClassVar[RouterRotationStatus] class UserRecordAccessLevel(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () RRAL_NONE: _ClassVar[UserRecordAccessLevel] RRAL_READ: _ClassVar[UserRecordAccessLevel] RRAL_SHARE: _ClassVar[UserRecordAccessLevel] @@ -37,7 +39,7 @@ class UserRecordAccessLevel(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): RRAL_OWNER: _ClassVar[UserRecordAccessLevel] class ServiceType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () UNSPECIFIED: _ClassVar[ServiceType] KA: _ClassVar[ServiceType] BI: _ClassVar[ServiceType] @@ -66,7 +68,7 @@ KA: ServiceType BI: ServiceType class RouterResponse(_message.Message): - __slots__ = ["responseCode", "errorMessage", "encryptedPayload"] + __slots__ = ("responseCode", "errorMessage", "encryptedPayload") RESPONSECODE_FIELD_NUMBER: _ClassVar[int] ERRORMESSAGE_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDPAYLOAD_FIELD_NUMBER: _ClassVar[int] @@ -76,7 +78,7 @@ class RouterResponse(_message.Message): def __init__(self, responseCode: _Optional[_Union[RouterResponseCode, str]] = ..., errorMessage: _Optional[str] = ..., encryptedPayload: _Optional[bytes] = ...) -> None: ... class RouterControllerMessage(_message.Message): - __slots__ = ["messageType", "messageUid", "controllerUid", "streamResponse", "payload", "timeout"] + __slots__ = ("messageType", "messageUid", "controllerUid", "streamResponse", "payload", "timeout") MESSAGETYPE_FIELD_NUMBER: _ClassVar[int] MESSAGEUID_FIELD_NUMBER: _ClassVar[int] CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] @@ -89,10 +91,10 @@ class RouterControllerMessage(_message.Message): streamResponse: bool payload: bytes timeout: int - def __init__(self, messageType: _Optional[_Union[_pam_pb2.ControllerMessageType, str]] = ..., messageUid: _Optional[bytes] = ..., controllerUid: _Optional[bytes] = ..., streamResponse: bool = ..., payload: _Optional[bytes] = ..., timeout: _Optional[int] = ...) -> None: ... + def __init__(self, messageType: _Optional[_Union[_pam_pb2.ControllerMessageType, str]] = ..., messageUid: _Optional[bytes] = ..., controllerUid: _Optional[bytes] = ..., streamResponse: _Optional[bool] = ..., payload: _Optional[bytes] = ..., timeout: _Optional[int] = ...) -> None: ... class RouterUserAuth(_message.Message): - __slots__ = ["transmissionKey", "sessionToken", "userId", "enterpriseUserId", "deviceName", "deviceToken", "clientVersionId", "needUsername", "username", "mspEnterpriseId"] + __slots__ = ("transmissionKey", "sessionToken", "userId", "enterpriseUserId", "deviceName", "deviceToken", "clientVersionId", "needUsername", "username", "mspEnterpriseId", "isPedmAdmin", "mcEnterpriseId") TRANSMISSIONKEY_FIELD_NUMBER: _ClassVar[int] SESSIONTOKEN_FIELD_NUMBER: _ClassVar[int] USERID_FIELD_NUMBER: _ClassVar[int] @@ -103,6 +105,8 @@ class RouterUserAuth(_message.Message): NEEDUSERNAME_FIELD_NUMBER: _ClassVar[int] USERNAME_FIELD_NUMBER: _ClassVar[int] MSPENTERPRISEID_FIELD_NUMBER: _ClassVar[int] + ISPEDMADMIN_FIELD_NUMBER: _ClassVar[int] + MCENTERPRISEID_FIELD_NUMBER: _ClassVar[int] transmissionKey: bytes sessionToken: bytes userId: int @@ -113,10 +117,12 @@ class RouterUserAuth(_message.Message): needUsername: bool username: str mspEnterpriseId: int - def __init__(self, transmissionKey: _Optional[bytes] = ..., sessionToken: _Optional[bytes] = ..., userId: _Optional[int] = ..., enterpriseUserId: _Optional[int] = ..., deviceName: _Optional[str] = ..., deviceToken: _Optional[bytes] = ..., clientVersionId: _Optional[int] = ..., needUsername: bool = ..., username: _Optional[str] = ..., mspEnterpriseId: _Optional[int] = ...) -> None: ... + isPedmAdmin: bool + mcEnterpriseId: int + def __init__(self, transmissionKey: _Optional[bytes] = ..., sessionToken: _Optional[bytes] = ..., userId: _Optional[int] = ..., enterpriseUserId: _Optional[int] = ..., deviceName: _Optional[str] = ..., deviceToken: _Optional[bytes] = ..., clientVersionId: _Optional[int] = ..., needUsername: _Optional[bool] = ..., username: _Optional[str] = ..., mspEnterpriseId: _Optional[int] = ..., isPedmAdmin: _Optional[bool] = ..., mcEnterpriseId: _Optional[int] = ...) -> None: ... class RouterDeviceAuth(_message.Message): - __slots__ = ["clientId", "clientVersion", "signature", "enterpriseId", "nodeId", "deviceName", "deviceToken", "controllerName", "controllerUid", "ownerUser", "challenge", "ownerId"] + __slots__ = ("clientId", "clientVersion", "signature", "enterpriseId", "nodeId", "deviceName", "deviceToken", "controllerName", "controllerUid", "ownerUser", "challenge", "ownerId", "maxInstanceCount") CLIENTID_FIELD_NUMBER: _ClassVar[int] CLIENTVERSION_FIELD_NUMBER: _ClassVar[int] SIGNATURE_FIELD_NUMBER: _ClassVar[int] @@ -129,6 +135,7 @@ class RouterDeviceAuth(_message.Message): OWNERUSER_FIELD_NUMBER: _ClassVar[int] CHALLENGE_FIELD_NUMBER: _ClassVar[int] OWNERID_FIELD_NUMBER: _ClassVar[int] + MAXINSTANCECOUNT_FIELD_NUMBER: _ClassVar[int] clientId: str clientVersion: str signature: bytes @@ -141,10 +148,11 @@ class RouterDeviceAuth(_message.Message): ownerUser: str challenge: str ownerId: int - def __init__(self, clientId: _Optional[str] = ..., clientVersion: _Optional[str] = ..., signature: _Optional[bytes] = ..., enterpriseId: _Optional[int] = ..., nodeId: _Optional[int] = ..., deviceName: _Optional[str] = ..., deviceToken: _Optional[bytes] = ..., controllerName: _Optional[str] = ..., controllerUid: _Optional[bytes] = ..., ownerUser: _Optional[str] = ..., challenge: _Optional[str] = ..., ownerId: _Optional[int] = ...) -> None: ... + maxInstanceCount: int + def __init__(self, clientId: _Optional[str] = ..., clientVersion: _Optional[str] = ..., signature: _Optional[bytes] = ..., enterpriseId: _Optional[int] = ..., nodeId: _Optional[int] = ..., deviceName: _Optional[str] = ..., deviceToken: _Optional[bytes] = ..., controllerName: _Optional[str] = ..., controllerUid: _Optional[bytes] = ..., ownerUser: _Optional[str] = ..., challenge: _Optional[str] = ..., ownerId: _Optional[int] = ..., maxInstanceCount: _Optional[int] = ...) -> None: ... class RouterRecordRotation(_message.Message): - __slots__ = ["recordUid", "configurationUid", "controllerUid", "resourceUid", "noSchedule"] + __slots__ = ("recordUid", "configurationUid", "controllerUid", "resourceUid", "noSchedule") RECORDUID_FIELD_NUMBER: _ClassVar[int] CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] @@ -155,10 +163,10 @@ class RouterRecordRotation(_message.Message): controllerUid: bytes resourceUid: bytes noSchedule: bool - def __init__(self, recordUid: _Optional[bytes] = ..., configurationUid: _Optional[bytes] = ..., controllerUid: _Optional[bytes] = ..., resourceUid: _Optional[bytes] = ..., noSchedule: bool = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., configurationUid: _Optional[bytes] = ..., controllerUid: _Optional[bytes] = ..., resourceUid: _Optional[bytes] = ..., noSchedule: _Optional[bool] = ...) -> None: ... class RouterRecordRotationsRequest(_message.Message): - __slots__ = ["enterpriseId", "records"] + __slots__ = ("enterpriseId", "records") ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] RECORDS_FIELD_NUMBER: _ClassVar[int] enterpriseId: int @@ -166,15 +174,15 @@ class RouterRecordRotationsRequest(_message.Message): def __init__(self, enterpriseId: _Optional[int] = ..., records: _Optional[_Iterable[bytes]] = ...) -> None: ... class RouterRecordRotationsResponse(_message.Message): - __slots__ = ["rotations", "hasMore"] + __slots__ = ("rotations", "hasMore") ROTATIONS_FIELD_NUMBER: _ClassVar[int] HASMORE_FIELD_NUMBER: _ClassVar[int] rotations: _containers.RepeatedCompositeFieldContainer[RouterRecordRotation] hasMore: bool - def __init__(self, rotations: _Optional[_Iterable[_Union[RouterRecordRotation, _Mapping]]] = ..., hasMore: bool = ...) -> None: ... + def __init__(self, rotations: _Optional[_Iterable[_Union[RouterRecordRotation, _Mapping]]] = ..., hasMore: _Optional[bool] = ...) -> None: ... class RouterRotationInfo(_message.Message): - __slots__ = ["status", "configurationUid", "resourceUid", "nodeId", "controllerUid", "controllerName", "scriptName", "pwdComplexity", "disabled"] + __slots__ = ("status", "configurationUid", "resourceUid", "nodeId", "controllerUid", "controllerName", "scriptName", "pwdComplexity", "disabled") STATUS_FIELD_NUMBER: _ClassVar[int] CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] RESOURCEUID_FIELD_NUMBER: _ClassVar[int] @@ -193,10 +201,10 @@ class RouterRotationInfo(_message.Message): scriptName: str pwdComplexity: str disabled: bool - def __init__(self, status: _Optional[_Union[RouterRotationStatus, str]] = ..., configurationUid: _Optional[bytes] = ..., resourceUid: _Optional[bytes] = ..., nodeId: _Optional[int] = ..., controllerUid: _Optional[bytes] = ..., controllerName: _Optional[str] = ..., scriptName: _Optional[str] = ..., pwdComplexity: _Optional[str] = ..., disabled: bool = ...) -> None: ... + def __init__(self, status: _Optional[_Union[RouterRotationStatus, str]] = ..., configurationUid: _Optional[bytes] = ..., resourceUid: _Optional[bytes] = ..., nodeId: _Optional[int] = ..., controllerUid: _Optional[bytes] = ..., controllerName: _Optional[str] = ..., scriptName: _Optional[str] = ..., pwdComplexity: _Optional[str] = ..., disabled: _Optional[bool] = ...) -> None: ... class RouterRecordRotationRequest(_message.Message): - __slots__ = ["recordUid", "revision", "configurationUid", "resourceUid", "schedule", "enterpriseUserId", "pwdComplexity", "disabled", "remoteAddress", "clientVersionId", "noop"] + __slots__ = ("recordUid", "revision", "configurationUid", "resourceUid", "schedule", "enterpriseUserId", "pwdComplexity", "disabled", "remoteAddress", "clientVersionId", "noop", "saasConfiguration") RECORDUID_FIELD_NUMBER: _ClassVar[int] REVISION_FIELD_NUMBER: _ClassVar[int] CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] @@ -208,6 +216,7 @@ class RouterRecordRotationRequest(_message.Message): REMOTEADDRESS_FIELD_NUMBER: _ClassVar[int] CLIENTVERSIONID_FIELD_NUMBER: _ClassVar[int] NOOP_FIELD_NUMBER: _ClassVar[int] + SAASCONFIGURATION_FIELD_NUMBER: _ClassVar[int] recordUid: bytes revision: int configurationUid: bytes @@ -219,10 +228,11 @@ class RouterRecordRotationRequest(_message.Message): remoteAddress: str clientVersionId: int noop: bool - def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., configurationUid: _Optional[bytes] = ..., resourceUid: _Optional[bytes] = ..., schedule: _Optional[str] = ..., enterpriseUserId: _Optional[int] = ..., pwdComplexity: _Optional[bytes] = ..., disabled: bool = ..., remoteAddress: _Optional[str] = ..., clientVersionId: _Optional[int] = ..., noop: bool = ...) -> None: ... + saasConfiguration: bytes + def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., configurationUid: _Optional[bytes] = ..., resourceUid: _Optional[bytes] = ..., schedule: _Optional[str] = ..., enterpriseUserId: _Optional[int] = ..., pwdComplexity: _Optional[bytes] = ..., disabled: _Optional[bool] = ..., remoteAddress: _Optional[str] = ..., clientVersionId: _Optional[int] = ..., noop: _Optional[bool] = ..., saasConfiguration: _Optional[bytes] = ...) -> None: ... class UserRecordAccessRequest(_message.Message): - __slots__ = ["userId", "recordUid"] + __slots__ = ("userId", "recordUid") USERID_FIELD_NUMBER: _ClassVar[int] RECORDUID_FIELD_NUMBER: _ClassVar[int] userId: int @@ -230,15 +240,27 @@ class UserRecordAccessRequest(_message.Message): def __init__(self, userId: _Optional[int] = ..., recordUid: _Optional[bytes] = ...) -> None: ... class UserRecordAccessResponse(_message.Message): - __slots__ = ["recordUid", "accessLevel"] + __slots__ = ("recordUid", "accessLevel") RECORDUID_FIELD_NUMBER: _ClassVar[int] ACCESSLEVEL_FIELD_NUMBER: _ClassVar[int] recordUid: bytes accessLevel: UserRecordAccessLevel def __init__(self, recordUid: _Optional[bytes] = ..., accessLevel: _Optional[_Union[UserRecordAccessLevel, str]] = ...) -> None: ... +class UserRecordAccessRequests(_message.Message): + __slots__ = ("requests",) + REQUESTS_FIELD_NUMBER: _ClassVar[int] + requests: _containers.RepeatedCompositeFieldContainer[UserRecordAccessRequest] + def __init__(self, requests: _Optional[_Iterable[_Union[UserRecordAccessRequest, _Mapping]]] = ...) -> None: ... + +class UserRecordAccessResponses(_message.Message): + __slots__ = ("responses",) + RESPONSES_FIELD_NUMBER: _ClassVar[int] + responses: _containers.RepeatedCompositeFieldContainer[UserRecordAccessResponse] + def __init__(self, responses: _Optional[_Iterable[_Union[UserRecordAccessResponse, _Mapping]]] = ...) -> None: ... + class RotationSchedule(_message.Message): - __slots__ = ["record_uid", "schedule"] + __slots__ = ("record_uid", "schedule") RECORD_UID_FIELD_NUMBER: _ClassVar[int] SCHEDULE_FIELD_NUMBER: _ClassVar[int] record_uid: bytes @@ -246,7 +268,7 @@ class RotationSchedule(_message.Message): def __init__(self, record_uid: _Optional[bytes] = ..., schedule: _Optional[str] = ...) -> None: ... class ApiCallbackRequest(_message.Message): - __slots__ = ["resourceUid", "schedules", "url", "serviceType"] + __slots__ = ("resourceUid", "schedules", "url", "serviceType") RESOURCEUID_FIELD_NUMBER: _ClassVar[int] SCHEDULES_FIELD_NUMBER: _ClassVar[int] URL_FIELD_NUMBER: _ClassVar[int] @@ -258,7 +280,7 @@ class ApiCallbackRequest(_message.Message): def __init__(self, resourceUid: _Optional[bytes] = ..., schedules: _Optional[_Iterable[_Union[ApiCallbackSchedule, _Mapping]]] = ..., url: _Optional[str] = ..., serviceType: _Optional[_Union[ServiceType, str]] = ...) -> None: ... class ApiCallbackSchedule(_message.Message): - __slots__ = ["schedule", "data"] + __slots__ = ("schedule", "data") SCHEDULE_FIELD_NUMBER: _ClassVar[int] DATA_FIELD_NUMBER: _ClassVar[int] schedule: str @@ -266,7 +288,7 @@ class ApiCallbackSchedule(_message.Message): def __init__(self, schedule: _Optional[str] = ..., data: _Optional[bytes] = ...) -> None: ... class RouterScheduledActions(_message.Message): - __slots__ = ["schedule", "resourceUids"] + __slots__ = ("schedule", "resourceUids") SCHEDULE_FIELD_NUMBER: _ClassVar[int] RESOURCEUIDS_FIELD_NUMBER: _ClassVar[int] schedule: str @@ -274,13 +296,13 @@ class RouterScheduledActions(_message.Message): def __init__(self, schedule: _Optional[str] = ..., resourceUids: _Optional[_Iterable[bytes]] = ...) -> None: ... class RouterRecordsRotationRequest(_message.Message): - __slots__ = ["rotationSchedules"] + __slots__ = ("rotationSchedules",) ROTATIONSCHEDULES_FIELD_NUMBER: _ClassVar[int] rotationSchedules: _containers.RepeatedCompositeFieldContainer[RouterScheduledActions] def __init__(self, rotationSchedules: _Optional[_Iterable[_Union[RouterScheduledActions, _Mapping]]] = ...) -> None: ... class ConnectionParameters(_message.Message): - __slots__ = ["connectionUid", "recordUid", "userId", "controllerUid", "credentialsRecordUid"] + __slots__ = ("connectionUid", "recordUid", "userId", "controllerUid", "credentialsRecordUid") CONNECTIONUID_FIELD_NUMBER: _ClassVar[int] RECORDUID_FIELD_NUMBER: _ClassVar[int] USERID_FIELD_NUMBER: _ClassVar[int] @@ -294,13 +316,13 @@ class ConnectionParameters(_message.Message): def __init__(self, connectionUid: _Optional[bytes] = ..., recordUid: _Optional[bytes] = ..., userId: _Optional[int] = ..., controllerUid: _Optional[bytes] = ..., credentialsRecordUid: _Optional[bytes] = ...) -> None: ... class ValidateConnectionsRequest(_message.Message): - __slots__ = ["connections"] + __slots__ = ("connections",) CONNECTIONS_FIELD_NUMBER: _ClassVar[int] connections: _containers.RepeatedCompositeFieldContainer[ConnectionParameters] def __init__(self, connections: _Optional[_Iterable[_Union[ConnectionParameters, _Mapping]]] = ...) -> None: ... class ConnectionValidationFailure(_message.Message): - __slots__ = ["connectionUid", "errorMessage"] + __slots__ = ("connectionUid", "errorMessage") CONNECTIONUID_FIELD_NUMBER: _ClassVar[int] ERRORMESSAGE_FIELD_NUMBER: _ClassVar[int] connectionUid: bytes @@ -308,19 +330,19 @@ class ConnectionValidationFailure(_message.Message): def __init__(self, connectionUid: _Optional[bytes] = ..., errorMessage: _Optional[str] = ...) -> None: ... class ValidateConnectionsResponse(_message.Message): - __slots__ = ["failedConnections"] + __slots__ = ("failedConnections",) FAILEDCONNECTIONS_FIELD_NUMBER: _ClassVar[int] failedConnections: _containers.RepeatedCompositeFieldContainer[ConnectionValidationFailure] def __init__(self, failedConnections: _Optional[_Iterable[_Union[ConnectionValidationFailure, _Mapping]]] = ...) -> None: ... class GetEnforcementRequest(_message.Message): - __slots__ = ["enterpriseUserId"] + __slots__ = ("enterpriseUserId",) ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] enterpriseUserId: int def __init__(self, enterpriseUserId: _Optional[int] = ...) -> None: ... class EnforcementType(_message.Message): - __slots__ = ["enforcementTypeId", "value"] + __slots__ = ("enforcementTypeId", "value") ENFORCEMENTTYPEID_FIELD_NUMBER: _ClassVar[int] VALUE_FIELD_NUMBER: _ClassVar[int] enforcementTypeId: int @@ -328,17 +350,17 @@ class EnforcementType(_message.Message): def __init__(self, enforcementTypeId: _Optional[int] = ..., value: _Optional[str] = ...) -> None: ... class GetEnforcementResponse(_message.Message): - __slots__ = ["enforcementTypes", "addOnIds", "isInTrial"] + __slots__ = ("enforcementTypes", "addOnIds", "isInTrial") ENFORCEMENTTYPES_FIELD_NUMBER: _ClassVar[int] ADDONIDS_FIELD_NUMBER: _ClassVar[int] ISINTRIAL_FIELD_NUMBER: _ClassVar[int] enforcementTypes: _containers.RepeatedCompositeFieldContainer[EnforcementType] addOnIds: _containers.RepeatedScalarFieldContainer[int] isInTrial: bool - def __init__(self, enforcementTypes: _Optional[_Iterable[_Union[EnforcementType, _Mapping]]] = ..., addOnIds: _Optional[_Iterable[int]] = ..., isInTrial: bool = ...) -> None: ... + def __init__(self, enforcementTypes: _Optional[_Iterable[_Union[EnforcementType, _Mapping]]] = ..., addOnIds: _Optional[_Iterable[int]] = ..., isInTrial: _Optional[bool] = ...) -> None: ... class PEDMTOTPValidateRequest(_message.Message): - __slots__ = ["username", "enterpriseId", "code"] + __slots__ = ("username", "enterpriseId", "code") USERNAME_FIELD_NUMBER: _ClassVar[int] ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] CODE_FIELD_NUMBER: _ClassVar[int] @@ -346,3 +368,75 @@ class PEDMTOTPValidateRequest(_message.Message): enterpriseId: int code: int def __init__(self, username: _Optional[str] = ..., enterpriseId: _Optional[int] = ..., code: _Optional[int] = ...) -> None: ... + +class GetPEDMAdminInfoResponse(_message.Message): + __slots__ = ("isPedmAdmin", "pedmAddonActive") + ISPEDMADMIN_FIELD_NUMBER: _ClassVar[int] + PEDMADDONACTIVE_FIELD_NUMBER: _ClassVar[int] + isPedmAdmin: bool + pedmAddonActive: bool + def __init__(self, isPedmAdmin: _Optional[bool] = ..., pedmAddonActive: _Optional[bool] = ...) -> None: ... + +class PAMNetworkSettings(_message.Message): + __slots__ = ("allowedSettings",) + ALLOWEDSETTINGS_FIELD_NUMBER: _ClassVar[int] + allowedSettings: bytes + def __init__(self, allowedSettings: _Optional[bytes] = ...) -> None: ... + +class PAMNetworkConfigurationRequest(_message.Message): + __slots__ = ("recordUid", "networkSettings", "resources", "rotations") + RECORDUID_FIELD_NUMBER: _ClassVar[int] + NETWORKSETTINGS_FIELD_NUMBER: _ClassVar[int] + RESOURCES_FIELD_NUMBER: _ClassVar[int] + ROTATIONS_FIELD_NUMBER: _ClassVar[int] + recordUid: bytes + networkSettings: PAMNetworkSettings + resources: _containers.RepeatedCompositeFieldContainer[_pam_pb2.PAMResourceConfig] + rotations: _containers.RepeatedCompositeFieldContainer[RouterRecordRotationRequest] + def __init__(self, recordUid: _Optional[bytes] = ..., networkSettings: _Optional[_Union[PAMNetworkSettings, _Mapping]] = ..., resources: _Optional[_Iterable[_Union[_pam_pb2.PAMResourceConfig, _Mapping]]] = ..., rotations: _Optional[_Iterable[_Union[RouterRecordRotationRequest, _Mapping]]] = ...) -> None: ... + +class PAMDiscoveryRulesSetRequest(_message.Message): + __slots__ = ("networkUid", "rules", "rulesKey") + NETWORKUID_FIELD_NUMBER: _ClassVar[int] + RULES_FIELD_NUMBER: _ClassVar[int] + RULESKEY_FIELD_NUMBER: _ClassVar[int] + networkUid: bytes + rules: bytes + rulesKey: bytes + def __init__(self, networkUid: _Optional[bytes] = ..., rules: _Optional[bytes] = ..., rulesKey: _Optional[bytes] = ...) -> None: ... + +class Router2FAValidateRequest(_message.Message): + __slots__ = ("transmissionKey", "sessionToken", "value") + TRANSMISSIONKEY_FIELD_NUMBER: _ClassVar[int] + SESSIONTOKEN_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + transmissionKey: bytes + sessionToken: bytes + value: str + def __init__(self, transmissionKey: _Optional[bytes] = ..., sessionToken: _Optional[bytes] = ..., value: _Optional[str] = ...) -> None: ... + +class Router2FASendPushRequest(_message.Message): + __slots__ = ("transmissionKey", "sessionToken", "pushType") + TRANSMISSIONKEY_FIELD_NUMBER: _ClassVar[int] + SESSIONTOKEN_FIELD_NUMBER: _ClassVar[int] + PUSHTYPE_FIELD_NUMBER: _ClassVar[int] + transmissionKey: bytes + sessionToken: bytes + pushType: _APIRequest_pb2.TwoFactorPushType + def __init__(self, transmissionKey: _Optional[bytes] = ..., sessionToken: _Optional[bytes] = ..., pushType: _Optional[_Union[_APIRequest_pb2.TwoFactorPushType, str]] = ...) -> None: ... + +class Router2FAGetWebAuthnChallengeRequest(_message.Message): + __slots__ = ("transmissionKey", "sessionToken") + TRANSMISSIONKEY_FIELD_NUMBER: _ClassVar[int] + SESSIONTOKEN_FIELD_NUMBER: _ClassVar[int] + transmissionKey: bytes + sessionToken: bytes + def __init__(self, transmissionKey: _Optional[bytes] = ..., sessionToken: _Optional[bytes] = ...) -> None: ... + +class Router2FAGetWebAuthnChallengeResponse(_message.Message): + __slots__ = ("challenge", "capabilities") + CHALLENGE_FIELD_NUMBER: _ClassVar[int] + CAPABILITIES_FIELD_NUMBER: _ClassVar[int] + challenge: str + capabilities: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, challenge: _Optional[str] = ..., capabilities: _Optional[_Iterable[str]] = ...) -> None: ... diff --git a/keepercommander/proto/workflow_pb2.py b/keepercommander/proto/workflow_pb2.py new file mode 100644 index 000000000..b28d14dac --- /dev/null +++ b/keepercommander/proto/workflow_pb2.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: workflow.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() + + +from . import GraphSync_pb2 as GraphSync__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eworkflow.proto\x12\x08Workflow\x1a\x0fGraphSync.proto\"\x82\x01\n\x10WorkflowApprover\x12\x0e\n\x04user\x18\x01 \x01(\tH\x00\x12\x10\n\x06userId\x18\x02 \x01(\x05H\x00\x12\x11\n\x07teamUid\x18\x03 \x01(\x0cH\x00\x12\x12\n\nescalation\x18\x04 \x01(\x08\x12\x19\n\x11\x65scalationAfterMs\x18\x05 \x01(\x03\x42\n\n\x08\x61pprover\"\x9d\x02\n\x12WorkflowParameters\x12)\n\x08resource\x18\x01 \x01(\x0b\x32\x17.GraphSync.GraphSyncRef\x12\x17\n\x0f\x61pprovalsNeeded\x18\x02 \x01(\x05\x12\x16\n\x0e\x63heckoutNeeded\x18\x03 \x01(\x08\x12\x1d\n\x15startAccessOnApproval\x18\x04 \x01(\x08\x12\x15\n\rrequireReason\x18\x05 \x01(\x08\x12\x15\n\rrequireTicket\x18\x06 \x01(\x08\x12\x12\n\nrequireMFA\x18\x07 \x01(\x08\x12\x14\n\x0c\x61\x63\x63\x65ssLength\x18\x08 \x01(\x03\x12\x34\n\x0c\x61llowedTimes\x18\t \x01(\x0b\x32\x1e.Workflow.TemporalAccessFilter\"\x84\x01\n\x0eWorkflowConfig\x12\x30\n\nparameters\x18\x01 \x01(\x0b\x32\x1c.Workflow.WorkflowParameters\x12-\n\tapprovers\x18\x02 \x03(\x0b\x32\x1a.Workflow.WorkflowApprover\x12\x11\n\tcreatedOn\x18\x03 \x01(\x03\"\xd0\x01\n\x0eWorkflowStatus\x12&\n\x05stage\x18\x01 \x01(\x0e\x32\x17.Workflow.WorkflowStage\x12-\n\nconditions\x18\x02 \x03(\x0e\x32\x19.Workflow.AccessCondition\x12.\n\napprovedBy\x18\x03 \x03(\x0b\x32\x1a.Workflow.WorkflowApproval\x12\x11\n\tstartedOn\x18\x04 \x01(\x03\x12\x11\n\texpiresOn\x18\x05 \x01(\x03\x12\x11\n\tescalated\x18\x06 \x01(\x08\"\xbd\x01\n\x0fWorkflowProcess\x12\x0f\n\x07\x66lowUid\x18\x01 \x01(\x0c\x12\x0e\n\x06userId\x18\x02 \x01(\x03\x12)\n\x08resource\x18\x03 \x01(\x0b\x32\x17.GraphSync.GraphSyncRef\x12\x11\n\tstartedOn\x18\x04 \x01(\x03\x12\x11\n\texpiresOn\x18\x05 \x01(\x03\x12\x0e\n\x06reason\x18\x06 \x01(\x0c\x12\x13\n\x0bmfaVerified\x18\x07 \x01(\x08\x12\x13\n\x0b\x65xternalRef\x18\x08 \x01(\x0c\"U\n\x10WorkflowApproval\x12\x0e\n\x06userId\x18\x01 \x01(\x03\x12\x0c\n\x04user\x18\x02 \x01(\t\x12\x0f\n\x07\x66lowUid\x18\x03 \x01(\x0c\x12\x12\n\napprovedOn\x18\x04 \x01(\x03\"\xcb\x01\n\x0fWorkflowContext\x12\x30\n\x0eworkflowConfig\x18\x01 \x01(\x0b\x32\x18.Workflow.WorkflowConfig\x12+\n\x08workflow\x18\x02 \x01(\x0b\x32\x19.Workflow.WorkflowProcess\x12-\n\tapprovals\x18\x03 \x03(\x0b\x32\x1a.Workflow.WorkflowApproval\x12*\n\x07\x62locker\x18\x04 \x01(\x0b\x32\x19.Workflow.WorkflowProcess\"u\n\rWorkflowState\x12\x0f\n\x07\x66lowUid\x18\x01 \x01(\x0c\x12)\n\x08resource\x18\x02 \x01(\x0b\x32\x17.GraphSync.GraphSyncRef\x12(\n\x06status\x18\x03 \x01(\x0b\x32\x18.Workflow.WorkflowStatus\"b\n\x15WorkflowAccessRequest\x12)\n\x08resource\x18\x01 \x01(\x0b\x32\x17.GraphSync.GraphSyncRef\x12\x0e\n\x06reason\x18\x02 \x01(\x0c\x12\x0e\n\x06ticket\x18\x03 \x01(\x0c\"O\n\x18WorkflowApprovalOrDenial\x12\x0f\n\x07\x66lowUid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64\x65ny\x18\x02 \x01(\x08\x12\x14\n\x0c\x64\x65nialReason\x18\x03 \x01(\t\"=\n\x0fUserAccessState\x12*\n\tworkflows\x18\x01 \x03(\x0b\x32\x17.Workflow.WorkflowState\"@\n\x10\x41pprovalRequests\x12,\n\tworkflows\x18\x01 \x03(\x0b\x32\x19.Workflow.WorkflowProcess\"4\n\x0eTimeOfDayRange\x12\x11\n\tstartTime\x18\x01 \x01(\x05\x12\x0f\n\x07\x65ndTime\x18\x02 \x01(\x05\"\xdd\x01\n\x12\x41pprovalQueueEntry\x12(\n\x07\x66lowRef\x18\x01 \x01(\x0b\x32\x17.GraphSync.GraphSyncRef\x12,\n\x0b\x61pproverRef\x18\x02 \x01(\x0b\x32\x17.GraphSync.GraphSyncRef\x12)\n\x04kind\x18\x03 \x01(\x0e\x32\x1b.Workflow.ApprovalQueueKind\x12\x12\n\nnotifyAtMs\x18\x04 \x01(\x03\x12\x1c\n\x0frequesterUserId\x18\x05 \x01(\x03H\x00\x88\x01\x01\x42\x12\n\x10_requesterUserId\"\x80\x01\n\x14TemporalAccessFilter\x12,\n\ntimeRanges\x18\x01 \x03(\x0b\x32\x18.Workflow.TimeOfDayRange\x12(\n\x0b\x61llowedDays\x18\x02 \x03(\x0e\x32\x13.Workflow.DayOfWeek\x12\x10\n\x08timeZone\x18\x03 \x01(\t\"#\n\x0f\x41uthorizedUsers\x12\x10\n\x08username\x18\x01 \x03(\t*[\n\rWorkflowStage\x12\x15\n\x11WS_READY_TO_START\x10\x00\x12\x0e\n\nWS_STARTED\x10\x01\x12\x13\n\x0fWS_NEEDS_ACTION\x10\x02\x12\x0e\n\nWS_WAITING\x10\x03*i\n\x0f\x41\x63\x63\x65ssCondition\x12\x0f\n\x0b\x41\x43_APPROVAL\x10\x00\x12\x0e\n\nAC_CHECKIN\x10\x01\x12\n\n\x06\x41\x43_MFA\x10\x02\x12\x0b\n\x07\x41\x43_TIME\x10\x03\x12\r\n\tAC_REASON\x10\x04\x12\r\n\tAC_TICKET\x10\x05*\x84\x01\n\tDayOfWeek\x12\x1b\n\x17\x44\x41Y_OF_WEEK_UNSPECIFIED\x10\x00\x12\n\n\x06MONDAY\x10\x01\x12\x0b\n\x07TUESDAY\x10\x02\x12\r\n\tWEDNESDAY\x10\x03\x12\x0c\n\x08THURSDAY\x10\x04\x12\n\n\x06\x46RIDAY\x10\x05\x12\x0c\n\x08SATURDAY\x10\x06\x12\n\n\x06SUNDAY\x10\x07*9\n\x11\x41pprovalQueueKind\x12\x10\n\x0c\x41QK_APPROVAL\x10\x00\x12\x12\n\x0e\x41QK_ESCALATION\x10\x01\x42$\n\x18\x63om.keepersecurity.protoB\x08Workflowb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'workflow_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\030com.keepersecurity.protoB\010Workflow' + _globals['_WORKFLOWSTAGE']._serialized_start=2172 + _globals['_WORKFLOWSTAGE']._serialized_end=2263 + _globals['_ACCESSCONDITION']._serialized_start=2265 + _globals['_ACCESSCONDITION']._serialized_end=2370 + _globals['_DAYOFWEEK']._serialized_start=2373 + _globals['_DAYOFWEEK']._serialized_end=2505 + _globals['_APPROVALQUEUEKIND']._serialized_start=2507 + _globals['_APPROVALQUEUEKIND']._serialized_end=2564 + _globals['_WORKFLOWAPPROVER']._serialized_start=46 + _globals['_WORKFLOWAPPROVER']._serialized_end=176 + _globals['_WORKFLOWPARAMETERS']._serialized_start=179 + _globals['_WORKFLOWPARAMETERS']._serialized_end=464 + _globals['_WORKFLOWCONFIG']._serialized_start=467 + _globals['_WORKFLOWCONFIG']._serialized_end=599 + _globals['_WORKFLOWSTATUS']._serialized_start=602 + _globals['_WORKFLOWSTATUS']._serialized_end=810 + _globals['_WORKFLOWPROCESS']._serialized_start=813 + _globals['_WORKFLOWPROCESS']._serialized_end=1002 + _globals['_WORKFLOWAPPROVAL']._serialized_start=1004 + _globals['_WORKFLOWAPPROVAL']._serialized_end=1089 + _globals['_WORKFLOWCONTEXT']._serialized_start=1092 + _globals['_WORKFLOWCONTEXT']._serialized_end=1295 + _globals['_WORKFLOWSTATE']._serialized_start=1297 + _globals['_WORKFLOWSTATE']._serialized_end=1414 + _globals['_WORKFLOWACCESSREQUEST']._serialized_start=1416 + _globals['_WORKFLOWACCESSREQUEST']._serialized_end=1514 + _globals['_WORKFLOWAPPROVALORDENIAL']._serialized_start=1516 + _globals['_WORKFLOWAPPROVALORDENIAL']._serialized_end=1595 + _globals['_USERACCESSSTATE']._serialized_start=1597 + _globals['_USERACCESSSTATE']._serialized_end=1658 + _globals['_APPROVALREQUESTS']._serialized_start=1660 + _globals['_APPROVALREQUESTS']._serialized_end=1724 + _globals['_TIMEOFDAYRANGE']._serialized_start=1726 + _globals['_TIMEOFDAYRANGE']._serialized_end=1778 + _globals['_APPROVALQUEUEENTRY']._serialized_start=1781 + _globals['_APPROVALQUEUEENTRY']._serialized_end=2002 + _globals['_TEMPORALACCESSFILTER']._serialized_start=2005 + _globals['_TEMPORALACCESSFILTER']._serialized_end=2133 + _globals['_AUTHORIZEDUSERS']._serialized_start=2135 + _globals['_AUTHORIZEDUSERS']._serialized_end=2170 +# @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/workflow_pb2.pyi b/keepercommander/proto/workflow_pb2.pyi new file mode 100644 index 000000000..2e186538d --- /dev/null +++ b/keepercommander/proto/workflow_pb2.pyi @@ -0,0 +1,247 @@ +import GraphSync_pb2 as _GraphSync_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class WorkflowStage(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + WS_READY_TO_START: _ClassVar[WorkflowStage] + WS_STARTED: _ClassVar[WorkflowStage] + WS_NEEDS_ACTION: _ClassVar[WorkflowStage] + WS_WAITING: _ClassVar[WorkflowStage] + +class AccessCondition(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + AC_APPROVAL: _ClassVar[AccessCondition] + AC_CHECKIN: _ClassVar[AccessCondition] + AC_MFA: _ClassVar[AccessCondition] + AC_TIME: _ClassVar[AccessCondition] + AC_REASON: _ClassVar[AccessCondition] + AC_TICKET: _ClassVar[AccessCondition] + +class DayOfWeek(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DAY_OF_WEEK_UNSPECIFIED: _ClassVar[DayOfWeek] + MONDAY: _ClassVar[DayOfWeek] + TUESDAY: _ClassVar[DayOfWeek] + WEDNESDAY: _ClassVar[DayOfWeek] + THURSDAY: _ClassVar[DayOfWeek] + FRIDAY: _ClassVar[DayOfWeek] + SATURDAY: _ClassVar[DayOfWeek] + SUNDAY: _ClassVar[DayOfWeek] + +class ApprovalQueueKind(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + AQK_APPROVAL: _ClassVar[ApprovalQueueKind] + AQK_ESCALATION: _ClassVar[ApprovalQueueKind] +WS_READY_TO_START: WorkflowStage +WS_STARTED: WorkflowStage +WS_NEEDS_ACTION: WorkflowStage +WS_WAITING: WorkflowStage +AC_APPROVAL: AccessCondition +AC_CHECKIN: AccessCondition +AC_MFA: AccessCondition +AC_TIME: AccessCondition +AC_REASON: AccessCondition +AC_TICKET: AccessCondition +DAY_OF_WEEK_UNSPECIFIED: DayOfWeek +MONDAY: DayOfWeek +TUESDAY: DayOfWeek +WEDNESDAY: DayOfWeek +THURSDAY: DayOfWeek +FRIDAY: DayOfWeek +SATURDAY: DayOfWeek +SUNDAY: DayOfWeek +AQK_APPROVAL: ApprovalQueueKind +AQK_ESCALATION: ApprovalQueueKind + +class WorkflowApprover(_message.Message): + __slots__ = ("user", "userId", "teamUid", "escalation", "escalationAfterMs") + USER_FIELD_NUMBER: _ClassVar[int] + USERID_FIELD_NUMBER: _ClassVar[int] + TEAMUID_FIELD_NUMBER: _ClassVar[int] + ESCALATION_FIELD_NUMBER: _ClassVar[int] + ESCALATIONAFTERMS_FIELD_NUMBER: _ClassVar[int] + user: str + userId: int + teamUid: bytes + escalation: bool + escalationAfterMs: int + def __init__(self, user: _Optional[str] = ..., userId: _Optional[int] = ..., teamUid: _Optional[bytes] = ..., escalation: _Optional[bool] = ..., escalationAfterMs: _Optional[int] = ...) -> None: ... + +class WorkflowParameters(_message.Message): + __slots__ = ("resource", "approvalsNeeded", "checkoutNeeded", "startAccessOnApproval", "requireReason", "requireTicket", "requireMFA", "accessLength", "allowedTimes") + RESOURCE_FIELD_NUMBER: _ClassVar[int] + APPROVALSNEEDED_FIELD_NUMBER: _ClassVar[int] + CHECKOUTNEEDED_FIELD_NUMBER: _ClassVar[int] + STARTACCESSONAPPROVAL_FIELD_NUMBER: _ClassVar[int] + REQUIREREASON_FIELD_NUMBER: _ClassVar[int] + REQUIRETICKET_FIELD_NUMBER: _ClassVar[int] + REQUIREMFA_FIELD_NUMBER: _ClassVar[int] + ACCESSLENGTH_FIELD_NUMBER: _ClassVar[int] + ALLOWEDTIMES_FIELD_NUMBER: _ClassVar[int] + resource: _GraphSync_pb2.GraphSyncRef + approvalsNeeded: int + checkoutNeeded: bool + startAccessOnApproval: bool + requireReason: bool + requireTicket: bool + requireMFA: bool + accessLength: int + allowedTimes: TemporalAccessFilter + def __init__(self, resource: _Optional[_Union[_GraphSync_pb2.GraphSyncRef, _Mapping]] = ..., approvalsNeeded: _Optional[int] = ..., checkoutNeeded: _Optional[bool] = ..., startAccessOnApproval: _Optional[bool] = ..., requireReason: _Optional[bool] = ..., requireTicket: _Optional[bool] = ..., requireMFA: _Optional[bool] = ..., accessLength: _Optional[int] = ..., allowedTimes: _Optional[_Union[TemporalAccessFilter, _Mapping]] = ...) -> None: ... + +class WorkflowConfig(_message.Message): + __slots__ = ("parameters", "approvers", "createdOn") + PARAMETERS_FIELD_NUMBER: _ClassVar[int] + APPROVERS_FIELD_NUMBER: _ClassVar[int] + CREATEDON_FIELD_NUMBER: _ClassVar[int] + parameters: WorkflowParameters + approvers: _containers.RepeatedCompositeFieldContainer[WorkflowApprover] + createdOn: int + def __init__(self, parameters: _Optional[_Union[WorkflowParameters, _Mapping]] = ..., approvers: _Optional[_Iterable[_Union[WorkflowApprover, _Mapping]]] = ..., createdOn: _Optional[int] = ...) -> None: ... + +class WorkflowStatus(_message.Message): + __slots__ = ("stage", "conditions", "approvedBy", "startedOn", "expiresOn", "escalated") + STAGE_FIELD_NUMBER: _ClassVar[int] + CONDITIONS_FIELD_NUMBER: _ClassVar[int] + APPROVEDBY_FIELD_NUMBER: _ClassVar[int] + STARTEDON_FIELD_NUMBER: _ClassVar[int] + EXPIRESON_FIELD_NUMBER: _ClassVar[int] + ESCALATED_FIELD_NUMBER: _ClassVar[int] + stage: WorkflowStage + conditions: _containers.RepeatedScalarFieldContainer[AccessCondition] + approvedBy: _containers.RepeatedCompositeFieldContainer[WorkflowApproval] + startedOn: int + expiresOn: int + escalated: bool + def __init__(self, stage: _Optional[_Union[WorkflowStage, str]] = ..., conditions: _Optional[_Iterable[_Union[AccessCondition, str]]] = ..., approvedBy: _Optional[_Iterable[_Union[WorkflowApproval, _Mapping]]] = ..., startedOn: _Optional[int] = ..., expiresOn: _Optional[int] = ..., escalated: _Optional[bool] = ...) -> None: ... + +class WorkflowProcess(_message.Message): + __slots__ = ("flowUid", "userId", "resource", "startedOn", "expiresOn", "reason", "mfaVerified", "externalRef") + FLOWUID_FIELD_NUMBER: _ClassVar[int] + USERID_FIELD_NUMBER: _ClassVar[int] + RESOURCE_FIELD_NUMBER: _ClassVar[int] + STARTEDON_FIELD_NUMBER: _ClassVar[int] + EXPIRESON_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + MFAVERIFIED_FIELD_NUMBER: _ClassVar[int] + EXTERNALREF_FIELD_NUMBER: _ClassVar[int] + flowUid: bytes + userId: int + resource: _GraphSync_pb2.GraphSyncRef + startedOn: int + expiresOn: int + reason: bytes + mfaVerified: bool + externalRef: bytes + def __init__(self, flowUid: _Optional[bytes] = ..., userId: _Optional[int] = ..., resource: _Optional[_Union[_GraphSync_pb2.GraphSyncRef, _Mapping]] = ..., startedOn: _Optional[int] = ..., expiresOn: _Optional[int] = ..., reason: _Optional[bytes] = ..., mfaVerified: _Optional[bool] = ..., externalRef: _Optional[bytes] = ...) -> None: ... + +class WorkflowApproval(_message.Message): + __slots__ = ("userId", "user", "flowUid", "approvedOn") + USERID_FIELD_NUMBER: _ClassVar[int] + USER_FIELD_NUMBER: _ClassVar[int] + FLOWUID_FIELD_NUMBER: _ClassVar[int] + APPROVEDON_FIELD_NUMBER: _ClassVar[int] + userId: int + user: str + flowUid: bytes + approvedOn: int + def __init__(self, userId: _Optional[int] = ..., user: _Optional[str] = ..., flowUid: _Optional[bytes] = ..., approvedOn: _Optional[int] = ...) -> None: ... + +class WorkflowContext(_message.Message): + __slots__ = ("workflowConfig", "workflow", "approvals", "blocker") + WORKFLOWCONFIG_FIELD_NUMBER: _ClassVar[int] + WORKFLOW_FIELD_NUMBER: _ClassVar[int] + APPROVALS_FIELD_NUMBER: _ClassVar[int] + BLOCKER_FIELD_NUMBER: _ClassVar[int] + workflowConfig: WorkflowConfig + workflow: WorkflowProcess + approvals: _containers.RepeatedCompositeFieldContainer[WorkflowApproval] + blocker: WorkflowProcess + def __init__(self, workflowConfig: _Optional[_Union[WorkflowConfig, _Mapping]] = ..., workflow: _Optional[_Union[WorkflowProcess, _Mapping]] = ..., approvals: _Optional[_Iterable[_Union[WorkflowApproval, _Mapping]]] = ..., blocker: _Optional[_Union[WorkflowProcess, _Mapping]] = ...) -> None: ... + +class WorkflowState(_message.Message): + __slots__ = ("flowUid", "resource", "status") + FLOWUID_FIELD_NUMBER: _ClassVar[int] + RESOURCE_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + flowUid: bytes + resource: _GraphSync_pb2.GraphSyncRef + status: WorkflowStatus + def __init__(self, flowUid: _Optional[bytes] = ..., resource: _Optional[_Union[_GraphSync_pb2.GraphSyncRef, _Mapping]] = ..., status: _Optional[_Union[WorkflowStatus, _Mapping]] = ...) -> None: ... + +class WorkflowAccessRequest(_message.Message): + __slots__ = ("resource", "reason", "ticket") + RESOURCE_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + TICKET_FIELD_NUMBER: _ClassVar[int] + resource: _GraphSync_pb2.GraphSyncRef + reason: bytes + ticket: bytes + def __init__(self, resource: _Optional[_Union[_GraphSync_pb2.GraphSyncRef, _Mapping]] = ..., reason: _Optional[bytes] = ..., ticket: _Optional[bytes] = ...) -> None: ... + +class WorkflowApprovalOrDenial(_message.Message): + __slots__ = ("flowUid", "deny", "denialReason") + FLOWUID_FIELD_NUMBER: _ClassVar[int] + DENY_FIELD_NUMBER: _ClassVar[int] + DENIALREASON_FIELD_NUMBER: _ClassVar[int] + flowUid: bytes + deny: bool + denialReason: str + def __init__(self, flowUid: _Optional[bytes] = ..., deny: _Optional[bool] = ..., denialReason: _Optional[str] = ...) -> None: ... + +class UserAccessState(_message.Message): + __slots__ = ("workflows",) + WORKFLOWS_FIELD_NUMBER: _ClassVar[int] + workflows: _containers.RepeatedCompositeFieldContainer[WorkflowState] + def __init__(self, workflows: _Optional[_Iterable[_Union[WorkflowState, _Mapping]]] = ...) -> None: ... + +class ApprovalRequests(_message.Message): + __slots__ = ("workflows",) + WORKFLOWS_FIELD_NUMBER: _ClassVar[int] + workflows: _containers.RepeatedCompositeFieldContainer[WorkflowProcess] + def __init__(self, workflows: _Optional[_Iterable[_Union[WorkflowProcess, _Mapping]]] = ...) -> None: ... + +class TimeOfDayRange(_message.Message): + __slots__ = ("startTime", "endTime") + STARTTIME_FIELD_NUMBER: _ClassVar[int] + ENDTIME_FIELD_NUMBER: _ClassVar[int] + startTime: int + endTime: int + def __init__(self, startTime: _Optional[int] = ..., endTime: _Optional[int] = ...) -> None: ... + +class ApprovalQueueEntry(_message.Message): + __slots__ = ("flowRef", "approverRef", "kind", "notifyAtMs", "requesterUserId") + FLOWREF_FIELD_NUMBER: _ClassVar[int] + APPROVERREF_FIELD_NUMBER: _ClassVar[int] + KIND_FIELD_NUMBER: _ClassVar[int] + NOTIFYATMS_FIELD_NUMBER: _ClassVar[int] + REQUESTERUSERID_FIELD_NUMBER: _ClassVar[int] + flowRef: _GraphSync_pb2.GraphSyncRef + approverRef: _GraphSync_pb2.GraphSyncRef + kind: ApprovalQueueKind + notifyAtMs: int + requesterUserId: int + def __init__(self, flowRef: _Optional[_Union[_GraphSync_pb2.GraphSyncRef, _Mapping]] = ..., approverRef: _Optional[_Union[_GraphSync_pb2.GraphSyncRef, _Mapping]] = ..., kind: _Optional[_Union[ApprovalQueueKind, str]] = ..., notifyAtMs: _Optional[int] = ..., requesterUserId: _Optional[int] = ...) -> None: ... + +class TemporalAccessFilter(_message.Message): + __slots__ = ("timeRanges", "allowedDays", "timeZone") + TIMERANGES_FIELD_NUMBER: _ClassVar[int] + ALLOWEDDAYS_FIELD_NUMBER: _ClassVar[int] + TIMEZONE_FIELD_NUMBER: _ClassVar[int] + timeRanges: _containers.RepeatedCompositeFieldContainer[TimeOfDayRange] + allowedDays: _containers.RepeatedScalarFieldContainer[DayOfWeek] + timeZone: str + def __init__(self, timeRanges: _Optional[_Iterable[_Union[TimeOfDayRange, _Mapping]]] = ..., allowedDays: _Optional[_Iterable[_Union[DayOfWeek, str]]] = ..., timeZone: _Optional[str] = ...) -> None: ... + +class AuthorizedUsers(_message.Message): + __slots__ = ("username",) + USERNAME_FIELD_NUMBER: _ClassVar[int] + username: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, username: _Optional[_Iterable[str]] = ...) -> None: ...