From 636d01da9c149ff29d17cb54dc08a901a4ffd5b5 Mon Sep 17 00:00:00 2001 From: idimov-keeper <78815270+idimov-keeper@users.noreply.github.com> Date: Wed, 27 May 2026 13:32:47 -0500 Subject: [PATCH 01/23] Add PAM RBI file uploads/downloads and `pam connection edit --scrollback` (#2093) --- .../tunnel/port_forward/tunnel_helpers.py | 26 ++ .../commands/tunnel_and_connections.py | 114 ++++++- .../pam/test_get_config_uid_via_pam_link.py | 70 ++++ .../test_pam_connection_edit_scrollback.py | 309 ++++++++++++++++++ unit-tests/pam/test_pam_rbi_edit.py | 102 +++++- 5 files changed, 619 insertions(+), 2 deletions(-) create mode 100644 unit-tests/pam/test_get_config_uid_via_pam_link.py create mode 100644 unit-tests/pam/test_pam_connection_edit_scrollback.py diff --git a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py index 82a494ff9..311b004e3 100644 --- a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py +++ b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py @@ -834,6 +834,32 @@ def get_config_uid(params, encrypted_session_token, encrypted_transmission_key, return None +def get_config_uid_via_pam_link(params, record_uid): + """Resolve a resource record's PAM Config UID via KRouter's PAM_LINK graph. + + Returns the config_uid that owns the resource. Used as a fallback when + ``get_config_uid`` (legacy ``/api/user/get_leafs`` with graphId=0) returns + nothing for resources whose link lives only in the new PAM_LINK stream. + + Returns the config_uid as a base64-url-safe string, or empty string on + failure / no link. + """ + try: + from ....keeper_dag.proto import GraphSync_pb2 as gs_pb2 + from ...pam.router_helper import _post_request_to_router + record_uid_bytes = url_safe_str_to_bytes(record_uid) + rq = gs_pb2.GraphSyncLeafsQuery(vertices=[record_uid_bytes]) + rs = _post_request_to_router(params, 'graph-sync/pam/get_leafs', + rq_proto=rq, rs_type=gs_pb2.GraphSyncRefsResult) + if rs and rs.refs: + for ref in rs.refs: + if ref.value: + return utils.base64_url_encode(ref.value) + except Exception as e: + logging.debug('get_config_uid_via_pam_link: lookup failed for %s: %s', record_uid, e) + return '' + + def get_keeper_tokens(params): transmission_key = generate_random_bytes(32) server_public_key = rest_api.SERVER_PUBLIC_KEYS[params.rest_context.server_key_id] diff --git a/keepercommander/commands/tunnel_and_connections.py b/keepercommander/commands/tunnel_and_connections.py index 77ef48fff..050d7b962 100644 --- a/keepercommander/commands/tunnel_and_connections.py +++ b/keepercommander/commands/tunnel_and_connections.py @@ -29,7 +29,8 @@ from .base import Command, GroupCommand, dump_report_data, RecordMixin from .tunnel.port_forward.TunnelGraph import TunnelDAG -from .tunnel.port_forward.tunnel_helpers import find_open_port, get_config_uid, get_keeper_tokens, \ +from .tunnel.port_forward.tunnel_helpers import find_open_port, get_config_uid, get_config_uid_via_pam_link, \ + get_keeper_tokens, \ get_or_create_tube_registry, get_gateway_uid_from_record, resolve_record, resolve_pam_config, resolve_folder, \ remove_field, start_rust_tunnel, get_tunnel_session, unregister_tunnel_session, CloseConnectionReasons, \ wait_for_tunnel_connection, create_rust_webrtc_settings, escalate_close, \ @@ -2624,6 +2625,10 @@ class PAMConnectionEditCommand(Command): 'the port from the record will be used.') parser.add_argument('--key-events', '-k', dest='key_events', choices=choices, help='Toggle Key Events settings') + parser.add_argument('--scrollback', '-sb', required=False, dest='scrollback', action='store', + help='Maximum Scrollback Size (terminal history). Integer to set, ' + 'empty string to remove. Supported only for pamDatabase (any DB protocol) and ' + 'pamMachine/pamDirectory (ssh/telnet/kubernetes).') parser.add_argument('--rotate-on-termination', required=False, dest='rotate_on_termination', choices=['on', 'off'], help='Rotate launch credentials when the PAM session ends (DAG resource meta)') @@ -2672,6 +2677,51 @@ def execute(self, params, **kwargs): f"pamRemoteBrowser, pamNetworkConfiguration pamAwsConfiguration, and " f"pamAzureConfiguration records{bcolors.ENDC}") + # --scrollback: validate record type + effective protocol before any mutation + scrollback_arg = kwargs.get('scrollback', None) + scrollback_clear = False + scrollback_value = None # parsed int, or None to skip apply + if scrollback_arg is not None: + db_scrollback_protocols = {'mysql', 'postgresql', 'sql-server', 'mariadb', 'oracle', + 'mongodb', 'redis', 'elasticsearch', 'clickhouse', 'dynamodb'} + terminal_scrollback_protocols = {'ssh', 'telnet', 'kubernetes'} + if record_type == 'pamDatabase': + allowed_protocols = db_scrollback_protocols + elif record_type in ('pamMachine', 'pamDirectory'): + allowed_protocols = terminal_scrollback_protocols + else: + raise CommandError('pam connection edit', + f'{bcolors.FAIL}--scrollback is only supported for pamDatabase, pamMachine, and pamDirectory ' + f'records. Record "{record_uid}" is of type "{record_type}".{bcolors.ENDC}') + + existing_ps = record.get_typed_field('pamSettings') + existing_protocol = '' + if existing_ps and existing_ps.value and isinstance(existing_ps.value[0], dict): + existing_protocol = existing_ps.value[0].get('connection', {}).get('protocol') or '' + new_protocol_arg = kwargs.get('protocol', None) + if kwargs.get('connections') == 'on' and new_protocol_arg is not None: + effective_protocol = new_protocol_arg # may be '' to clear + else: + effective_protocol = existing_protocol + if effective_protocol not in allowed_protocols: + raise CommandError('pam connection edit', + f'{bcolors.FAIL}--scrollback is not supported for protocol "{effective_protocol or "(unset)"}" ' + f'on {record_type} records. Allowed protocols: {", ".join(sorted(allowed_protocols))}.{bcolors.ENDC}') + + if scrollback_arg == '': + scrollback_clear = True + else: + try: + scrollback_value = int(scrollback_arg) + except (ValueError, TypeError): + raise CommandError('pam connection edit', + f'{bcolors.FAIL}--scrollback must be a non-negative integer or empty string. ' + f'Got: "{scrollback_arg}".{bcolors.ENDC}') + if scrollback_value < 0: + raise CommandError('pam connection edit', + f'{bcolors.FAIL}--scrollback must be a non-negative integer or empty string. ' + f'Got: "{scrollback_arg}".{bcolors.ENDC}') + encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) if record_type in "pamNetworkConfiguration pamAwsConfiguration pamAzureConfiguration".split(): tdag = TunnelDAG(params, encrypted_session_token, encrypted_transmission_key, record_uid, is_config=True, @@ -2758,6 +2808,24 @@ def execute(self, params, **kwargs): else: logging.debug(f'Unexpected value for --key-events {key_events} (ignored)') + # --scrollback: apply (validated above; record_type + effective protocol already checked) + if scrollback_clear or scrollback_value is not None: + psv = pam_settings.value[0] if pam_settings and pam_settings.value else {} + vcon = psv.get('connection', {}) if isinstance(psv, dict) else {} + current_sb = vcon.get('scrollback') if isinstance(vcon, dict) else None + if scrollback_clear: + if current_sb is not None: + pam_settings.value[0]["connection"].pop('scrollback', None) + dirty = True + else: + logging.debug(f'scrollback is already unset on record={record_uid}') + else: + if current_sb != scrollback_value: + pam_settings.value[0]["connection"]["scrollback"] = scrollback_value + dirty = True + else: + logging.debug(f'scrollback is already {scrollback_value} on record={record_uid}') + if dirty: record_management.update_record(params, record) api.sync_down(params) @@ -2768,8 +2836,36 @@ def execute(self, params, **kwargs): f"Please make sure you have edit rights to record {record_uid} {bcolors.ENDC}") dirty = False + # If only record-level args were passed (e.g. --scrollback, --key-events, --protocol + # alone), the record update above is complete — skip the DAG/config lookup, which + # would otherwise raise a misleading "No PAM Configuration UID set" error when the + # resource isn't linked to a config yet. Mirrors the PAMRbiEditCommand pattern. + dag_affecting = (kwargs.get('config') or kwargs.get('admin') or kwargs.get('launch_user') + or kwargs.get('clear_launch_user') or kwargs.get('connections') + or kwargs.get('recording') or kwargs.get('typescriptrecording') + or kwargs.get('rotate_on_termination')) + if not dag_affecting: + return + existing_config_uid = get_config_uid(params, encrypted_session_token, encrypted_transmission_key, record_uid) + # When the user did not pass --configuration, fall back to + # first the legacy /api/user/get_leafs if that also returned nothing, + # then the new /api/user/graph-sync/pam/get_leafs (PAM_LINK stream) + # Without this, `tdag` would be built with config_uid=None, has_graph=False, + # and the misleading "No PAM Configuration UID set" error would fire + # even though the link is resolvable. + if not config_uid: + if existing_config_uid: + config_uid = existing_config_uid + logging.debug('pam connection edit: using DAG-resolved config_uid: %s', config_uid) + else: + found = get_config_uid_via_pam_link(params, record_uid) + if found: + logging.debug('pam connection edit: resolved config_uid via graph-sync/pam fallback: %s', found) + existing_config_uid = found + config_uid = found + tdag = TunnelDAG(params, encrypted_session_token, encrypted_transmission_key, config_uid, transmission_key=transmission_key) old_dag = TunnelDAG(params, encrypted_session_token, encrypted_transmission_key, existing_config_uid, @@ -3149,6 +3245,10 @@ class PAMRbiEditCommand(Command): help='Allow navigation via direct URL manipulation (on/off/default)') parser.add_argument('--ignore-server-cert', '-isc', dest='ignore_server_cert', choices=choices, help='Ignore server certificate errors (on/off/default)') + parser.add_argument('--allow-file-uploads', '-fu', dest='allow_file_uploads', choices=choices, + help='Allow file uploads in RBI sessions (on/off/default)') + parser.add_argument('--allow-file-downloads', '-fd', dest='allow_file_downloads', choices=choices, + help='Allow file downloads in RBI sessions (on/off/default)') # URL Filtering parser.add_argument('--allowed-urls', '-au', dest='allowed_urls', action='append', @@ -3197,6 +3297,8 @@ def execute(self, params, **kwargs): # New RBI settings (Phase 1 - KC-1034) allow_url_navigation = kwargs.get('allow_url_navigation') # on/off/default/None ignore_server_cert = kwargs.get('ignore_server_cert') # on/off/default/None + allow_file_uploads = kwargs.get('allow_file_uploads') # on/off/default/None + allow_file_downloads = kwargs.get('allow_file_downloads') # on/off/default/None allowed_urls = kwargs.get('allowed_urls') # list or None allowed_resource_urls = kwargs.get('allowed_resource_urls') # list or None autofill_targets = kwargs.get('autofill_targets') # list or None @@ -3214,6 +3316,8 @@ def execute(self, params, **kwargs): has_new_settings = any([ allow_url_navigation is not None, ignore_server_cert is not None, + allow_file_uploads is not None, + allow_file_downloads is not None, allowed_urls is not None, allowed_resource_urls is not None, autofill_targets is not None, @@ -3389,6 +3493,14 @@ def update_connection_int(field_name, value): if ignore_server_cert: update_connection_toggle('ignoreInitialSslCert', ignore_server_cert) + # Browser Settings - allowFileUploads (on/off/default) + if allow_file_uploads: + update_connection_toggle('allowFileUploads', allow_file_uploads) + + # Browser Settings - allowFileDownloads (on/off/default) + if allow_file_downloads: + update_connection_toggle('allowFileDownloads', allow_file_downloads) + # URL Filtering - allowedUrlPatterns (multi-value, joined with newlines) if allowed_urls is not None: update_connection_string('allowedUrlPatterns', allowed_urls) diff --git a/unit-tests/pam/test_get_config_uid_via_pam_link.py b/unit-tests/pam/test_get_config_uid_via_pam_link.py new file mode 100644 index 000000000..54735fa23 --- /dev/null +++ b/unit-tests/pam/test_get_config_uid_via_pam_link.py @@ -0,0 +1,70 @@ +""" +Unit tests for tunnel_helpers.get_config_uid_via_pam_link — the KRouter +graph-sync/pam/get_leafs fallback used by `pam connection edit` when the +legacy get_leafs lookup misses. Mirrors the web vault's +`getMultiLeafsPamLinkDag` call (see vault/js/lib/api/pam/api-dag-pam-link.ts). +""" + +import unittest +from unittest import mock + +skip_tests = False +skip_reason = "" +try: + from keepercommander.commands.tunnel.port_forward.tunnel_helpers import get_config_uid_via_pam_link + from keepercommander.keeper_dag.proto import GraphSync_pb2 as gs_pb2 + from keepercommander import utils +except ImportError as e: + skip_tests = True + skip_reason = f"Cannot import tunnel_helpers/GraphSync_pb2: {e}" + + +@unittest.skipIf(skip_tests, skip_reason) +class TestGetConfigUidViaPamLink(unittest.TestCase): + def setUp(self): + self.params = mock.MagicMock() + self.record_uid = 'AAAAAAAAAAAAAAAAAAAAAA' # roundtrip-safe base64url for 16 bytes + self.config_uid = 'AQEBAQEBAQEBAQEBAQEBAQ' # roundtrip-safe base64url for 16 bytes + + @mock.patch('keepercommander.commands.pam.router_helper._post_request_to_router') + def test_returns_config_uid_on_single_ref(self, mock_post): + cfg_bytes = utils.base64_url_decode(self.config_uid) + mock_post.return_value = gs_pb2.GraphSyncRefsResult( + refs=[gs_pb2.GraphSyncRef(type=gs_pb2.RFT_PAM_NETWORK, value=cfg_bytes, name='')] + ) + result = get_config_uid_via_pam_link(self.params, self.record_uid) + self.assertEqual(result, self.config_uid) + # Verify endpoint + query shape + args, kwargs = mock_post.call_args + self.assertEqual(args[1], 'graph-sync/pam/get_leafs') + rq = kwargs.get('rq_proto') or args[2] + self.assertEqual(len(rq.vertices), 1) + self.assertEqual(rq.vertices[0], utils.base64_url_decode(self.record_uid)) + + @mock.patch('keepercommander.commands.pam.router_helper._post_request_to_router') + def test_returns_empty_string_when_no_refs(self, mock_post): + mock_post.return_value = gs_pb2.GraphSyncRefsResult(refs=[]) + self.assertEqual(get_config_uid_via_pam_link(self.params, self.record_uid), '') + + @mock.patch('keepercommander.commands.pam.router_helper._post_request_to_router') + def test_returns_empty_string_when_response_is_none(self, mock_post): + mock_post.return_value = None + self.assertEqual(get_config_uid_via_pam_link(self.params, self.record_uid), '') + + @mock.patch('keepercommander.commands.pam.router_helper._post_request_to_router') + def test_skips_refs_with_empty_value(self, mock_post): + cfg_bytes = utils.base64_url_decode(self.config_uid) + mock_post.return_value = gs_pb2.GraphSyncRefsResult(refs=[ + gs_pb2.GraphSyncRef(type=gs_pb2.RFT_PAM_NETWORK, value=b'', name=''), + gs_pb2.GraphSyncRef(type=gs_pb2.RFT_PAM_NETWORK, value=cfg_bytes, name=''), + ]) + self.assertEqual(get_config_uid_via_pam_link(self.params, self.record_uid), self.config_uid) + + @mock.patch('keepercommander.commands.pam.router_helper._post_request_to_router') + def test_swallows_exceptions_and_returns_empty(self, mock_post): + mock_post.side_effect = RuntimeError('krouter unreachable') + self.assertEqual(get_config_uid_via_pam_link(self.params, self.record_uid), '') + + +if __name__ == '__main__': + unittest.main() diff --git a/unit-tests/pam/test_pam_connection_edit_scrollback.py b/unit-tests/pam/test_pam_connection_edit_scrollback.py new file mode 100644 index 000000000..7f758652d --- /dev/null +++ b/unit-tests/pam/test_pam_connection_edit_scrollback.py @@ -0,0 +1,309 @@ +""" +Unit tests for PAM Connection Edit `--scrollback` flag. + +Covers argument parsing and the up-front validation that runs before any DAG / +record mutation: allowed record types (pamDatabase, pamMachine, pamDirectory), +allowed protocols per type, and value parsing (int / empty string / invalid). +""" + +import unittest +from unittest import mock + +skip_tests = False +skip_reason = "" +try: + from keepercommander.commands.tunnel_and_connections import PAMConnectionEditCommand + from keepercommander.error import CommandError + from keepercommander import vault +except ImportError as e: + skip_tests = True + skip_reason = f"Cannot import tunnel_and_connections: {e}" + + +@unittest.skipIf(skip_tests, skip_reason) +class TestPamConnectionEditScrollbackArgs(unittest.TestCase): + def setUp(self): + self.parser = PAMConnectionEditCommand.parser + + def test_scrollback_int(self): + args = self.parser.parse_args(['rec', '--scrollback', '1234']) + self.assertEqual(args.scrollback, '1234') + + def test_scrollback_empty_string(self): + args = self.parser.parse_args(['rec', '--scrollback', '']) + self.assertEqual(args.scrollback, '') + + def test_scrollback_short_alias(self): + args = self.parser.parse_args(['rec', '-sb', '5000']) + self.assertEqual(args.scrollback, '5000') + + def test_scrollback_not_provided(self): + args = self.parser.parse_args(['rec']) + self.assertIsNone(args.scrollback) + + def test_help_includes_scrollback(self): + help_text = self.parser.format_help() + self.assertIn('--scrollback', help_text) + self.assertIn('-sb', help_text) + + +@unittest.skipIf(skip_tests, skip_reason) +class TestPamConnectionEditScrollbackValidation(unittest.TestCase): + """Validation runs before DAG / token operations, so we can drive execute() + with mocks that only need to satisfy resolve_single_record + the typed-field + accessor for pamSettings.""" + + def _mock_record(self, record_type, protocol): + rec = mock.MagicMock(spec=vault.TypedRecord) + rec.record_uid = 'rec-uid' + rec.record_type = record_type + rec.version = 3 + ps_field = mock.MagicMock() + if protocol is None: + ps_field.value = [] + else: + ps_field.value = [{'connection': {'protocol': protocol}}] + rec.get_typed_field.side_effect = lambda name: ps_field if name == 'pamSettings' else None + return rec + + def _execute(self, record, **kwargs): + cmd = PAMConnectionEditCommand() + params = mock.MagicMock() + with mock.patch( + 'keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record', + return_value=record, + ): + cmd.execute(params, record='rec', **kwargs) + + def test_pam_remote_browser_rejected(self): + rec = self._mock_record('pamRemoteBrowser', 'http') + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='100') + self.assertIn('--scrollback is only supported for pamDatabase, pamMachine, and pamDirectory', + str(ctx.exception)) + + def test_pam_user_rejected(self): + rec = self._mock_record('pamUser', None) + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='100') + # pamUser fails the outer record-type check before scrollback validation runs + self.assertIn("type is not supported for connections", str(ctx.exception)) + + def test_pam_network_configuration_rejected(self): + rec = self._mock_record('pamNetworkConfiguration', None) + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='100') + self.assertIn('--scrollback is only supported for pamDatabase, pamMachine, and pamDirectory', + str(ctx.exception)) + + def test_pam_machine_rdp_rejected(self): + rec = self._mock_record('pamMachine', 'rdp') + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='100') + msg = str(ctx.exception) + self.assertIn('not supported for protocol "rdp"', msg) + self.assertIn('pamMachine', msg) + + def test_pam_machine_vnc_rejected(self): + rec = self._mock_record('pamMachine', 'vnc') + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='100') + self.assertIn('not supported for protocol "vnc"', str(ctx.exception)) + + def test_pam_machine_no_protocol_rejected(self): + rec = self._mock_record('pamMachine', None) + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='100') + self.assertIn('not supported for protocol "(unset)"', str(ctx.exception)) + + def test_pam_directory_http_rejected(self): + rec = self._mock_record('pamDirectory', 'http') + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='100') + self.assertIn('not supported for protocol "http"', str(ctx.exception)) + + def test_non_numeric_rejected(self): + rec = self._mock_record('pamMachine', 'ssh') + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='not-a-number') + self.assertIn('--scrollback must be a non-negative integer', str(ctx.exception)) + + def test_float_rejected(self): + rec = self._mock_record('pamMachine', 'ssh') + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='1.5') + self.assertIn('--scrollback must be a non-negative integer', str(ctx.exception)) + + def test_negative_integer_rejected(self): + rec = self._mock_record('pamMachine', 'ssh') + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='-100') + self.assertIn('--scrollback must be a non-negative integer', str(ctx.exception)) + + def test_negative_one_rejected(self): + rec = self._mock_record('pamMachine', 'ssh') + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='-1') + self.assertIn('--scrollback must be a non-negative integer', str(ctx.exception)) + + def test_zero_accepted(self): + """Zero is a non-negative integer and should pass validation.""" + rec = self._mock_record('pamMachine', 'ssh') + try: + self._execute(rec, scrollback='0') + except CommandError as e: + self.assertNotIn('--scrollback must be', str(e)) + except Exception: + pass # downstream DAG failure expected; only validation is under test + + def test_protocol_change_in_same_command_validated_against_new(self): + """When --connections=on and --protocol=rdp are passed alongside --scrollback, + validation uses the new (post-mutation) protocol — rdp -> reject.""" + rec = self._mock_record('pamMachine', 'ssh') + with self.assertRaises(CommandError) as ctx: + self._execute(rec, scrollback='100', connections='on', protocol='rdp') + self.assertIn('not supported for protocol "rdp"', str(ctx.exception)) + + def test_protocol_change_without_connections_uses_existing(self): + """--protocol is only honored alongside --connections=on; without it, + validation uses the existing record protocol.""" + rec = self._mock_record('pamMachine', 'ssh') + # Without --connections=on, the bogus --protocol is ignored, existing + # 'ssh' wins. Validation should not raise (it gets past the protocol + # check); we expect it to proceed to the DAG layer and fail there. + # We just assert the error is NOT the scrollback-protocol error. + with self.assertRaises(Exception) as ctx: + self._execute(rec, scrollback='100', protocol='rdp') + self.assertNotIn('not supported for protocol', str(ctx.exception)) + + +@unittest.skipIf(skip_tests, skip_reason) +class TestPamConnectionEditScrollbackAllowedCombinations(unittest.TestCase): + """For each allowed (record_type, protocol) pair, validation must not raise + a scrollback-related error. We don't run the full execute path (which would + require mocking the entire DAG layer), only verify validation passes.""" + + DB_PROTOCOLS = ['mysql', 'postgresql', 'sql-server', 'mariadb', 'oracle', + 'mongodb', 'redis', 'elasticsearch', 'clickhouse', 'dynamodb'] + TERMINAL_PROTOCOLS = ['ssh', 'telnet', 'kubernetes'] + + def _assert_validation_passes(self, record_type, protocol): + rec = mock.MagicMock(spec=vault.TypedRecord) + rec.record_uid = 'rec-uid' + rec.record_type = record_type + rec.version = 3 + ps_field = mock.MagicMock() + ps_field.value = [{'connection': {'protocol': protocol}}] + rec.get_typed_field.side_effect = lambda name: ps_field if name == 'pamSettings' else None + + cmd = PAMConnectionEditCommand() + params = mock.MagicMock() + with mock.patch( + 'keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record', + return_value=rec, + ): + try: + cmd.execute(params, record='rec', scrollback='100') + except CommandError as e: + self.assertNotIn('--scrollback is only supported', str(e)) + self.assertNotIn('--scrollback is not supported for protocol', str(e)) + self.assertNotIn('--scrollback must be an integer', str(e)) + except Exception: + pass # downstream DAG/token failures are not what we're testing + + def test_pam_database_all_db_protocols(self): + for proto in self.DB_PROTOCOLS: + with self.subTest(protocol=proto): + self._assert_validation_passes('pamDatabase', proto) + + def test_pam_machine_terminal_protocols(self): + for proto in self.TERMINAL_PROTOCOLS: + with self.subTest(protocol=proto): + self._assert_validation_passes('pamMachine', proto) + + def test_pam_directory_terminal_protocols(self): + for proto in self.TERMINAL_PROTOCOLS: + with self.subTest(protocol=proto): + self._assert_validation_passes('pamDirectory', proto) + + +@unittest.skipIf(skip_tests, skip_reason) +class TestPamConnectionEditScrollbackEarlyReturn(unittest.TestCase): + """When only record-level args (scrollback, key-events, protocol alone) are passed, + the command should return after the record update without touching the DAG. Locks in + the fix for the misleading 'No PAM Configuration UID set' error when --scrollback is + used on a resource that isn't linked to a config.""" + + def _mock_record(self, record_type='pamMachine', protocol='ssh'): + rec = mock.MagicMock(spec=vault.TypedRecord) + rec.record_uid = 'rec-uid' + rec.record_type = record_type + rec.version = 3 + rec.fields = [] + rec.custom = [] + ps_field = mock.MagicMock() + ps_field.value = [{'connection': {'protocol': protocol}}] + rec.get_typed_field.side_effect = lambda name: ps_field if name == 'pamSettings' else ( + mock.MagicMock(value=['seed']) if name == 'trafficEncryptionSeed' else None + ) + return rec + + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') + @mock.patch('keepercommander.commands.tunnel_and_connections.get_keeper_tokens', + return_value=(b'st', b'tk', b'tr')) + @mock.patch('keepercommander.commands.tunnel_and_connections.get_config_uid') + @mock.patch('keepercommander.commands.tunnel_and_connections.TunnelDAG') + def test_scrollback_alone_skips_dag(self, mock_tdag, mock_get_config_uid, + mock_tokens, mock_sync, mock_update, mock_resolve): + """Running with only --scrollback should NOT invoke get_config_uid or TunnelDAG.""" + rec = self._mock_record() + mock_resolve.return_value = rec + cmd = PAMConnectionEditCommand() + cmd.execute(mock.MagicMock(), record='rec', scrollback='1234') + mock_get_config_uid.assert_not_called() + mock_tdag.assert_not_called() + # The record update IS expected to run (scrollback was written) + mock_update.assert_called_once() + + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') + @mock.patch('keepercommander.commands.tunnel_and_connections.get_keeper_tokens', + return_value=(b'st', b'tk', b'tr')) + @mock.patch('keepercommander.commands.tunnel_and_connections.get_config_uid') + @mock.patch('keepercommander.commands.tunnel_and_connections.TunnelDAG') + def test_key_events_alone_skips_dag(self, mock_tdag, mock_get_config_uid, + mock_tokens, mock_sync, mock_update, mock_resolve): + """Same early-return applies to --key-events alone.""" + rec = self._mock_record() + mock_resolve.return_value = rec + cmd = PAMConnectionEditCommand() + cmd.execute(mock.MagicMock(), record='rec', key_events='on') + mock_get_config_uid.assert_not_called() + mock_tdag.assert_not_called() + + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') + @mock.patch('keepercommander.commands.tunnel_and_connections.get_keeper_tokens', + return_value=(b'st', b'tk', b'tr')) + @mock.patch('keepercommander.commands.tunnel_and_connections.get_config_uid', + return_value=None) + def test_scrollback_with_connections_still_runs_dag(self, mock_get_config_uid, + mock_tokens, mock_sync, mock_update, mock_resolve): + """When --connections is passed alongside --scrollback, the DAG block must still run + (and is expected to surface its own errors). We just verify get_config_uid is reached.""" + rec = self._mock_record() + mock_resolve.return_value = rec + cmd = PAMConnectionEditCommand() + try: + cmd.execute(mock.MagicMock(), record='rec', scrollback='1234', connections='on') + except Exception: + pass # downstream TunnelDAG instantiation will fail; not under test here + mock_get_config_uid.assert_called_once() + + +if __name__ == '__main__': + unittest.main() diff --git a/unit-tests/pam/test_pam_rbi_edit.py b/unit-tests/pam/test_pam_rbi_edit.py index 94c91ee84..ce52be84d 100644 --- a/unit-tests/pam/test_pam_rbi_edit.py +++ b/unit-tests/pam/test_pam_rbi_edit.py @@ -2,7 +2,7 @@ Unit tests for PAM RBI Edit command - KC-1034 Feature Parity Tests the new CLI arguments added to expose RBI settings: -- Browser Settings: --allow-url-navigation, --ignore-server-cert (on/off/default) +- Browser Settings: --allow-url-navigation, --ignore-server-cert, --allow-file-uploads, --allow-file-downloads (on/off/default) - URL Filtering: --allowed-urls, --allowed-resource-urls (multi-value) - Autofill: --autofill-targets (multi-value) - Clipboard: --allow-copy, --allow-paste (on/off/default) @@ -67,6 +67,46 @@ def test_ignore_server_cert_default(self): args = self.parser.parse_args(['--record', 'test-record', '--ignore-server-cert', 'default']) self.assertEqual(args.ignore_server_cert, 'default') + def test_allow_file_uploads_on(self): + args = self.parser.parse_args(['--record', 'test-record', '--allow-file-uploads', 'on']) + self.assertEqual(args.allow_file_uploads, 'on') + + def test_allow_file_uploads_off(self): + args = self.parser.parse_args(['--record', 'test-record', '--allow-file-uploads', 'off']) + self.assertEqual(args.allow_file_uploads, 'off') + + def test_allow_file_uploads_default(self): + args = self.parser.parse_args(['--record', 'test-record', '--allow-file-uploads', 'default']) + self.assertEqual(args.allow_file_uploads, 'default') + + def test_allow_file_uploads_invalid(self): + with self.assertRaises(SystemExit): + self.parser.parse_args(['--record', 'test-record', '--allow-file-uploads', 'invalid']) + + def test_allow_file_uploads_not_provided(self): + args = self.parser.parse_args(['--record', 'test-record', '--key-events', 'on']) + self.assertIsNone(args.allow_file_uploads) + + def test_allow_file_downloads_on(self): + args = self.parser.parse_args(['--record', 'test-record', '--allow-file-downloads', 'on']) + self.assertEqual(args.allow_file_downloads, 'on') + + def test_allow_file_downloads_off(self): + args = self.parser.parse_args(['--record', 'test-record', '--allow-file-downloads', 'off']) + self.assertEqual(args.allow_file_downloads, 'off') + + def test_allow_file_downloads_default(self): + args = self.parser.parse_args(['--record', 'test-record', '--allow-file-downloads', 'default']) + self.assertEqual(args.allow_file_downloads, 'default') + + def test_allow_file_downloads_invalid(self): + with self.assertRaises(SystemExit): + self.parser.parse_args(['--record', 'test-record', '--allow-file-downloads', 'invalid']) + + def test_allow_file_downloads_not_provided(self): + args = self.parser.parse_args(['--record', 'test-record', '--key-events', 'on']) + self.assertIsNone(args.allow_file_downloads) + def test_allowed_urls_single(self): args = self.parser.parse_args(['--record', 'test-record', '--allowed-urls', '*.example.com']) self.assertEqual(args.allowed_urls, ['*.example.com']) @@ -214,6 +254,56 @@ def test_ignore_server_cert_on_sets_true(self, mock_sync, mock_update, mock_reso self.command.execute(self.mock_params, record='test-record', ignore_server_cert='on') self.assertEqual(self.pam_settings['connection'].get('ignoreInitialSslCert'), True) + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') + def test_allow_file_uploads_on_sets_true(self, mock_sync, mock_update, mock_resolve): + mock_resolve.return_value = self.mock_record + self.command.execute(self.mock_params, record='test-record', allow_file_uploads='on') + self.assertEqual(self.pam_settings['connection'].get('allowFileUploads'), True) + + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') + def test_allow_file_uploads_off_sets_false(self, mock_sync, mock_update, mock_resolve): + mock_resolve.return_value = self.mock_record + self.command.execute(self.mock_params, record='test-record', allow_file_uploads='off') + self.assertEqual(self.pam_settings['connection'].get('allowFileUploads'), False) + + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') + def test_allow_file_uploads_default_removes_field(self, mock_sync, mock_update, mock_resolve): + mock_resolve.return_value = self.mock_record + self.pam_settings['connection']['allowFileUploads'] = True + self.command.execute(self.mock_params, record='test-record', allow_file_uploads='default') + self.assertNotIn('allowFileUploads', self.pam_settings['connection']) + + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') + def test_allow_file_downloads_on_sets_true(self, mock_sync, mock_update, mock_resolve): + mock_resolve.return_value = self.mock_record + self.command.execute(self.mock_params, record='test-record', allow_file_downloads='on') + self.assertEqual(self.pam_settings['connection'].get('allowFileDownloads'), True) + + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') + def test_allow_file_downloads_off_sets_false(self, mock_sync, mock_update, mock_resolve): + mock_resolve.return_value = self.mock_record + self.command.execute(self.mock_params, record='test-record', allow_file_downloads='off') + self.assertEqual(self.pam_settings['connection'].get('allowFileDownloads'), False) + + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') + @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') + def test_allow_file_downloads_default_removes_field(self, mock_sync, mock_update, mock_resolve): + mock_resolve.return_value = self.mock_record + self.pam_settings['connection']['allowFileDownloads'] = True + self.command.execute(self.mock_params, record='test-record', allow_file_downloads='default') + self.assertNotIn('allowFileDownloads', self.pam_settings['connection']) + @mock.patch('keepercommander.commands.tunnel_and_connections.RecordMixin.resolve_single_record') @mock.patch('keepercommander.commands.tunnel_and_connections.record_management.update_record') @mock.patch('keepercommander.commands.tunnel_and_connections.api.sync_down') @@ -356,6 +446,8 @@ def test_help_includes_new_arguments(self): help_text = PAMRbiEditCommand.parser.format_help() self.assertIn('--allow-url-navigation', help_text) self.assertIn('--ignore-server-cert', help_text) + self.assertIn('--allow-file-uploads', help_text) + self.assertIn('--allow-file-downloads', help_text) self.assertIn('--allowed-urls', help_text) self.assertIn('--allowed-resource-urls', help_text) self.assertIn('--autofill-targets', help_text) @@ -386,6 +478,14 @@ def test_alias_isc(self): args = self.parser.parse_args(['--record', 'test-record', '-isc', 'on']) self.assertEqual(args.ignore_server_cert, 'on') + def test_alias_fu(self): + args = self.parser.parse_args(['--record', 'test-record', '-fu', 'on']) + self.assertEqual(args.allow_file_uploads, 'on') + + def test_alias_fd(self): + args = self.parser.parse_args(['--record', 'test-record', '-fd', 'on']) + self.assertEqual(args.allow_file_downloads, 'on') + def test_alias_au(self): args = self.parser.parse_args(['--record', 'test-record', '-au', '*.example.com']) self.assertEqual(args.allowed_urls, ['*.example.com']) From fa8757a2caf6ed6cfa49e95fe94f158eec4974ae Mon Sep 17 00:00:00 2001 From: sshrushanth-ks Date: Thu, 28 May 2026 21:01:16 +0530 Subject: [PATCH 02/23] KC-1260: Support combined folder search with multiple -c flags (#2084) (#2096) * Support combined folder search with multiple -c flags search -c s -c d now returns Classic shared folders and KeeperDrive folders together in one JSON response. Changed -c from action='store' to action='append' so repeated flags accumulate instead of overwriting. JSON details field now shows Folder Category: Classic or Folder Category: KeeperDrive for folder results. * Fix nested_share_folder details always shown in JSON search output NSF folders with no parent UID previously rendered an empty details field. Now always shows Folder Category: NestedShare, with Parent UID appended when the folder is not at root level. * Fix help text for -c flag to use Nested Share Folders instead of KeeperDrive --- keepercommander/commands/record.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/keepercommander/commands/record.py b/keepercommander/commands/record.py index a507c0bd6..422243d21 100644 --- a/keepercommander/commands/record.py +++ b/keepercommander/commands/record.py @@ -143,9 +143,11 @@ def register_command_info(aliases, command_info): search_parser = argparse.ArgumentParser(prog='search', description='Search the vault. Words can be in any order.') search_parser.add_argument('pattern', nargs='*', type=str, action='store', help='search terms (space-separated, order independent)') search_parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='verbose output') -search_parser.add_argument('-c', '--categories', dest='categories', action='store', - help='One or more of these letters for categories to search: "r" = records, ' - '"s" = shared folders, "t" = teams, "d" = Nested Share Folders') +search_parser.add_argument('-c', '--categories', dest='categories', action='append', + help='Category to search — repeatable: "r" = records, "s" = shared folders, ' + '"t" = teams, "d" = Nested Share folders. ' + 'Pass multiple times (e.g. -c s -c d) or combine letters (e.g. -c sd). ' + 'Default when omitted: all categories (rstd).') search_parser.add_argument('--regex', dest='regex', action='store_true', help='treat pattern as a regular expression instead of space-separated search terms') search_parser.add_argument('--device', dest='device', action='store_true', @@ -1455,7 +1457,7 @@ def execute(self, params, **kwargs): else: pattern = '' # Empty pattern matches all in token mode - categories = (kwargs.get('categories') or 'rstd').lower() + categories = ''.join(kwargs.get('categories') or ['rstd']).lower() skip_details = not verbose nsf_records_map = getattr(params, 'nested_share_records', {}) or {} @@ -1575,13 +1577,14 @@ def execute(self, params, **kwargs): f"Type: {item['record_type']}, Description: {item['description']}, Record Category: {item.get('record_category', 'Classic')}"] elif item['type'] == 'shared_folder': row = [item['type'], item['shared_folder_uid'], item['name'], - f"Can Edit: {item['can_edit']}, Can Share: {item['can_share']}"] + f"Folder Category: Classic, Can Edit: {item['can_edit']}, Can Share: {item['can_share']}"] elif item['type'] == 'team': row = [item['type'], item['team_uid'], item['name'], f"Restrict Edit: {item['restrict_edit']}, Restrict View: {item['restrict_view']}, Restrict Share: {item['restrict_share']}"] elif item['type'] == 'nested_share_folder': - details = (f"Parent UID: {item['parent_uid']}" - if item.get('parent_uid') else '') + details = 'Folder Category: NestedShare' + if item.get('parent_uid'): + details += f", Parent UID: {item['parent_uid']}" row = [item['type'], item['folder_uid'], item['name'], details] table.append(row) From 1117f7ab9352f129206ede7e1867839c191daa3e Mon Sep 17 00:00:00 2001 From: pvagare-ks Date: Fri, 29 May 2026 17:10:50 +0530 Subject: [PATCH 03/23] Fix NSF CLI record updates, nested directory creation and Service Mode delete response handling (#2097) --- .../nested_share_folder/folder_commands.py | 112 +++++++++++++----- .../commands/nested_share_folder/parsers.py | 4 +- .../nested_share_folder/record_commands.py | 52 ++++++-- .../nested_share_folder/folder_api.py | 13 +- .../nested_share_folder/record_api.py | 4 +- 5 files changed, 139 insertions(+), 46 deletions(-) diff --git a/keepercommander/commands/nested_share_folder/folder_commands.py b/keepercommander/commands/nested_share_folder/folder_commands.py index fa1218a84..57c04a7af 100644 --- a/keepercommander/commands/nested_share_folder/folder_commands.py +++ b/keepercommander/commands/nested_share_folder/folder_commands.py @@ -61,52 +61,102 @@ def execute(self, params, **kwargs): if current and current in getattr(params, 'nested_share_folders', {}): base_folder_uid = current - folder_name = self._parse_path(folder_path) + segments = self._parse_path(folder_path) + + parent_uid = base_folder_uid + last_idx = len(segments) - 1 + created_uid = None + + for idx, segment in enumerate(segments): + is_leaf = (idx == last_idx) + existing_uid = self._find_existing_child(params, segment, parent_uid) + if existing_uid: + if is_leaf: + logging.warning('nsf-mkdir: Folder "%s" already exists', segment) + return existing_uid + parent_uid = existing_uid + continue - existing_uid = self._find_existing_child(params, folder_name, base_folder_uid) - if existing_uid: - logging.warning('nsf-mkdir: Folder "%s" already exists', folder_name) - return existing_uid + seg_color = color if is_leaf else None + seg_inherit = inherit_permissions if is_leaf else True - with command_error_handler('nsf-mkdir'): - result = _nsf.create_folder_v3( - params=params, folder_name=folder_name, - parent_uid=base_folder_uid, - color=color, - inherit_permissions=inherit_permissions, - ) - check_result(result, 'nsf-mkdir') + with command_error_handler('nsf-mkdir'): + result = _nsf.create_folder_v3( + params=params, folder_name=segment, + parent_uid=parent_uid, + color=seg_color, + inherit_permissions=seg_inherit, + ) + check_result(result, 'nsf-mkdir') + + created_uid = result['folder_uid'] + self._cache_new_folder( + params, created_uid, segment, parent_uid, + folder_key=result.get('folder_key_unencrypted')) + + if not is_leaf: + logging.debug('nsf-mkdir: Created intermediate folder "%s"', segment) + parent_uid = created_uid params.sync_data = True - return result['folder_uid'] + return created_uid @staticmethod def _parse_path(folder_path): - # Collapse escaped slashes (//) to a sentinel so we can detect any - # stray path separator and refuse it — nsf-mkdir creates a single - # folder, not a nested hierarchy. - collapsed = folder_path.replace('//', '\x00') - if '/' in collapsed: - raise CommandError('nsf-mkdir', - 'Character "/" is reserved. Use "//" inside folder name') - name = collapsed.replace('\x00', '/').strip() - if not name: + """Split *folder_path* into a list of segment names. + """ + sentinel = '\x00' + collapsed = folder_path.replace('//', sentinel) + raw_segments = collapsed.split('/') + segments = [] + for raw in raw_segments: + name = raw.replace(sentinel, '/').strip() + if name: + segments.append(name) + if not segments: raise CommandError('nsf-mkdir', 'Invalid folder name') - return name + return segments + + @staticmethod + def _cache_new_folder(params, folder_uid, name, parent_uid, folder_key=None): + """Insert a just-created folder into the local NSF cache so that + subsequent segments in the same path can discover it as a parent + without requiring a full sync round-trip. + """ + nsf = getattr(params, 'nested_share_folders', None) + if nsf is None: + return + entry = { + 'name': name, + 'parent_uid': parent_uid or '', + } + if folder_key: + entry['folder_key_unencrypted'] = folder_key + nsf[folder_uid] = entry @staticmethod def _find_existing_child(params, folder_name, parent_uid): + """Find an existing NSF folder named *folder_name* whose parent matches + *parent_uid*. ``parent_uid=None`` means "root level". + """ nsf_folders = getattr(params, 'nested_share_folders', {}) name_lower = folder_name.lower() - expected_parent = parent_uid or '' + looking_for_root = not parent_uid for fuid, fobj in nsf_folders.items(): if fobj.get('name', '').lower() != name_lower: continue - existing_parent = normalize_parent_uid(fobj.get('parent_uid', '')) - if existing_parent == 'root': - existing_parent = '' - if existing_parent == expected_parent: - return fuid + raw_parent = fobj.get('parent_uid') or '' + normalized = normalize_parent_uid(raw_parent) + is_root_child = ( + normalized in ('', 'root') + or (raw_parent and raw_parent not in nsf_folders) + ) + if looking_for_root: + if is_root_child: + return fuid + else: + if raw_parent == parent_uid: + return fuid return None @@ -459,7 +509,7 @@ def _preview_and_confirm(self, params, removals, operation, force, dry_run, quie self._impact_summary(pr['folder_uid'], name, operation, pr.get('impact'), quiet) ) - if summary_lines: + if summary_lines and (dry_run or not force): for line in summary_lines: print(line) diff --git a/keepercommander/commands/nested_share_folder/parsers.py b/keepercommander/commands/nested_share_folder/parsers.py index 1539371ea..0d0e821b0 100644 --- a/keepercommander/commands/nested_share_folder/parsers.py +++ b/keepercommander/commands/nested_share_folder/parsers.py @@ -36,7 +36,9 @@ def _make_parser(prog, description): 'nsf-mkdir', 'Create a new Nested Share Folder using v3 API') nested_share_folder_mkdir_parser.add_argument( 'folder', type=str, - help='Folder name to create (use "//" to embed a literal "/" in the name)') + help='Folder name or path (e.g. "Parent/Child/Grand") to create. ' + 'Intermediate folders are created automatically. ' + 'Use "//" to embed a literal "/" in a segment name.') nested_share_folder_mkdir_parser.add_argument( '--color', type=str, choices=['none', 'red', 'orange', 'yellow', 'green', 'blue', 'gray'], diff --git a/keepercommander/commands/nested_share_folder/record_commands.py b/keepercommander/commands/nested_share_folder/record_commands.py index 164acab01..bbcdfe005 100644 --- a/keepercommander/commands/nested_share_folder/record_commands.py +++ b/keepercommander/commands/nested_share_folder/record_commands.py @@ -179,6 +179,26 @@ def __init__(self): def get_parser(self): return nested_share_record_update_parser + def _resolve_field_value(self, parsed): + raw = parsed.value + if not raw: + return raw + + action_params = [] + if self.is_json_value(raw, action_params): + return action_params[0] if action_params else None + action_params.clear() + if self.is_generate_value(raw, action_params): + if parsed.type == 'password': + return self.generate_password(action_params) + if parsed.type in ('oneTimeCode', 'otp'): + return self.generate_totp_url() + return raw + action_params.clear() + if self.is_base64_value(raw, action_params): + return action_params[0] if action_params else None + return raw + def execute(self, params, **kwargs): if kwargs.get('syntax_help'): print(record_fields_description) @@ -198,15 +218,24 @@ def execute(self, params, **kwargs): for spec in [f.strip() for f in kwargs.get('fields', []) if f.strip()]: try: parsed = RecordEditMixin.parse_field(spec) + value = self._resolve_field_value(parsed) + if value is None: + continue if parsed.type in fields: existing = fields[parsed.type] fields[parsed.type] = ([existing] if not isinstance(existing, list) - else existing) + [parsed.value] + else existing) + [value] else: - fields[parsed.type] = parsed.value + fields[parsed.type] = value except ValueError as e: raise CommandError('nsf-record-update', f'Invalid field specification: {e}') + if self.warnings: + for w in self.warnings: + logging.warning(w) + if not kwargs.get('force'): + return + with command_error_handler('nsf-record-update'): for identifier in record_uids: record_uid = _nsf.resolve_nested_share_record_uid(params, identifier) @@ -524,28 +553,35 @@ def _build_removals(self, params, record_args, folder_uid, operation): def _preview_and_confirm(self, params, removals, operation, force, dry_run): result = _nsf.remove_record_v3(params, removals, dry_run=True) any_error = False - summary_lines = [] + error_lines = [] + info_lines = [] for pr in result['preview_results']: title = self._record_title(params, pr['record_uid']) if pr.get('error'): any_error = True err = pr['error'] - summary_lines.append( + error_lines.append( f" {title} [{pr['record_uid']}]: " f"{err.get('code', '')} — {err.get('message', '')}" ) else: - summary_lines.extend( + info_lines.extend( self._impact_summary(pr['record_uid'], title, operation, pr.get('impact')) ) - for line in summary_lines: - print(line) - + # Errors must always surface, even in --force mode, so the caller (or + # Service Mode HTTP layer) can see why the operation aborted. if any_error: + for line in error_lines: + print(line) print('\nOne or more records could not be previewed. Aborting.') return + + if dry_run or not force: + for line in info_lines: + print(line) + if dry_run: print('\n[Dry-run] No records were deleted.') return diff --git a/keepercommander/nested_share_folder/folder_api.py b/keepercommander/nested_share_folder/folder_api.py index 94f90179e..e6d8f2307 100644 --- a/keepercommander/nested_share_folder/folder_api.py +++ b/keepercommander/nested_share_folder/folder_api.py @@ -93,7 +93,7 @@ def _prepare_folder_for_creation(params, folder_uid, folder_name, parent_uid, else SetBooleanValue.BOOLEAN_FALSE), color=color) fd.folderKey = encrypted_fk - return fd + return fd, folder_key # ══════════════════════════════════════════════════════════════════════════ @@ -173,13 +173,14 @@ def resolve_folder_identifier(params, folder_identifier): def create_folder_v3(params, folder_name, parent_uid=None, color=None, inherit_permissions=True): uid = utils.generate_uid() - fd = _prepare_folder_for_creation(params, uid, folder_name, parent_uid, - color, inherit_permissions) + fd, folder_key = _prepare_folder_for_creation( + params, uid, folder_name, parent_uid, color, inherit_permissions) response = folder_add_v3(params, [fd]) if response.folderAddResults: r = response.folderAddResults[0] return { 'folder_uid': uid, + 'folder_key_unencrypted': folder_key, 'status': folder_pb2.FolderModifyStatus.Name(r.status), 'message': r.message, 'success': r.status == folder_pb2.SUCCESS, @@ -190,20 +191,22 @@ def create_folder_v3(params, folder_name, parent_uid=None, color=None, def create_folders_batch_v3(params, folder_specs): if len(folder_specs) > 100: raise ValueError("Maximum 100 folders at a time") - fd_list, uid_map = [], {} + fd_list, uid_map, key_map = [], {}, {} for idx, spec in enumerate(folder_specs): uid = utils.generate_uid() uid_map[idx] = uid name = spec.get('name') if not name: raise ValueError(f"Spec at index {idx} missing 'name'") - fd = _prepare_folder_for_creation( + fd, folder_key = _prepare_folder_for_creation( params, uid, name, spec.get('parent_uid'), spec.get('color'), spec.get('inherit_permissions', True)) fd_list.append(fd) + key_map[idx] = folder_key response = folder_add_v3(params, fd_list) return [{ 'folder_uid': uid_map.get(i, utils.base64_url_encode(r.folderUid)), + 'folder_key_unencrypted': key_map.get(i), 'name': folder_specs[i].get('name'), 'status': folder_pb2.FolderModifyStatus.Name(r.status), 'message': r.message, diff --git a/keepercommander/nested_share_folder/record_api.py b/keepercommander/nested_share_folder/record_api.py index 7eb432567..db886a78b 100644 --- a/keepercommander/nested_share_folder/record_api.py +++ b/keepercommander/nested_share_folder/record_api.py @@ -151,7 +151,9 @@ def update_record_v3(params, record_uid, data=None, title=None, if 'data_unencrypted' in rec: raw = rec['data_unencrypted'] if isinstance(raw, bytes): - existing = json.loads(raw.decode()) + existing = json.loads(raw.decode('utf-8')) + elif isinstance(raw, str): + existing = json.loads(raw) data = existing.copy() if existing else {'fields': []} if title is not None: data['title'] = title From fa82d64c13dc016e9921ae8f63e989e571d3d720 Mon Sep 17 00:00:00 2001 From: amangalampalli-ks Date: Fri, 29 May 2026 18:04:41 +0530 Subject: [PATCH 04/23] Fix: Stop silently inheriting SSL_CERT_FILE from process env (#2094) * fix: honor bundled certifi store on Windows * fix: rename os-keychain plugin directory to os_keychain for valid Python package name The hyphen in os-keychain caused find_packages() to skip the directory, so it was not included in the PyPI wheel. PyInstaller also failed to bundle it correctly. Renaming to os_keychain (underscore) fixes both. loader.py normalises the URI scheme (os-keychain -> os_keychain) before resolving the plugin module, so config.json entries are unchanged. Co-authored-by: Cursor --------- Co-authored-by: sshrushanth-ks Co-authored-by: Cursor --- keepercommander/__main__.py | 73 +------------- keepercommander/config_storage/loader.py | 2 +- .../{os-keychain => os_keychain}/__init__.py | 0 .../os_keychain_storage.py | 0 keepercommander/utils.py | 94 ++++++++----------- 5 files changed, 44 insertions(+), 125 deletions(-) rename keepercommander/config_storage/{os-keychain => os_keychain}/__init__.py (100%) rename keepercommander/config_storage/{os-keychain => os_keychain}/os_keychain_storage.py (100%) diff --git a/keepercommander/__main__.py b/keepercommander/__main__.py index eaf7cba03..cb69ef2c4 100644 --- a/keepercommander/__main__.py +++ b/keepercommander/__main__.py @@ -12,15 +12,12 @@ import argparse -import certifi import json import logging import os import re import shlex import sys -import ssl -import platform from pathlib import Path from typing import Optional @@ -178,69 +175,6 @@ def handle_exceptions(exc_type, exc_value, exc_traceback): sys.exit(-1) -def get_ssl_cert_file(): - """Get SSL certificate file path, preferring system CA store for corporate environments like Zscaler""" - - # Allow user to override via environment variable - user_cert_file = os.getenv('KEEPER_SSL_CERT_FILE') - if user_cert_file: - if user_cert_file.lower() == 'system': - # User explicitly wants system certs - pass # Continue with system detection below - elif user_cert_file.lower() == 'certifi': - # User explicitly wants certifi - return certifi.where() - elif user_cert_file.lower() == 'none' or user_cert_file.lower() == 'false': - # User wants to disable SSL verification (not recommended) - return None - elif os.path.exists(user_cert_file): - # User provided specific cert file - return user_cert_file - else: - logging.warning(f"SSL cert file specified in KEEPER_SSL_CERT_FILE not found: {user_cert_file}") - - # Try to use system CA store first for corporate environments - try: - # On macOS, try Homebrew certificates first (better for corporate environments like Zscaler) - if platform.system() == 'Darwin': - system_ca_paths = [ - '/opt/homebrew/etc/ca-certificates/cert.pem', # Homebrew CA bundle (best for Zscaler) - '/usr/local/etc/ssl/cert.pem', # Homebrew SSL (older location) - '/etc/ssl/cert.pem', # macOS system CA bundle - ] - for ca_path in system_ca_paths: - if os.path.exists(ca_path): - return ca_path - - # On Linux/Unix systems - elif platform.system() == 'Linux': - system_ca_paths = [ - '/etc/ssl/certs/ca-certificates.crt', # Debian/Ubuntu - '/etc/pki/tls/certs/ca-bundle.crt', # RHEL/CentOS - '/etc/ssl/ca-bundle.pem', # OpenSUSE - '/etc/ssl/cert.pem', # Generic - ] - for ca_path in system_ca_paths: - if os.path.exists(ca_path): - return ca_path - - # Try to get default SSL context locations - try: - default_locations = ssl.get_default_verify_paths() - if default_locations.cafile and os.path.exists(default_locations.cafile): - return default_locations.cafile - if default_locations.capath and os.path.exists(default_locations.capath): - return default_locations.capath - except: - pass - - except Exception: - pass - - # Fall back to certifi if system CA not available - return certifi.where() - - def main(from_package=False): if sys.platform == 'win32': try: @@ -253,15 +187,12 @@ def main(from_package=False): if logger: logger.name = 'keepercommander' - # Use system CA certificates when available (supports Zscaler), fallback to certifi - ssl_cert_file = get_ssl_cert_file() + ssl_cert_file = utils.get_ssl_cert_file() if ssl_cert_file: os.environ['SSL_CERT_FILE'] = ssl_cert_file else: - # User explicitly disabled SSL verification logging.warning("Warning: SSL certificate verification has been disabled. This is not recommended for production use.") - if 'SSL_CERT_FILE' in os.environ: - del os.environ['SSL_CERT_FILE'] + os.environ.pop('SSL_CERT_FILE', None) errno = 0 diff --git a/keepercommander/config_storage/loader.py b/keepercommander/config_storage/loader.py index 978a37d61..219e5951e 100644 --- a/keepercommander/config_storage/loader.py +++ b/keepercommander/config_storage/loader.py @@ -110,7 +110,7 @@ def _get_plugin(url): # type: (str) -> SecureStorageBase if not par.scheme: raise SecureStorageException(f'Configuration file error: "{CONFIG_STORAGE_URL}" is not URL') - plugin_name = par.scheme + plugin_name = par.scheme.replace('-', '_') if not any(x for x in pkgutil.iter_modules(config_storage.__path__) if x.name == plugin_name): raise SecureStorageException(f'Protected storage "{plugin_name}" is not supported') diff --git a/keepercommander/config_storage/os-keychain/__init__.py b/keepercommander/config_storage/os_keychain/__init__.py similarity index 100% rename from keepercommander/config_storage/os-keychain/__init__.py rename to keepercommander/config_storage/os_keychain/__init__.py diff --git a/keepercommander/config_storage/os-keychain/os_keychain_storage.py b/keepercommander/config_storage/os_keychain/os_keychain_storage.py similarity index 100% rename from keepercommander/config_storage/os-keychain/os_keychain_storage.py rename to keepercommander/config_storage/os_keychain/os_keychain_storage.py diff --git a/keepercommander/utils.py b/keepercommander/utils.py index fd0ebfdde..d9c8c7cef 100644 --- a/keepercommander/utils.py +++ b/keepercommander/utils.py @@ -531,66 +531,54 @@ def value_to_boolean(value): return None def get_ssl_cert_file(): - """Get SSL certificate file path, preferring system CA store for corporate environments like Zscaler""" - import ssl - import platform + """Resolve the SSL CA bundle path. + + KEEPER_SSL_CERT_FILE accepts: 'certifi', 'system', 'none'/'false', or a PEM path. + Defaults to the bundled certifi store; does not consult SSL_CERT_FILE from the + environment to avoid silent trust-store hijacking by unrelated tools. + """ import certifi - import os - - # Allow user to override via environment variable + user_cert_file = os.getenv('KEEPER_SSL_CERT_FILE') if user_cert_file: - if user_cert_file.lower() == 'system': - pass # Continue with system detection below - elif user_cert_file.lower() == 'certifi': + choice = user_cert_file.lower() + if choice == 'certifi': return certifi.where() - elif user_cert_file.lower() == 'none' or user_cert_file.lower() == 'false': - return False # Disable SSL verification - elif os.path.exists(user_cert_file): - return user_cert_file - else: - # Don't use logging here as it can interfere with main logging config - print(f"Warning: SSL cert file specified in KEEPER_SSL_CERT_FILE not found: {user_cert_file}", file=sys.stderr) - - # Try to use system CA store first for corporate environments + if choice in ('none', 'false'): + return False + if choice != 'system': + if os.path.exists(user_cert_file): + return user_cert_file + print( + f"Warning: KEEPER_SSL_CERT_FILE points to a non-existent file: " + f"{user_cert_file}; falling back to the bundled certifi store.", + file=sys.stderr, + ) + return certifi.where() + try: - # On macOS, try Homebrew certificates first (better for corporate environments like Zscaler) - if platform.system() == 'Darwin': - system_ca_paths = [ - '/opt/homebrew/etc/ca-certificates/cert.pem', # Homebrew CA bundle (best for Zscaler) - '/usr/local/etc/ssl/cert.pem', # Homebrew SSL (older location) - '/etc/ssl/cert.pem', # macOS system CA bundle - ] - for ca_path in system_ca_paths: - if os.path.exists(ca_path): - return ca_path - - # On Linux/Unix systems - elif platform.system() == 'Linux': - system_ca_paths = [ - '/etc/ssl/certs/ca-certificates.crt', # Debian/Ubuntu - '/etc/pki/tls/certs/ca-bundle.crt', # RHEL/CentOS - '/etc/ssl/ca-bundle.pem', # OpenSUSE - '/etc/ssl/cert.pem', # Generic - ] - for ca_path in system_ca_paths: - if os.path.exists(ca_path): - return ca_path - - # Try to get default SSL context locations - try: - default_locations = ssl.get_default_verify_paths() - if default_locations.cafile and os.path.exists(default_locations.cafile): - return default_locations.cafile - if default_locations.capath and os.path.exists(default_locations.capath): - return default_locations.capath - except: - pass - + system = platform.system() + if system == 'Darwin': + candidates = ( + '/opt/homebrew/etc/ca-certificates/cert.pem', + '/usr/local/etc/ssl/cert.pem', + '/etc/ssl/cert.pem', + ) + elif system == 'Linux': + candidates = ( + '/etc/ssl/certs/ca-certificates.crt', + '/etc/pki/tls/certs/ca-bundle.crt', + '/etc/ssl/ca-bundle.pem', + '/etc/ssl/cert.pem', + ) + else: + candidates = () + for ca_path in candidates: + if os.path.exists(ca_path): + return ca_path except Exception: pass - - # Fall back to certifi if system CA not available + return certifi.where() From a628515df344b63a1301126e04535d2fc4db92b0 Mon Sep 17 00:00:00 2001 From: idimov-keeper <78815270+idimov-keeper@users.noreply.github.com> Date: Fri, 29 May 2026 20:25:06 -0500 Subject: [PATCH 05/23] Remove force_close escalation on workflow lease expiry; soft-close only (#2100) --- keepercommander/commands/pam_launch/launch.py | 40 ++++------ .../commands/pam_launch/rust_log_filter.py | 23 ++---- .../tunnel/port_forward/tunnel_helpers.py | 74 ------------------- .../commands/tunnel_and_connections.py | 30 ++++---- requirements.txt | 2 +- setup.cfg | 2 +- 6 files changed, 34 insertions(+), 137 deletions(-) diff --git a/keepercommander/commands/pam_launch/launch.py b/keepercommander/commands/pam_launch/launch.py index 1435ab112..503478a75 100644 --- a/keepercommander/commands/pam_launch/launch.py +++ b/keepercommander/commands/pam_launch/launch.py @@ -62,7 +62,6 @@ unregister_tunnel_session, unregister_conversation_key, get_keeper_tokens, - escalate_close, CloseConnectionReasons, ) from ..tunnel.port_forward.TunnelGraph import TunnelDAG @@ -1570,17 +1569,13 @@ def signal_handler_fn(signum, frame): original_handler = signal.signal(signal.SIGINT, signal_handler_fn) - # Workflow lease expiry: schedule a hard kill at expiresOn matching + # Workflow lease expiry: schedule teardown at expiresOn matching # the web vault (immediate teardown, no grace period, no reconnect). # The "Access expired" line is printed AFTER terminal reset in finally # so the message survives reset_local_terminal_after_pam_session(). - # On expiry we soft-close the tube and escalate to force_close_tube - # after FORCE_CLOSE_DELAY_SECONDS so any in-flight forwarded streams - # (SSH bytes etc.) are severed instead of lingering until the user - # disconnects manually. Escalation is gated on local hasattr + - # remote SDP version (FORCE_CLOSE_MIN_VERSION). + # On expiry we close the tube; the connection-closed cleanup path then + # stops the websocket and unregisters the tunnel session. lease_timer = None - force_close_timer_holder = {} # mutable holder so cleanup can cancel if workflow_expires_on_ms and workflow_expires_on_ms > 0: seconds_until_expiry = (workflow_expires_on_ms / 1000.0) - time.time() _lease_tube_id = tunnel_result['tunnel'].get('tube_id') @@ -1591,27 +1586,20 @@ def _on_lease_expired(): lease_expired = True shutdown_requested = True if _lease_tube_id and _lease_tube_registry is not None: - # Fetch remote version lazily: the SDP answer arrives - # asynchronously; capturing eagerly at schedule time - # would race for short leases scheduled before SDP. - remote_ver = tunnel_result['tunnel'].get('remote_webrtc_version') - if not remote_ver: - sess = get_tunnel_session(_lease_tube_id) - remote_ver = ( - getattr(sess, 'remote_webrtc_version', None) - if sess else None + try: + _lease_tube_registry.close_tube( + _lease_tube_id, + reason=CloseConnectionReasons.AdminClosed, + ) + except Exception as e: + logging.debug( + f"[lease-expiry launch tube={_lease_tube_id[:8]}] " + f"close_tube failed: {e}" ) - force_close_timer_holder['t'] = escalate_close( - _lease_tube_registry, - _lease_tube_id, - remote_webrtc_version=remote_ver, - reason=CloseConnectionReasons.AdminClosed, - log_prefix=f"[lease-expiry launch tube={_lease_tube_id[:8]}] ", - ) if seconds_until_expiry <= 0: - # Already expired at session start: run the close-and-escalate - # path immediately so cleanup goes through the same flow as a + # Already expired at session start: run the close path + # immediately so cleanup goes through the same flow as a # mid-session expiry. _on_lease_expired() else: diff --git a/keepercommander/commands/pam_launch/rust_log_filter.py b/keepercommander/commands/pam_launch/rust_log_filter.py index b1ee55e96..54f75d8fa 100644 --- a/keepercommander/commands/pam_launch/rust_log_filter.py +++ b/keepercommander/commands/pam_launch/rust_log_filter.py @@ -191,23 +191,9 @@ def enter_pam_launch_terminal_rust_logging(): # the channel is torn down, or TURN ``fail to refresh permissions`` warnings # from the relay-conn task as it observes the deallocated allocation. # -# The window must outlive both: -# 1. The soft→hard close escalation in ``escalate_close`` -# (``FORCE_CLOSE_DELAY_SECONDS`` = 3 s) -# 2. A brief TURN refresh-task latency after the PeerConnection drop cascade -# -# Imported lazily below to avoid a top-level cycle (this module is imported -# during pam_launch init, before the tunnel helpers are loaded for some -# callers). -def _force_close_delay_seconds(): - try: - from ..tunnel.port_forward.tunnel_helpers import FORCE_CLOSE_DELAY_SECONDS - return FORCE_CLOSE_DELAY_SECONDS - except Exception: - return 3.0 - - -_DEFAULT_RUST_LOG_FILTER_GRACE_SEC = _force_close_delay_seconds() + 1.5 +# The window must outlive both the tube close + teardown cascade (~3 s) and a +# brief TURN refresh-task latency after the PeerConnection drop cascade. +_DEFAULT_RUST_LOG_FILTER_GRACE_SEC = 4 # Refcount of active pam-launch sessions that have rust-log filtering installed. # Incremented in enter_*, decremented at the END of the grace timer in @@ -282,7 +268,8 @@ def _do_exit_rust_logging(token): def exit_pam_launch_terminal_rust_logging(token, grace_sec=_DEFAULT_RUST_LOG_FILTER_GRACE_SEC): """Restore Rust/webrtc logger state after pam launch terminal session. - The filter is removed after ``grace_sec`` seconds (default 2.5s) so that + The filter is removed after ``grace_sec`` seconds (default + ``_DEFAULT_RUST_LOG_FILTER_GRACE_SEC``) so that late records from the Rust runtime (e.g. ``webrtc-sctp`` stream teardown messages that arrive just after session exit) are still caught by the filter and do not leak to the console in front of the subsequent diff --git a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py index 311b004e3..1349f71d2 100644 --- a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py +++ b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py @@ -129,20 +129,6 @@ def parse(v): return False -# Minimum keeper-pam-webrtc-rs version that exposes force_close_tube. Both the -# local Rust crate AND the remote peer must satisfy this gate before Commander -# escalates a soft close to a force close. Local check uses hasattr (the binding -# attribute is missing on older crates), remote check uses the SDP-advertised -# version string. -FORCE_CLOSE_MIN_VERSION = "2.1.18" - -# Default delay between the soft close and the force-close escalation. Matches -# the consumer-side budget agreed with the gateway (gateway-side -# KEEPER_GATEWAY_FORCE_CLOSE_TIMEOUT is 6s; we run faster on the consumer because -# at lease expiry there is no reason to wait long). -FORCE_CLOSE_DELAY_SECONDS = 3.0 - - def print_above_keeper_prompt(msg): """Print ``msg`` so the keeper-shell prompt redraws itself underneath it. @@ -181,66 +167,6 @@ def print_above_keeper_prompt(msg): pass -def escalate_close( - tube_registry, - tube_id, - *, - remote_webrtc_version=None, - reason=None, - hard_after_seconds=FORCE_CLOSE_DELAY_SECONDS, - log_prefix="", -): - """ - Soft-close a tube now, then escalate to force_close_tube after - `hard_after_seconds` if both endpoints support it. - - The soft close stops new channel creation and emits CloseConnection control - frames; the force close (when available) drops the local TCP listener, - severs in-flight forwarded TCP streams (SSH, MySQL, etc.) and tears down - the peer connection on a short bounded budget. - - Returns the scheduled `threading.Timer` (or None if escalation is not - available) so callers can cancel it on a clean exit. - """ - if reason is None: - reason = CloseConnectionReasons.AdminClosed - - try: - tube_registry.close_tube(tube_id, reason=reason) - except Exception as e: - logging.debug(f"{log_prefix}soft close_tube failed: {e}") - - has_local = hasattr(tube_registry, "force_close_tube") - has_remote = _version_at_least(remote_webrtc_version, FORCE_CLOSE_MIN_VERSION) - if not has_local: - logging.debug( - f"{log_prefix}force_close_tube unavailable in local keeper_pam_webrtc_rs - " - f"soft close only" - ) - return None - if not has_remote: - logging.debug( - f"{log_prefix}remote keeper-pam-webrtc {remote_webrtc_version!r} < " - f"{FORCE_CLOSE_MIN_VERSION} - soft close only" - ) - return None - - def _do_force_close(): - try: - logging.debug( - f"{log_prefix}escalating to force_close_tube({tube_id}) after " - f"{hard_after_seconds}s" - ) - tube_registry.force_close_tube(tube_id, reason=reason) - except Exception as e: - logging.debug(f"{log_prefix}force_close_tube failed: {e}") - - timer = threading.Timer(hard_after_seconds, _do_force_close) - timer.daemon = True - timer.start() - return timer - - # Constants NONCE_LENGTH = 12 MAIN_NONCE_LENGTH = 16 diff --git a/keepercommander/commands/tunnel_and_connections.py b/keepercommander/commands/tunnel_and_connections.py index 050d7b962..c5f165c35 100644 --- a/keepercommander/commands/tunnel_and_connections.py +++ b/keepercommander/commands/tunnel_and_connections.py @@ -33,7 +33,7 @@ get_keeper_tokens, \ get_or_create_tube_registry, get_gateway_uid_from_record, resolve_record, resolve_pam_config, resolve_folder, \ remove_field, start_rust_tunnel, get_tunnel_session, unregister_tunnel_session, CloseConnectionReasons, \ - wait_for_tunnel_connection, create_rust_webrtc_settings, escalate_close, \ + wait_for_tunnel_connection, create_rust_webrtc_settings, \ print_above_keeper_prompt from .pam.router_helper import get_dag_leafs from .tunnel_registry import ( @@ -1015,13 +1015,9 @@ def execute(self, params, **kwargs): self._print_keeperdb_proxy_banner(host, port, db_type_for_banner) # Workflow lease expiry handling. # - # At expiresOn we soft-close the tube (stops new channels, sends - # CloseConnection control frames) and, after a short delay, escalate - # to force_close_tube which drops the local TCP listener and severs - # any active forwarded streams (SSH, MySQL, etc.). The escalation - # only fires when both the local Rust crate and the remote peer - # advertise FORCE_CLOSE_MIN_VERSION; older peers get the soft close - # only and the in-flight session lingers until natural disconnect. + # At expiresOn we close the tube (stops new channels, sends + # CloseConnection control frames); the connection-closed cleanup + # path then stops the websocket and unregisters the tunnel session. if workflow_expires_on_ms and workflow_expires_on_ms > 0: seconds_until_expiry = (workflow_expires_on_ms / 1000.0) - time.time() tube_id = result.get('tube_id') @@ -1046,15 +1042,15 @@ def _close_on_lease_expiry(_tube_id=tube_id, _record_uid=record_uid): f"forwarded connections will be terminated." f"{bcolors.ENDC}" ) - sess = get_tunnel_session(_tube_id) - remote_ver = getattr(sess, 'remote_webrtc_version', None) if sess else None - escalate_close( - tube_registry, - _tube_id, - remote_webrtc_version=remote_ver, - reason=CloseConnectionReasons.AdminClosed, - log_prefix=f"[lease-expiry tunnel record={_record_uid}] ", - ) + try: + tube_registry.close_tube( + _tube_id, reason=CloseConnectionReasons.AdminClosed, + ) + except Exception as e: + logging.debug( + f"[lease-expiry tunnel record={_record_uid}] " + f"close_tube failed: {e}" + ) # Wake any --foreground / --run blocking wait so the # process self-terminates. Default interactive mode # does not register an event here. diff --git a/requirements.txt b/requirements.txt index 5264fa503..7af34666c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ requests>=2.31.0 cryptography>=46.0.6 protobuf>=5.29.6 keeper-secrets-manager-core>=16.6.0 -keeper_pam_webrtc_rs>=2.1.17 +keeper_pam_webrtc_rs>=2.1.18 pydantic>=2.6.4 flask pyngrok>=7.5.0 diff --git a/setup.cfg b/setup.cfg index dd9a215b7..6c52380c5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -53,7 +53,7 @@ install_requires = requests>=2.31.0 tabulate websockets - keeper_pam_webrtc_rs>=2.1.17 + keeper_pam_webrtc_rs>=2.1.18 pydantic>=2.6.4 fpdf2>=2.8.3 tzlocal>=5.0 From dc533fc1a98a8b9997c58022cfcd56648e2ca3e5 Mon Sep 17 00:00:00 2001 From: amangalampalli-ks Date: Tue, 2 Jun 2026 20:08:35 +0530 Subject: [PATCH 06/23] fix: limit enterprise object name lengths and change ei role column to string from dict (#2101) (#2104) --- keepercommander/commands/enterprise.py | 44 ++++++++++++++--- .../commands/helpers/enterprise.py | 49 +++++++++++++++++++ 2 files changed, 85 insertions(+), 8 deletions(-) diff --git a/keepercommander/commands/enterprise.py b/keepercommander/commands/enterprise.py index c6213d2b3..110e0d237 100644 --- a/keepercommander/commands/enterprise.py +++ b/keepercommander/commands/enterprise.py @@ -40,6 +40,7 @@ from .base import user_choice, suppress_exit, raise_parse_exception, dump_report_data, Command, field_to_title, \ report_output_parser from .enterprise_common import EnterpriseCommand +from .helpers.enterprise import is_valid_name_length, simplify_batch_responses from .automator import AutomatorListCommand from .enterprise_push import EnterprisePushCommand, enterprise_push_parser from .transfer_account import EnterpriseTransferUserCommand, transfer_user_parser @@ -811,10 +812,6 @@ def tree_node(node): role_ids.update(team_roles[team_uid]) if column == 'role_count': row.append(len(role_ids)) - elif kwargs.get('format') == 'json': - role_info = [{'role_id': rid, 'role_name': roles[rid]['name']} - for rid in role_ids if rid in roles] - row.append(role_info) else: role_names = [roles[role_id]['name'] for role_id in role_ids if role_id in roles] row.append(role_names) @@ -1170,7 +1167,10 @@ def execute(self, params, **kwargs): if not node_name or not node_name.strip(): logging.warning('Empty node name provided. Skipping.') continue - + + if not is_valid_name_length(node_name, 'Node name', 'enterprise-node'): + continue + n = node_lookup.get(node_name) if not n: n = node_lookup.get(node_name.lower()) @@ -1497,6 +1497,8 @@ def traverse_to_root(node_id, depth): return elif parent_id or kwargs.get('displayname'): display_name = kwargs.get('displayname') + if display_name and not is_valid_name_length(display_name, 'Node display name', 'enterprise-node'): + display_name = None def is_in_chain(node_id, parent_id): if node_id == parent_id: return True @@ -1506,8 +1508,10 @@ def is_in_chain(node_id, parent_id): return is_in_chain(nn['parent_id'], parent_id) if display_name and len(matched_nodes) > 1: - logging.warning('Cannot assign the same name to % nodes', len(matched_nodes)) + logging.warning('Cannot assign the same name to %s nodes', len(matched_nodes)) display_name = None + if not parent_id and not display_name: + return if not parent_id or not display_name: for node in matched_nodes: encrypted_data = node['encrypted_data'] @@ -1531,6 +1535,7 @@ def is_in_chain(node_id, parent_id): if request_batch: rss = api.execute_batch(params, request_batch) + simplify_batch_responses(rss) for rq, rs in zip(request_batch, rss): command = rq.get('command') if command == 'node_add': @@ -2000,6 +2005,7 @@ def execute(self, params, **kwargs): results = None if request_batch: results = api.execute_batch(params, request_batch) + simplify_batch_responses(results) for rq, rs in zip(request_batch, results): command = rq.get('command') if command == 'enterprise_user_add': @@ -2306,6 +2312,8 @@ def execute(self, params, **kwargs): # Collect role_ids for newly created roles new_role_ids = [] for role_name in role_names: + if not is_valid_name_length(role_name, 'Role name', 'enterprise-role'): + continue data = json.dumps({ "displayname": role_name }).encode('utf-8') role_id = self.get_enterprise_id(params) new_role_ids.append(role_id) @@ -2821,6 +2829,8 @@ def execute(self, params, **kwargs): role = matched_roles[0] if not role_name: role_name = role['data'].get('displayname') + if not is_valid_name_length(role_name, 'Role name', 'enterprise-role'): + return if not node_id: node_id = role['node_id'] dt = json.dumps({ "displayname": role_name }) @@ -2832,7 +2842,8 @@ def execute(self, params, **kwargs): "encrypted_data": utils.base64_url_encode( crypto.encrypt_aes_v1(dt.encode('utf-8'), params.enterprise['unencrypted_tree_key'])), "visible_below": role.get('visible_below') or False, - "new_user_inherit": role.get('new_user_inherit') or False + "new_user_inherit": role.get('new_user_inherit') or False, + "role_name": role_name, } request_batch.append(rq) if 'role_enforcements' in params.enterprise: @@ -2889,6 +2900,12 @@ def execute(self, params, **kwargs): logging.warning('Cannot assign the same name to %s roles', len(matched_roles)) kwargs['name'] = None + if kwargs.get('name') and not is_valid_name_length(kwargs.get('name'), 'Role name', 'enterprise-role'): + kwargs['name'] = None + + if not (node_id or kwargs.get('visible_below') or kwargs.get('new_user') or kwargs.get('name')): + return + for role in matched_roles: encrypted_data = role['encrypted_data'] if kwargs.get('name'): @@ -2910,11 +2927,12 @@ def execute(self, params, **kwargs): if request_batch: rss = api.execute_batch(params, request_batch) + simplify_batch_responses(rss) for rq, rs in zip(request_batch, rss): command = rq.get('command') if command == 'role_add': if rs['result'] == 'success': - logging.info('%s Role created with Role ID : %s', rq['role_name'], rq['role_id']) + logging.info('%s Role created with Role ID : %s', rq.get('role_name') or rq.get('role_id'), rq['role_id']) else: logging.warning('Failed to create role: %s', rs['message']) else: @@ -3403,6 +3421,8 @@ def execute(self, params, **kwargs): for item in queue: is_new_team = type(item) == str team_name = item if is_new_team else item['name'] + if is_new_team and not is_valid_name_length(team_name, 'Team name', 'enterprise-team'): + continue team_node_id = node_id if is_new_team else item['node_id'] team_uid = api.generate_record_uid() if is_new_team else item['team_uid'] team_key = api.generate_aes_key() @@ -3608,6 +3628,12 @@ def execute(self, params, **kwargs): logging.warning('Cannot set same name to %s teams', len(matched_teams)) kwargs['name'] = None + if kwargs.get('name') and not is_valid_name_length(kwargs.get('name'), 'Team name', 'enterprise-team'): + kwargs['name'] = None + + if not (node_id or kwargs.get('name') or kwargs.get('restrict_edit') or kwargs.get('restrict_share') or kwargs.get('restrict_view')): + return + for team in matched_teams: rq = { 'command': 'team_update', @@ -3622,6 +3648,7 @@ def execute(self, params, **kwargs): if request_batch: rss = api.execute_batch(params, request_batch) + simplify_batch_responses(rss) for rq, rs in zip(request_batch, rss): command = rq.get('command') team_name = None @@ -4014,6 +4041,7 @@ def execute(self, params, **kwargs): if request_batch: if not kwargs.get('dry_run'): rs = api.execute_batch(params, request_batch) + simplify_batch_responses(rs) if rs: team_add_success = 0 team_add_failure = 0 diff --git a/keepercommander/commands/helpers/enterprise.py b/keepercommander/commands/helpers/enterprise.py index cd0976876..97179f5f2 100644 --- a/keepercommander/commands/helpers/enterprise.py +++ b/keepercommander/commands/helpers/enterprise.py @@ -1,8 +1,57 @@ import logging +import re from ... import utils, crypto from ...params import KeeperParams +MAX_ENTERPRISE_NAME_LENGTH = 255 +_NAME_PREVIEW_LENGTH = 40 + +# Backend length-violation responses look like: ``max=185, length=255, value=`` +_BACKEND_LENGTH_ERROR_RE = re.compile( + r'max\s*=\s*(\d+)\s*,\s*length\s*=\s*(\d+)(?:\s*,\s*value\s*=.*)?', + re.IGNORECASE | re.DOTALL, +) + + +def is_valid_name_length(name, field_label, command_label): + """Return True if name fits within the enterprise name length limit; otherwise warn and return False.""" + if name is None: + return True + name = str(name) + if len(name) <= MAX_ENTERPRISE_NAME_LENGTH: + return True + preview = name[:_NAME_PREVIEW_LENGTH] + if len(name) > _NAME_PREVIEW_LENGTH: + preview += '...' + logging.warning( + '%s: %s \'%s\' is %d characters long. Maximum allowed is %d. Skipping.', + command_label, field_label, preview, len(name), MAX_ENTERPRISE_NAME_LENGTH, + ) + return False + + +def simplify_backend_message(message): + """Rewrite the backend's ``max=N, length=N, value=...`` length error into a friendlier sentence.""" + if not message or not isinstance(message, str): + return message + match = _BACKEND_LENGTH_ERROR_RE.search(message) + if not match: + return message + actual_len = int(match.group(2)) + max_len = int(match.group(1)) + return 'value is {0} characters but the maximum allowed is {1}'.format(actual_len, max_len) + + +def simplify_batch_responses(responses): + """Rewrite known noisy server validation messages in place on each response dict.""" + if not responses: + return + for rs in responses: + if isinstance(rs, dict) and rs.get('message'): + rs['message'] = simplify_backend_message(rs['message']) + + def is_addon_enabled(params, addon_name): # type: (KeeperParams, Dict[str, ]) -> Boolean def is_enabled(addon): return addon.get('enabled') or addon.get('included_in_product') From 1830206add168678384a74d5dac8c5aa239cf0e4 Mon Sep 17 00:00:00 2001 From: Sergey Kolupaev Date: Tue, 2 Jun 2026 12:02:49 -0700 Subject: [PATCH 07/23] 'epm scim' from AzureAD: use UPN as Username --- keepercommander/scim/data_sources.py | 149 +++++++++++++++++++++------ 1 file changed, 118 insertions(+), 31 deletions(-) diff --git a/keepercommander/scim/data_sources.py b/keepercommander/scim/data_sources.py index 98ebe945e..b90951a1a 100644 --- a/keepercommander/scim/data_sources.py +++ b/keepercommander/scim/data_sources.py @@ -5,7 +5,7 @@ import ssl from collections import namedtuple from contextlib import contextmanager -from typing import Iterable, Union, Callable, Dict, List, Optional, Set, Any +from typing import Iterable, Union, Callable, Dict, List, Optional, Set, Any, Tuple import requests @@ -575,12 +575,16 @@ def populate(self) -> Iterable[Union[ScimGroup, ScimUser]]: class AzureAdCrmDataSource(ICrmDataSource): - def __init__(self, tenant_id, client_id, client_secret, azure_cloud=None): # type: (str, str, str, Optional[str]) -> None + def __init__(self, tenant_id, client_id, client_secret, azure_cloud=None, resolve_membership=False): + # type: (str, str, str, Optional[str], bool) -> None super().__init__() self.tenant_id = tenant_id self.client_id = client_id self.client_secret = client_secret self.azure_cloud = azure_cloud or AZURE_GLOBAL_CLOUD + # Group membership resolution is opt-in: it is the slowest stage and is + # not consumed by every caller. Enable it only when membership is needed. + self.resolve_membership = resolve_membership def populate(self) -> Iterable[Union[ScimGroup, ScimUser]]: endpoints = self._resolve_cloud(self.azure_cloud) @@ -588,6 +592,7 @@ def populate(self) -> Iterable[Union[ScimGroup, ScimUser]]: token = self._get_token(endpoints) headers = {'Authorization': f'Bearer {token}'} + # 1. Load all groups groups = {} # type: Dict[str, ScimGroup] for group in self._paged_get(f'{graph_base}/v1.0/groups?$select=id,displayName', headers): group_id = group.get('id') @@ -599,45 +604,60 @@ def populate(self) -> Iterable[Union[ScimGroup, ScimUser]]: sg.external_id = group_id sg.name = display_name groups[group_id] = sg + self.debug_logger(f'Loaded {len(groups)} Azure AD group(s)') + # 2. Load all users users = {} # type: Dict[str, ScimUser] - for group_id in groups: - for member in self._paged_get( - f'{graph_base}/v1.0/groups/{group_id}/members?$select=id,userPrincipalName,mail,displayName,givenName,surname,accountEnabled', - headers): - if member.get('@odata.type') != '#microsoft.graph.user': - continue + for member in self._paged_get( + f'{graph_base}/v1.0/users?$select=id,userPrincipalName,mail,displayName,givenName,surname,accountEnabled,userType&$top=999', + headers): + su = self._parse_user(member) + if su: + users[su.id] = su + self.debug_logger(f'Loaded {len(users)} Azure AD user(s)') + + # 3. Resolve group membership (in batches), when requested. + # Only the member "id" is selected here - the user objects are already + # loaded in step 2, so membership is just a mapping from member id to group id. + if self.resolve_membership: + member_requests = [(group_id, f'/v1.0/groups/{group_id}/members?$select=id&$top=999') + for group_id in groups] + for group_id, member in self._batch_get(graph_base, headers, member_requests): user_id = member.get('id') if not user_id: continue - if str(member.get('userType') or '').lower() == 'guest': - continue su = users.get(user_id) - if not su: - su = ScimUser() - su.id = user_id - su.external_id = user_id - upn = member.get('userPrincipalName') or '' - if '#EXT#' in upn: - continue - su.email = member.get('mail') or upn or '' - if upn: - if '@' in upn: - su.login = upn.split('@', 1)[0] - su.domain = upn.split('@', 1)[1] - else: - su.login = upn - su.first_name = member.get('givenName') or '' - su.last_name = member.get('surname') or '' - display_name = member.get('displayName') or '' - su.full_name = display_name or f'{su.first_name} {su.last_name}'.strip() - su.active = member.get('accountEnabled') is True - users[user_id] = su - su.groups.append(group_id) + if su: + # non-user members (nested groups, devices, etc.) are not in `users` + su.groups.append(group_id) + else: + self.debug_logger('Skipping Azure AD group membership resolution') yield from groups.values() yield from users.values() + @staticmethod + def _parse_user(member): # type: (dict) -> Optional[ScimUser] + user_id = member.get('id') + if not user_id: + return None + if str(member.get('userType') or '').lower() == 'guest': + return None + upn = member.get('userPrincipalName') or '' + if '#EXT#' in upn: + return None + su = ScimUser() + su.id = user_id + su.external_id = user_id + su.login = upn + su.email = member.get('mail') or '' + su.first_name = member.get('givenName') or '' + su.last_name = member.get('surname') or '' + display_name = member.get('displayName') or '' + su.full_name = display_name or f'{su.first_name} {su.last_name}'.strip() + su.active = member.get('accountEnabled') is True + return su + def _get_token(self, endpoints) -> str: url = f'{endpoints.activeDirectory}/{self.tenant_id}/oauth2/v2.0/token' data = { @@ -666,6 +686,73 @@ def _paged_get(url, headers): yield item next_url = body.get('@odata.nextLink') + # Microsoft Graph JSON batching allows up to 20 sub-requests per HTTP call. + BATCH_SIZE = 20 + + @classmethod + def _batch_get(cls, graph_base, headers, requests_map): + # type: (str, dict, List[Tuple[str, str]]) -> Iterable[Tuple[str, dict]] + """Execute many GET requests using Microsoft Graph JSON batching. + + Combines up to BATCH_SIZE requests into a single call to /$batch, which + dramatically reduces round-trips compared to issuing one request per group. + + :param graph_base: Graph resource base, e.g. 'https://graph.microsoft.com' + :param headers: auth headers (Authorization) + :param requests_map: list of (tag, relative_url) tuples. The relative URL is + rooted at the version segment, e.g. '/v1.0/groups/{id}/members?...'. + :return: iterator of (tag, item) pairs, where item is one element of the + 'value' array of the corresponding response. Paged responses + (@odata.nextLink) and throttled requests (429) are re-queued. + """ + import time + + batch_url = f'{graph_base}/v1.0/$batch' + batch_headers = dict(headers) + batch_headers['Content-Type'] = 'application/json' + + pending = list(requests_map) # type: List[Tuple[str, str]] + while pending: + chunk = pending[:cls.BATCH_SIZE] + pending = pending[cls.BATCH_SIZE:] + + payload = {'requests': [{'id': str(idx), 'method': 'GET', 'url': url} + for idx, (_, url) in enumerate(chunk)]} + rs = requests.post(batch_url, headers=batch_headers, json=payload) + if rs.status_code != 200: + raise CommandError('', f'Azure AD batch request failed: {rs.status_code}') + + responses = {r.get('id'): r for r in rs.json().get('responses', [])} + retry_after = 0 + for idx, (tag, url) in enumerate(chunk): + resp = responses.get(str(idx)) + if not resp: + continue + status = resp.get('status') + if status == 429: + # Throttled - re-queue and honor the largest Retry-After in the batch. + resp_headers = resp.get('headers') or {} + try: + retry_after = max(retry_after, int(resp_headers.get('Retry-After', 0))) + except (TypeError, ValueError): + pass + pending.append((tag, url)) + continue + if status != 200: + continue + body = resp.get('body') or {} + for item in body.get('value', []): + yield tag, item + next_link = body.get('@odata.nextLink') + if next_link: + # Re-queue the next page as a relative URL for a subsequent batch. + if next_link.startswith(graph_base): + next_link = next_link[len(graph_base):] + pending.append((tag, next_link)) + + if retry_after: + time.sleep(retry_after) + @staticmethod def _resolve_cloud(azure_cloud): # normalize common aliases From 34f7391f7090d3690fc124cced1abc7adc2970a9 Mon Sep 17 00:00:00 2001 From: Sergey Kolupaev Date: Tue, 2 Jun 2026 15:46:00 -0700 Subject: [PATCH 08/23] Update proto files --- keepercommander/proto/APIRequest_pb2.py | 523 ++++++------ keepercommander/proto/APIRequest_pb2.pyi | 65 +- keepercommander/proto/AccountSummary_pb2.py | 11 + keepercommander/proto/AccountSummary_pb2.pyi | 25 +- keepercommander/proto/BI_pb2.py | 339 +++++--- keepercommander/proto/BI_pb2.pyi | 563 +++++++++++-- keepercommander/proto/DeviceManagement_pb2.py | 14 +- keepercommander/proto/GraphSync_pb2.py | 11 + keepercommander/proto/GraphSync_pb2.pyi | 5 +- .../proto/NotificationCenter_pb2.py | 79 +- .../proto/NotificationCenter_pb2.pyi | 11 +- keepercommander/proto/SyncDown_pb2.py | 11 + keepercommander/proto/SyncDown_pb2.pyi | 35 +- keepercommander/proto/automator_pb2.py | 147 ++-- keepercommander/proto/automator_pb2.pyi | 108 ++- keepercommander/proto/breachwatch_pb2.py | 15 +- keepercommander/proto/breachwatch_pb2.pyi | 48 +- keepercommander/proto/client_pb2.py | 15 +- keepercommander/proto/client_pb2.pyi | 12 +- keepercommander/proto/dag_pb2.py | 14 +- keepercommander/proto/dag_pb2.pyi | 5 +- keepercommander/proto/enterprise_pb2.py | 641 +++++++------- keepercommander/proto/enterprise_pb2.pyi | 118 +-- keepercommander/proto/folder_access_pb2.py | 11 + keepercommander/proto/folder_access_pb2.pyi | 5 +- keepercommander/proto/folder_pb2.py | 13 +- keepercommander/proto/folder_pb2.pyi | 21 +- keepercommander/proto/pagination_pb2.py | 11 + keepercommander/proto/pagination_pb2.pyi | 2 +- keepercommander/proto/pam_pb2.py | 787 +++--------------- keepercommander/proto/pam_pb2.pyi | 243 +++++- keepercommander/proto/pedm_pb2.py | 252 +++--- keepercommander/proto/pedm_pb2.pyi | 134 +-- keepercommander/proto/publicapi_pb2.py | 213 ++--- keepercommander/proto/publicapi_pb2.pyi | 161 ++++ keepercommander/proto/record_details_pb2.py | 12 + keepercommander/proto/record_details_pb2.pyi | 3 +- keepercommander/proto/record_endpoints_pb2.py | 11 + .../proto/record_endpoints_pb2.pyi | 3 +- keepercommander/proto/record_pb2.py | 10 + keepercommander/proto/record_pb2.pyi | 29 +- keepercommander/proto/record_sharing_pb2.py | 11 + keepercommander/proto/record_sharing_pb2.pyi | 7 +- keepercommander/proto/remove_pb2.py | 11 +- keepercommander/proto/remove_pb2.pyi | 3 +- keepercommander/proto/rmd_pb2.py | 21 +- keepercommander/proto/rmd_pb2.pyi | 30 +- keepercommander/proto/router_pb2.py | 183 ++-- keepercommander/proto/router_pb2.pyi | 94 ++- keepercommander/proto/ssocloud_pb2.py | 31 +- keepercommander/proto/ssocloud_pb2.pyi | 62 +- keepercommander/proto/tla_pb2.py | 13 +- keepercommander/proto/tla_pb2.pyi | 2 +- keepercommander/proto/version_pb2.py | 15 +- keepercommander/proto/version_pb2.pyi | 2 +- keepercommander/proto/workflow_pb2.py | 12 + keepercommander/proto/workflow_pb2.pyi | 13 +- requirements.txt | 2 +- setup.cfg | 2 +- 59 files changed, 2929 insertions(+), 2321 deletions(-) create mode 100644 keepercommander/proto/publicapi_pb2.pyi diff --git a/keepercommander/proto/APIRequest_pb2.py b/keepercommander/proto/APIRequest_pb2.py index 4167880b6..8f4543ddb 100644 --- a/keepercommander/proto/APIRequest_pb2.py +++ b/keepercommander/proto/APIRequest_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: APIRequest.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'APIRequest.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,7 +25,7 @@ from . import enterprise_pb2 as enterprise__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x41PIRequest.proto\x12\x0e\x41uthentication\x1a\x10\x65nterprise.proto\"{\n\rQrcMessageKey\x12\x19\n\x11\x63lientEcPublicKey\x18\x01 \x01(\x0c\x12\x1c\n\x14mlKemEncapsulatedKey\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12\x12\n\nmsgVersion\x18\x04 \x01(\x05\x12\x0f\n\x07\x65\x63KeyId\x18\x05 \x01(\x05\"\xe6\x01\n\nApiRequest\x12 \n\x18\x65ncryptedTransmissionKey\x18\x01 \x01(\x0c\x12\x13\n\x0bpublicKeyId\x18\x02 \x01(\x05\x12\x0e\n\x06locale\x18\x03 \x01(\t\x12\x18\n\x10\x65ncryptedPayload\x18\x04 \x01(\x0c\x12\x16\n\x0e\x65ncryptionType\x18\x05 \x01(\x05\x12\x11\n\trecaptcha\x18\x06 \x01(\t\x12\x16\n\x0esubEnvironment\x18\x07 \x01(\t\x12\x34\n\rqrcMessageKey\x18\x08 \x01(\x0b\x32\x1d.Authentication.QrcMessageKey\"j\n\x11\x41piRequestPayload\x12\x0f\n\x07payload\x18\x01 \x01(\x0c\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x02 \x01(\x0c\x12\x11\n\ttimeToken\x18\x03 \x01(\x0c\x12\x12\n\napiVersion\x18\x04 \x01(\x05\"6\n\tTransform\x12\x0b\n\x03key\x18\x01 \x01(\x0c\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x02 \x01(\x0c\"\xa0\x01\n\rDeviceRequest\x12\x15\n\rclientVersion\x18\x01 \x01(\t\x12\x12\n\ndeviceName\x18\x02 \x01(\t\x12\x16\n\x0e\x64\x65vicePlatform\x18\x03 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x04 \x01(\x0e\x32 .Authentication.ClientFormFactor\x12\x10\n\x08username\x18\x05 \x01(\t\"T\n\x0b\x41uthRequest\x12\x15\n\rclientVersion\x18\x01 \x01(\t\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x03 \x01(\x0c\"\xc3\x01\n\x14NewUserMinimumParams\x12\x19\n\x11minimumIterations\x18\x01 \x01(\x05\x12\x1a\n\x12passwordMatchRegex\x18\x02 \x03(\t\x12 \n\x18passwordMatchDescription\x18\x03 \x03(\t\x12\x1a\n\x12isEnterpriseDomain\x18\x04 \x01(\x08\x12\x1e\n\x16\x65nterpriseEccPublicKey\x18\x05 \x01(\x0c\x12\x16\n\x0e\x66orbidKeyType2\x18\x06 \x01(\x08\"\x89\x01\n\x0fPreLoginRequest\x12\x30\n\x0b\x61uthRequest\x18\x01 \x01(\x0b\x32\x1b.Authentication.AuthRequest\x12,\n\tloginType\x18\x02 \x01(\x0e\x32\x19.Authentication.LoginType\x12\x16\n\x0etwoFactorToken\x18\x03 \x01(\x0c\"\x80\x02\n\x0cLoginRequest\x12\x30\n\x0b\x61uthRequest\x18\x01 \x01(\x0b\x32\x1b.Authentication.AuthRequest\x12,\n\tloginType\x18\x02 \x01(\x0e\x32\x19.Authentication.LoginType\x12\x1f\n\x17\x61uthenticationHashPrime\x18\x03 \x01(\x0c\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x04 \x01(\x0c\x12\x14\n\x0c\x61uthResponse\x18\x05 \x01(\x0c\x12\x16\n\x0emcEnterpriseId\x18\x06 \x01(\x05\x12\x12\n\npush_token\x18\x07 \x01(\t\x12\x10\n\x08platform\x18\x08 \x01(\t\"\\\n\x0e\x44\x65viceResponse\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12,\n\x06status\x18\x02 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\"V\n\x04Salt\x12\x12\n\niterations\x18\x01 \x01(\x05\x12\x0c\n\x04salt\x18\x02 \x01(\x0c\x12\x11\n\talgorithm\x18\x03 \x01(\x05\x12\x0b\n\x03uid\x18\x04 \x01(\x0c\x12\x0c\n\x04name\x18\x05 \x01(\t\" \n\x10TwoFactorChannel\x12\x0c\n\x04type\x18\x01 \x01(\x05\"\xfc\x02\n\x11StartLoginRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x15\n\rclientVersion\x18\x03 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x04 \x01(\x0c\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x05 \x01(\x0c\x12,\n\tloginType\x18\x06 \x01(\x0e\x32\x19.Authentication.LoginType\x12\x16\n\x0emcEnterpriseId\x18\x07 \x01(\x05\x12\x30\n\x0bloginMethod\x18\x08 \x01(\x0e\x32\x1b.Authentication.LoginMethod\x12\x15\n\rforceNewLogin\x18\t \x01(\x08\x12\x11\n\tcloneCode\x18\n \x01(\x0c\x12\x18\n\x10v2TwoFactorToken\x18\x0b \x01(\t\x12\x12\n\naccountUid\x18\x0c \x01(\x0c\x12\x18\n\x10\x66romSessionToken\x18\r \x01(\x0c\"\xa7\x04\n\rLoginResponse\x12.\n\nloginState\x18\x01 \x01(\x0e\x32\x1a.Authentication.LoginState\x12\x12\n\naccountUid\x18\x02 \x01(\x0c\x12\x17\n\x0fprimaryUsername\x18\x03 \x01(\t\x12\x18\n\x10\x65ncryptedDataKey\x18\x04 \x01(\x0c\x12\x42\n\x14\x65ncryptedDataKeyType\x18\x05 \x01(\x0e\x32$.Authentication.EncryptedDataKeyType\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x06 \x01(\x0c\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x07 \x01(\x0c\x12:\n\x10sessionTokenType\x18\x08 \x01(\x0e\x32 .Authentication.SessionTokenType\x12\x0f\n\x07message\x18\t \x01(\t\x12\x0b\n\x03url\x18\n \x01(\t\x12\x36\n\x08\x63hannels\x18\x0b \x03(\x0b\x32$.Authentication.TwoFactorChannelInfo\x12\"\n\x04salt\x18\x0c \x03(\x0b\x32\x14.Authentication.Salt\x12\x11\n\tcloneCode\x18\r \x01(\x0c\x12\x1a\n\x12stateSpecificValue\x18\x0e \x01(\t\x12\x18\n\x10ssoClientVersion\x18\x0f \x01(\t\x12 \n\x18sessionTokenTypeModifier\x18\x10 \x01(\t\"v\n\x11SwitchListElement\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08\x66ullName\x18\x02 \x01(\t\x12\x14\n\x0c\x61uthRequired\x18\x03 \x01(\x08\x12\x10\n\x08isLinked\x18\x04 \x01(\x08\x12\x15\n\rprofilePicUrl\x18\x05 \x01(\t\"I\n\x12SwitchListResponse\x12\x33\n\x08\x65lements\x18\x01 \x03(\x0b\x32!.Authentication.SwitchListElement\"\x8c\x01\n\x0bSsoUserInfo\x12\x13\n\x0b\x63ompanyName\x18\x01 \x01(\t\x12\x13\n\x0bsamlRequest\x18\x02 \x01(\t\x12\x17\n\x0fsamlRequestType\x18\x03 \x01(\t\x12\x15\n\rssoDomainName\x18\x04 \x01(\t\x12\x10\n\x08loginUrl\x18\x05 \x01(\t\x12\x11\n\tlogoutUrl\x18\x06 \x01(\t\"\xd6\x01\n\x10PreLoginResponse\x12\x32\n\x0c\x64\x65viceStatus\x18\x01 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\x12\"\n\x04salt\x18\x02 \x03(\x0b\x32\x14.Authentication.Salt\x12\x38\n\x0eOBSOLETE_FIELD\x18\x03 \x03(\x0b\x32 .Authentication.TwoFactorChannel\x12\x30\n\x0bssoUserInfo\x18\x04 \x01(\x0b\x32\x1b.Authentication.SsoUserInfo\"&\n\x12LoginAsUserRequest\x12\x10\n\x08username\x18\x01 \x01(\t\"W\n\x13LoginAsUserResponse\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x01 \x01(\x0c\x12!\n\x19\x65ncryptedSharedAccountKey\x18\x02 \x01(\x0c\"\x84\x01\n\x17ValidateAuthHashRequest\x12\x36\n\x0epasswordMethod\x18\x01 \x01(\x0e\x32\x1e.Authentication.PasswordMethod\x12\x14\n\x0c\x61uthResponse\x18\x02 \x01(\x0c\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x03 \x01(\x0c\"\xc4\x02\n\x14TwoFactorChannelInfo\x12\x39\n\x0b\x63hannelType\x18\x01 \x01(\x0e\x32$.Authentication.TwoFactorChannelType\x12\x13\n\x0b\x63hannel_uid\x18\x02 \x01(\x0c\x12\x13\n\x0b\x63hannelName\x18\x03 \x01(\t\x12\x11\n\tchallenge\x18\x04 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x13\n\x0bphoneNumber\x18\x06 \x01(\t\x12:\n\rmaxExpiration\x18\x07 \x01(\x0e\x32#.Authentication.TwoFactorExpiration\x12\x11\n\tcreatedOn\x18\x08 \x01(\x03\x12:\n\rlastFrequency\x18\t \x01(\x0e\x32#.Authentication.TwoFactorExpiration\"d\n\x12TwoFactorDuoStatus\x12\x14\n\x0c\x63\x61pabilities\x18\x01 \x03(\t\x12\x13\n\x0bphoneNumber\x18\x02 \x01(\t\x12\x12\n\nenroll_url\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\"\xc7\x01\n\x13TwoFactorAddRequest\x12\x39\n\x0b\x63hannelType\x18\x01 \x01(\x0e\x32$.Authentication.TwoFactorChannelType\x12\x13\n\x0b\x63hannel_uid\x18\x02 \x01(\x0c\x12\x13\n\x0b\x63hannelName\x18\x03 \x01(\t\x12\x13\n\x0bphoneNumber\x18\x04 \x01(\t\x12\x36\n\x0b\x64uoPushType\x18\x05 \x01(\x0e\x32!.Authentication.TwoFactorPushType\"B\n\x16TwoFactorRenameRequest\x12\x13\n\x0b\x63hannel_uid\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63hannelName\x18\x02 \x01(\t\"=\n\x14TwoFactorAddResponse\x12\x11\n\tchallenge\x18\x01 \x01(\t\x12\x12\n\nbackupKeys\x18\x02 \x03(\t\"-\n\x16TwoFactorDeleteRequest\x12\x13\n\x0b\x63hannel_uid\x18\x01 \x01(\x0c\"a\n\x15TwoFactorListResponse\x12\x36\n\x08\x63hannels\x18\x01 \x03(\x0b\x32$.Authentication.TwoFactorChannelInfo\x12\x10\n\x08\x65xpireOn\x18\x02 \x01(\x03\"Y\n TwoFactorUpdateExpirationRequest\x12\x35\n\x08\x65xpireIn\x18\x01 \x01(\x0e\x32#.Authentication.TwoFactorExpiration\"\xc9\x01\n\x18TwoFactorValidateRequest\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\x12\x35\n\tvalueType\x18\x02 \x01(\x0e\x32\".Authentication.TwoFactorValueType\x12\r\n\x05value\x18\x03 \x01(\t\x12\x13\n\x0b\x63hannel_uid\x18\x04 \x01(\x0c\x12\x35\n\x08\x65xpireIn\x18\x05 \x01(\x0e\x32#.Authentication.TwoFactorExpiration\"8\n\x19TwoFactorValidateResponse\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\"\xb8\x01\n\x18TwoFactorSendPushRequest\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\x12\x33\n\x08pushType\x18\x02 \x01(\x0e\x32!.Authentication.TwoFactorPushType\x12\x13\n\x0b\x63hannel_uid\x18\x03 \x01(\x0c\x12\x35\n\x08\x65xpireIn\x18\x04 \x01(\x0e\x32#.Authentication.TwoFactorExpiration\"\x83\x01\n\x07License\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x12\n\nexpiration\x18\x02 \x01(\x03\x12\x34\n\rlicenseStatus\x18\x03 \x01(\x0e\x32\x1d.Authentication.LicenseStatus\x12\x0c\n\x04paid\x18\x04 \x01(\x08\x12\x0f\n\x07message\x18\x05 \x01(\t\"G\n\x0fOwnerlessRecord\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x11\n\trecordKey\x18\x02 \x01(\x0c\x12\x0e\n\x06status\x18\x03 \x01(\x05\"L\n\x10OwnerlessRecords\x12\x38\n\x0fownerlessRecord\x18\x01 \x03(\x0b\x32\x1f.Authentication.OwnerlessRecord\"\xd7\x01\n\x0fUserAuthRequest\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0c\n\x04salt\x18\x02 \x01(\x0c\x12\x12\n\niterations\x18\x03 \x01(\x05\x12\x1a\n\x12\x65ncryptedClientKey\x18\x04 \x01(\x0c\x12\x10\n\x08\x61uthHash\x18\x05 \x01(\x0c\x12\x18\n\x10\x65ncryptedDataKey\x18\x06 \x01(\x0c\x12,\n\tloginType\x18\x07 \x01(\x0e\x32\x19.Authentication.LoginType\x12\x0c\n\x04name\x18\x08 \x01(\t\x12\x11\n\talgorithm\x18\t \x01(\x05\"\x19\n\nUidRequest\x12\x0b\n\x03uid\x18\x01 \x03(\x0c\"\xff\x01\n\x13\x44\x65viceUpdateRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x12\n\ndeviceName\x18\x03 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x04 \x01(\x0c\x12\x32\n\x0c\x64\x65viceStatus\x18\x05 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\x12\x16\n\x0e\x64\x65vicePlatform\x18\x06 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x07 \x01(\x0e\x32 .Authentication.ClientFormFactor\"\x80\x02\n\x14\x44\x65viceUpdateResponse\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x12\n\ndeviceName\x18\x03 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x04 \x01(\x0c\x12\x32\n\x0c\x64\x65viceStatus\x18\x05 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\x12\x16\n\x0e\x64\x65vicePlatform\x18\x06 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x07 \x01(\x0e\x32 .Authentication.ClientFormFactor\"\xd5\x01\n\x1dRegisterDeviceInRegionRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x12\n\ndeviceName\x18\x03 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x04 \x01(\x0c\x12\x16\n\x0e\x64\x65vicePlatform\x18\x05 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x06 \x01(\x0e\x32 .Authentication.ClientFormFactor\"\xf8\x02\n\x13RegistrationRequest\x12\x30\n\x0b\x61uthRequest\x18\x01 \x01(\x0b\x32\x1b.Authentication.AuthRequest\x12\x38\n\x0fuserAuthRequest\x18\x02 \x01(\x0b\x32\x1f.Authentication.UserAuthRequest\x12\x1a\n\x12\x65ncryptedClientKey\x18\x03 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x04 \x01(\x0c\x12\x11\n\tpublicKey\x18\x05 \x01(\x0c\x12\x18\n\x10verificationCode\x18\x06 \x01(\t\x12\x1e\n\x16\x64\x65precatedAuthHashHash\x18\x07 \x01(\x0c\x12$\n\x1c\x64\x65precatedEncryptedClientKey\x18\x08 \x01(\x0c\x12%\n\x1d\x64\x65precatedEncryptedPrivateKey\x18\t \x01(\x0c\x12\"\n\x1a\x64\x65precatedEncryptionParams\x18\n \x01(\x0c\"\xd0\x01\n\x16\x43onvertUserToV3Request\x12\x30\n\x0b\x61uthRequest\x18\x01 \x01(\x0b\x32\x1b.Authentication.AuthRequest\x12\x38\n\x0fuserAuthRequest\x18\x02 \x01(\x0b\x32\x1f.Authentication.UserAuthRequest\x12\x1a\n\x12\x65ncryptedClientKey\x18\x03 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x04 \x01(\x0c\x12\x11\n\tpublicKey\x18\x05 \x01(\x0c\"$\n\x10RevisionResponse\x12\x10\n\x08revision\x18\x01 \x01(\x03\"&\n\x12\x43hangeEmailRequest\x12\x10\n\x08newEmail\x18\x01 \x01(\t\"8\n\x13\x43hangeEmailResponse\x12!\n\x19\x65ncryptedChangeEmailToken\x18\x01 \x01(\x0c\"6\n\x1d\x45mailVerificationLinkResponse\x12\x15\n\remailVerified\x18\x01 \x01(\x08\")\n\x0cSecurityData\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"@\n\x11SecurityScoreData\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x10\n\x08revision\x18\x03 \x01(\x03\"\x8b\x02\n\x13SecurityDataRequest\x12\x38\n\x12recordSecurityData\x18\x01 \x03(\x0b\x32\x1c.Authentication.SecurityData\x12@\n\x1amasterPasswordSecurityData\x18\x02 \x03(\x0b\x32\x1c.Authentication.SecurityData\x12\x34\n\x0e\x65ncryptionType\x18\x03 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x42\n\x17recordSecurityScoreData\x18\x04 \x03(\x0b\x32!.Authentication.SecurityScoreData\"\xc6\x02\n\x1dSecurityReportIncrementalData\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1b\n\x13\x63urrentSecurityData\x18\x02 \x01(\x0c\x12#\n\x1b\x63urrentSecurityDataRevision\x18\x03 \x01(\x03\x12\x17\n\x0foldSecurityData\x18\x04 \x01(\x0c\x12\x1f\n\x17oldSecurityDataRevision\x18\x05 \x01(\x03\x12?\n\x19\x63urrentDataEncryptionType\x18\x06 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12;\n\x15oldDataEncryptionType\x18\x07 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x11\n\trecordUid\x18\x08 \x01(\x0c\"\x9f\x02\n\x0eSecurityReport\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1b\n\x13\x65ncryptedReportData\x18\x02 \x01(\x0c\x12\x10\n\x08revision\x18\x03 \x01(\x03\x12\x11\n\ttwoFactor\x18\x04 \x01(\t\x12\x11\n\tlastLogin\x18\x05 \x01(\x03\x12\x1e\n\x16numberOfReusedPassword\x18\x06 \x01(\x05\x12T\n\x1dsecurityReportIncrementalData\x18\x07 \x03(\x0b\x32-.Authentication.SecurityReportIncrementalData\x12\x0e\n\x06userId\x18\x08 \x01(\x05\x12\x18\n\x10hasOldEncryption\x18\t \x01(\x08\"n\n\x19SecurityReportSaveRequest\x12\x36\n\x0esecurityReport\x18\x01 \x03(\x0b\x32\x1e.Authentication.SecurityReport\x12\x19\n\x11\x63ontinuationToken\x18\x02 \x01(\x0c\")\n\x15SecurityReportRequest\x12\x10\n\x08\x66romPage\x18\x01 \x01(\x03\"\xf5\x01\n\x16SecurityReportResponse\x12\x1c\n\x14\x65nterprisePrivateKey\x18\x01 \x01(\x0c\x12\x36\n\x0esecurityReport\x18\x02 \x03(\x0b\x32\x1e.Authentication.SecurityReport\x12\x14\n\x0c\x61sOfRevision\x18\x03 \x01(\x03\x12\x10\n\x08\x66romPage\x18\x04 \x01(\x03\x12\x0e\n\x06toPage\x18\x05 \x01(\x03\x12\x10\n\x08\x63omplete\x18\x06 \x01(\x08\x12\x1f\n\x17\x65nterpriseEccPrivateKey\x18\x07 \x01(\x0c\x12\x1a\n\x12hasIncrementalData\x18\x08 \x01(\x08\";\n\x1eIncrementalSecurityDataRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\"\x92\x01\n\x1fIncrementalSecurityDataResponse\x12T\n\x1dsecurityReportIncrementalData\x18\x01 \x03(\x0b\x32-.Authentication.SecurityReportIncrementalData\x12\x19\n\x11\x63ontinuationToken\x18\x02 \x01(\x0c\"\'\n\x16ReusedPasswordsRequest\x12\r\n\x05\x63ount\x18\x01 \x01(\x05\">\n\x14SummaryConsoleReport\x12\x12\n\nreportType\x18\x01 \x01(\x05\x12\x12\n\nreportData\x18\x02 \x01(\x0c\"|\n\x12\x43hangeToKeyTypeOne\x12/\n\nobjectType\x18\x01 \x01(\x0e\x32\x1b.Authentication.ObjectTypes\x12\x12\n\nprimaryUid\x18\x02 \x01(\x0c\x12\x14\n\x0csecondaryUid\x18\x03 \x01(\x0c\x12\x0b\n\x03key\x18\x04 \x01(\x0c\"[\n\x19\x43hangeToKeyTypeOneRequest\x12>\n\x12\x63hangeToKeyTypeOne\x18\x01 \x03(\x0b\x32\".Authentication.ChangeToKeyTypeOne\"U\n\x18\x43hangeToKeyTypeOneStatus\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x0e\n\x06reason\x18\x04 \x01(\t\"h\n\x1a\x43hangeToKeyTypeOneResponse\x12J\n\x18\x63hangeToKeyTypeOneStatus\x18\x01 \x03(\x0b\x32(.Authentication.ChangeToKeyTypeOneStatus\"\xb9\x01\n\x18GetChangeKeyTypesRequest\x12=\n\x10onlyTheseObjects\x18\x01 \x03(\x0e\x32#.Authentication.EncryptedObjectType\x12\r\n\x05limit\x18\x02 \x01(\x05\x12\x1a\n\x12includeRecommended\x18\x03 \x01(\x08\x12\x13\n\x0bincludeKeys\x18\x04 \x01(\x08\x12\x1e\n\x16includeAllowedKeyTypes\x18\x05 \x01(\x08\"\x82\x01\n\x19GetChangeKeyTypesResponse\x12+\n\x04keys\x18\x01 \x03(\x0b\x32\x1d.Authentication.ChangeKeyType\x12\x38\n\x0f\x61llowedKeyTypes\x18\x02 \x03(\x0b\x32\x1f.Authentication.AllowedKeyTypes\"\x81\x01\n\x0f\x41llowedKeyTypes\x12\x37\n\nobjectType\x18\x01 \x01(\x0e\x32#.Authentication.EncryptedObjectType\x12\x35\n\x0f\x61llowedKeyTypes\x18\x02 \x03(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"=\n\x0e\x43hangeKeyTypes\x12+\n\x04keys\x18\x01 \x03(\x0b\x32\x1d.Authentication.ChangeKeyType\"\xd6\x01\n\rChangeKeyType\x12\x37\n\nobjectType\x18\x01 \x01(\x0e\x32#.Authentication.EncryptedObjectType\x12\x0b\n\x03uid\x18\x02 \x01(\x0c\x12\x14\n\x0csecondaryUid\x18\x03 \x01(\x0c\x12\x0b\n\x03key\x18\x04 \x01(\x0c\x12-\n\x07keyType\x18\x05 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12-\n\x06status\x18\x06 \x01(\x0e\x32\x1d.Authentication.GenericStatus\"!\n\x06SetKey\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0b\n\x03key\x18\x02 \x01(\x0c\"5\n\rSetKeyRequest\x12$\n\x04keys\x18\x01 \x03(\x0b\x32\x16.Authentication.SetKey\"\x92\x05\n\x11\x43reateUserRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x14\n\x0c\x61uthVerifier\x18\x02 \x01(\x0c\x12\x18\n\x10\x65ncryptionParams\x18\x03 \x01(\x0c\x12\x14\n\x0crsaPublicKey\x18\x04 \x01(\x0c\x12\x1e\n\x16rsaEncryptedPrivateKey\x18\x05 \x01(\x0c\x12\x14\n\x0c\x65\x63\x63PublicKey\x18\x06 \x01(\x0c\x12\x1e\n\x16\x65\x63\x63\x45ncryptedPrivateKey\x18\x07 \x01(\x0c\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x08 \x01(\x0c\x12\x1a\n\x12\x65ncryptedClientKey\x18\t \x01(\x0c\x12\x15\n\rclientVersion\x18\n \x01(\t\x12\x1e\n\x16\x65ncryptedDeviceDataKey\x18\x0b \x01(\x0c\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x0c \x01(\x0c\x12\x19\n\x11messageSessionUid\x18\r \x01(\x0c\x12\x17\n\x0finstallReferrer\x18\x0e \x01(\t\x12\x0e\n\x06mccMNC\x18\x0f \x01(\x05\x12\x0b\n\x03mfg\x18\x10 \x01(\t\x12\r\n\x05model\x18\x11 \x01(\t\x12\r\n\x05\x62rand\x18\x12 \x01(\t\x12\x0f\n\x07product\x18\x13 \x01(\t\x12\x0e\n\x06\x64\x65vice\x18\x14 \x01(\t\x12\x0f\n\x07\x63\x61rrier\x18\x15 \x01(\t\x12\x18\n\x10verificationCode\x18\x16 \x01(\t\x12\x42\n\x16\x65nterpriseRegistration\x18\x17 \x01(\x0b\x32\".Enterprise.EnterpriseRegistration\x12\"\n\x1a\x65ncryptedVerificationToken\x18\x18 \x01(\x0c\x12\x1e\n\x16\x65nterpriseUsersDataKey\x18\x19 \x01(\x0c\"W\n!NodeEnforcementAddOrUpdateRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x13\n\x0b\x65nforcement\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\"C\n\x1cNodeEnforcementRemoveRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x13\n\x0b\x65nforcement\x18\x02 \x01(\t\"\x9f\x01\n\x0f\x41piRequestByKey\x12\r\n\x05keyId\x18\x01 \x01(\x05\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x10\n\x08username\x18\x03 \x01(\t\x12\x0e\n\x06locale\x18\x04 \x01(\t\x12<\n\x11supportedLanguage\x18\x05 \x01(\x0e\x32!.Authentication.SupportedLanguage\x12\x0c\n\x04type\x18\x06 \x01(\x05\"\xc7\x01\n\x15\x41piRequestByKAtoKAKey\x12,\n\x0csourceRegion\x18\x01 \x01(\x0e\x32\x16.Authentication.Region\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12<\n\x11supportedLanguage\x18\x03 \x01(\x0e\x32!.Authentication.SupportedLanguage\x12\x31\n\x11\x64\x65stinationRegion\x18\x04 \x01(\x0e\x32\x16.Authentication.Region\".\n\x0fMemcacheRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x0e\n\x06userId\x18\x02 \x01(\x05\".\n\x10MemcacheResponse\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"w\n\x1cMasterPasswordReentryRequest\x12\x16\n\x0epbkdf2Password\x18\x01 \x01(\t\x12?\n\x06\x61\x63tion\x18\x02 \x01(\x0e\x32/.Authentication.MasterPasswordReentryActionType\"\\\n\x1dMasterPasswordReentryResponse\x12;\n\x06status\x18\x01 \x01(\x0e\x32+.Authentication.MasterPasswordReentryStatus\"\xc5\x01\n\x19\x44\x65viceRegistrationRequest\x12\x15\n\rclientVersion\x18\x01 \x01(\t\x12\x12\n\ndeviceName\x18\x02 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x03 \x01(\x0c\x12\x16\n\x0e\x64\x65vicePlatform\x18\x04 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x05 \x01(\x0e\x32 .Authentication.ClientFormFactor\x12\x10\n\x08username\x18\x06 \x01(\t\"\x9a\x01\n\x19\x44\x65viceVerificationRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x1b\n\x13verificationChannel\x18\x03 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x04 \x01(\x0c\x12\x15\n\rclientVersion\x18\x05 \x01(\t\"\xb2\x01\n\x1a\x44\x65viceVerificationResponse\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x03 \x01(\x0c\x12\x15\n\rclientVersion\x18\x04 \x01(\t\x12\x32\n\x0c\x64\x65viceStatus\x18\x05 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\"\xc8\x01\n\x15\x44\x65viceApprovalRequest\x12\r\n\x05\x65mail\x18\x01 \x01(\t\x12\x18\n\x10twoFactorChannel\x18\x02 \x01(\t\x12\x15\n\rclientVersion\x18\x03 \x01(\t\x12\x0e\n\x06locale\x18\x04 \x01(\t\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x05 \x01(\x0c\x12\x10\n\x08totpCode\x18\x06 \x01(\t\x12\x10\n\x08\x64\x65viceIp\x18\x07 \x01(\t\x12\x1d\n\x15\x64\x65viceTokenExpireDays\x18\x08 \x01(\t\"9\n\x16\x44\x65viceApprovalResponse\x12\x1f\n\x17\x65ncryptedTwoFactorToken\x18\x01 \x01(\x0c\"~\n\x14\x41pproveDeviceRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x1e\n\x16\x65ncryptedDeviceDataKey\x18\x02 \x01(\x0c\x12\x14\n\x0c\x64\x65nyApproval\x18\x03 \x01(\x08\x12\x12\n\nlinkDevice\x18\x04 \x01(\x08\"E\n\x1a\x45nterpriseUserAliasRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\r\n\x05\x61lias\x18\x02 \x01(\t\"Y\n\x1d\x45nterpriseUserAddAliasRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\x0f\n\x07primary\x18\x03 \x01(\x08\"w\n\x1f\x45nterpriseUserAddAliasRequestV2\x12T\n\x1d\x65nterpriseUserAddAliasRequest\x18\x01 \x03(\x0b\x32-.Authentication.EnterpriseUserAddAliasRequest\"H\n\x1c\x45nterpriseUserAddAliasStatus\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06status\x18\x02 \x01(\t\"^\n\x1e\x45nterpriseUserAddAliasResponse\x12<\n\x06status\x18\x01 \x03(\x0b\x32,.Authentication.EnterpriseUserAddAliasStatus\"&\n\x06\x44\x65vice\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\"\\\n\x1cRegisterDeviceDataKeyRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x1e\n\x16\x65ncryptedDeviceDataKey\x18\x02 \x01(\x0c\"n\n)ValidateCreateUserVerificationCodeRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x18\n\x10verificationCode\x18\x03 \x01(\t\"\xa3\x01\n%ValidateDeviceVerificationCodeRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x18\n\x10verificationCode\x18\x03 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x04 \x01(\x0c\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x05 \x01(\x0c\"Y\n\x19SendSessionMessageRequest\x12\x19\n\x11messageSessionUid\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63ommand\x18\x02 \x01(\t\x12\x10\n\x08username\x18\x03 \x01(\t\"M\n\x11GlobalUserAccount\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x12\n\naccountUid\x18\x02 \x01(\x0c\x12\x12\n\nregionName\x18\x03 \x01(\t\"7\n\x0f\x41\x63\x63ountUsername\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x12\n\ndateActive\x18\x02 \x01(\t\"P\n\x19SsoServiceProviderRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x0e\n\x06locale\x18\x03 \x01(\t\"a\n\x1aSsoServiceProviderResponse\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05spUrl\x18\x02 \x01(\t\x12\x0f\n\x07isCloud\x18\x03 \x01(\x08\x12\x15\n\rclientVersion\x18\x04 \x01(\t\"4\n\x12UserSettingRequest\x12\x0f\n\x07setting\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"f\n\rThrottleState\x12*\n\x04type\x18\x01 \x01(\x0e\x32\x1c.Authentication.ThrottleType\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\x12\r\n\x05state\x18\x04 \x01(\x08\"\xb5\x01\n\x0eThrottleState2\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x16\n\x0ekeyDescription\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\x12\x18\n\x10valueDescription\x18\x04 \x01(\t\x12\x12\n\nidentifier\x18\x05 \x01(\t\x12\x0e\n\x06locked\x18\x06 \x01(\x08\x12\x1a\n\x12includedInAllClear\x18\x07 \x01(\x08\x12\x15\n\rexpireSeconds\x18\x08 \x01(\x05\"\x97\x01\n\x11\x44\x65viceInformation\x12\x10\n\x08\x64\x65viceId\x18\x01 \x01(\x03\x12\x12\n\ndeviceName\x18\x02 \x01(\t\x12\x15\n\rclientVersion\x18\x03 \x01(\t\x12\x11\n\tlastLogin\x18\x04 \x01(\x03\x12\x32\n\x0c\x64\x65viceStatus\x18\x05 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\"*\n\x0bUserSetting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x08\".\n\x12UserDataKeyRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x03(\x03\"+\n\x18UserDataKeyByNodeRequest\x12\x0f\n\x07nodeIds\x18\x01 \x03(\x03\"\x80\x01\n\x1b\x45nterpriseUserIdDataKeyPair\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x18\n\x10\x65ncryptedDataKey\x18\x02 \x01(\x0c\x12-\n\x07keyType\x18\x03 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"\x95\x01\n\x0bUserDataKey\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x0f\n\x07roleKey\x18\x02 \x01(\x0c\x12\x12\n\nprivateKey\x18\x03 \x01(\t\x12Q\n\x1c\x65nterpriseUserIdDataKeyPairs\x18\x04 \x03(\x0b\x32+.Authentication.EnterpriseUserIdDataKeyPair\"z\n\x13UserDataKeyResponse\x12\x31\n\x0cuserDataKeys\x18\x01 \x03(\x0b\x32\x1b.Authentication.UserDataKey\x12\x14\n\x0c\x61\x63\x63\x65ssDenied\x18\x02 \x03(\x03\x12\x1a\n\x12noEncryptedDataKey\x18\x03 \x03(\x03\"H\n)MasterPasswordRecoveryVerificationRequest\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\"U\n\x1cGetSecurityQuestionV3Request\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\x12\x18\n\x10verificationCode\x18\x02 \x01(\t\"r\n\x1dGetSecurityQuestionV3Response\x12\x18\n\x10securityQuestion\x18\x01 \x01(\t\x12\x15\n\rbackupKeyDate\x18\x02 \x01(\x03\x12\x0c\n\x04salt\x18\x03 \x01(\x0c\x12\x12\n\niterations\x18\x04 \x01(\x05\"n\n\x19GetDataKeyBackupV3Request\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\x12\x18\n\x10verificationCode\x18\x02 \x01(\t\x12\x1a\n\x12securityAnswerHash\x18\x03 \x01(\x0c\"v\n\rPasswordRules\x12\x10\n\x08ruleType\x18\x01 \x01(\t\x12\r\n\x05match\x18\x02 \x01(\x08\x12\x0f\n\x07pattern\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x0f\n\x07minimum\x18\x05 \x01(\x05\x12\r\n\x05value\x18\x06 \x01(\t\"\xc9\x02\n\x1aGetDataKeyBackupV3Response\x12\x15\n\rdataKeyBackup\x18\x01 \x01(\x0c\x12\x19\n\x11\x64\x61taKeyBackupDate\x18\x02 \x01(\x03\x12\x11\n\tpublicKey\x18\x03 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x04 \x01(\x0c\x12\x11\n\tclientKey\x18\x05 \x01(\x0c\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x06 \x01(\x0c\x12\x34\n\rpasswordRules\x18\x07 \x03(\x0b\x32\x1d.Authentication.PasswordRules\x12\x1a\n\x12passwordRulesIntro\x18\x08 \x01(\t\x12\x1f\n\x17minimumPbkdf2Iterations\x18\t \x01(\x05\x12$\n\x07keyType\x18\n \x01(\x0e\x32\x13.Enterprise.KeyType\")\n\x14GetPublicKeysRequest\x12\x11\n\tusernames\x18\x01 \x03(\t\"r\n\x11PublicKeyResponse\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x11\n\tpublicKey\x18\x02 \x01(\x0c\x12\x14\n\x0cpublicEccKey\x18\x03 \x01(\x0c\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x11\n\terrorCode\x18\x05 \x01(\t\"P\n\x15GetPublicKeysResponse\x12\x37\n\x0ckeyResponses\x18\x01 \x03(\x0b\x32!.Authentication.PublicKeyResponse\"F\n\x14SetEccKeyPairRequest\x12\x11\n\tpublicKey\x18\x01 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x02 \x01(\x0c\"I\n\x15SetEccKeyPairsRequest\x12\x30\n\x08teamKeys\x18\x01 \x03(\x0b\x32\x1e.Authentication.TeamEccKeyPair\"R\n\x16SetEccKeyPairsResponse\x12\x38\n\x08teamKeys\x18\x01 \x03(\x0b\x32&.Authentication.TeamEccKeyPairResponse\"Q\n\x0eTeamEccKeyPair\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x11\n\tpublicKey\x18\x02 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x03 \x01(\x0c\"X\n\x16TeamEccKeyPairResponse\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12-\n\x06status\x18\x02 \x01(\x0e\x32\x1d.Authentication.GenericStatus\"D\n\x17GetKsmPublicKeysRequest\x12\x11\n\tclientIds\x18\x01 \x03(\x0c\x12\x16\n\x0e\x63ontrollerUids\x18\x02 \x03(\x0c\"U\n\x17\x44\x65vicePublicKeyResponse\x12\x10\n\x08\x63lientId\x18\x01 \x01(\x0c\x12\x11\n\tpublicKey\x18\x02 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\"Y\n\x18GetKsmPublicKeysResponse\x12=\n\x0ckeyResponses\x18\x01 \x03(\x0b\x32\'.Authentication.DevicePublicKeyResponse\"X\n\x13\x41\x64\x64\x41ppSharesRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12+\n\x06shares\x18\x02 \x03(\x0b\x32\x1b.Authentication.AppShareAdd\">\n\x16RemoveAppSharesRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12\x0e\n\x06shares\x18\x02 \x03(\x0c\"\x87\x01\n\x0b\x41ppShareAdd\x12\x11\n\tsecretUid\x18\x02 \x01(\x0c\x12\x37\n\tshareType\x18\x03 \x01(\x0e\x32$.Authentication.ApplicationShareType\x12\x1a\n\x12\x65ncryptedSecretKey\x18\x04 \x01(\x0c\x12\x10\n\x08\x65\x64itable\x18\x05 \x01(\x08\"\x89\x01\n\x08\x41ppShare\x12\x11\n\tsecretUid\x18\x01 \x01(\x0c\x12\x37\n\tshareType\x18\x02 \x01(\x0e\x32$.Authentication.ApplicationShareType\x12\x10\n\x08\x65\x64itable\x18\x03 \x01(\x08\x12\x11\n\tcreatedOn\x18\x04 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\"\xd9\x01\n\x13\x41\x64\x64\x41ppClientRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12\x17\n\x0f\x65ncryptedAppKey\x18\x02 \x01(\x0c\x12\x10\n\x08\x63lientId\x18\x03 \x01(\x0c\x12\x0e\n\x06lockIp\x18\x04 \x01(\x08\x12\x1b\n\x13\x66irstAccessExpireOn\x18\x05 \x01(\x03\x12\x16\n\x0e\x61\x63\x63\x65ssExpireOn\x18\x06 \x01(\x03\x12\n\n\x02id\x18\x07 \x01(\t\x12\x30\n\rappClientType\x18\x08 \x01(\x0e\x32\x19.Enterprise.AppClientType\"@\n\x17RemoveAppClientsRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63lients\x18\x02 \x03(\x0c\"\xaa\x01\n\x17\x41\x64\x64\x45xternalShareRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x1a\n\x12\x65ncryptedRecordKey\x18\x02 \x01(\x0c\x12\x10\n\x08\x63lientId\x18\x03 \x01(\x0c\x12\x16\n\x0e\x61\x63\x63\x65ssExpireOn\x18\x04 \x01(\x03\x12\n\n\x02id\x18\x05 \x01(\t\x12\x16\n\x0eisSelfDestruct\x18\x06 \x01(\x08\x12\x12\n\nisEditable\x18\x07 \x01(\x08\"\x93\x02\n\tAppClient\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08\x63lientId\x18\x02 \x01(\x0c\x12\x11\n\tcreatedOn\x18\x03 \x01(\x03\x12\x13\n\x0b\x66irstAccess\x18\x04 \x01(\x03\x12\x12\n\nlastAccess\x18\x05 \x01(\x03\x12\x11\n\tpublicKey\x18\x06 \x01(\x0c\x12\x0e\n\x06lockIp\x18\x07 \x01(\x08\x12\x11\n\tipAddress\x18\x08 \x01(\t\x12\x1b\n\x13\x66irstAccessExpireOn\x18\t \x01(\x03\x12\x16\n\x0e\x61\x63\x63\x65ssExpireOn\x18\n \x01(\x03\x12\x30\n\rappClientType\x18\x0b \x01(\x0e\x32\x19.Enterprise.AppClientType\x12\x0f\n\x07\x63\x61nEdit\x18\x0c \x01(\x08\")\n\x11GetAppInfoRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x03(\x0c\"\x8e\x01\n\x07\x41ppInfo\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12(\n\x06shares\x18\x02 \x03(\x0b\x32\x18.Authentication.AppShare\x12*\n\x07\x63lients\x18\x03 \x03(\x0b\x32\x19.Authentication.AppClient\x12\x17\n\x0fisExternalShare\x18\x04 \x01(\x08\">\n\x12GetAppInfoResponse\x12(\n\x07\x61ppInfo\x18\x01 \x03(\x0b\x32\x17.Authentication.AppInfo\"\xd5\x01\n\x12\x41pplicationSummary\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12\x12\n\nlastAccess\x18\x02 \x01(\x03\x12\x14\n\x0crecordShares\x18\x03 \x01(\x05\x12\x14\n\x0c\x66olderShares\x18\x04 \x01(\x05\x12\x15\n\rfolderRecords\x18\x05 \x01(\x05\x12\x13\n\x0b\x63lientCount\x18\x06 \x01(\x05\x12\x1a\n\x12\x65xpiredClientCount\x18\x07 \x01(\x05\x12\x10\n\x08username\x18\x08 \x01(\t\x12\x0f\n\x07\x61ppData\x18\t \x01(\x0c\"`\n\x1eGetApplicationsSummaryResponse\x12>\n\x12\x61pplicationSummary\x18\x01 \x03(\x0b\x32\".Authentication.ApplicationSummary\"/\n\x1bGetVerificationTokenRequest\x12\x10\n\x08username\x18\x01 \x01(\t\"B\n\x1cGetVerificationTokenResponse\x12\"\n\x1a\x65ncryptedVerificationToken\x18\x01 \x01(\x0c\"\'\n\x16SendShareInviteRequest\x12\r\n\x05\x65mail\x18\x01 \x01(\t\"\xc5\x01\n\x18TimeLimitedAccessRequest\x12\x12\n\naccountUid\x18\x01 \x03(\x0c\x12\x0f\n\x07teamUid\x18\x02 \x03(\x0c\x12\x11\n\trecordUid\x18\x03 \x03(\x0c\x12\x17\n\x0fsharedObjectUid\x18\x04 \x01(\x0c\x12\x44\n\x15timeLimitedAccessType\x18\x05 \x01(\x0e\x32%.Authentication.TimeLimitedAccessType\x12\x12\n\nexpiration\x18\x06 \x01(\x03\"7\n\x17TimeLimitedAccessStatus\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xf8\x01\n\x19TimeLimitedAccessResponse\x12\x10\n\x08revision\x18\x01 \x01(\x03\x12\x41\n\x10userAccessStatus\x18\x02 \x03(\x0b\x32\'.Authentication.TimeLimitedAccessStatus\x12\x41\n\x10teamAccessStatus\x18\x03 \x03(\x0b\x32\'.Authentication.TimeLimitedAccessStatus\x12\x43\n\x12recordAccessStatus\x18\x04 \x03(\x0b\x32\'.Authentication.TimeLimitedAccessStatus\"+\n\x16RequestDownloadRequest\x12\x11\n\tfileNames\x18\x01 \x03(\t\"g\n\x17RequestDownloadResponse\x12\x0e\n\x06result\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\t\x12+\n\tdownloads\x18\x03 \x03(\x0b\x32\x18.Authentication.Download\"D\n\x08\x44ownload\x12\x10\n\x08\x66ileName\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x19\n\x11successStatusCode\x18\x03 \x01(\x05\"#\n\x11\x44\x65leteUserRequest\x12\x0e\n\x06reason\x18\x01 \x01(\t\"\x84\x01\n\x1b\x43hangeMasterPasswordRequest\x12\x14\n\x0c\x61uthVerifier\x18\x01 \x01(\x0c\x12\x18\n\x10\x65ncryptionParams\x18\x02 \x01(\x0c\x12\x1b\n\x13\x66romServiceProvider\x18\x03 \x01(\x08\x12\x18\n\x10iterationsChange\x18\x04 \x01(\x08\"=\n\x1c\x43hangeMasterPasswordResponse\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x01 \x01(\x0c\"Y\n\x1b\x41\x63\x63ountRecoverySetupRequest\x12 \n\x18recoveryEncryptedDataKey\x18\x01 \x01(\x0c\x12\x18\n\x10recoveryAuthHash\x18\x02 \x01(\x0c\"\xac\x01\n!AccountRecoveryVerifyCodeResponse\x12\x34\n\rbackupKeyType\x18\x01 \x01(\x0e\x32\x1d.Authentication.BackupKeyType\x12\x15\n\rbackupKeyDate\x18\x02 \x01(\x03\x12\x18\n\x10securityQuestion\x18\x03 \x01(\t\x12\x0c\n\x04salt\x18\x04 \x01(\x0c\x12\x12\n\niterations\x18\x05 \x01(\x05\",\n\x1b\x45mergencyAccessLoginRequest\x12\r\n\x05owner\x18\x01 \x01(\t\"\xb5\x01\n\x1c\x45mergencyAccessLoginResponse\x12\x14\n\x0csessionToken\x18\x01 \x01(\x0c\x12%\n\x07\x64\x61taKey\x18\x02 \x01(\x0b\x32\x14.Enterprise.TypedKey\x12+\n\rrsaPrivateKey\x18\x03 \x01(\x0b\x32\x14.Enterprise.TypedKey\x12+\n\reccPrivateKey\x18\x04 \x01(\x0b\x32\x14.Enterprise.TypedKey\"\xb2\x01\n\x0bUserTeamKey\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x18\n\x10\x65nterpriseUserId\x18\x03 \x01(\x03\x12\x1b\n\x13\x65ncryptedTeamKeyRSA\x18\x04 \x01(\x0c\x12\x1a\n\x12\x65ncryptedTeamKeyEC\x18\x05 \x01(\x0c\x12-\n\x06status\x18\x06 \x01(\x0e\x32\x1d.Authentication.GenericStatus\")\n\x16GenericRequestResponse\x12\x0f\n\x07request\x18\x01 \x03(\x0c\"f\n\x1aPasskeyRegistrationRequest\x12H\n\x17\x61uthenticatorAttachment\x18\x01 \x01(\x0e\x32\'.Authentication.AuthenticatorAttachment\"P\n\x1bPasskeyRegistrationResponse\x12\x16\n\x0e\x63hallengeToken\x18\x01 \x01(\x0c\x12\x19\n\x11pkCreationOptions\x18\x02 \x01(\t\"\x84\x01\n\x1fPasskeyRegistrationFinalization\x12\x16\n\x0e\x63hallengeToken\x18\x01 \x01(\x0c\x12\x1d\n\x15\x61uthenticatorResponse\x18\x02 \x01(\t\x12\x19\n\x0c\x66riendlyName\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0f\n\r_friendlyName\"\xb3\x02\n\x1cPasskeyAuthenticationRequest\x12H\n\x17\x61uthenticatorAttachment\x18\x01 \x01(\x0e\x32\'.Authentication.AuthenticatorAttachment\x12\x36\n\x0epasskeyPurpose\x18\x02 \x01(\x0e\x32\x1e.Authentication.PasskeyPurpose\x12\x15\n\rclientVersion\x18\x03 \x01(\t\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x04 \x01(\x0c\x12\x15\n\x08username\x18\x05 \x01(\tH\x00\x88\x01\x01\x12 \n\x13\x65ncryptedLoginToken\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0b\n\t_usernameB\x16\n\x14_encryptedLoginToken\"\x8b\x01\n\x1dPasskeyAuthenticationResponse\x12\x18\n\x10pkRequestOptions\x18\x01 \x01(\t\x12\x16\n\x0e\x63hallengeToken\x18\x02 \x01(\x0c\x12 \n\x13\x65ncryptedLoginToken\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\x16\n\x14_encryptedLoginToken\"\xbf\x01\n\x18PasskeyValidationRequest\x12\x16\n\x0e\x63hallengeToken\x18\x01 \x01(\x0c\x12\x19\n\x11\x61ssertionResponse\x18\x02 \x01(\x0c\x12\x36\n\x0epasskeyPurpose\x18\x03 \x01(\x0e\x32\x1e.Authentication.PasskeyPurpose\x12 \n\x13\x65ncryptedLoginToken\x18\x04 \x01(\x0cH\x00\x88\x01\x01\x42\x16\n\x14_encryptedLoginToken\"I\n\x19PasskeyValidationResponse\x12\x0f\n\x07isValid\x18\x01 \x01(\x08\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x02 \x01(\x0c\"h\n\x14UpdatePasskeyRequest\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x14\n\x0c\x63redentialId\x18\x02 \x01(\x0c\x12\x19\n\x0c\x66riendlyName\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0f\n\r_friendlyName\"-\n\x12PasskeyListRequest\x12\x17\n\x0fincludeDisabled\x18\x01 \x01(\x08\"\xa4\x01\n\x0bPasskeyInfo\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x14\n\x0c\x63redentialId\x18\x02 \x01(\x0c\x12\x14\n\x0c\x66riendlyName\x18\x03 \x01(\t\x12\x0e\n\x06\x41\x41GUID\x18\x04 \x01(\t\x12\x17\n\x0f\x63reatedAtMillis\x18\x05 \x01(\x03\x12\x16\n\x0elastUsedMillis\x18\x06 \x01(\x03\x12\x18\n\x10\x64isabledAtMillis\x18\x07 \x01(\x03\"G\n\x13PasskeyListResponse\x12\x30\n\x0bpasskeyInfo\x18\x01 \x03(\x0b\x32\x1b.Authentication.PasskeyInfo\"C\n\x0fTranslationInfo\x12\x16\n\x0etranslationKey\x18\x01 \x01(\t\x12\x18\n\x10translationValue\x18\x02 \x01(\t\",\n\x12TranslationRequest\x12\x16\n\x0etranslationKey\x18\x01 \x03(\t\"O\n\x13TranslationResponse\x12\x38\n\x0ftranslationInfo\x18\x01 \x03(\x0b\x32\x1f.Authentication.TranslationInfo*\xd3\x02\n\x11SupportedLanguage\x12\x0b\n\x07\x45NGLISH\x10\x00\x12\n\n\x06\x41RABIC\x10\x01\x12\x0b\n\x07\x42RITISH\x10\x02\x12\x0b\n\x07\x43HINESE\x10\x03\x12\x15\n\x11\x43HINESE_HONG_KONG\x10\x04\x12\x12\n\x0e\x43HINESE_TAIWAN\x10\x05\x12\t\n\x05\x44UTCH\x10\x06\x12\n\n\x06\x46RENCH\x10\x07\x12\n\n\x06GERMAN\x10\x08\x12\t\n\x05GREEK\x10\t\x12\n\n\x06HEBREW\x10\n\x12\x0b\n\x07ITALIAN\x10\x0b\x12\x0c\n\x08JAPANESE\x10\x0c\x12\n\n\x06KOREAN\x10\r\x12\n\n\x06POLISH\x10\x0e\x12\x0e\n\nPORTUGUESE\x10\x0f\x12\x15\n\x11PORTUGUESE_BRAZIL\x10\x10\x12\x0c\n\x08ROMANIAN\x10\x11\x12\x0b\n\x07RUSSIAN\x10\x12\x12\n\n\x06SLOVAK\x10\x13\x12\x0b\n\x07SPANISH\x10\x14\x12\x0b\n\x07\x46INNISH\x10\x15\x12\x0b\n\x07SWEDISH\x10\x16*k\n\tLoginType\x12\n\n\x06NORMAL\x10\x00\x12\x07\n\x03SSO\x10\x01\x12\x07\n\x03\x42IO\x10\x02\x12\r\n\tALTERNATE\x10\x03\x12\x0b\n\x07OFFLINE\x10\x04\x12\x13\n\x0f\x46ORGOT_PASSWORD\x10\x05\x12\x0f\n\x0bPASSKEY_BIO\x10\x06*q\n\x0c\x44\x65viceStatus\x12\x19\n\x15\x44\x45VICE_NEEDS_APPROVAL\x10\x00\x12\r\n\tDEVICE_OK\x10\x01\x12\x1b\n\x17\x44\x45VICE_DISABLED_BY_USER\x10\x02\x12\x1a\n\x16\x44\x45VICE_LOCKED_BY_ADMIN\x10\x03*A\n\rLicenseStatus\x12\t\n\x05OTHER\x10\x00\x12\n\n\x06\x41\x43TIVE\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x12\x0c\n\x08\x44ISABLED\x10\x03*7\n\x0b\x41\x63\x63ountType\x12\x0c\n\x08\x43ONSUMER\x10\x00\x12\n\n\x06\x46\x41MILY\x10\x01\x12\x0e\n\nENTERPRISE\x10\x02*\x9f\x02\n\x10SessionTokenType\x12\x12\n\x0eNO_RESTRICTION\x10\x00\x12\x14\n\x10\x41\x43\x43OUNT_RECOVERY\x10\x01\x12\x11\n\rSHARE_ACCOUNT\x10\x02\x12\x0c\n\x08PURCHASE\x10\x03\x12\x0c\n\x08RESTRICT\x10\x04\x12\x11\n\rACCEPT_INVITE\x10\x05\x12\x12\n\x0eSUPPORT_SERVER\x10\x06\x12\x17\n\x13\x45NTERPRISE_CREATION\x10\x07\x12\x1f\n\x1b\x45XPIRED_BUT_ALLOWED_TO_SYNC\x10\x08\x12\x18\n\x14\x41\x43\x43\x45PT_FAMILY_INVITE\x10\t\x12!\n\x1d\x45NTERPRISE_CREATION_PURCHASED\x10\n\x12\x14\n\x10\x45MERGENCY_ACCESS\x10\x0b*G\n\x07Version\x12\x13\n\x0finvalid_version\x10\x00\x12\x13\n\x0f\x64\x65\x66\x61ult_version\x10\x01\x12\x12\n\x0esecond_version\x10\x02*7\n\x1fMasterPasswordReentryActionType\x12\n\n\x06UNMASK\x10\x00\x12\x08\n\x04\x43OPY\x10\x01*l\n\x0bLoginMethod\x12\x17\n\x13INVALID_LOGINMETHOD\x10\x00\x12\x14\n\x10\x45XISTING_ACCOUNT\x10\x01\x12\x0e\n\nSSO_DOMAIN\x10\x02\x12\r\n\tAFTER_SSO\x10\x03\x12\x0f\n\x0bNEW_ACCOUNT\x10\x04*\xbe\x04\n\nLoginState\x12\x16\n\x12INVALID_LOGINSTATE\x10\x00\x12\x0e\n\nLOGGED_OUT\x10\x01\x12\x1c\n\x18\x44\x45VICE_APPROVAL_REQUIRED\x10\x02\x12\x11\n\rDEVICE_LOCKED\x10\x03\x12\x12\n\x0e\x41\x43\x43OUNT_LOCKED\x10\x04\x12\x19\n\x15\x44\x45VICE_ACCOUNT_LOCKED\x10\x05\x12\x0b\n\x07UPGRADE\x10\x06\x12\x13\n\x0fLICENSE_EXPIRED\x10\x07\x12\x13\n\x0fREGION_REDIRECT\x10\x08\x12\x16\n\x12REDIRECT_CLOUD_SSO\x10\t\x12\x17\n\x13REDIRECT_ONSITE_SSO\x10\n\x12\x10\n\x0cREQUIRES_2FA\x10\x0c\x12\x16\n\x12REQUIRES_AUTH_HASH\x10\r\x12\x15\n\x11REQUIRES_USERNAME\x10\x0e\x12\x19\n\x15\x41\x46TER_CLOUD_SSO_LOGIN\x10\x0f\x12\x1d\n\x19REQUIRES_ACCOUNT_CREATION\x10\x10\x12&\n\"REQUIRES_DEVICE_ENCRYPTED_DATA_KEY\x10\x11\x12\x17\n\x13LOGIN_TOKEN_EXPIRED\x10\x12\x12\x1e\n\x1aPASSKEY_INITIATE_CHALLENGE\x10\x13\x12\x19\n\x15PASSKEY_AUTH_REQUIRED\x10\x14\x12!\n\x1dPASSKEY_VERIFY_AUTHENTICATION\x10\x15\x12\x17\n\x13\x41\x46TER_PASSKEY_LOGIN\x10\x16\x12\r\n\tLOGGED_IN\x10\x63*k\n\x14\x45ncryptedDataKeyType\x12\n\n\x06NO_KEY\x10\x00\x12\x18\n\x14\x42Y_DEVICE_PUBLIC_KEY\x10\x01\x12\x0f\n\x0b\x42Y_PASSWORD\x10\x02\x12\x10\n\x0c\x42Y_ALTERNATE\x10\x03\x12\n\n\x06\x42Y_BIO\x10\x04*-\n\x0ePasswordMethod\x12\x0b\n\x07\x45NTERED\x10\x00\x12\x0e\n\nBIOMETRICS\x10\x01*\xb9\x01\n\x11TwoFactorPushType\x12\x14\n\x10TWO_FA_PUSH_NONE\x10\x00\x12\x13\n\x0fTWO_FA_PUSH_SMS\x10\x01\x12\x16\n\x12TWO_FA_PUSH_KEEPER\x10\x02\x12\x18\n\x14TWO_FA_PUSH_DUO_PUSH\x10\x03\x12\x18\n\x14TWO_FA_PUSH_DUO_TEXT\x10\x04\x12\x18\n\x14TWO_FA_PUSH_DUO_CALL\x10\x05\x12\x13\n\x0fTWO_FA_PUSH_DNA\x10\x06*\xc3\x01\n\x12TwoFactorValueType\x12\x14\n\x10TWO_FA_CODE_NONE\x10\x00\x12\x14\n\x10TWO_FA_CODE_TOTP\x10\x01\x12\x13\n\x0fTWO_FA_CODE_SMS\x10\x02\x12\x13\n\x0fTWO_FA_CODE_DUO\x10\x03\x12\x13\n\x0fTWO_FA_CODE_RSA\x10\x04\x12\x13\n\x0fTWO_FA_RESP_U2F\x10\x05\x12\x18\n\x14TWO_FA_RESP_WEBAUTHN\x10\x06\x12\x13\n\x0fTWO_FA_CODE_DNA\x10\x07*\xe1\x01\n\x14TwoFactorChannelType\x12\x12\n\x0eTWO_FA_CT_NONE\x10\x00\x12\x12\n\x0eTWO_FA_CT_TOTP\x10\x01\x12\x11\n\rTWO_FA_CT_SMS\x10\x02\x12\x11\n\rTWO_FA_CT_DUO\x10\x03\x12\x11\n\rTWO_FA_CT_RSA\x10\x04\x12\x14\n\x10TWO_FA_CT_BACKUP\x10\x05\x12\x11\n\rTWO_FA_CT_U2F\x10\x06\x12\x16\n\x12TWO_FA_CT_WEBAUTHN\x10\x07\x12\x14\n\x10TWO_FA_CT_KEEPER\x10\x08\x12\x11\n\rTWO_FA_CT_DNA\x10\t*\xab\x01\n\x13TwoFactorExpiration\x12\x1a\n\x16TWO_FA_EXP_IMMEDIATELY\x10\x00\x12\x18\n\x14TWO_FA_EXP_5_MINUTES\x10\x01\x12\x17\n\x13TWO_FA_EXP_12_HOURS\x10\x02\x12\x17\n\x13TWO_FA_EXP_24_HOURS\x10\x03\x12\x16\n\x12TWO_FA_EXP_30_DAYS\x10\x04\x12\x14\n\x10TWO_FA_EXP_NEVER\x10\x05*@\n\x0bLicenseType\x12\t\n\x05VAULT\x10\x00\x12\x08\n\x04\x43HAT\x10\x01\x12\x0b\n\x07STORAGE\x10\x02\x12\x0f\n\x0b\x42REACHWATCH\x10\x03*i\n\x0bObjectTypes\x12\n\n\x06RECORD\x10\x00\x12\x16\n\x12SHARED_FOLDER_USER\x10\x01\x12\x16\n\x12SHARED_FOLDER_TEAM\x10\x02\x12\x0f\n\x0bUSER_FOLDER\x10\x03\x12\r\n\tTEAM_USER\x10\x04*\xa1\x02\n\x13\x45ncryptedObjectType\x12\x13\n\x0f\x45OT_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x45OT_RECORD_KEY\x10\x01\x12\x1e\n\x1a\x45OT_SHARED_FOLDER_USER_KEY\x10\x02\x12\x1e\n\x1a\x45OT_SHARED_FOLDER_TEAM_KEY\x10\x03\x12\x15\n\x11\x45OT_TEAM_USER_KEY\x10\x04\x12\x17\n\x13\x45OT_USER_FOLDER_KEY\x10\x05\x12\x15\n\x11\x45OT_SECURITY_DATA\x10\x06\x12%\n!EOT_SECURITY_DATA_MASTER_PASSWORD\x10\x07\x12\x1c\n\x18\x45OT_EMERGENCY_ACCESS_KEY\x10\x08\x12\x15\n\x11\x45OT_V2_RECORD_KEY\x10\t*M\n\x1bMasterPasswordReentryStatus\x12\x0e\n\nMP_UNKNOWN\x10\x00\x12\x0e\n\nMP_SUCCESS\x10\x01\x12\x0e\n\nMP_FAILURE\x10\x02*`\n\x1b\x41lternateAuthenticationType\x12\x1d\n\x19\x41LTERNATE_MASTER_PASSWORD\x10\x00\x12\r\n\tBIOMETRIC\x10\x01\x12\x13\n\x0f\x41\x43\x43OUNT_RECOVER\x10\x02*\x9a\x02\n\x0cThrottleType\x12\x1b\n\x17PASSWORD_RETRY_THROTTLE\x10\x00\x12\"\n\x1ePASSWORD_RETRY_LEGACY_THROTTLE\x10\x01\x12\x13\n\x0fTWO_FA_THROTTLE\x10\x02\x12\x1a\n\x16TWO_FA_LEGACY_THROTTLE\x10\x03\x12\x15\n\x11QA_RETRY_THROTTLE\x10\x04\x12\x1c\n\x18\x41\x43\x43OUNT_RECOVER_THROTTLE\x10\x05\x12.\n*VALIDATE_DEVICE_VERIFICATION_CODE_THROTTLE\x10\x06\x12\x33\n/VALIDATE_CREATE_USER_VERIFICATION_CODE_THROTTLE\x10\x07*H\n\x06Region\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x06\n\x02\x65u\x10\x01\x12\x06\n\x02us\x10\x02\x12\t\n\x05usgov\x10\x03\x12\x06\n\x02\x61u\x10\x04\x12\x06\n\x02jp\x10\x05\x12\x06\n\x02\x63\x61\x10\x06*D\n\x14\x41pplicationShareType\x12\x15\n\x11SHARE_TYPE_RECORD\x10\x00\x12\x15\n\x11SHARE_TYPE_FOLDER\x10\x01*\xa4\x01\n\x15TimeLimitedAccessType\x12$\n INVALID_TIME_LIMITED_ACCESS_TYPE\x10\x00\x12\x19\n\x15USER_ACCESS_TO_RECORD\x10\x01\x12\'\n#USER_OR_TEAM_ACCESS_TO_SHAREDFOLDER\x10\x02\x12!\n\x1dRECORD_ACCESS_TO_SHAREDFOLDER\x10\x03*<\n\rBackupKeyType\x12\x12\n\x0e\x42KT_SEC_ANSWER\x10\x00\x12\x17\n\x13\x42KT_PASSPHRASE_HASH\x10\x01*W\n\rGenericStatus\x12\x0b\n\x07SUCCESS\x10\x00\x12\x12\n\x0eINVALID_OBJECT\x10\x01\x12\x12\n\x0e\x41LREADY_EXISTS\x10\x02\x12\x11\n\rACCESS_DENIED\x10\x03*N\n\x17\x41uthenticatorAttachment\x12\x12\n\x0e\x43ROSS_PLATFORM\x10\x00\x12\x0c\n\x08PLATFORM\x10\x01\x12\x11\n\rALL_SUPPORTED\x10\x02*-\n\x0ePasskeyPurpose\x12\x0c\n\x08PK_LOGIN\x10\x00\x12\r\n\tPK_REAUTH\x10\x01*K\n\x10\x43lientFormFactor\x12\x0c\n\x08\x46\x46_EMPTY\x10\x00\x12\x0c\n\x08\x46\x46_PHONE\x10\x01\x12\r\n\tFF_TABLET\x10\x02\x12\x0c\n\x08\x46\x46_WATCH\x10\x03\x42*\n\x18\x63om.keepersecurity.protoB\x0e\x41uthenticationb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x41PIRequest.proto\x12\x0e\x41uthentication\x1a\x10\x65nterprise.proto\"{\n\rQrcMessageKey\x12\x19\n\x11\x63lientEcPublicKey\x18\x01 \x01(\x0c\x12\x1c\n\x14mlKemEncapsulatedKey\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12\x12\n\nmsgVersion\x18\x04 \x01(\x05\x12\x0f\n\x07\x65\x63KeyId\x18\x05 \x01(\x05\"\xe6\x01\n\nApiRequest\x12 \n\x18\x65ncryptedTransmissionKey\x18\x01 \x01(\x0c\x12\x13\n\x0bpublicKeyId\x18\x02 \x01(\x05\x12\x0e\n\x06locale\x18\x03 \x01(\t\x12\x18\n\x10\x65ncryptedPayload\x18\x04 \x01(\x0c\x12\x16\n\x0e\x65ncryptionType\x18\x05 \x01(\x05\x12\x11\n\trecaptcha\x18\x06 \x01(\t\x12\x16\n\x0esubEnvironment\x18\x07 \x01(\t\x12\x34\n\rqrcMessageKey\x18\x08 \x01(\x0b\x32\x1d.Authentication.QrcMessageKey\"j\n\x11\x41piRequestPayload\x12\x0f\n\x07payload\x18\x01 \x01(\x0c\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x02 \x01(\x0c\x12\x11\n\ttimeToken\x18\x03 \x01(\x0c\x12\x12\n\napiVersion\x18\x04 \x01(\x05\"6\n\tTransform\x12\x0b\n\x03key\x18\x01 \x01(\x0c\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x02 \x01(\x0c\"\xa0\x01\n\rDeviceRequest\x12\x15\n\rclientVersion\x18\x01 \x01(\t\x12\x12\n\ndeviceName\x18\x02 \x01(\t\x12\x16\n\x0e\x64\x65vicePlatform\x18\x03 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x04 \x01(\x0e\x32 .Authentication.ClientFormFactor\x12\x10\n\x08username\x18\x05 \x01(\t\"T\n\x0b\x41uthRequest\x12\x15\n\rclientVersion\x18\x01 \x01(\t\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x03 \x01(\x0c\"\xc3\x01\n\x14NewUserMinimumParams\x12\x19\n\x11minimumIterations\x18\x01 \x01(\x05\x12\x1a\n\x12passwordMatchRegex\x18\x02 \x03(\t\x12 \n\x18passwordMatchDescription\x18\x03 \x03(\t\x12\x1a\n\x12isEnterpriseDomain\x18\x04 \x01(\x08\x12\x1e\n\x16\x65nterpriseEccPublicKey\x18\x05 \x01(\x0c\x12\x16\n\x0e\x66orbidKeyType2\x18\x06 \x01(\x08\"\x89\x01\n\x0fPreLoginRequest\x12\x30\n\x0b\x61uthRequest\x18\x01 \x01(\x0b\x32\x1b.Authentication.AuthRequest\x12,\n\tloginType\x18\x02 \x01(\x0e\x32\x19.Authentication.LoginType\x12\x16\n\x0etwoFactorToken\x18\x03 \x01(\x0c\"\x80\x02\n\x0cLoginRequest\x12\x30\n\x0b\x61uthRequest\x18\x01 \x01(\x0b\x32\x1b.Authentication.AuthRequest\x12,\n\tloginType\x18\x02 \x01(\x0e\x32\x19.Authentication.LoginType\x12\x1f\n\x17\x61uthenticationHashPrime\x18\x03 \x01(\x0c\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x04 \x01(\x0c\x12\x14\n\x0c\x61uthResponse\x18\x05 \x01(\x0c\x12\x16\n\x0emcEnterpriseId\x18\x06 \x01(\x05\x12\x12\n\npush_token\x18\x07 \x01(\t\x12\x10\n\x08platform\x18\x08 \x01(\t\"\\\n\x0e\x44\x65viceResponse\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12,\n\x06status\x18\x02 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\"V\n\x04Salt\x12\x12\n\niterations\x18\x01 \x01(\x05\x12\x0c\n\x04salt\x18\x02 \x01(\x0c\x12\x11\n\talgorithm\x18\x03 \x01(\x05\x12\x0b\n\x03uid\x18\x04 \x01(\x0c\x12\x0c\n\x04name\x18\x05 \x01(\t\" \n\x10TwoFactorChannel\x12\x0c\n\x04type\x18\x01 \x01(\x05\"\xfc\x02\n\x11StartLoginRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x15\n\rclientVersion\x18\x03 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x04 \x01(\x0c\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x05 \x01(\x0c\x12,\n\tloginType\x18\x06 \x01(\x0e\x32\x19.Authentication.LoginType\x12\x16\n\x0emcEnterpriseId\x18\x07 \x01(\x05\x12\x30\n\x0bloginMethod\x18\x08 \x01(\x0e\x32\x1b.Authentication.LoginMethod\x12\x15\n\rforceNewLogin\x18\t \x01(\x08\x12\x11\n\tcloneCode\x18\n \x01(\x0c\x12\x18\n\x10v2TwoFactorToken\x18\x0b \x01(\t\x12\x12\n\naccountUid\x18\x0c \x01(\x0c\x12\x18\n\x10\x66romSessionToken\x18\r \x01(\x0c\"\xa7\x04\n\rLoginResponse\x12.\n\nloginState\x18\x01 \x01(\x0e\x32\x1a.Authentication.LoginState\x12\x12\n\naccountUid\x18\x02 \x01(\x0c\x12\x17\n\x0fprimaryUsername\x18\x03 \x01(\t\x12\x18\n\x10\x65ncryptedDataKey\x18\x04 \x01(\x0c\x12\x42\n\x14\x65ncryptedDataKeyType\x18\x05 \x01(\x0e\x32$.Authentication.EncryptedDataKeyType\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x06 \x01(\x0c\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x07 \x01(\x0c\x12:\n\x10sessionTokenType\x18\x08 \x01(\x0e\x32 .Authentication.SessionTokenType\x12\x0f\n\x07message\x18\t \x01(\t\x12\x0b\n\x03url\x18\n \x01(\t\x12\x36\n\x08\x63hannels\x18\x0b \x03(\x0b\x32$.Authentication.TwoFactorChannelInfo\x12\"\n\x04salt\x18\x0c \x03(\x0b\x32\x14.Authentication.Salt\x12\x11\n\tcloneCode\x18\r \x01(\x0c\x12\x1a\n\x12stateSpecificValue\x18\x0e \x01(\t\x12\x18\n\x10ssoClientVersion\x18\x0f \x01(\t\x12 \n\x18sessionTokenTypeModifier\x18\x10 \x01(\t\"v\n\x11SwitchListElement\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08\x66ullName\x18\x02 \x01(\t\x12\x14\n\x0c\x61uthRequired\x18\x03 \x01(\x08\x12\x10\n\x08isLinked\x18\x04 \x01(\x08\x12\x15\n\rprofilePicUrl\x18\x05 \x01(\t\"I\n\x12SwitchListResponse\x12\x33\n\x08\x65lements\x18\x01 \x03(\x0b\x32!.Authentication.SwitchListElement\"\x8c\x01\n\x0bSsoUserInfo\x12\x13\n\x0b\x63ompanyName\x18\x01 \x01(\t\x12\x13\n\x0bsamlRequest\x18\x02 \x01(\t\x12\x17\n\x0fsamlRequestType\x18\x03 \x01(\t\x12\x15\n\rssoDomainName\x18\x04 \x01(\t\x12\x10\n\x08loginUrl\x18\x05 \x01(\t\x12\x11\n\tlogoutUrl\x18\x06 \x01(\t\"\xd6\x01\n\x10PreLoginResponse\x12\x32\n\x0c\x64\x65viceStatus\x18\x01 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\x12\"\n\x04salt\x18\x02 \x03(\x0b\x32\x14.Authentication.Salt\x12\x38\n\x0eOBSOLETE_FIELD\x18\x03 \x03(\x0b\x32 .Authentication.TwoFactorChannel\x12\x30\n\x0bssoUserInfo\x18\x04 \x01(\x0b\x32\x1b.Authentication.SsoUserInfo\"&\n\x12LoginAsUserRequest\x12\x10\n\x08username\x18\x01 \x01(\t\"W\n\x13LoginAsUserResponse\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x01 \x01(\x0c\x12!\n\x19\x65ncryptedSharedAccountKey\x18\x02 \x01(\x0c\"\x84\x01\n\x17ValidateAuthHashRequest\x12\x36\n\x0epasswordMethod\x18\x01 \x01(\x0e\x32\x1e.Authentication.PasswordMethod\x12\x14\n\x0c\x61uthResponse\x18\x02 \x01(\x0c\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x03 \x01(\x0c\"\xc4\x02\n\x14TwoFactorChannelInfo\x12\x39\n\x0b\x63hannelType\x18\x01 \x01(\x0e\x32$.Authentication.TwoFactorChannelType\x12\x13\n\x0b\x63hannel_uid\x18\x02 \x01(\x0c\x12\x13\n\x0b\x63hannelName\x18\x03 \x01(\t\x12\x11\n\tchallenge\x18\x04 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x05 \x03(\t\x12\x13\n\x0bphoneNumber\x18\x06 \x01(\t\x12:\n\rmaxExpiration\x18\x07 \x01(\x0e\x32#.Authentication.TwoFactorExpiration\x12\x11\n\tcreatedOn\x18\x08 \x01(\x03\x12:\n\rlastFrequency\x18\t \x01(\x0e\x32#.Authentication.TwoFactorExpiration\"d\n\x12TwoFactorDuoStatus\x12\x14\n\x0c\x63\x61pabilities\x18\x01 \x03(\t\x12\x13\n\x0bphoneNumber\x18\x02 \x01(\t\x12\x12\n\nenroll_url\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\"\xc7\x01\n\x13TwoFactorAddRequest\x12\x39\n\x0b\x63hannelType\x18\x01 \x01(\x0e\x32$.Authentication.TwoFactorChannelType\x12\x13\n\x0b\x63hannel_uid\x18\x02 \x01(\x0c\x12\x13\n\x0b\x63hannelName\x18\x03 \x01(\t\x12\x13\n\x0bphoneNumber\x18\x04 \x01(\t\x12\x36\n\x0b\x64uoPushType\x18\x05 \x01(\x0e\x32!.Authentication.TwoFactorPushType\"B\n\x16TwoFactorRenameRequest\x12\x13\n\x0b\x63hannel_uid\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63hannelName\x18\x02 \x01(\t\"=\n\x14TwoFactorAddResponse\x12\x11\n\tchallenge\x18\x01 \x01(\t\x12\x12\n\nbackupKeys\x18\x02 \x03(\t\"-\n\x16TwoFactorDeleteRequest\x12\x13\n\x0b\x63hannel_uid\x18\x01 \x01(\x0c\"a\n\x15TwoFactorListResponse\x12\x36\n\x08\x63hannels\x18\x01 \x03(\x0b\x32$.Authentication.TwoFactorChannelInfo\x12\x10\n\x08\x65xpireOn\x18\x02 \x01(\x03\"Y\n TwoFactorUpdateExpirationRequest\x12\x35\n\x08\x65xpireIn\x18\x01 \x01(\x0e\x32#.Authentication.TwoFactorExpiration\"\xc9\x01\n\x18TwoFactorValidateRequest\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\x12\x35\n\tvalueType\x18\x02 \x01(\x0e\x32\".Authentication.TwoFactorValueType\x12\r\n\x05value\x18\x03 \x01(\t\x12\x13\n\x0b\x63hannel_uid\x18\x04 \x01(\x0c\x12\x35\n\x08\x65xpireIn\x18\x05 \x01(\x0e\x32#.Authentication.TwoFactorExpiration\"8\n\x19TwoFactorValidateResponse\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\"\xb8\x01\n\x18TwoFactorSendPushRequest\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\x12\x33\n\x08pushType\x18\x02 \x01(\x0e\x32!.Authentication.TwoFactorPushType\x12\x13\n\x0b\x63hannel_uid\x18\x03 \x01(\x0c\x12\x35\n\x08\x65xpireIn\x18\x04 \x01(\x0e\x32#.Authentication.TwoFactorExpiration\"\x83\x01\n\x07License\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x12\n\nexpiration\x18\x02 \x01(\x03\x12\x34\n\rlicenseStatus\x18\x03 \x01(\x0e\x32\x1d.Authentication.LicenseStatus\x12\x0c\n\x04paid\x18\x04 \x01(\x08\x12\x0f\n\x07message\x18\x05 \x01(\t\"G\n\x0fOwnerlessRecord\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x11\n\trecordKey\x18\x02 \x01(\x0c\x12\x0e\n\x06status\x18\x03 \x01(\x05\"L\n\x10OwnerlessRecords\x12\x38\n\x0fownerlessRecord\x18\x01 \x03(\x0b\x32\x1f.Authentication.OwnerlessRecord\"\xd7\x01\n\x0fUserAuthRequest\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0c\n\x04salt\x18\x02 \x01(\x0c\x12\x12\n\niterations\x18\x03 \x01(\x05\x12\x1a\n\x12\x65ncryptedClientKey\x18\x04 \x01(\x0c\x12\x10\n\x08\x61uthHash\x18\x05 \x01(\x0c\x12\x18\n\x10\x65ncryptedDataKey\x18\x06 \x01(\x0c\x12,\n\tloginType\x18\x07 \x01(\x0e\x32\x19.Authentication.LoginType\x12\x0c\n\x04name\x18\x08 \x01(\t\x12\x11\n\talgorithm\x18\t \x01(\x05\"\x19\n\nUidRequest\x12\x0b\n\x03uid\x18\x01 \x03(\x0c\"\xff\x01\n\x13\x44\x65viceUpdateRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x12\n\ndeviceName\x18\x03 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x04 \x01(\x0c\x12\x32\n\x0c\x64\x65viceStatus\x18\x05 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\x12\x16\n\x0e\x64\x65vicePlatform\x18\x06 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x07 \x01(\x0e\x32 .Authentication.ClientFormFactor\"\x80\x02\n\x14\x44\x65viceUpdateResponse\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x12\n\ndeviceName\x18\x03 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x04 \x01(\x0c\x12\x32\n\x0c\x64\x65viceStatus\x18\x05 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\x12\x16\n\x0e\x64\x65vicePlatform\x18\x06 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x07 \x01(\x0e\x32 .Authentication.ClientFormFactor\"\xd5\x01\n\x1dRegisterDeviceInRegionRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x12\n\ndeviceName\x18\x03 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x04 \x01(\x0c\x12\x16\n\x0e\x64\x65vicePlatform\x18\x05 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x06 \x01(\x0e\x32 .Authentication.ClientFormFactor\"\xf8\x02\n\x13RegistrationRequest\x12\x30\n\x0b\x61uthRequest\x18\x01 \x01(\x0b\x32\x1b.Authentication.AuthRequest\x12\x38\n\x0fuserAuthRequest\x18\x02 \x01(\x0b\x32\x1f.Authentication.UserAuthRequest\x12\x1a\n\x12\x65ncryptedClientKey\x18\x03 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x04 \x01(\x0c\x12\x11\n\tpublicKey\x18\x05 \x01(\x0c\x12\x18\n\x10verificationCode\x18\x06 \x01(\t\x12\x1e\n\x16\x64\x65precatedAuthHashHash\x18\x07 \x01(\x0c\x12$\n\x1c\x64\x65precatedEncryptedClientKey\x18\x08 \x01(\x0c\x12%\n\x1d\x64\x65precatedEncryptedPrivateKey\x18\t \x01(\x0c\x12\"\n\x1a\x64\x65precatedEncryptionParams\x18\n \x01(\x0c\"\xd0\x01\n\x16\x43onvertUserToV3Request\x12\x30\n\x0b\x61uthRequest\x18\x01 \x01(\x0b\x32\x1b.Authentication.AuthRequest\x12\x38\n\x0fuserAuthRequest\x18\x02 \x01(\x0b\x32\x1f.Authentication.UserAuthRequest\x12\x1a\n\x12\x65ncryptedClientKey\x18\x03 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x04 \x01(\x0c\x12\x11\n\tpublicKey\x18\x05 \x01(\x0c\"$\n\x10RevisionResponse\x12\x10\n\x08revision\x18\x01 \x01(\x03\"&\n\x12\x43hangeEmailRequest\x12\x10\n\x08newEmail\x18\x01 \x01(\t\"8\n\x13\x43hangeEmailResponse\x12!\n\x19\x65ncryptedChangeEmailToken\x18\x01 \x01(\x0c\"6\n\x1d\x45mailVerificationLinkResponse\x12\x15\n\remailVerified\x18\x01 \x01(\x08\")\n\x0cSecurityData\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"@\n\x11SecurityScoreData\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x10\n\x08revision\x18\x03 \x01(\x03\"\x8b\x02\n\x13SecurityDataRequest\x12\x38\n\x12recordSecurityData\x18\x01 \x03(\x0b\x32\x1c.Authentication.SecurityData\x12@\n\x1amasterPasswordSecurityData\x18\x02 \x03(\x0b\x32\x1c.Authentication.SecurityData\x12\x34\n\x0e\x65ncryptionType\x18\x03 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x42\n\x17recordSecurityScoreData\x18\x04 \x03(\x0b\x32!.Authentication.SecurityScoreData\"\xc6\x02\n\x1dSecurityReportIncrementalData\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1b\n\x13\x63urrentSecurityData\x18\x02 \x01(\x0c\x12#\n\x1b\x63urrentSecurityDataRevision\x18\x03 \x01(\x03\x12\x17\n\x0foldSecurityData\x18\x04 \x01(\x0c\x12\x1f\n\x17oldSecurityDataRevision\x18\x05 \x01(\x03\x12?\n\x19\x63urrentDataEncryptionType\x18\x06 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12;\n\x15oldDataEncryptionType\x18\x07 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x11\n\trecordUid\x18\x08 \x01(\x0c\"\x9f\x02\n\x0eSecurityReport\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1b\n\x13\x65ncryptedReportData\x18\x02 \x01(\x0c\x12\x10\n\x08revision\x18\x03 \x01(\x03\x12\x11\n\ttwoFactor\x18\x04 \x01(\t\x12\x11\n\tlastLogin\x18\x05 \x01(\x03\x12\x1e\n\x16numberOfReusedPassword\x18\x06 \x01(\x05\x12T\n\x1dsecurityReportIncrementalData\x18\x07 \x03(\x0b\x32-.Authentication.SecurityReportIncrementalData\x12\x0e\n\x06userId\x18\x08 \x01(\x05\x12\x18\n\x10hasOldEncryption\x18\t \x01(\x08\"n\n\x19SecurityReportSaveRequest\x12\x36\n\x0esecurityReport\x18\x01 \x03(\x0b\x32\x1e.Authentication.SecurityReport\x12\x19\n\x11\x63ontinuationToken\x18\x02 \x01(\x0c\")\n\x15SecurityReportRequest\x12\x10\n\x08\x66romPage\x18\x01 \x01(\x03\"\xf5\x01\n\x16SecurityReportResponse\x12\x1c\n\x14\x65nterprisePrivateKey\x18\x01 \x01(\x0c\x12\x36\n\x0esecurityReport\x18\x02 \x03(\x0b\x32\x1e.Authentication.SecurityReport\x12\x14\n\x0c\x61sOfRevision\x18\x03 \x01(\x03\x12\x10\n\x08\x66romPage\x18\x04 \x01(\x03\x12\x0e\n\x06toPage\x18\x05 \x01(\x03\x12\x10\n\x08\x63omplete\x18\x06 \x01(\x08\x12\x1f\n\x17\x65nterpriseEccPrivateKey\x18\x07 \x01(\x0c\x12\x1a\n\x12hasIncrementalData\x18\x08 \x01(\x08\";\n\x1eIncrementalSecurityDataRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\"\x92\x01\n\x1fIncrementalSecurityDataResponse\x12T\n\x1dsecurityReportIncrementalData\x18\x01 \x03(\x0b\x32-.Authentication.SecurityReportIncrementalData\x12\x19\n\x11\x63ontinuationToken\x18\x02 \x01(\x0c\"\'\n\x16ReusedPasswordsRequest\x12\r\n\x05\x63ount\x18\x01 \x01(\x05\">\n\x14SummaryConsoleReport\x12\x12\n\nreportType\x18\x01 \x01(\x05\x12\x12\n\nreportData\x18\x02 \x01(\x0c\"|\n\x12\x43hangeToKeyTypeOne\x12/\n\nobjectType\x18\x01 \x01(\x0e\x32\x1b.Authentication.ObjectTypes\x12\x12\n\nprimaryUid\x18\x02 \x01(\x0c\x12\x14\n\x0csecondaryUid\x18\x03 \x01(\x0c\x12\x0b\n\x03key\x18\x04 \x01(\x0c\"[\n\x19\x43hangeToKeyTypeOneRequest\x12>\n\x12\x63hangeToKeyTypeOne\x18\x01 \x03(\x0b\x32\".Authentication.ChangeToKeyTypeOne\"U\n\x18\x43hangeToKeyTypeOneStatus\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x0e\n\x06reason\x18\x04 \x01(\t\"h\n\x1a\x43hangeToKeyTypeOneResponse\x12J\n\x18\x63hangeToKeyTypeOneStatus\x18\x01 \x03(\x0b\x32(.Authentication.ChangeToKeyTypeOneStatus\"\xb9\x01\n\x18GetChangeKeyTypesRequest\x12=\n\x10onlyTheseObjects\x18\x01 \x03(\x0e\x32#.Authentication.EncryptedObjectType\x12\r\n\x05limit\x18\x02 \x01(\x05\x12\x1a\n\x12includeRecommended\x18\x03 \x01(\x08\x12\x13\n\x0bincludeKeys\x18\x04 \x01(\x08\x12\x1e\n\x16includeAllowedKeyTypes\x18\x05 \x01(\x08\"\x82\x01\n\x19GetChangeKeyTypesResponse\x12+\n\x04keys\x18\x01 \x03(\x0b\x32\x1d.Authentication.ChangeKeyType\x12\x38\n\x0f\x61llowedKeyTypes\x18\x02 \x03(\x0b\x32\x1f.Authentication.AllowedKeyTypes\"\x81\x01\n\x0f\x41llowedKeyTypes\x12\x37\n\nobjectType\x18\x01 \x01(\x0e\x32#.Authentication.EncryptedObjectType\x12\x35\n\x0f\x61llowedKeyTypes\x18\x02 \x03(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"=\n\x0e\x43hangeKeyTypes\x12+\n\x04keys\x18\x01 \x03(\x0b\x32\x1d.Authentication.ChangeKeyType\"\xd6\x01\n\rChangeKeyType\x12\x37\n\nobjectType\x18\x01 \x01(\x0e\x32#.Authentication.EncryptedObjectType\x12\x0b\n\x03uid\x18\x02 \x01(\x0c\x12\x14\n\x0csecondaryUid\x18\x03 \x01(\x0c\x12\x0b\n\x03key\x18\x04 \x01(\x0c\x12-\n\x07keyType\x18\x05 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12-\n\x06status\x18\x06 \x01(\x0e\x32\x1d.Authentication.GenericStatus\"!\n\x06SetKey\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0b\n\x03key\x18\x02 \x01(\x0c\"5\n\rSetKeyRequest\x12$\n\x04keys\x18\x01 \x03(\x0b\x32\x16.Authentication.SetKey\"\x92\x05\n\x11\x43reateUserRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x14\n\x0c\x61uthVerifier\x18\x02 \x01(\x0c\x12\x18\n\x10\x65ncryptionParams\x18\x03 \x01(\x0c\x12\x14\n\x0crsaPublicKey\x18\x04 \x01(\x0c\x12\x1e\n\x16rsaEncryptedPrivateKey\x18\x05 \x01(\x0c\x12\x14\n\x0c\x65\x63\x63PublicKey\x18\x06 \x01(\x0c\x12\x1e\n\x16\x65\x63\x63\x45ncryptedPrivateKey\x18\x07 \x01(\x0c\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x08 \x01(\x0c\x12\x1a\n\x12\x65ncryptedClientKey\x18\t \x01(\x0c\x12\x15\n\rclientVersion\x18\n \x01(\t\x12\x1e\n\x16\x65ncryptedDeviceDataKey\x18\x0b \x01(\x0c\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x0c \x01(\x0c\x12\x19\n\x11messageSessionUid\x18\r \x01(\x0c\x12\x17\n\x0finstallReferrer\x18\x0e \x01(\t\x12\x0e\n\x06mccMNC\x18\x0f \x01(\x05\x12\x0b\n\x03mfg\x18\x10 \x01(\t\x12\r\n\x05model\x18\x11 \x01(\t\x12\r\n\x05\x62rand\x18\x12 \x01(\t\x12\x0f\n\x07product\x18\x13 \x01(\t\x12\x0e\n\x06\x64\x65vice\x18\x14 \x01(\t\x12\x0f\n\x07\x63\x61rrier\x18\x15 \x01(\t\x12\x18\n\x10verificationCode\x18\x16 \x01(\t\x12\x42\n\x16\x65nterpriseRegistration\x18\x17 \x01(\x0b\x32\".Enterprise.EnterpriseRegistration\x12\"\n\x1a\x65ncryptedVerificationToken\x18\x18 \x01(\x0c\x12\x1e\n\x16\x65nterpriseUsersDataKey\x18\x19 \x01(\x0c\"W\n!NodeEnforcementAddOrUpdateRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x13\n\x0b\x65nforcement\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\"C\n\x1cNodeEnforcementRemoveRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x13\n\x0b\x65nforcement\x18\x02 \x01(\t\"\xb7\x01\n\x0f\x41piRequestByKey\x12\r\n\x05keyId\x18\x01 \x01(\x05\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x10\n\x08username\x18\x03 \x01(\t\x12\x0e\n\x06locale\x18\x04 \x01(\t\x12<\n\x11supportedLanguage\x18\x05 \x01(\x0e\x32!.Authentication.SupportedLanguage\x12\x0c\n\x04type\x18\x06 \x01(\x05\x12\x16\n\x0eparentThreadId\x18\x07 \x01(\t\"\xc7\x01\n\x15\x41piRequestByKAtoKAKey\x12,\n\x0csourceRegion\x18\x01 \x01(\x0e\x32\x16.Authentication.Region\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12<\n\x11supportedLanguage\x18\x03 \x01(\x0e\x32!.Authentication.SupportedLanguage\x12\x31\n\x11\x64\x65stinationRegion\x18\x04 \x01(\x0e\x32\x16.Authentication.Region\".\n\x0fMemcacheRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x0e\n\x06userId\x18\x02 \x01(\x05\".\n\x10MemcacheResponse\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"w\n\x1cMasterPasswordReentryRequest\x12\x16\n\x0epbkdf2Password\x18\x01 \x01(\t\x12?\n\x06\x61\x63tion\x18\x02 \x01(\x0e\x32/.Authentication.MasterPasswordReentryActionType\"\\\n\x1dMasterPasswordReentryResponse\x12;\n\x06status\x18\x01 \x01(\x0e\x32+.Authentication.MasterPasswordReentryStatus\"\xc5\x01\n\x19\x44\x65viceRegistrationRequest\x12\x15\n\rclientVersion\x18\x01 \x01(\t\x12\x12\n\ndeviceName\x18\x02 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x03 \x01(\x0c\x12\x16\n\x0e\x64\x65vicePlatform\x18\x04 \x01(\t\x12:\n\x10\x63lientFormFactor\x18\x05 \x01(\x0e\x32 .Authentication.ClientFormFactor\x12\x10\n\x08username\x18\x06 \x01(\t\"\x9a\x01\n\x19\x44\x65viceVerificationRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x1b\n\x13verificationChannel\x18\x03 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x04 \x01(\x0c\x12\x15\n\rclientVersion\x18\x05 \x01(\t\"\xb2\x01\n\x1a\x44\x65viceVerificationResponse\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x03 \x01(\x0c\x12\x15\n\rclientVersion\x18\x04 \x01(\t\x12\x32\n\x0c\x64\x65viceStatus\x18\x05 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\"\xc8\x01\n\x15\x44\x65viceApprovalRequest\x12\r\n\x05\x65mail\x18\x01 \x01(\t\x12\x18\n\x10twoFactorChannel\x18\x02 \x01(\t\x12\x15\n\rclientVersion\x18\x03 \x01(\t\x12\x0e\n\x06locale\x18\x04 \x01(\t\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x05 \x01(\x0c\x12\x10\n\x08totpCode\x18\x06 \x01(\t\x12\x10\n\x08\x64\x65viceIp\x18\x07 \x01(\t\x12\x1d\n\x15\x64\x65viceTokenExpireDays\x18\x08 \x01(\t\"9\n\x16\x44\x65viceApprovalResponse\x12\x1f\n\x17\x65ncryptedTwoFactorToken\x18\x01 \x01(\x0c\"~\n\x14\x41pproveDeviceRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x1e\n\x16\x65ncryptedDeviceDataKey\x18\x02 \x01(\x0c\x12\x14\n\x0c\x64\x65nyApproval\x18\x03 \x01(\x08\x12\x12\n\nlinkDevice\x18\x04 \x01(\x08\"E\n\x1a\x45nterpriseUserAliasRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\r\n\x05\x61lias\x18\x02 \x01(\t\"Y\n\x1d\x45nterpriseUserAddAliasRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\x0f\n\x07primary\x18\x03 \x01(\x08\"w\n\x1f\x45nterpriseUserAddAliasRequestV2\x12T\n\x1d\x65nterpriseUserAddAliasRequest\x18\x01 \x03(\x0b\x32-.Authentication.EnterpriseUserAddAliasRequest\"H\n\x1c\x45nterpriseUserAddAliasStatus\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06status\x18\x02 \x01(\t\"^\n\x1e\x45nterpriseUserAddAliasResponse\x12<\n\x06status\x18\x01 \x03(\x0b\x32,.Authentication.EnterpriseUserAddAliasStatus\"&\n\x06\x44\x65vice\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\"\\\n\x1cRegisterDeviceDataKeyRequest\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x01 \x01(\x0c\x12\x1e\n\x16\x65ncryptedDeviceDataKey\x18\x02 \x01(\x0c\"n\n)ValidateCreateUserVerificationCodeRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x18\n\x10verificationCode\x18\x03 \x01(\t\"\xa3\x01\n%ValidateDeviceVerificationCodeRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x18\n\x10verificationCode\x18\x03 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x04 \x01(\x0c\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x05 \x01(\x0c\"Y\n\x19SendSessionMessageRequest\x12\x19\n\x11messageSessionUid\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63ommand\x18\x02 \x01(\t\x12\x10\n\x08username\x18\x03 \x01(\t\"M\n\x11GlobalUserAccount\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x12\n\naccountUid\x18\x02 \x01(\x0c\x12\x12\n\nregionName\x18\x03 \x01(\t\"7\n\x0f\x41\x63\x63ountUsername\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x12\n\ndateActive\x18\x02 \x01(\t\"P\n\x19SsoServiceProviderRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x0e\n\x06locale\x18\x03 \x01(\t\"a\n\x1aSsoServiceProviderResponse\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05spUrl\x18\x02 \x01(\t\x12\x0f\n\x07isCloud\x18\x03 \x01(\x08\x12\x15\n\rclientVersion\x18\x04 \x01(\t\"4\n\x12UserSettingRequest\x12\x0f\n\x07setting\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"f\n\rThrottleState\x12*\n\x04type\x18\x01 \x01(\x0e\x32\x1c.Authentication.ThrottleType\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\x12\r\n\x05state\x18\x04 \x01(\x08\"\xb5\x01\n\x0eThrottleState2\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x16\n\x0ekeyDescription\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\x12\x18\n\x10valueDescription\x18\x04 \x01(\t\x12\x12\n\nidentifier\x18\x05 \x01(\t\x12\x0e\n\x06locked\x18\x06 \x01(\x08\x12\x1a\n\x12includedInAllClear\x18\x07 \x01(\x08\x12\x15\n\rexpireSeconds\x18\x08 \x01(\x05\"\x97\x01\n\x11\x44\x65viceInformation\x12\x10\n\x08\x64\x65viceId\x18\x01 \x01(\x03\x12\x12\n\ndeviceName\x18\x02 \x01(\t\x12\x15\n\rclientVersion\x18\x03 \x01(\t\x12\x11\n\tlastLogin\x18\x04 \x01(\x03\x12\x32\n\x0c\x64\x65viceStatus\x18\x05 \x01(\x0e\x32\x1c.Authentication.DeviceStatus\"*\n\x0bUserSetting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x08\".\n\x12UserDataKeyRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x03(\x03\"+\n\x18UserDataKeyByNodeRequest\x12\x0f\n\x07nodeIds\x18\x01 \x03(\x03\"\x80\x01\n\x1b\x45nterpriseUserIdDataKeyPair\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x18\n\x10\x65ncryptedDataKey\x18\x02 \x01(\x0c\x12-\n\x07keyType\x18\x03 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"\x95\x01\n\x0bUserDataKey\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x0f\n\x07roleKey\x18\x02 \x01(\x0c\x12\x12\n\nprivateKey\x18\x03 \x01(\t\x12Q\n\x1c\x65nterpriseUserIdDataKeyPairs\x18\x04 \x03(\x0b\x32+.Authentication.EnterpriseUserIdDataKeyPair\"z\n\x13UserDataKeyResponse\x12\x31\n\x0cuserDataKeys\x18\x01 \x03(\x0b\x32\x1b.Authentication.UserDataKey\x12\x14\n\x0c\x61\x63\x63\x65ssDenied\x18\x02 \x03(\x03\x12\x1a\n\x12noEncryptedDataKey\x18\x03 \x03(\x03\"H\n)MasterPasswordRecoveryVerificationRequest\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\"U\n\x1cGetSecurityQuestionV3Request\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\x12\x18\n\x10verificationCode\x18\x02 \x01(\t\"r\n\x1dGetSecurityQuestionV3Response\x12\x18\n\x10securityQuestion\x18\x01 \x01(\t\x12\x15\n\rbackupKeyDate\x18\x02 \x01(\x03\x12\x0c\n\x04salt\x18\x03 \x01(\x0c\x12\x12\n\niterations\x18\x04 \x01(\x05\"n\n\x19GetDataKeyBackupV3Request\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x01 \x01(\x0c\x12\x18\n\x10verificationCode\x18\x02 \x01(\t\x12\x1a\n\x12securityAnswerHash\x18\x03 \x01(\x0c\"v\n\rPasswordRules\x12\x10\n\x08ruleType\x18\x01 \x01(\t\x12\r\n\x05match\x18\x02 \x01(\x08\x12\x0f\n\x07pattern\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12\x0f\n\x07minimum\x18\x05 \x01(\x05\x12\r\n\x05value\x18\x06 \x01(\t\"\xc9\x02\n\x1aGetDataKeyBackupV3Response\x12\x15\n\rdataKeyBackup\x18\x01 \x01(\x0c\x12\x19\n\x11\x64\x61taKeyBackupDate\x18\x02 \x01(\x03\x12\x11\n\tpublicKey\x18\x03 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x04 \x01(\x0c\x12\x11\n\tclientKey\x18\x05 \x01(\x0c\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x06 \x01(\x0c\x12\x34\n\rpasswordRules\x18\x07 \x03(\x0b\x32\x1d.Authentication.PasswordRules\x12\x1a\n\x12passwordRulesIntro\x18\x08 \x01(\t\x12\x1f\n\x17minimumPbkdf2Iterations\x18\t \x01(\x05\x12$\n\x07keyType\x18\n \x01(\x0e\x32\x13.Enterprise.KeyType\")\n\x14GetPublicKeysRequest\x12\x11\n\tusernames\x18\x01 \x03(\t\"\x86\x01\n\x11PublicKeyResponse\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x11\n\tpublicKey\x18\x02 \x01(\x0c\x12\x14\n\x0cpublicEccKey\x18\x03 \x01(\x0c\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x11\n\terrorCode\x18\x05 \x01(\t\x12\x12\n\naccountUid\x18\x06 \x01(\x0c\"P\n\x15GetPublicKeysResponse\x12\x37\n\x0ckeyResponses\x18\x01 \x03(\x0b\x32!.Authentication.PublicKeyResponse\"F\n\x14SetEccKeyPairRequest\x12\x11\n\tpublicKey\x18\x01 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x02 \x01(\x0c\"I\n\x15SetEccKeyPairsRequest\x12\x30\n\x08teamKeys\x18\x01 \x03(\x0b\x32\x1e.Authentication.TeamEccKeyPair\"R\n\x16SetEccKeyPairsResponse\x12\x38\n\x08teamKeys\x18\x01 \x03(\x0b\x32&.Authentication.TeamEccKeyPairResponse\"Q\n\x0eTeamEccKeyPair\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x11\n\tpublicKey\x18\x02 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x03 \x01(\x0c\"X\n\x16TeamEccKeyPairResponse\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12-\n\x06status\x18\x02 \x01(\x0e\x32\x1d.Authentication.GenericStatus\"D\n\x17GetKsmPublicKeysRequest\x12\x11\n\tclientIds\x18\x01 \x03(\x0c\x12\x16\n\x0e\x63ontrollerUids\x18\x02 \x03(\x0c\"U\n\x17\x44\x65vicePublicKeyResponse\x12\x10\n\x08\x63lientId\x18\x01 \x01(\x0c\x12\x11\n\tpublicKey\x18\x02 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\"Y\n\x18GetKsmPublicKeysResponse\x12=\n\x0ckeyResponses\x18\x01 \x03(\x0b\x32\'.Authentication.DevicePublicKeyResponse\"X\n\x13\x41\x64\x64\x41ppSharesRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12+\n\x06shares\x18\x02 \x03(\x0b\x32\x1b.Authentication.AppShareAdd\">\n\x16RemoveAppSharesRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12\x0e\n\x06shares\x18\x02 \x03(\x0c\"\x87\x01\n\x0b\x41ppShareAdd\x12\x11\n\tsecretUid\x18\x02 \x01(\x0c\x12\x37\n\tshareType\x18\x03 \x01(\x0e\x32$.Authentication.ApplicationShareType\x12\x1a\n\x12\x65ncryptedSecretKey\x18\x04 \x01(\x0c\x12\x10\n\x08\x65\x64itable\x18\x05 \x01(\x08\"\x89\x01\n\x08\x41ppShare\x12\x11\n\tsecretUid\x18\x01 \x01(\x0c\x12\x37\n\tshareType\x18\x02 \x01(\x0e\x32$.Authentication.ApplicationShareType\x12\x10\n\x08\x65\x64itable\x18\x03 \x01(\x08\x12\x11\n\tcreatedOn\x18\x04 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\"\xd9\x01\n\x13\x41\x64\x64\x41ppClientRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12\x17\n\x0f\x65ncryptedAppKey\x18\x02 \x01(\x0c\x12\x10\n\x08\x63lientId\x18\x03 \x01(\x0c\x12\x0e\n\x06lockIp\x18\x04 \x01(\x08\x12\x1b\n\x13\x66irstAccessExpireOn\x18\x05 \x01(\x03\x12\x16\n\x0e\x61\x63\x63\x65ssExpireOn\x18\x06 \x01(\x03\x12\n\n\x02id\x18\x07 \x01(\t\x12\x30\n\rappClientType\x18\x08 \x01(\x0e\x32\x19.Enterprise.AppClientType\"@\n\x17RemoveAppClientsRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63lients\x18\x02 \x03(\x0c\"\xaa\x01\n\x17\x41\x64\x64\x45xternalShareRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x1a\n\x12\x65ncryptedRecordKey\x18\x02 \x01(\x0c\x12\x10\n\x08\x63lientId\x18\x03 \x01(\x0c\x12\x16\n\x0e\x61\x63\x63\x65ssExpireOn\x18\x04 \x01(\x03\x12\n\n\x02id\x18\x05 \x01(\t\x12\x16\n\x0eisSelfDestruct\x18\x06 \x01(\x08\x12\x12\n\nisEditable\x18\x07 \x01(\x08\"\x93\x02\n\tAppClient\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08\x63lientId\x18\x02 \x01(\x0c\x12\x11\n\tcreatedOn\x18\x03 \x01(\x03\x12\x13\n\x0b\x66irstAccess\x18\x04 \x01(\x03\x12\x12\n\nlastAccess\x18\x05 \x01(\x03\x12\x11\n\tpublicKey\x18\x06 \x01(\x0c\x12\x0e\n\x06lockIp\x18\x07 \x01(\x08\x12\x11\n\tipAddress\x18\x08 \x01(\t\x12\x1b\n\x13\x66irstAccessExpireOn\x18\t \x01(\x03\x12\x16\n\x0e\x61\x63\x63\x65ssExpireOn\x18\n \x01(\x03\x12\x30\n\rappClientType\x18\x0b \x01(\x0e\x32\x19.Enterprise.AppClientType\x12\x0f\n\x07\x63\x61nEdit\x18\x0c \x01(\x08\")\n\x11GetAppInfoRequest\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x03(\x0c\"\x8e\x01\n\x07\x41ppInfo\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12(\n\x06shares\x18\x02 \x03(\x0b\x32\x18.Authentication.AppShare\x12*\n\x07\x63lients\x18\x03 \x03(\x0b\x32\x19.Authentication.AppClient\x12\x17\n\x0fisExternalShare\x18\x04 \x01(\x08\">\n\x12GetAppInfoResponse\x12(\n\x07\x61ppInfo\x18\x01 \x03(\x0b\x32\x17.Authentication.AppInfo\"\xd5\x01\n\x12\x41pplicationSummary\x12\x14\n\x0c\x61ppRecordUid\x18\x01 \x01(\x0c\x12\x12\n\nlastAccess\x18\x02 \x01(\x03\x12\x14\n\x0crecordShares\x18\x03 \x01(\x05\x12\x14\n\x0c\x66olderShares\x18\x04 \x01(\x05\x12\x15\n\rfolderRecords\x18\x05 \x01(\x05\x12\x13\n\x0b\x63lientCount\x18\x06 \x01(\x05\x12\x1a\n\x12\x65xpiredClientCount\x18\x07 \x01(\x05\x12\x10\n\x08username\x18\x08 \x01(\t\x12\x0f\n\x07\x61ppData\x18\t \x01(\x0c\"`\n\x1eGetApplicationsSummaryResponse\x12>\n\x12\x61pplicationSummary\x18\x01 \x03(\x0b\x32\".Authentication.ApplicationSummary\"/\n\x1bGetVerificationTokenRequest\x12\x10\n\x08username\x18\x01 \x01(\t\"B\n\x1cGetVerificationTokenResponse\x12\"\n\x1a\x65ncryptedVerificationToken\x18\x01 \x01(\x0c\"\'\n\x16SendShareInviteRequest\x12\r\n\x05\x65mail\x18\x01 \x01(\t\"\xc5\x01\n\x18TimeLimitedAccessRequest\x12\x12\n\naccountUid\x18\x01 \x03(\x0c\x12\x0f\n\x07teamUid\x18\x02 \x03(\x0c\x12\x11\n\trecordUid\x18\x03 \x03(\x0c\x12\x17\n\x0fsharedObjectUid\x18\x04 \x01(\x0c\x12\x44\n\x15timeLimitedAccessType\x18\x05 \x01(\x0e\x32%.Authentication.TimeLimitedAccessType\x12\x12\n\nexpiration\x18\x06 \x01(\x03\"7\n\x17TimeLimitedAccessStatus\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xf8\x01\n\x19TimeLimitedAccessResponse\x12\x10\n\x08revision\x18\x01 \x01(\x03\x12\x41\n\x10userAccessStatus\x18\x02 \x03(\x0b\x32\'.Authentication.TimeLimitedAccessStatus\x12\x41\n\x10teamAccessStatus\x18\x03 \x03(\x0b\x32\'.Authentication.TimeLimitedAccessStatus\x12\x43\n\x12recordAccessStatus\x18\x04 \x03(\x0b\x32\'.Authentication.TimeLimitedAccessStatus\"+\n\x16RequestDownloadRequest\x12\x11\n\tfileNames\x18\x01 \x03(\t\"g\n\x17RequestDownloadResponse\x12\x0e\n\x06result\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\t\x12+\n\tdownloads\x18\x03 \x03(\x0b\x32\x18.Authentication.Download\"D\n\x08\x44ownload\x12\x10\n\x08\x66ileName\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x19\n\x11successStatusCode\x18\x03 \x01(\x05\"#\n\x11\x44\x65leteUserRequest\x12\x0e\n\x06reason\x18\x01 \x01(\t\"\x84\x01\n\x1b\x43hangeMasterPasswordRequest\x12\x14\n\x0c\x61uthVerifier\x18\x01 \x01(\x0c\x12\x18\n\x10\x65ncryptionParams\x18\x02 \x01(\x0c\x12\x1b\n\x13\x66romServiceProvider\x18\x03 \x01(\x08\x12\x18\n\x10iterationsChange\x18\x04 \x01(\x08\"=\n\x1c\x43hangeMasterPasswordResponse\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x01 \x01(\x0c\"Y\n\x1b\x41\x63\x63ountRecoverySetupRequest\x12 \n\x18recoveryEncryptedDataKey\x18\x01 \x01(\x0c\x12\x18\n\x10recoveryAuthHash\x18\x02 \x01(\x0c\"\xac\x01\n!AccountRecoveryVerifyCodeResponse\x12\x34\n\rbackupKeyType\x18\x01 \x01(\x0e\x32\x1d.Authentication.BackupKeyType\x12\x15\n\rbackupKeyDate\x18\x02 \x01(\x03\x12\x18\n\x10securityQuestion\x18\x03 \x01(\t\x12\x0c\n\x04salt\x18\x04 \x01(\x0c\x12\x12\n\niterations\x18\x05 \x01(\x05\",\n\x1b\x45mergencyAccessLoginRequest\x12\r\n\x05owner\x18\x01 \x01(\t\"\xb5\x01\n\x1c\x45mergencyAccessLoginResponse\x12\x14\n\x0csessionToken\x18\x01 \x01(\x0c\x12%\n\x07\x64\x61taKey\x18\x02 \x01(\x0b\x32\x14.Enterprise.TypedKey\x12+\n\rrsaPrivateKey\x18\x03 \x01(\x0b\x32\x14.Enterprise.TypedKey\x12+\n\reccPrivateKey\x18\x04 \x01(\x0b\x32\x14.Enterprise.TypedKey\"\xb2\x01\n\x0bUserTeamKey\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x18\n\x10\x65nterpriseUserId\x18\x03 \x01(\x03\x12\x1b\n\x13\x65ncryptedTeamKeyRSA\x18\x04 \x01(\x0c\x12\x1a\n\x12\x65ncryptedTeamKeyEC\x18\x05 \x01(\x0c\x12-\n\x06status\x18\x06 \x01(\x0e\x32\x1d.Authentication.GenericStatus\")\n\x16GenericRequestResponse\x12\x0f\n\x07request\x18\x01 \x03(\x0c\"f\n\x1aPasskeyRegistrationRequest\x12H\n\x17\x61uthenticatorAttachment\x18\x01 \x01(\x0e\x32\'.Authentication.AuthenticatorAttachment\"P\n\x1bPasskeyRegistrationResponse\x12\x16\n\x0e\x63hallengeToken\x18\x01 \x01(\x0c\x12\x19\n\x11pkCreationOptions\x18\x02 \x01(\t\"\x84\x01\n\x1fPasskeyRegistrationFinalization\x12\x16\n\x0e\x63hallengeToken\x18\x01 \x01(\x0c\x12\x1d\n\x15\x61uthenticatorResponse\x18\x02 \x01(\t\x12\x19\n\x0c\x66riendlyName\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0f\n\r_friendlyName\"\xb3\x02\n\x1cPasskeyAuthenticationRequest\x12H\n\x17\x61uthenticatorAttachment\x18\x01 \x01(\x0e\x32\'.Authentication.AuthenticatorAttachment\x12\x36\n\x0epasskeyPurpose\x18\x02 \x01(\x0e\x32\x1e.Authentication.PasskeyPurpose\x12\x15\n\rclientVersion\x18\x03 \x01(\t\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x04 \x01(\x0c\x12\x15\n\x08username\x18\x05 \x01(\tH\x00\x88\x01\x01\x12 \n\x13\x65ncryptedLoginToken\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0b\n\t_usernameB\x16\n\x14_encryptedLoginToken\"\x8b\x01\n\x1dPasskeyAuthenticationResponse\x12\x18\n\x10pkRequestOptions\x18\x01 \x01(\t\x12\x16\n\x0e\x63hallengeToken\x18\x02 \x01(\x0c\x12 \n\x13\x65ncryptedLoginToken\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\x16\n\x14_encryptedLoginToken\"\xbf\x01\n\x18PasskeyValidationRequest\x12\x16\n\x0e\x63hallengeToken\x18\x01 \x01(\x0c\x12\x19\n\x11\x61ssertionResponse\x18\x02 \x01(\x0c\x12\x36\n\x0epasskeyPurpose\x18\x03 \x01(\x0e\x32\x1e.Authentication.PasskeyPurpose\x12 \n\x13\x65ncryptedLoginToken\x18\x04 \x01(\x0cH\x00\x88\x01\x01\x42\x16\n\x14_encryptedLoginToken\"I\n\x19PasskeyValidationResponse\x12\x0f\n\x07isValid\x18\x01 \x01(\x08\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x02 \x01(\x0c\"h\n\x14UpdatePasskeyRequest\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x14\n\x0c\x63redentialId\x18\x02 \x01(\x0c\x12\x19\n\x0c\x66riendlyName\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0f\n\r_friendlyName\"-\n\x12PasskeyListRequest\x12\x17\n\x0fincludeDisabled\x18\x01 \x01(\x08\"\xa4\x01\n\x0bPasskeyInfo\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x14\n\x0c\x63redentialId\x18\x02 \x01(\x0c\x12\x14\n\x0c\x66riendlyName\x18\x03 \x01(\t\x12\x0e\n\x06\x41\x41GUID\x18\x04 \x01(\t\x12\x17\n\x0f\x63reatedAtMillis\x18\x05 \x01(\x03\x12\x16\n\x0elastUsedMillis\x18\x06 \x01(\x03\x12\x18\n\x10\x64isabledAtMillis\x18\x07 \x01(\x03\"G\n\x13PasskeyListResponse\x12\x30\n\x0bpasskeyInfo\x18\x01 \x03(\x0b\x32\x1b.Authentication.PasskeyInfo\"C\n\x0fTranslationInfo\x12\x16\n\x0etranslationKey\x18\x01 \x01(\t\x12\x18\n\x10translationValue\x18\x02 \x01(\t\",\n\x12TranslationRequest\x12\x16\n\x0etranslationKey\x18\x01 \x03(\t\"O\n\x13TranslationResponse\x12\x38\n\x0ftranslationInfo\x18\x01 \x03(\x0b\x32\x1f.Authentication.TranslationInfo*\xd3\x02\n\x11SupportedLanguage\x12\x0b\n\x07\x45NGLISH\x10\x00\x12\n\n\x06\x41RABIC\x10\x01\x12\x0b\n\x07\x42RITISH\x10\x02\x12\x0b\n\x07\x43HINESE\x10\x03\x12\x15\n\x11\x43HINESE_HONG_KONG\x10\x04\x12\x12\n\x0e\x43HINESE_TAIWAN\x10\x05\x12\t\n\x05\x44UTCH\x10\x06\x12\n\n\x06\x46RENCH\x10\x07\x12\n\n\x06GERMAN\x10\x08\x12\t\n\x05GREEK\x10\t\x12\n\n\x06HEBREW\x10\n\x12\x0b\n\x07ITALIAN\x10\x0b\x12\x0c\n\x08JAPANESE\x10\x0c\x12\n\n\x06KOREAN\x10\r\x12\n\n\x06POLISH\x10\x0e\x12\x0e\n\nPORTUGUESE\x10\x0f\x12\x15\n\x11PORTUGUESE_BRAZIL\x10\x10\x12\x0c\n\x08ROMANIAN\x10\x11\x12\x0b\n\x07RUSSIAN\x10\x12\x12\n\n\x06SLOVAK\x10\x13\x12\x0b\n\x07SPANISH\x10\x14\x12\x0b\n\x07\x46INNISH\x10\x15\x12\x0b\n\x07SWEDISH\x10\x16*k\n\tLoginType\x12\n\n\x06NORMAL\x10\x00\x12\x07\n\x03SSO\x10\x01\x12\x07\n\x03\x42IO\x10\x02\x12\r\n\tALTERNATE\x10\x03\x12\x0b\n\x07OFFLINE\x10\x04\x12\x13\n\x0f\x46ORGOT_PASSWORD\x10\x05\x12\x0f\n\x0bPASSKEY_BIO\x10\x06*q\n\x0c\x44\x65viceStatus\x12\x19\n\x15\x44\x45VICE_NEEDS_APPROVAL\x10\x00\x12\r\n\tDEVICE_OK\x10\x01\x12\x1b\n\x17\x44\x45VICE_DISABLED_BY_USER\x10\x02\x12\x1a\n\x16\x44\x45VICE_LOCKED_BY_ADMIN\x10\x03*A\n\rLicenseStatus\x12\t\n\x05OTHER\x10\x00\x12\n\n\x06\x41\x43TIVE\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x12\x0c\n\x08\x44ISABLED\x10\x03*7\n\x0b\x41\x63\x63ountType\x12\x0c\n\x08\x43ONSUMER\x10\x00\x12\n\n\x06\x46\x41MILY\x10\x01\x12\x0e\n\nENTERPRISE\x10\x02*\x9f\x02\n\x10SessionTokenType\x12\x12\n\x0eNO_RESTRICTION\x10\x00\x12\x14\n\x10\x41\x43\x43OUNT_RECOVERY\x10\x01\x12\x11\n\rSHARE_ACCOUNT\x10\x02\x12\x0c\n\x08PURCHASE\x10\x03\x12\x0c\n\x08RESTRICT\x10\x04\x12\x11\n\rACCEPT_INVITE\x10\x05\x12\x12\n\x0eSUPPORT_SERVER\x10\x06\x12\x17\n\x13\x45NTERPRISE_CREATION\x10\x07\x12\x1f\n\x1b\x45XPIRED_BUT_ALLOWED_TO_SYNC\x10\x08\x12\x18\n\x14\x41\x43\x43\x45PT_FAMILY_INVITE\x10\t\x12!\n\x1d\x45NTERPRISE_CREATION_PURCHASED\x10\n\x12\x14\n\x10\x45MERGENCY_ACCESS\x10\x0b*G\n\x07Version\x12\x13\n\x0finvalid_version\x10\x00\x12\x13\n\x0f\x64\x65\x66\x61ult_version\x10\x01\x12\x12\n\x0esecond_version\x10\x02*7\n\x1fMasterPasswordReentryActionType\x12\n\n\x06UNMASK\x10\x00\x12\x08\n\x04\x43OPY\x10\x01*l\n\x0bLoginMethod\x12\x17\n\x13INVALID_LOGINMETHOD\x10\x00\x12\x14\n\x10\x45XISTING_ACCOUNT\x10\x01\x12\x0e\n\nSSO_DOMAIN\x10\x02\x12\r\n\tAFTER_SSO\x10\x03\x12\x0f\n\x0bNEW_ACCOUNT\x10\x04*\xbe\x04\n\nLoginState\x12\x16\n\x12INVALID_LOGINSTATE\x10\x00\x12\x0e\n\nLOGGED_OUT\x10\x01\x12\x1c\n\x18\x44\x45VICE_APPROVAL_REQUIRED\x10\x02\x12\x11\n\rDEVICE_LOCKED\x10\x03\x12\x12\n\x0e\x41\x43\x43OUNT_LOCKED\x10\x04\x12\x19\n\x15\x44\x45VICE_ACCOUNT_LOCKED\x10\x05\x12\x0b\n\x07UPGRADE\x10\x06\x12\x13\n\x0fLICENSE_EXPIRED\x10\x07\x12\x13\n\x0fREGION_REDIRECT\x10\x08\x12\x16\n\x12REDIRECT_CLOUD_SSO\x10\t\x12\x17\n\x13REDIRECT_ONSITE_SSO\x10\n\x12\x10\n\x0cREQUIRES_2FA\x10\x0c\x12\x16\n\x12REQUIRES_AUTH_HASH\x10\r\x12\x15\n\x11REQUIRES_USERNAME\x10\x0e\x12\x19\n\x15\x41\x46TER_CLOUD_SSO_LOGIN\x10\x0f\x12\x1d\n\x19REQUIRES_ACCOUNT_CREATION\x10\x10\x12&\n\"REQUIRES_DEVICE_ENCRYPTED_DATA_KEY\x10\x11\x12\x17\n\x13LOGIN_TOKEN_EXPIRED\x10\x12\x12\x1e\n\x1aPASSKEY_INITIATE_CHALLENGE\x10\x13\x12\x19\n\x15PASSKEY_AUTH_REQUIRED\x10\x14\x12!\n\x1dPASSKEY_VERIFY_AUTHENTICATION\x10\x15\x12\x17\n\x13\x41\x46TER_PASSKEY_LOGIN\x10\x16\x12\r\n\tLOGGED_IN\x10\x63*k\n\x14\x45ncryptedDataKeyType\x12\n\n\x06NO_KEY\x10\x00\x12\x18\n\x14\x42Y_DEVICE_PUBLIC_KEY\x10\x01\x12\x0f\n\x0b\x42Y_PASSWORD\x10\x02\x12\x10\n\x0c\x42Y_ALTERNATE\x10\x03\x12\n\n\x06\x42Y_BIO\x10\x04*-\n\x0ePasswordMethod\x12\x0b\n\x07\x45NTERED\x10\x00\x12\x0e\n\nBIOMETRICS\x10\x01*\xb9\x01\n\x11TwoFactorPushType\x12\x14\n\x10TWO_FA_PUSH_NONE\x10\x00\x12\x13\n\x0fTWO_FA_PUSH_SMS\x10\x01\x12\x16\n\x12TWO_FA_PUSH_KEEPER\x10\x02\x12\x18\n\x14TWO_FA_PUSH_DUO_PUSH\x10\x03\x12\x18\n\x14TWO_FA_PUSH_DUO_TEXT\x10\x04\x12\x18\n\x14TWO_FA_PUSH_DUO_CALL\x10\x05\x12\x13\n\x0fTWO_FA_PUSH_DNA\x10\x06*\xc3\x01\n\x12TwoFactorValueType\x12\x14\n\x10TWO_FA_CODE_NONE\x10\x00\x12\x14\n\x10TWO_FA_CODE_TOTP\x10\x01\x12\x13\n\x0fTWO_FA_CODE_SMS\x10\x02\x12\x13\n\x0fTWO_FA_CODE_DUO\x10\x03\x12\x13\n\x0fTWO_FA_CODE_RSA\x10\x04\x12\x13\n\x0fTWO_FA_RESP_U2F\x10\x05\x12\x18\n\x14TWO_FA_RESP_WEBAUTHN\x10\x06\x12\x13\n\x0fTWO_FA_CODE_DNA\x10\x07*\xe1\x01\n\x14TwoFactorChannelType\x12\x12\n\x0eTWO_FA_CT_NONE\x10\x00\x12\x12\n\x0eTWO_FA_CT_TOTP\x10\x01\x12\x11\n\rTWO_FA_CT_SMS\x10\x02\x12\x11\n\rTWO_FA_CT_DUO\x10\x03\x12\x11\n\rTWO_FA_CT_RSA\x10\x04\x12\x14\n\x10TWO_FA_CT_BACKUP\x10\x05\x12\x11\n\rTWO_FA_CT_U2F\x10\x06\x12\x16\n\x12TWO_FA_CT_WEBAUTHN\x10\x07\x12\x14\n\x10TWO_FA_CT_KEEPER\x10\x08\x12\x11\n\rTWO_FA_CT_DNA\x10\t*\xab\x01\n\x13TwoFactorExpiration\x12\x1a\n\x16TWO_FA_EXP_IMMEDIATELY\x10\x00\x12\x18\n\x14TWO_FA_EXP_5_MINUTES\x10\x01\x12\x17\n\x13TWO_FA_EXP_12_HOURS\x10\x02\x12\x17\n\x13TWO_FA_EXP_24_HOURS\x10\x03\x12\x16\n\x12TWO_FA_EXP_30_DAYS\x10\x04\x12\x14\n\x10TWO_FA_EXP_NEVER\x10\x05*@\n\x0bLicenseType\x12\t\n\x05VAULT\x10\x00\x12\x08\n\x04\x43HAT\x10\x01\x12\x0b\n\x07STORAGE\x10\x02\x12\x0f\n\x0b\x42REACHWATCH\x10\x03*i\n\x0bObjectTypes\x12\n\n\x06RECORD\x10\x00\x12\x16\n\x12SHARED_FOLDER_USER\x10\x01\x12\x16\n\x12SHARED_FOLDER_TEAM\x10\x02\x12\x0f\n\x0bUSER_FOLDER\x10\x03\x12\r\n\tTEAM_USER\x10\x04*\xa1\x02\n\x13\x45ncryptedObjectType\x12\x13\n\x0f\x45OT_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x45OT_RECORD_KEY\x10\x01\x12\x1e\n\x1a\x45OT_SHARED_FOLDER_USER_KEY\x10\x02\x12\x1e\n\x1a\x45OT_SHARED_FOLDER_TEAM_KEY\x10\x03\x12\x15\n\x11\x45OT_TEAM_USER_KEY\x10\x04\x12\x17\n\x13\x45OT_USER_FOLDER_KEY\x10\x05\x12\x15\n\x11\x45OT_SECURITY_DATA\x10\x06\x12%\n!EOT_SECURITY_DATA_MASTER_PASSWORD\x10\x07\x12\x1c\n\x18\x45OT_EMERGENCY_ACCESS_KEY\x10\x08\x12\x15\n\x11\x45OT_V2_RECORD_KEY\x10\t*M\n\x1bMasterPasswordReentryStatus\x12\x0e\n\nMP_UNKNOWN\x10\x00\x12\x0e\n\nMP_SUCCESS\x10\x01\x12\x0e\n\nMP_FAILURE\x10\x02*`\n\x1b\x41lternateAuthenticationType\x12\x1d\n\x19\x41LTERNATE_MASTER_PASSWORD\x10\x00\x12\r\n\tBIOMETRIC\x10\x01\x12\x13\n\x0f\x41\x43\x43OUNT_RECOVER\x10\x02*\x9a\x02\n\x0cThrottleType\x12\x1b\n\x17PASSWORD_RETRY_THROTTLE\x10\x00\x12\"\n\x1ePASSWORD_RETRY_LEGACY_THROTTLE\x10\x01\x12\x13\n\x0fTWO_FA_THROTTLE\x10\x02\x12\x1a\n\x16TWO_FA_LEGACY_THROTTLE\x10\x03\x12\x15\n\x11QA_RETRY_THROTTLE\x10\x04\x12\x1c\n\x18\x41\x43\x43OUNT_RECOVER_THROTTLE\x10\x05\x12.\n*VALIDATE_DEVICE_VERIFICATION_CODE_THROTTLE\x10\x06\x12\x33\n/VALIDATE_CREATE_USER_VERIFICATION_CODE_THROTTLE\x10\x07*H\n\x06Region\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x06\n\x02\x65u\x10\x01\x12\x06\n\x02us\x10\x02\x12\t\n\x05usgov\x10\x03\x12\x06\n\x02\x61u\x10\x04\x12\x06\n\x02jp\x10\x05\x12\x06\n\x02\x63\x61\x10\x06*D\n\x14\x41pplicationShareType\x12\x15\n\x11SHARE_TYPE_RECORD\x10\x00\x12\x15\n\x11SHARE_TYPE_FOLDER\x10\x01*\xa4\x01\n\x15TimeLimitedAccessType\x12$\n INVALID_TIME_LIMITED_ACCESS_TYPE\x10\x00\x12\x19\n\x15USER_ACCESS_TO_RECORD\x10\x01\x12\'\n#USER_OR_TEAM_ACCESS_TO_SHAREDFOLDER\x10\x02\x12!\n\x1dRECORD_ACCESS_TO_SHAREDFOLDER\x10\x03*<\n\rBackupKeyType\x12\x12\n\x0e\x42KT_SEC_ANSWER\x10\x00\x12\x17\n\x13\x42KT_PASSPHRASE_HASH\x10\x01*r\n\rGenericStatus\x12\x0b\n\x07SUCCESS\x10\x00\x12\x12\n\x0eINVALID_OBJECT\x10\x01\x12\x12\n\x0e\x41LREADY_EXISTS\x10\x02\x12\x11\n\rACCESS_DENIED\x10\x03\x12\x19\n\x15LICENSE_SEAT_EXCEEDED\x10\x04*N\n\x17\x41uthenticatorAttachment\x12\x12\n\x0e\x43ROSS_PLATFORM\x10\x00\x12\x0c\n\x08PLATFORM\x10\x01\x12\x11\n\rALL_SUPPORTED\x10\x02*-\n\x0ePasskeyPurpose\x12\x0c\n\x08PK_LOGIN\x10\x00\x12\r\n\tPK_REAUTH\x10\x01*K\n\x10\x43lientFormFactor\x12\x0c\n\x08\x46\x46_EMPTY\x10\x00\x12\x0c\n\x08\x46\x46_PHONE\x10\x01\x12\r\n\tFF_TABLET\x10\x02\x12\x0c\n\x08\x46\x46_WATCH\x10\x03\x42*\n\x18\x63om.keepersecurity.protoB\x0e\x41uthenticationb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -22,66 +33,66 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\030com.keepersecurity.protoB\016Authentication' - _globals['_SUPPORTEDLANGUAGE']._serialized_start=21515 - _globals['_SUPPORTEDLANGUAGE']._serialized_end=21854 - _globals['_LOGINTYPE']._serialized_start=21856 - _globals['_LOGINTYPE']._serialized_end=21963 - _globals['_DEVICESTATUS']._serialized_start=21965 - _globals['_DEVICESTATUS']._serialized_end=22078 - _globals['_LICENSESTATUS']._serialized_start=22080 - _globals['_LICENSESTATUS']._serialized_end=22145 - _globals['_ACCOUNTTYPE']._serialized_start=22147 - _globals['_ACCOUNTTYPE']._serialized_end=22202 - _globals['_SESSIONTOKENTYPE']._serialized_start=22205 - _globals['_SESSIONTOKENTYPE']._serialized_end=22492 - _globals['_VERSION']._serialized_start=22494 - _globals['_VERSION']._serialized_end=22565 - _globals['_MASTERPASSWORDREENTRYACTIONTYPE']._serialized_start=22567 - _globals['_MASTERPASSWORDREENTRYACTIONTYPE']._serialized_end=22622 - _globals['_LOGINMETHOD']._serialized_start=22624 - _globals['_LOGINMETHOD']._serialized_end=22732 - _globals['_LOGINSTATE']._serialized_start=22735 - _globals['_LOGINSTATE']._serialized_end=23309 - _globals['_ENCRYPTEDDATAKEYTYPE']._serialized_start=23311 - _globals['_ENCRYPTEDDATAKEYTYPE']._serialized_end=23418 - _globals['_PASSWORDMETHOD']._serialized_start=23420 - _globals['_PASSWORDMETHOD']._serialized_end=23465 - _globals['_TWOFACTORPUSHTYPE']._serialized_start=23468 - _globals['_TWOFACTORPUSHTYPE']._serialized_end=23653 - _globals['_TWOFACTORVALUETYPE']._serialized_start=23656 - _globals['_TWOFACTORVALUETYPE']._serialized_end=23851 - _globals['_TWOFACTORCHANNELTYPE']._serialized_start=23854 - _globals['_TWOFACTORCHANNELTYPE']._serialized_end=24079 - _globals['_TWOFACTOREXPIRATION']._serialized_start=24082 - _globals['_TWOFACTOREXPIRATION']._serialized_end=24253 - _globals['_LICENSETYPE']._serialized_start=24255 - _globals['_LICENSETYPE']._serialized_end=24319 - _globals['_OBJECTTYPES']._serialized_start=24321 - _globals['_OBJECTTYPES']._serialized_end=24426 - _globals['_ENCRYPTEDOBJECTTYPE']._serialized_start=24429 - _globals['_ENCRYPTEDOBJECTTYPE']._serialized_end=24718 - _globals['_MASTERPASSWORDREENTRYSTATUS']._serialized_start=24720 - _globals['_MASTERPASSWORDREENTRYSTATUS']._serialized_end=24797 - _globals['_ALTERNATEAUTHENTICATIONTYPE']._serialized_start=24799 - _globals['_ALTERNATEAUTHENTICATIONTYPE']._serialized_end=24895 - _globals['_THROTTLETYPE']._serialized_start=24898 - _globals['_THROTTLETYPE']._serialized_end=25180 - _globals['_REGION']._serialized_start=25182 - _globals['_REGION']._serialized_end=25254 - _globals['_APPLICATIONSHARETYPE']._serialized_start=25256 - _globals['_APPLICATIONSHARETYPE']._serialized_end=25324 - _globals['_TIMELIMITEDACCESSTYPE']._serialized_start=25327 - _globals['_TIMELIMITEDACCESSTYPE']._serialized_end=25491 - _globals['_BACKUPKEYTYPE']._serialized_start=25493 - _globals['_BACKUPKEYTYPE']._serialized_end=25553 - _globals['_GENERICSTATUS']._serialized_start=25555 - _globals['_GENERICSTATUS']._serialized_end=25642 - _globals['_AUTHENTICATORATTACHMENT']._serialized_start=25644 - _globals['_AUTHENTICATORATTACHMENT']._serialized_end=25722 - _globals['_PASSKEYPURPOSE']._serialized_start=25724 - _globals['_PASSKEYPURPOSE']._serialized_end=25769 - _globals['_CLIENTFORMFACTOR']._serialized_start=25771 - _globals['_CLIENTFORMFACTOR']._serialized_end=25846 + _globals['_SUPPORTEDLANGUAGE']._serialized_start=21560 + _globals['_SUPPORTEDLANGUAGE']._serialized_end=21899 + _globals['_LOGINTYPE']._serialized_start=21901 + _globals['_LOGINTYPE']._serialized_end=22008 + _globals['_DEVICESTATUS']._serialized_start=22010 + _globals['_DEVICESTATUS']._serialized_end=22123 + _globals['_LICENSESTATUS']._serialized_start=22125 + _globals['_LICENSESTATUS']._serialized_end=22190 + _globals['_ACCOUNTTYPE']._serialized_start=22192 + _globals['_ACCOUNTTYPE']._serialized_end=22247 + _globals['_SESSIONTOKENTYPE']._serialized_start=22250 + _globals['_SESSIONTOKENTYPE']._serialized_end=22537 + _globals['_VERSION']._serialized_start=22539 + _globals['_VERSION']._serialized_end=22610 + _globals['_MASTERPASSWORDREENTRYACTIONTYPE']._serialized_start=22612 + _globals['_MASTERPASSWORDREENTRYACTIONTYPE']._serialized_end=22667 + _globals['_LOGINMETHOD']._serialized_start=22669 + _globals['_LOGINMETHOD']._serialized_end=22777 + _globals['_LOGINSTATE']._serialized_start=22780 + _globals['_LOGINSTATE']._serialized_end=23354 + _globals['_ENCRYPTEDDATAKEYTYPE']._serialized_start=23356 + _globals['_ENCRYPTEDDATAKEYTYPE']._serialized_end=23463 + _globals['_PASSWORDMETHOD']._serialized_start=23465 + _globals['_PASSWORDMETHOD']._serialized_end=23510 + _globals['_TWOFACTORPUSHTYPE']._serialized_start=23513 + _globals['_TWOFACTORPUSHTYPE']._serialized_end=23698 + _globals['_TWOFACTORVALUETYPE']._serialized_start=23701 + _globals['_TWOFACTORVALUETYPE']._serialized_end=23896 + _globals['_TWOFACTORCHANNELTYPE']._serialized_start=23899 + _globals['_TWOFACTORCHANNELTYPE']._serialized_end=24124 + _globals['_TWOFACTOREXPIRATION']._serialized_start=24127 + _globals['_TWOFACTOREXPIRATION']._serialized_end=24298 + _globals['_LICENSETYPE']._serialized_start=24300 + _globals['_LICENSETYPE']._serialized_end=24364 + _globals['_OBJECTTYPES']._serialized_start=24366 + _globals['_OBJECTTYPES']._serialized_end=24471 + _globals['_ENCRYPTEDOBJECTTYPE']._serialized_start=24474 + _globals['_ENCRYPTEDOBJECTTYPE']._serialized_end=24763 + _globals['_MASTERPASSWORDREENTRYSTATUS']._serialized_start=24765 + _globals['_MASTERPASSWORDREENTRYSTATUS']._serialized_end=24842 + _globals['_ALTERNATEAUTHENTICATIONTYPE']._serialized_start=24844 + _globals['_ALTERNATEAUTHENTICATIONTYPE']._serialized_end=24940 + _globals['_THROTTLETYPE']._serialized_start=24943 + _globals['_THROTTLETYPE']._serialized_end=25225 + _globals['_REGION']._serialized_start=25227 + _globals['_REGION']._serialized_end=25299 + _globals['_APPLICATIONSHARETYPE']._serialized_start=25301 + _globals['_APPLICATIONSHARETYPE']._serialized_end=25369 + _globals['_TIMELIMITEDACCESSTYPE']._serialized_start=25372 + _globals['_TIMELIMITEDACCESSTYPE']._serialized_end=25536 + _globals['_BACKUPKEYTYPE']._serialized_start=25538 + _globals['_BACKUPKEYTYPE']._serialized_end=25598 + _globals['_GENERICSTATUS']._serialized_start=25600 + _globals['_GENERICSTATUS']._serialized_end=25714 + _globals['_AUTHENTICATORATTACHMENT']._serialized_start=25716 + _globals['_AUTHENTICATORATTACHMENT']._serialized_end=25794 + _globals['_PASSKEYPURPOSE']._serialized_start=25796 + _globals['_PASSKEYPURPOSE']._serialized_end=25841 + _globals['_CLIENTFORMFACTOR']._serialized_start=25843 + _globals['_CLIENTFORMFACTOR']._serialized_end=25918 _globals['_QRCMESSAGEKEY']._serialized_start=54 _globals['_QRCMESSAGEKEY']._serialized_end=177 _globals['_APIREQUEST']._serialized_start=180 @@ -227,199 +238,199 @@ _globals['_NODEENFORCEMENTREMOVEREQUEST']._serialized_start=10588 _globals['_NODEENFORCEMENTREMOVEREQUEST']._serialized_end=10655 _globals['_APIREQUESTBYKEY']._serialized_start=10658 - _globals['_APIREQUESTBYKEY']._serialized_end=10817 - _globals['_APIREQUESTBYKATOKAKEY']._serialized_start=10820 - _globals['_APIREQUESTBYKATOKAKEY']._serialized_end=11019 - _globals['_MEMCACHEREQUEST']._serialized_start=11021 - _globals['_MEMCACHEREQUEST']._serialized_end=11067 - _globals['_MEMCACHERESPONSE']._serialized_start=11069 - _globals['_MEMCACHERESPONSE']._serialized_end=11115 - _globals['_MASTERPASSWORDREENTRYREQUEST']._serialized_start=11117 - _globals['_MASTERPASSWORDREENTRYREQUEST']._serialized_end=11236 - _globals['_MASTERPASSWORDREENTRYRESPONSE']._serialized_start=11238 - _globals['_MASTERPASSWORDREENTRYRESPONSE']._serialized_end=11330 - _globals['_DEVICEREGISTRATIONREQUEST']._serialized_start=11333 - _globals['_DEVICEREGISTRATIONREQUEST']._serialized_end=11530 - _globals['_DEVICEVERIFICATIONREQUEST']._serialized_start=11533 - _globals['_DEVICEVERIFICATIONREQUEST']._serialized_end=11687 - _globals['_DEVICEVERIFICATIONRESPONSE']._serialized_start=11690 - _globals['_DEVICEVERIFICATIONRESPONSE']._serialized_end=11868 - _globals['_DEVICEAPPROVALREQUEST']._serialized_start=11871 - _globals['_DEVICEAPPROVALREQUEST']._serialized_end=12071 - _globals['_DEVICEAPPROVALRESPONSE']._serialized_start=12073 - _globals['_DEVICEAPPROVALRESPONSE']._serialized_end=12130 - _globals['_APPROVEDEVICEREQUEST']._serialized_start=12132 - _globals['_APPROVEDEVICEREQUEST']._serialized_end=12258 - _globals['_ENTERPRISEUSERALIASREQUEST']._serialized_start=12260 - _globals['_ENTERPRISEUSERALIASREQUEST']._serialized_end=12329 - _globals['_ENTERPRISEUSERADDALIASREQUEST']._serialized_start=12331 - _globals['_ENTERPRISEUSERADDALIASREQUEST']._serialized_end=12420 - _globals['_ENTERPRISEUSERADDALIASREQUESTV2']._serialized_start=12422 - _globals['_ENTERPRISEUSERADDALIASREQUESTV2']._serialized_end=12541 - _globals['_ENTERPRISEUSERADDALIASSTATUS']._serialized_start=12543 - _globals['_ENTERPRISEUSERADDALIASSTATUS']._serialized_end=12615 - _globals['_ENTERPRISEUSERADDALIASRESPONSE']._serialized_start=12617 - _globals['_ENTERPRISEUSERADDALIASRESPONSE']._serialized_end=12711 - _globals['_DEVICE']._serialized_start=12713 - _globals['_DEVICE']._serialized_end=12751 - _globals['_REGISTERDEVICEDATAKEYREQUEST']._serialized_start=12753 - _globals['_REGISTERDEVICEDATAKEYREQUEST']._serialized_end=12845 - _globals['_VALIDATECREATEUSERVERIFICATIONCODEREQUEST']._serialized_start=12847 - _globals['_VALIDATECREATEUSERVERIFICATIONCODEREQUEST']._serialized_end=12957 - _globals['_VALIDATEDEVICEVERIFICATIONCODEREQUEST']._serialized_start=12960 - _globals['_VALIDATEDEVICEVERIFICATIONCODEREQUEST']._serialized_end=13123 - _globals['_SENDSESSIONMESSAGEREQUEST']._serialized_start=13125 - _globals['_SENDSESSIONMESSAGEREQUEST']._serialized_end=13214 - _globals['_GLOBALUSERACCOUNT']._serialized_start=13216 - _globals['_GLOBALUSERACCOUNT']._serialized_end=13293 - _globals['_ACCOUNTUSERNAME']._serialized_start=13295 - _globals['_ACCOUNTUSERNAME']._serialized_end=13350 - _globals['_SSOSERVICEPROVIDERREQUEST']._serialized_start=13352 - _globals['_SSOSERVICEPROVIDERREQUEST']._serialized_end=13432 - _globals['_SSOSERVICEPROVIDERRESPONSE']._serialized_start=13434 - _globals['_SSOSERVICEPROVIDERRESPONSE']._serialized_end=13531 - _globals['_USERSETTINGREQUEST']._serialized_start=13533 - _globals['_USERSETTINGREQUEST']._serialized_end=13585 - _globals['_THROTTLESTATE']._serialized_start=13587 - _globals['_THROTTLESTATE']._serialized_end=13689 - _globals['_THROTTLESTATE2']._serialized_start=13692 - _globals['_THROTTLESTATE2']._serialized_end=13873 - _globals['_DEVICEINFORMATION']._serialized_start=13876 - _globals['_DEVICEINFORMATION']._serialized_end=14027 - _globals['_USERSETTING']._serialized_start=14029 - _globals['_USERSETTING']._serialized_end=14071 - _globals['_USERDATAKEYREQUEST']._serialized_start=14073 - _globals['_USERDATAKEYREQUEST']._serialized_end=14119 - _globals['_USERDATAKEYBYNODEREQUEST']._serialized_start=14121 - _globals['_USERDATAKEYBYNODEREQUEST']._serialized_end=14164 - _globals['_ENTERPRISEUSERIDDATAKEYPAIR']._serialized_start=14167 - _globals['_ENTERPRISEUSERIDDATAKEYPAIR']._serialized_end=14295 - _globals['_USERDATAKEY']._serialized_start=14298 - _globals['_USERDATAKEY']._serialized_end=14447 - _globals['_USERDATAKEYRESPONSE']._serialized_start=14449 - _globals['_USERDATAKEYRESPONSE']._serialized_end=14571 - _globals['_MASTERPASSWORDRECOVERYVERIFICATIONREQUEST']._serialized_start=14573 - _globals['_MASTERPASSWORDRECOVERYVERIFICATIONREQUEST']._serialized_end=14645 - _globals['_GETSECURITYQUESTIONV3REQUEST']._serialized_start=14647 - _globals['_GETSECURITYQUESTIONV3REQUEST']._serialized_end=14732 - _globals['_GETSECURITYQUESTIONV3RESPONSE']._serialized_start=14734 - _globals['_GETSECURITYQUESTIONV3RESPONSE']._serialized_end=14848 - _globals['_GETDATAKEYBACKUPV3REQUEST']._serialized_start=14850 - _globals['_GETDATAKEYBACKUPV3REQUEST']._serialized_end=14960 - _globals['_PASSWORDRULES']._serialized_start=14962 - _globals['_PASSWORDRULES']._serialized_end=15080 - _globals['_GETDATAKEYBACKUPV3RESPONSE']._serialized_start=15083 - _globals['_GETDATAKEYBACKUPV3RESPONSE']._serialized_end=15412 - _globals['_GETPUBLICKEYSREQUEST']._serialized_start=15414 - _globals['_GETPUBLICKEYSREQUEST']._serialized_end=15455 - _globals['_PUBLICKEYRESPONSE']._serialized_start=15457 - _globals['_PUBLICKEYRESPONSE']._serialized_end=15571 - _globals['_GETPUBLICKEYSRESPONSE']._serialized_start=15573 - _globals['_GETPUBLICKEYSRESPONSE']._serialized_end=15653 - _globals['_SETECCKEYPAIRREQUEST']._serialized_start=15655 - _globals['_SETECCKEYPAIRREQUEST']._serialized_end=15725 - _globals['_SETECCKEYPAIRSREQUEST']._serialized_start=15727 - _globals['_SETECCKEYPAIRSREQUEST']._serialized_end=15800 - _globals['_SETECCKEYPAIRSRESPONSE']._serialized_start=15802 - _globals['_SETECCKEYPAIRSRESPONSE']._serialized_end=15884 - _globals['_TEAMECCKEYPAIR']._serialized_start=15886 - _globals['_TEAMECCKEYPAIR']._serialized_end=15967 - _globals['_TEAMECCKEYPAIRRESPONSE']._serialized_start=15969 - _globals['_TEAMECCKEYPAIRRESPONSE']._serialized_end=16057 - _globals['_GETKSMPUBLICKEYSREQUEST']._serialized_start=16059 - _globals['_GETKSMPUBLICKEYSREQUEST']._serialized_end=16127 - _globals['_DEVICEPUBLICKEYRESPONSE']._serialized_start=16129 - _globals['_DEVICEPUBLICKEYRESPONSE']._serialized_end=16214 - _globals['_GETKSMPUBLICKEYSRESPONSE']._serialized_start=16216 - _globals['_GETKSMPUBLICKEYSRESPONSE']._serialized_end=16305 - _globals['_ADDAPPSHARESREQUEST']._serialized_start=16307 - _globals['_ADDAPPSHARESREQUEST']._serialized_end=16395 - _globals['_REMOVEAPPSHARESREQUEST']._serialized_start=16397 - _globals['_REMOVEAPPSHARESREQUEST']._serialized_end=16459 - _globals['_APPSHAREADD']._serialized_start=16462 - _globals['_APPSHAREADD']._serialized_end=16597 - _globals['_APPSHARE']._serialized_start=16600 - _globals['_APPSHARE']._serialized_end=16737 - _globals['_ADDAPPCLIENTREQUEST']._serialized_start=16740 - _globals['_ADDAPPCLIENTREQUEST']._serialized_end=16957 - _globals['_REMOVEAPPCLIENTSREQUEST']._serialized_start=16959 - _globals['_REMOVEAPPCLIENTSREQUEST']._serialized_end=17023 - _globals['_ADDEXTERNALSHAREREQUEST']._serialized_start=17026 - _globals['_ADDEXTERNALSHAREREQUEST']._serialized_end=17196 - _globals['_APPCLIENT']._serialized_start=17199 - _globals['_APPCLIENT']._serialized_end=17474 - _globals['_GETAPPINFOREQUEST']._serialized_start=17476 - _globals['_GETAPPINFOREQUEST']._serialized_end=17517 - _globals['_APPINFO']._serialized_start=17520 - _globals['_APPINFO']._serialized_end=17662 - _globals['_GETAPPINFORESPONSE']._serialized_start=17664 - _globals['_GETAPPINFORESPONSE']._serialized_end=17726 - _globals['_APPLICATIONSUMMARY']._serialized_start=17729 - _globals['_APPLICATIONSUMMARY']._serialized_end=17942 - _globals['_GETAPPLICATIONSSUMMARYRESPONSE']._serialized_start=17944 - _globals['_GETAPPLICATIONSSUMMARYRESPONSE']._serialized_end=18040 - _globals['_GETVERIFICATIONTOKENREQUEST']._serialized_start=18042 - _globals['_GETVERIFICATIONTOKENREQUEST']._serialized_end=18089 - _globals['_GETVERIFICATIONTOKENRESPONSE']._serialized_start=18091 - _globals['_GETVERIFICATIONTOKENRESPONSE']._serialized_end=18157 - _globals['_SENDSHAREINVITEREQUEST']._serialized_start=18159 - _globals['_SENDSHAREINVITEREQUEST']._serialized_end=18198 - _globals['_TIMELIMITEDACCESSREQUEST']._serialized_start=18201 - _globals['_TIMELIMITEDACCESSREQUEST']._serialized_end=18398 - _globals['_TIMELIMITEDACCESSSTATUS']._serialized_start=18400 - _globals['_TIMELIMITEDACCESSSTATUS']._serialized_end=18455 - _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_start=18458 - _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_end=18706 - _globals['_REQUESTDOWNLOADREQUEST']._serialized_start=18708 - _globals['_REQUESTDOWNLOADREQUEST']._serialized_end=18751 - _globals['_REQUESTDOWNLOADRESPONSE']._serialized_start=18753 - _globals['_REQUESTDOWNLOADRESPONSE']._serialized_end=18856 - _globals['_DOWNLOAD']._serialized_start=18858 - _globals['_DOWNLOAD']._serialized_end=18926 - _globals['_DELETEUSERREQUEST']._serialized_start=18928 - _globals['_DELETEUSERREQUEST']._serialized_end=18963 - _globals['_CHANGEMASTERPASSWORDREQUEST']._serialized_start=18966 - _globals['_CHANGEMASTERPASSWORDREQUEST']._serialized_end=19098 - _globals['_CHANGEMASTERPASSWORDRESPONSE']._serialized_start=19100 - _globals['_CHANGEMASTERPASSWORDRESPONSE']._serialized_end=19161 - _globals['_ACCOUNTRECOVERYSETUPREQUEST']._serialized_start=19163 - _globals['_ACCOUNTRECOVERYSETUPREQUEST']._serialized_end=19252 - _globals['_ACCOUNTRECOVERYVERIFYCODERESPONSE']._serialized_start=19255 - _globals['_ACCOUNTRECOVERYVERIFYCODERESPONSE']._serialized_end=19427 - _globals['_EMERGENCYACCESSLOGINREQUEST']._serialized_start=19429 - _globals['_EMERGENCYACCESSLOGINREQUEST']._serialized_end=19473 - _globals['_EMERGENCYACCESSLOGINRESPONSE']._serialized_start=19476 - _globals['_EMERGENCYACCESSLOGINRESPONSE']._serialized_end=19657 - _globals['_USERTEAMKEY']._serialized_start=19660 - _globals['_USERTEAMKEY']._serialized_end=19838 - _globals['_GENERICREQUESTRESPONSE']._serialized_start=19840 - _globals['_GENERICREQUESTRESPONSE']._serialized_end=19881 - _globals['_PASSKEYREGISTRATIONREQUEST']._serialized_start=19883 - _globals['_PASSKEYREGISTRATIONREQUEST']._serialized_end=19985 - _globals['_PASSKEYREGISTRATIONRESPONSE']._serialized_start=19987 - _globals['_PASSKEYREGISTRATIONRESPONSE']._serialized_end=20067 - _globals['_PASSKEYREGISTRATIONFINALIZATION']._serialized_start=20070 - _globals['_PASSKEYREGISTRATIONFINALIZATION']._serialized_end=20202 - _globals['_PASSKEYAUTHENTICATIONREQUEST']._serialized_start=20205 - _globals['_PASSKEYAUTHENTICATIONREQUEST']._serialized_end=20512 - _globals['_PASSKEYAUTHENTICATIONRESPONSE']._serialized_start=20515 - _globals['_PASSKEYAUTHENTICATIONRESPONSE']._serialized_end=20654 - _globals['_PASSKEYVALIDATIONREQUEST']._serialized_start=20657 - _globals['_PASSKEYVALIDATIONREQUEST']._serialized_end=20848 - _globals['_PASSKEYVALIDATIONRESPONSE']._serialized_start=20850 - _globals['_PASSKEYVALIDATIONRESPONSE']._serialized_end=20923 - _globals['_UPDATEPASSKEYREQUEST']._serialized_start=20925 - _globals['_UPDATEPASSKEYREQUEST']._serialized_end=21029 - _globals['_PASSKEYLISTREQUEST']._serialized_start=21031 - _globals['_PASSKEYLISTREQUEST']._serialized_end=21076 - _globals['_PASSKEYINFO']._serialized_start=21079 - _globals['_PASSKEYINFO']._serialized_end=21243 - _globals['_PASSKEYLISTRESPONSE']._serialized_start=21245 - _globals['_PASSKEYLISTRESPONSE']._serialized_end=21316 - _globals['_TRANSLATIONINFO']._serialized_start=21318 - _globals['_TRANSLATIONINFO']._serialized_end=21385 - _globals['_TRANSLATIONREQUEST']._serialized_start=21387 - _globals['_TRANSLATIONREQUEST']._serialized_end=21431 - _globals['_TRANSLATIONRESPONSE']._serialized_start=21433 - _globals['_TRANSLATIONRESPONSE']._serialized_end=21512 + _globals['_APIREQUESTBYKEY']._serialized_end=10841 + _globals['_APIREQUESTBYKATOKAKEY']._serialized_start=10844 + _globals['_APIREQUESTBYKATOKAKEY']._serialized_end=11043 + _globals['_MEMCACHEREQUEST']._serialized_start=11045 + _globals['_MEMCACHEREQUEST']._serialized_end=11091 + _globals['_MEMCACHERESPONSE']._serialized_start=11093 + _globals['_MEMCACHERESPONSE']._serialized_end=11139 + _globals['_MASTERPASSWORDREENTRYREQUEST']._serialized_start=11141 + _globals['_MASTERPASSWORDREENTRYREQUEST']._serialized_end=11260 + _globals['_MASTERPASSWORDREENTRYRESPONSE']._serialized_start=11262 + _globals['_MASTERPASSWORDREENTRYRESPONSE']._serialized_end=11354 + _globals['_DEVICEREGISTRATIONREQUEST']._serialized_start=11357 + _globals['_DEVICEREGISTRATIONREQUEST']._serialized_end=11554 + _globals['_DEVICEVERIFICATIONREQUEST']._serialized_start=11557 + _globals['_DEVICEVERIFICATIONREQUEST']._serialized_end=11711 + _globals['_DEVICEVERIFICATIONRESPONSE']._serialized_start=11714 + _globals['_DEVICEVERIFICATIONRESPONSE']._serialized_end=11892 + _globals['_DEVICEAPPROVALREQUEST']._serialized_start=11895 + _globals['_DEVICEAPPROVALREQUEST']._serialized_end=12095 + _globals['_DEVICEAPPROVALRESPONSE']._serialized_start=12097 + _globals['_DEVICEAPPROVALRESPONSE']._serialized_end=12154 + _globals['_APPROVEDEVICEREQUEST']._serialized_start=12156 + _globals['_APPROVEDEVICEREQUEST']._serialized_end=12282 + _globals['_ENTERPRISEUSERALIASREQUEST']._serialized_start=12284 + _globals['_ENTERPRISEUSERALIASREQUEST']._serialized_end=12353 + _globals['_ENTERPRISEUSERADDALIASREQUEST']._serialized_start=12355 + _globals['_ENTERPRISEUSERADDALIASREQUEST']._serialized_end=12444 + _globals['_ENTERPRISEUSERADDALIASREQUESTV2']._serialized_start=12446 + _globals['_ENTERPRISEUSERADDALIASREQUESTV2']._serialized_end=12565 + _globals['_ENTERPRISEUSERADDALIASSTATUS']._serialized_start=12567 + _globals['_ENTERPRISEUSERADDALIASSTATUS']._serialized_end=12639 + _globals['_ENTERPRISEUSERADDALIASRESPONSE']._serialized_start=12641 + _globals['_ENTERPRISEUSERADDALIASRESPONSE']._serialized_end=12735 + _globals['_DEVICE']._serialized_start=12737 + _globals['_DEVICE']._serialized_end=12775 + _globals['_REGISTERDEVICEDATAKEYREQUEST']._serialized_start=12777 + _globals['_REGISTERDEVICEDATAKEYREQUEST']._serialized_end=12869 + _globals['_VALIDATECREATEUSERVERIFICATIONCODEREQUEST']._serialized_start=12871 + _globals['_VALIDATECREATEUSERVERIFICATIONCODEREQUEST']._serialized_end=12981 + _globals['_VALIDATEDEVICEVERIFICATIONCODEREQUEST']._serialized_start=12984 + _globals['_VALIDATEDEVICEVERIFICATIONCODEREQUEST']._serialized_end=13147 + _globals['_SENDSESSIONMESSAGEREQUEST']._serialized_start=13149 + _globals['_SENDSESSIONMESSAGEREQUEST']._serialized_end=13238 + _globals['_GLOBALUSERACCOUNT']._serialized_start=13240 + _globals['_GLOBALUSERACCOUNT']._serialized_end=13317 + _globals['_ACCOUNTUSERNAME']._serialized_start=13319 + _globals['_ACCOUNTUSERNAME']._serialized_end=13374 + _globals['_SSOSERVICEPROVIDERREQUEST']._serialized_start=13376 + _globals['_SSOSERVICEPROVIDERREQUEST']._serialized_end=13456 + _globals['_SSOSERVICEPROVIDERRESPONSE']._serialized_start=13458 + _globals['_SSOSERVICEPROVIDERRESPONSE']._serialized_end=13555 + _globals['_USERSETTINGREQUEST']._serialized_start=13557 + _globals['_USERSETTINGREQUEST']._serialized_end=13609 + _globals['_THROTTLESTATE']._serialized_start=13611 + _globals['_THROTTLESTATE']._serialized_end=13713 + _globals['_THROTTLESTATE2']._serialized_start=13716 + _globals['_THROTTLESTATE2']._serialized_end=13897 + _globals['_DEVICEINFORMATION']._serialized_start=13900 + _globals['_DEVICEINFORMATION']._serialized_end=14051 + _globals['_USERSETTING']._serialized_start=14053 + _globals['_USERSETTING']._serialized_end=14095 + _globals['_USERDATAKEYREQUEST']._serialized_start=14097 + _globals['_USERDATAKEYREQUEST']._serialized_end=14143 + _globals['_USERDATAKEYBYNODEREQUEST']._serialized_start=14145 + _globals['_USERDATAKEYBYNODEREQUEST']._serialized_end=14188 + _globals['_ENTERPRISEUSERIDDATAKEYPAIR']._serialized_start=14191 + _globals['_ENTERPRISEUSERIDDATAKEYPAIR']._serialized_end=14319 + _globals['_USERDATAKEY']._serialized_start=14322 + _globals['_USERDATAKEY']._serialized_end=14471 + _globals['_USERDATAKEYRESPONSE']._serialized_start=14473 + _globals['_USERDATAKEYRESPONSE']._serialized_end=14595 + _globals['_MASTERPASSWORDRECOVERYVERIFICATIONREQUEST']._serialized_start=14597 + _globals['_MASTERPASSWORDRECOVERYVERIFICATIONREQUEST']._serialized_end=14669 + _globals['_GETSECURITYQUESTIONV3REQUEST']._serialized_start=14671 + _globals['_GETSECURITYQUESTIONV3REQUEST']._serialized_end=14756 + _globals['_GETSECURITYQUESTIONV3RESPONSE']._serialized_start=14758 + _globals['_GETSECURITYQUESTIONV3RESPONSE']._serialized_end=14872 + _globals['_GETDATAKEYBACKUPV3REQUEST']._serialized_start=14874 + _globals['_GETDATAKEYBACKUPV3REQUEST']._serialized_end=14984 + _globals['_PASSWORDRULES']._serialized_start=14986 + _globals['_PASSWORDRULES']._serialized_end=15104 + _globals['_GETDATAKEYBACKUPV3RESPONSE']._serialized_start=15107 + _globals['_GETDATAKEYBACKUPV3RESPONSE']._serialized_end=15436 + _globals['_GETPUBLICKEYSREQUEST']._serialized_start=15438 + _globals['_GETPUBLICKEYSREQUEST']._serialized_end=15479 + _globals['_PUBLICKEYRESPONSE']._serialized_start=15482 + _globals['_PUBLICKEYRESPONSE']._serialized_end=15616 + _globals['_GETPUBLICKEYSRESPONSE']._serialized_start=15618 + _globals['_GETPUBLICKEYSRESPONSE']._serialized_end=15698 + _globals['_SETECCKEYPAIRREQUEST']._serialized_start=15700 + _globals['_SETECCKEYPAIRREQUEST']._serialized_end=15770 + _globals['_SETECCKEYPAIRSREQUEST']._serialized_start=15772 + _globals['_SETECCKEYPAIRSREQUEST']._serialized_end=15845 + _globals['_SETECCKEYPAIRSRESPONSE']._serialized_start=15847 + _globals['_SETECCKEYPAIRSRESPONSE']._serialized_end=15929 + _globals['_TEAMECCKEYPAIR']._serialized_start=15931 + _globals['_TEAMECCKEYPAIR']._serialized_end=16012 + _globals['_TEAMECCKEYPAIRRESPONSE']._serialized_start=16014 + _globals['_TEAMECCKEYPAIRRESPONSE']._serialized_end=16102 + _globals['_GETKSMPUBLICKEYSREQUEST']._serialized_start=16104 + _globals['_GETKSMPUBLICKEYSREQUEST']._serialized_end=16172 + _globals['_DEVICEPUBLICKEYRESPONSE']._serialized_start=16174 + _globals['_DEVICEPUBLICKEYRESPONSE']._serialized_end=16259 + _globals['_GETKSMPUBLICKEYSRESPONSE']._serialized_start=16261 + _globals['_GETKSMPUBLICKEYSRESPONSE']._serialized_end=16350 + _globals['_ADDAPPSHARESREQUEST']._serialized_start=16352 + _globals['_ADDAPPSHARESREQUEST']._serialized_end=16440 + _globals['_REMOVEAPPSHARESREQUEST']._serialized_start=16442 + _globals['_REMOVEAPPSHARESREQUEST']._serialized_end=16504 + _globals['_APPSHAREADD']._serialized_start=16507 + _globals['_APPSHAREADD']._serialized_end=16642 + _globals['_APPSHARE']._serialized_start=16645 + _globals['_APPSHARE']._serialized_end=16782 + _globals['_ADDAPPCLIENTREQUEST']._serialized_start=16785 + _globals['_ADDAPPCLIENTREQUEST']._serialized_end=17002 + _globals['_REMOVEAPPCLIENTSREQUEST']._serialized_start=17004 + _globals['_REMOVEAPPCLIENTSREQUEST']._serialized_end=17068 + _globals['_ADDEXTERNALSHAREREQUEST']._serialized_start=17071 + _globals['_ADDEXTERNALSHAREREQUEST']._serialized_end=17241 + _globals['_APPCLIENT']._serialized_start=17244 + _globals['_APPCLIENT']._serialized_end=17519 + _globals['_GETAPPINFOREQUEST']._serialized_start=17521 + _globals['_GETAPPINFOREQUEST']._serialized_end=17562 + _globals['_APPINFO']._serialized_start=17565 + _globals['_APPINFO']._serialized_end=17707 + _globals['_GETAPPINFORESPONSE']._serialized_start=17709 + _globals['_GETAPPINFORESPONSE']._serialized_end=17771 + _globals['_APPLICATIONSUMMARY']._serialized_start=17774 + _globals['_APPLICATIONSUMMARY']._serialized_end=17987 + _globals['_GETAPPLICATIONSSUMMARYRESPONSE']._serialized_start=17989 + _globals['_GETAPPLICATIONSSUMMARYRESPONSE']._serialized_end=18085 + _globals['_GETVERIFICATIONTOKENREQUEST']._serialized_start=18087 + _globals['_GETVERIFICATIONTOKENREQUEST']._serialized_end=18134 + _globals['_GETVERIFICATIONTOKENRESPONSE']._serialized_start=18136 + _globals['_GETVERIFICATIONTOKENRESPONSE']._serialized_end=18202 + _globals['_SENDSHAREINVITEREQUEST']._serialized_start=18204 + _globals['_SENDSHAREINVITEREQUEST']._serialized_end=18243 + _globals['_TIMELIMITEDACCESSREQUEST']._serialized_start=18246 + _globals['_TIMELIMITEDACCESSREQUEST']._serialized_end=18443 + _globals['_TIMELIMITEDACCESSSTATUS']._serialized_start=18445 + _globals['_TIMELIMITEDACCESSSTATUS']._serialized_end=18500 + _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_start=18503 + _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_end=18751 + _globals['_REQUESTDOWNLOADREQUEST']._serialized_start=18753 + _globals['_REQUESTDOWNLOADREQUEST']._serialized_end=18796 + _globals['_REQUESTDOWNLOADRESPONSE']._serialized_start=18798 + _globals['_REQUESTDOWNLOADRESPONSE']._serialized_end=18901 + _globals['_DOWNLOAD']._serialized_start=18903 + _globals['_DOWNLOAD']._serialized_end=18971 + _globals['_DELETEUSERREQUEST']._serialized_start=18973 + _globals['_DELETEUSERREQUEST']._serialized_end=19008 + _globals['_CHANGEMASTERPASSWORDREQUEST']._serialized_start=19011 + _globals['_CHANGEMASTERPASSWORDREQUEST']._serialized_end=19143 + _globals['_CHANGEMASTERPASSWORDRESPONSE']._serialized_start=19145 + _globals['_CHANGEMASTERPASSWORDRESPONSE']._serialized_end=19206 + _globals['_ACCOUNTRECOVERYSETUPREQUEST']._serialized_start=19208 + _globals['_ACCOUNTRECOVERYSETUPREQUEST']._serialized_end=19297 + _globals['_ACCOUNTRECOVERYVERIFYCODERESPONSE']._serialized_start=19300 + _globals['_ACCOUNTRECOVERYVERIFYCODERESPONSE']._serialized_end=19472 + _globals['_EMERGENCYACCESSLOGINREQUEST']._serialized_start=19474 + _globals['_EMERGENCYACCESSLOGINREQUEST']._serialized_end=19518 + _globals['_EMERGENCYACCESSLOGINRESPONSE']._serialized_start=19521 + _globals['_EMERGENCYACCESSLOGINRESPONSE']._serialized_end=19702 + _globals['_USERTEAMKEY']._serialized_start=19705 + _globals['_USERTEAMKEY']._serialized_end=19883 + _globals['_GENERICREQUESTRESPONSE']._serialized_start=19885 + _globals['_GENERICREQUESTRESPONSE']._serialized_end=19926 + _globals['_PASSKEYREGISTRATIONREQUEST']._serialized_start=19928 + _globals['_PASSKEYREGISTRATIONREQUEST']._serialized_end=20030 + _globals['_PASSKEYREGISTRATIONRESPONSE']._serialized_start=20032 + _globals['_PASSKEYREGISTRATIONRESPONSE']._serialized_end=20112 + _globals['_PASSKEYREGISTRATIONFINALIZATION']._serialized_start=20115 + _globals['_PASSKEYREGISTRATIONFINALIZATION']._serialized_end=20247 + _globals['_PASSKEYAUTHENTICATIONREQUEST']._serialized_start=20250 + _globals['_PASSKEYAUTHENTICATIONREQUEST']._serialized_end=20557 + _globals['_PASSKEYAUTHENTICATIONRESPONSE']._serialized_start=20560 + _globals['_PASSKEYAUTHENTICATIONRESPONSE']._serialized_end=20699 + _globals['_PASSKEYVALIDATIONREQUEST']._serialized_start=20702 + _globals['_PASSKEYVALIDATIONREQUEST']._serialized_end=20893 + _globals['_PASSKEYVALIDATIONRESPONSE']._serialized_start=20895 + _globals['_PASSKEYVALIDATIONRESPONSE']._serialized_end=20968 + _globals['_UPDATEPASSKEYREQUEST']._serialized_start=20970 + _globals['_UPDATEPASSKEYREQUEST']._serialized_end=21074 + _globals['_PASSKEYLISTREQUEST']._serialized_start=21076 + _globals['_PASSKEYLISTREQUEST']._serialized_end=21121 + _globals['_PASSKEYINFO']._serialized_start=21124 + _globals['_PASSKEYINFO']._serialized_end=21288 + _globals['_PASSKEYLISTRESPONSE']._serialized_start=21290 + _globals['_PASSKEYLISTRESPONSE']._serialized_end=21361 + _globals['_TRANSLATIONINFO']._serialized_start=21363 + _globals['_TRANSLATIONINFO']._serialized_end=21430 + _globals['_TRANSLATIONREQUEST']._serialized_start=21432 + _globals['_TRANSLATIONREQUEST']._serialized_end=21476 + _globals['_TRANSLATIONRESPONSE']._serialized_start=21478 + _globals['_TRANSLATIONRESPONSE']._serialized_end=21557 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/APIRequest_pb2.pyi b/keepercommander/proto/APIRequest_pb2.pyi index 988eacb2d..571e14096 100644 --- a/keepercommander/proto/APIRequest_pb2.pyi +++ b/keepercommander/proto/APIRequest_pb2.pyi @@ -3,8 +3,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -264,6 +263,7 @@ class GenericStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): INVALID_OBJECT: _ClassVar[GenericStatus] ALREADY_EXISTS: _ClassVar[GenericStatus] ACCESS_DENIED: _ClassVar[GenericStatus] + LICENSE_SEAT_EXCEEDED: _ClassVar[GenericStatus] class AuthenticatorAttachment(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -458,6 +458,7 @@ SUCCESS: GenericStatus INVALID_OBJECT: GenericStatus ALREADY_EXISTS: GenericStatus ACCESS_DENIED: GenericStatus +LICENSE_SEAT_EXCEEDED: GenericStatus CROSS_PLATFORM: AuthenticatorAttachment PLATFORM: AuthenticatorAttachment ALL_SUPPORTED: AuthenticatorAttachment @@ -560,7 +561,7 @@ class NewUserMinimumParams(_message.Message): isEnterpriseDomain: bool enterpriseEccPublicKey: bytes forbidKeyType2: bool - def __init__(self, minimumIterations: _Optional[int] = ..., passwordMatchRegex: _Optional[_Iterable[str]] = ..., passwordMatchDescription: _Optional[_Iterable[str]] = ..., isEnterpriseDomain: _Optional[bool] = ..., enterpriseEccPublicKey: _Optional[bytes] = ..., forbidKeyType2: _Optional[bool] = ...) -> None: ... + def __init__(self, minimumIterations: _Optional[int] = ..., passwordMatchRegex: _Optional[_Iterable[str]] = ..., passwordMatchDescription: _Optional[_Iterable[str]] = ..., isEnterpriseDomain: bool = ..., enterpriseEccPublicKey: _Optional[bytes] = ..., forbidKeyType2: bool = ...) -> None: ... class PreLoginRequest(_message.Message): __slots__ = ("authRequest", "loginType", "twoFactorToken") @@ -648,7 +649,7 @@ class StartLoginRequest(_message.Message): v2TwoFactorToken: str accountUid: bytes fromSessionToken: bytes - def __init__(self, encryptedDeviceToken: _Optional[bytes] = ..., username: _Optional[str] = ..., clientVersion: _Optional[str] = ..., messageSessionUid: _Optional[bytes] = ..., encryptedLoginToken: _Optional[bytes] = ..., loginType: _Optional[_Union[LoginType, str]] = ..., mcEnterpriseId: _Optional[int] = ..., loginMethod: _Optional[_Union[LoginMethod, str]] = ..., forceNewLogin: _Optional[bool] = ..., cloneCode: _Optional[bytes] = ..., v2TwoFactorToken: _Optional[str] = ..., accountUid: _Optional[bytes] = ..., fromSessionToken: _Optional[bytes] = ...) -> None: ... + def __init__(self, encryptedDeviceToken: _Optional[bytes] = ..., username: _Optional[str] = ..., clientVersion: _Optional[str] = ..., messageSessionUid: _Optional[bytes] = ..., encryptedLoginToken: _Optional[bytes] = ..., loginType: _Optional[_Union[LoginType, str]] = ..., mcEnterpriseId: _Optional[int] = ..., loginMethod: _Optional[_Union[LoginMethod, str]] = ..., forceNewLogin: bool = ..., cloneCode: _Optional[bytes] = ..., v2TwoFactorToken: _Optional[str] = ..., accountUid: _Optional[bytes] = ..., fromSessionToken: _Optional[bytes] = ...) -> None: ... class LoginResponse(_message.Message): __slots__ = ("loginState", "accountUid", "primaryUsername", "encryptedDataKey", "encryptedDataKeyType", "encryptedLoginToken", "encryptedSessionToken", "sessionTokenType", "message", "url", "channels", "salt", "cloneCode", "stateSpecificValue", "ssoClientVersion", "sessionTokenTypeModifier") @@ -698,7 +699,7 @@ class SwitchListElement(_message.Message): authRequired: bool isLinked: bool profilePicUrl: str - def __init__(self, username: _Optional[str] = ..., fullName: _Optional[str] = ..., authRequired: _Optional[bool] = ..., isLinked: _Optional[bool] = ..., profilePicUrl: _Optional[str] = ...) -> None: ... + def __init__(self, username: _Optional[str] = ..., fullName: _Optional[str] = ..., authRequired: bool = ..., isLinked: bool = ..., profilePicUrl: _Optional[str] = ...) -> None: ... class SwitchListResponse(_message.Message): __slots__ = ("elements",) @@ -886,7 +887,7 @@ class License(_message.Message): licenseStatus: LicenseStatus paid: bool message: str - def __init__(self, created: _Optional[int] = ..., expiration: _Optional[int] = ..., licenseStatus: _Optional[_Union[LicenseStatus, str]] = ..., paid: _Optional[bool] = ..., message: _Optional[str] = ...) -> None: ... + def __init__(self, created: _Optional[int] = ..., expiration: _Optional[int] = ..., licenseStatus: _Optional[_Union[LicenseStatus, str]] = ..., paid: bool = ..., message: _Optional[str] = ...) -> None: ... class OwnerlessRecord(_message.Message): __slots__ = ("recordUid", "recordKey", "status") @@ -1044,7 +1045,7 @@ class EmailVerificationLinkResponse(_message.Message): __slots__ = ("emailVerified",) EMAILVERIFIED_FIELD_NUMBER: _ClassVar[int] emailVerified: bool - def __init__(self, emailVerified: _Optional[bool] = ...) -> None: ... + def __init__(self, emailVerified: bool = ...) -> None: ... class SecurityData(_message.Message): __slots__ = ("uid", "data") @@ -1116,7 +1117,7 @@ class SecurityReport(_message.Message): securityReportIncrementalData: _containers.RepeatedCompositeFieldContainer[SecurityReportIncrementalData] userId: int hasOldEncryption: bool - def __init__(self, enterpriseUserId: _Optional[int] = ..., encryptedReportData: _Optional[bytes] = ..., revision: _Optional[int] = ..., twoFactor: _Optional[str] = ..., lastLogin: _Optional[int] = ..., numberOfReusedPassword: _Optional[int] = ..., securityReportIncrementalData: _Optional[_Iterable[_Union[SecurityReportIncrementalData, _Mapping]]] = ..., userId: _Optional[int] = ..., hasOldEncryption: _Optional[bool] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., encryptedReportData: _Optional[bytes] = ..., revision: _Optional[int] = ..., twoFactor: _Optional[str] = ..., lastLogin: _Optional[int] = ..., numberOfReusedPassword: _Optional[int] = ..., securityReportIncrementalData: _Optional[_Iterable[_Union[SecurityReportIncrementalData, _Mapping]]] = ..., userId: _Optional[int] = ..., hasOldEncryption: bool = ...) -> None: ... class SecurityReportSaveRequest(_message.Message): __slots__ = ("securityReport", "continuationToken") @@ -1150,7 +1151,7 @@ class SecurityReportResponse(_message.Message): complete: bool enterpriseEccPrivateKey: bytes hasIncrementalData: bool - def __init__(self, enterprisePrivateKey: _Optional[bytes] = ..., securityReport: _Optional[_Iterable[_Union[SecurityReport, _Mapping]]] = ..., asOfRevision: _Optional[int] = ..., fromPage: _Optional[int] = ..., toPage: _Optional[int] = ..., complete: _Optional[bool] = ..., enterpriseEccPrivateKey: _Optional[bytes] = ..., hasIncrementalData: _Optional[bool] = ...) -> None: ... + def __init__(self, enterprisePrivateKey: _Optional[bytes] = ..., securityReport: _Optional[_Iterable[_Union[SecurityReport, _Mapping]]] = ..., asOfRevision: _Optional[int] = ..., fromPage: _Optional[int] = ..., toPage: _Optional[int] = ..., complete: bool = ..., enterpriseEccPrivateKey: _Optional[bytes] = ..., hasIncrementalData: bool = ...) -> None: ... class IncrementalSecurityDataRequest(_message.Message): __slots__ = ("continuationToken",) @@ -1228,7 +1229,7 @@ class GetChangeKeyTypesRequest(_message.Message): includeRecommended: bool includeKeys: bool includeAllowedKeyTypes: bool - def __init__(self, onlyTheseObjects: _Optional[_Iterable[_Union[EncryptedObjectType, str]]] = ..., limit: _Optional[int] = ..., includeRecommended: _Optional[bool] = ..., includeKeys: _Optional[bool] = ..., includeAllowedKeyTypes: _Optional[bool] = ...) -> None: ... + def __init__(self, onlyTheseObjects: _Optional[_Iterable[_Union[EncryptedObjectType, str]]] = ..., limit: _Optional[int] = ..., includeRecommended: bool = ..., includeKeys: bool = ..., includeAllowedKeyTypes: bool = ...) -> None: ... class GetChangeKeyTypesResponse(_message.Message): __slots__ = ("keys", "allowedKeyTypes") @@ -1355,20 +1356,22 @@ class NodeEnforcementRemoveRequest(_message.Message): def __init__(self, nodeId: _Optional[int] = ..., enforcement: _Optional[str] = ...) -> None: ... class ApiRequestByKey(_message.Message): - __slots__ = ("keyId", "payload", "username", "locale", "supportedLanguage", "type") + __slots__ = ("keyId", "payload", "username", "locale", "supportedLanguage", "type", "parentThreadId") KEYID_FIELD_NUMBER: _ClassVar[int] PAYLOAD_FIELD_NUMBER: _ClassVar[int] USERNAME_FIELD_NUMBER: _ClassVar[int] LOCALE_FIELD_NUMBER: _ClassVar[int] SUPPORTEDLANGUAGE_FIELD_NUMBER: _ClassVar[int] TYPE_FIELD_NUMBER: _ClassVar[int] + PARENTTHREADID_FIELD_NUMBER: _ClassVar[int] keyId: int payload: bytes username: str locale: str supportedLanguage: SupportedLanguage type: int - def __init__(self, keyId: _Optional[int] = ..., payload: _Optional[bytes] = ..., username: _Optional[str] = ..., locale: _Optional[str] = ..., supportedLanguage: _Optional[_Union[SupportedLanguage, str]] = ..., type: _Optional[int] = ...) -> None: ... + parentThreadId: str + def __init__(self, keyId: _Optional[int] = ..., payload: _Optional[bytes] = ..., username: _Optional[str] = ..., locale: _Optional[str] = ..., supportedLanguage: _Optional[_Union[SupportedLanguage, str]] = ..., type: _Optional[int] = ..., parentThreadId: _Optional[str] = ...) -> None: ... class ApiRequestByKAtoKAKey(_message.Message): __slots__ = ("sourceRegion", "payload", "supportedLanguage", "destinationRegion") @@ -1492,7 +1495,7 @@ class ApproveDeviceRequest(_message.Message): encryptedDeviceDataKey: bytes denyApproval: bool linkDevice: bool - def __init__(self, encryptedDeviceToken: _Optional[bytes] = ..., encryptedDeviceDataKey: _Optional[bytes] = ..., denyApproval: _Optional[bool] = ..., linkDevice: _Optional[bool] = ...) -> None: ... + def __init__(self, encryptedDeviceToken: _Optional[bytes] = ..., encryptedDeviceDataKey: _Optional[bytes] = ..., denyApproval: bool = ..., linkDevice: bool = ...) -> None: ... class EnterpriseUserAliasRequest(_message.Message): __slots__ = ("enterpriseUserId", "alias") @@ -1510,7 +1513,7 @@ class EnterpriseUserAddAliasRequest(_message.Message): enterpriseUserId: int alias: str primary: bool - def __init__(self, enterpriseUserId: _Optional[int] = ..., alias: _Optional[str] = ..., primary: _Optional[bool] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., alias: _Optional[str] = ..., primary: bool = ...) -> None: ... class EnterpriseUserAddAliasRequestV2(_message.Message): __slots__ = ("enterpriseUserAddAliasRequest",) @@ -1618,7 +1621,7 @@ class SsoServiceProviderResponse(_message.Message): spUrl: str isCloud: bool clientVersion: str - def __init__(self, name: _Optional[str] = ..., spUrl: _Optional[str] = ..., isCloud: _Optional[bool] = ..., clientVersion: _Optional[str] = ...) -> None: ... + def __init__(self, name: _Optional[str] = ..., spUrl: _Optional[str] = ..., isCloud: bool = ..., clientVersion: _Optional[str] = ...) -> None: ... class UserSettingRequest(_message.Message): __slots__ = ("setting", "value") @@ -1638,7 +1641,7 @@ class ThrottleState(_message.Message): key: str value: str state: bool - def __init__(self, type: _Optional[_Union[ThrottleType, str]] = ..., key: _Optional[str] = ..., value: _Optional[str] = ..., state: _Optional[bool] = ...) -> None: ... + def __init__(self, type: _Optional[_Union[ThrottleType, str]] = ..., key: _Optional[str] = ..., value: _Optional[str] = ..., state: bool = ...) -> None: ... class ThrottleState2(_message.Message): __slots__ = ("key", "keyDescription", "value", "valueDescription", "identifier", "locked", "includedInAllClear", "expireSeconds") @@ -1658,7 +1661,7 @@ class ThrottleState2(_message.Message): locked: bool includedInAllClear: bool expireSeconds: int - def __init__(self, key: _Optional[str] = ..., keyDescription: _Optional[str] = ..., value: _Optional[str] = ..., valueDescription: _Optional[str] = ..., identifier: _Optional[str] = ..., locked: _Optional[bool] = ..., includedInAllClear: _Optional[bool] = ..., expireSeconds: _Optional[int] = ...) -> None: ... + def __init__(self, key: _Optional[str] = ..., keyDescription: _Optional[str] = ..., value: _Optional[str] = ..., valueDescription: _Optional[str] = ..., identifier: _Optional[str] = ..., locked: bool = ..., includedInAllClear: bool = ..., expireSeconds: _Optional[int] = ...) -> None: ... class DeviceInformation(_message.Message): __slots__ = ("deviceId", "deviceName", "clientVersion", "lastLogin", "deviceStatus") @@ -1680,7 +1683,7 @@ class UserSetting(_message.Message): VALUE_FIELD_NUMBER: _ClassVar[int] name: str value: bool - def __init__(self, name: _Optional[str] = ..., value: _Optional[bool] = ...) -> None: ... + def __init__(self, name: _Optional[str] = ..., value: bool = ...) -> None: ... class UserDataKeyRequest(_message.Message): __slots__ = ("enterpriseUserId",) @@ -1776,7 +1779,7 @@ class PasswordRules(_message.Message): description: str minimum: int value: str - def __init__(self, ruleType: _Optional[str] = ..., match: _Optional[bool] = ..., pattern: _Optional[str] = ..., description: _Optional[str] = ..., minimum: _Optional[int] = ..., value: _Optional[str] = ...) -> None: ... + def __init__(self, ruleType: _Optional[str] = ..., match: bool = ..., pattern: _Optional[str] = ..., description: _Optional[str] = ..., minimum: _Optional[int] = ..., value: _Optional[str] = ...) -> None: ... class GetDataKeyBackupV3Response(_message.Message): __slots__ = ("dataKeyBackup", "dataKeyBackupDate", "publicKey", "encryptedPrivateKey", "clientKey", "encryptedSessionToken", "passwordRules", "passwordRulesIntro", "minimumPbkdf2Iterations", "keyType") @@ -1809,18 +1812,20 @@ class GetPublicKeysRequest(_message.Message): def __init__(self, usernames: _Optional[_Iterable[str]] = ...) -> None: ... class PublicKeyResponse(_message.Message): - __slots__ = ("username", "publicKey", "publicEccKey", "message", "errorCode") + __slots__ = ("username", "publicKey", "publicEccKey", "message", "errorCode", "accountUid") USERNAME_FIELD_NUMBER: _ClassVar[int] PUBLICKEY_FIELD_NUMBER: _ClassVar[int] PUBLICECCKEY_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] ERRORCODE_FIELD_NUMBER: _ClassVar[int] + ACCOUNTUID_FIELD_NUMBER: _ClassVar[int] username: str publicKey: bytes publicEccKey: bytes message: str errorCode: str - def __init__(self, username: _Optional[str] = ..., publicKey: _Optional[bytes] = ..., publicEccKey: _Optional[bytes] = ..., message: _Optional[str] = ..., errorCode: _Optional[str] = ...) -> None: ... + accountUid: bytes + def __init__(self, username: _Optional[str] = ..., publicKey: _Optional[bytes] = ..., publicEccKey: _Optional[bytes] = ..., message: _Optional[str] = ..., errorCode: _Optional[str] = ..., accountUid: _Optional[bytes] = ...) -> None: ... class GetPublicKeysResponse(_message.Message): __slots__ = ("keyResponses",) @@ -1916,7 +1921,7 @@ class AppShareAdd(_message.Message): shareType: ApplicationShareType encryptedSecretKey: bytes editable: bool - def __init__(self, secretUid: _Optional[bytes] = ..., shareType: _Optional[_Union[ApplicationShareType, str]] = ..., encryptedSecretKey: _Optional[bytes] = ..., editable: _Optional[bool] = ...) -> None: ... + def __init__(self, secretUid: _Optional[bytes] = ..., shareType: _Optional[_Union[ApplicationShareType, str]] = ..., encryptedSecretKey: _Optional[bytes] = ..., editable: bool = ...) -> None: ... class AppShare(_message.Message): __slots__ = ("secretUid", "shareType", "editable", "createdOn", "data") @@ -1930,7 +1935,7 @@ class AppShare(_message.Message): editable: bool createdOn: int data: bytes - def __init__(self, secretUid: _Optional[bytes] = ..., shareType: _Optional[_Union[ApplicationShareType, str]] = ..., editable: _Optional[bool] = ..., createdOn: _Optional[int] = ..., data: _Optional[bytes] = ...) -> None: ... + def __init__(self, secretUid: _Optional[bytes] = ..., shareType: _Optional[_Union[ApplicationShareType, str]] = ..., editable: bool = ..., createdOn: _Optional[int] = ..., data: _Optional[bytes] = ...) -> None: ... class AddAppClientRequest(_message.Message): __slots__ = ("appRecordUid", "encryptedAppKey", "clientId", "lockIp", "firstAccessExpireOn", "accessExpireOn", "id", "appClientType") @@ -1950,7 +1955,7 @@ class AddAppClientRequest(_message.Message): accessExpireOn: int id: str appClientType: _enterprise_pb2.AppClientType - def __init__(self, appRecordUid: _Optional[bytes] = ..., encryptedAppKey: _Optional[bytes] = ..., clientId: _Optional[bytes] = ..., lockIp: _Optional[bool] = ..., firstAccessExpireOn: _Optional[int] = ..., accessExpireOn: _Optional[int] = ..., id: _Optional[str] = ..., appClientType: _Optional[_Union[_enterprise_pb2.AppClientType, str]] = ...) -> None: ... + def __init__(self, appRecordUid: _Optional[bytes] = ..., encryptedAppKey: _Optional[bytes] = ..., clientId: _Optional[bytes] = ..., lockIp: bool = ..., firstAccessExpireOn: _Optional[int] = ..., accessExpireOn: _Optional[int] = ..., id: _Optional[str] = ..., appClientType: _Optional[_Union[_enterprise_pb2.AppClientType, str]] = ...) -> None: ... class RemoveAppClientsRequest(_message.Message): __slots__ = ("appRecordUid", "clients") @@ -1976,7 +1981,7 @@ class AddExternalShareRequest(_message.Message): id: str isSelfDestruct: bool isEditable: bool - def __init__(self, recordUid: _Optional[bytes] = ..., encryptedRecordKey: _Optional[bytes] = ..., clientId: _Optional[bytes] = ..., accessExpireOn: _Optional[int] = ..., id: _Optional[str] = ..., isSelfDestruct: _Optional[bool] = ..., isEditable: _Optional[bool] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., encryptedRecordKey: _Optional[bytes] = ..., clientId: _Optional[bytes] = ..., accessExpireOn: _Optional[int] = ..., id: _Optional[str] = ..., isSelfDestruct: bool = ..., isEditable: bool = ...) -> None: ... class AppClient(_message.Message): __slots__ = ("id", "clientId", "createdOn", "firstAccess", "lastAccess", "publicKey", "lockIp", "ipAddress", "firstAccessExpireOn", "accessExpireOn", "appClientType", "canEdit") @@ -2004,7 +2009,7 @@ class AppClient(_message.Message): accessExpireOn: int appClientType: _enterprise_pb2.AppClientType canEdit: bool - def __init__(self, id: _Optional[str] = ..., clientId: _Optional[bytes] = ..., createdOn: _Optional[int] = ..., firstAccess: _Optional[int] = ..., lastAccess: _Optional[int] = ..., publicKey: _Optional[bytes] = ..., lockIp: _Optional[bool] = ..., ipAddress: _Optional[str] = ..., firstAccessExpireOn: _Optional[int] = ..., accessExpireOn: _Optional[int] = ..., appClientType: _Optional[_Union[_enterprise_pb2.AppClientType, str]] = ..., canEdit: _Optional[bool] = ...) -> None: ... + def __init__(self, id: _Optional[str] = ..., clientId: _Optional[bytes] = ..., createdOn: _Optional[int] = ..., firstAccess: _Optional[int] = ..., lastAccess: _Optional[int] = ..., publicKey: _Optional[bytes] = ..., lockIp: bool = ..., ipAddress: _Optional[str] = ..., firstAccessExpireOn: _Optional[int] = ..., accessExpireOn: _Optional[int] = ..., appClientType: _Optional[_Union[_enterprise_pb2.AppClientType, str]] = ..., canEdit: bool = ...) -> None: ... class GetAppInfoRequest(_message.Message): __slots__ = ("appRecordUid",) @@ -2022,7 +2027,7 @@ class AppInfo(_message.Message): shares: _containers.RepeatedCompositeFieldContainer[AppShare] clients: _containers.RepeatedCompositeFieldContainer[AppClient] isExternalShare: bool - def __init__(self, appRecordUid: _Optional[bytes] = ..., shares: _Optional[_Iterable[_Union[AppShare, _Mapping]]] = ..., clients: _Optional[_Iterable[_Union[AppClient, _Mapping]]] = ..., isExternalShare: _Optional[bool] = ...) -> None: ... + def __init__(self, appRecordUid: _Optional[bytes] = ..., shares: _Optional[_Iterable[_Union[AppShare, _Mapping]]] = ..., clients: _Optional[_Iterable[_Union[AppClient, _Mapping]]] = ..., isExternalShare: bool = ...) -> None: ... class GetAppInfoResponse(_message.Message): __slots__ = ("appInfo",) @@ -2154,7 +2159,7 @@ class ChangeMasterPasswordRequest(_message.Message): encryptionParams: bytes fromServiceProvider: bool iterationsChange: bool - def __init__(self, authVerifier: _Optional[bytes] = ..., encryptionParams: _Optional[bytes] = ..., fromServiceProvider: _Optional[bool] = ..., iterationsChange: _Optional[bool] = ...) -> None: ... + def __init__(self, authVerifier: _Optional[bytes] = ..., encryptionParams: _Optional[bytes] = ..., fromServiceProvider: bool = ..., iterationsChange: bool = ...) -> None: ... class ChangeMasterPasswordResponse(_message.Message): __slots__ = ("encryptedSessionToken",) @@ -2292,7 +2297,7 @@ class PasskeyValidationResponse(_message.Message): ENCRYPTEDLOGINTOKEN_FIELD_NUMBER: _ClassVar[int] isValid: bool encryptedLoginToken: bytes - def __init__(self, isValid: _Optional[bool] = ..., encryptedLoginToken: _Optional[bytes] = ...) -> None: ... + def __init__(self, isValid: bool = ..., encryptedLoginToken: _Optional[bytes] = ...) -> None: ... class UpdatePasskeyRequest(_message.Message): __slots__ = ("userId", "credentialId", "friendlyName") @@ -2308,7 +2313,7 @@ class PasskeyListRequest(_message.Message): __slots__ = ("includeDisabled",) INCLUDEDISABLED_FIELD_NUMBER: _ClassVar[int] includeDisabled: bool - def __init__(self, includeDisabled: _Optional[bool] = ...) -> None: ... + def __init__(self, includeDisabled: bool = ...) -> None: ... class PasskeyInfo(_message.Message): __slots__ = ("userId", "credentialId", "friendlyName", "AAGUID", "createdAtMillis", "lastUsedMillis", "disabledAtMillis") diff --git a/keepercommander/proto/AccountSummary_pb2.py b/keepercommander/proto/AccountSummary_pb2.py index b6e830a35..353062c78 100644 --- a/keepercommander/proto/AccountSummary_pb2.py +++ b/keepercommander/proto/AccountSummary_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: AccountSummary.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'AccountSummary.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/AccountSummary_pb2.pyi b/keepercommander/proto/AccountSummary_pb2.pyi index 40127ad83..380597751 100644 --- a/keepercommander/proto/AccountSummary_pb2.pyi +++ b/keepercommander/proto/AccountSummary_pb2.pyi @@ -2,8 +2,7 @@ import APIRequest_pb2 as _APIRequest_pb2 from google.protobuf.internal import containers as _containers 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -13,7 +12,7 @@ class AccountSummaryRequest(_message.Message): INCLUDERECENTACTIVITY_FIELD_NUMBER: _ClassVar[int] summaryVersion: int includeRecentActivity: bool - def __init__(self, summaryVersion: _Optional[int] = ..., includeRecentActivity: _Optional[bool] = ...) -> None: ... + def __init__(self, summaryVersion: _Optional[int] = ..., includeRecentActivity: bool = ...) -> None: ... class AccountSummaryElements(_message.Message): __slots__ = ("clientKey", "settings", "keysInfo", "syncLogs", "isEnterpriseAdmin", "license", "group", "Enforcements", "Images", "personalLicense", "fixSharedFolderRecords", "usernames", "devices", "isShareAdmin", "accountRecovery", "accountRecoveryPrompt", "minMasterPasswordLengthNoPrompt", "forbidKeyType2", "forbidKeyType1", "disallowedFeatures") @@ -57,7 +56,7 @@ class AccountSummaryElements(_message.Message): forbidKeyType2: bool forbidKeyType1: bool disallowedFeatures: _containers.RepeatedScalarFieldContainer[str] - def __init__(self, clientKey: _Optional[bytes] = ..., settings: _Optional[_Union[Settings, _Mapping]] = ..., keysInfo: _Optional[_Union[KeysInfo, _Mapping]] = ..., syncLogs: _Optional[_Iterable[_Union[SyncLog, _Mapping]]] = ..., isEnterpriseAdmin: _Optional[bool] = ..., license: _Optional[_Union[License, _Mapping]] = ..., group: _Optional[_Union[Group, _Mapping]] = ..., Enforcements: _Optional[_Union[Enforcements, _Mapping]] = ..., Images: _Optional[_Iterable[_Union[KeyValue, _Mapping]]] = ..., personalLicense: _Optional[_Union[License, _Mapping]] = ..., fixSharedFolderRecords: _Optional[bool] = ..., usernames: _Optional[_Iterable[str]] = ..., devices: _Optional[_Iterable[_Union[DeviceInfo, _Mapping]]] = ..., isShareAdmin: _Optional[bool] = ..., accountRecovery: _Optional[bool] = ..., accountRecoveryPrompt: _Optional[bool] = ..., minMasterPasswordLengthNoPrompt: _Optional[int] = ..., forbidKeyType2: _Optional[bool] = ..., forbidKeyType1: _Optional[bool] = ..., disallowedFeatures: _Optional[_Iterable[str]] = ...) -> None: ... + def __init__(self, clientKey: _Optional[bytes] = ..., settings: _Optional[_Union[Settings, _Mapping]] = ..., keysInfo: _Optional[_Union[KeysInfo, _Mapping]] = ..., syncLogs: _Optional[_Iterable[_Union[SyncLog, _Mapping]]] = ..., isEnterpriseAdmin: bool = ..., license: _Optional[_Union[License, _Mapping]] = ..., group: _Optional[_Union[Group, _Mapping]] = ..., Enforcements: _Optional[_Union[Enforcements, _Mapping]] = ..., Images: _Optional[_Iterable[_Union[KeyValue, _Mapping]]] = ..., personalLicense: _Optional[_Union[License, _Mapping]] = ..., fixSharedFolderRecords: bool = ..., usernames: _Optional[_Iterable[str]] = ..., devices: _Optional[_Iterable[_Union[DeviceInfo, _Mapping]]] = ..., isShareAdmin: bool = ..., accountRecovery: bool = ..., accountRecoveryPrompt: bool = ..., minMasterPasswordLengthNoPrompt: _Optional[int] = ..., forbidKeyType2: bool = ..., forbidKeyType1: bool = ..., disallowedFeatures: _Optional[_Iterable[str]] = ...) -> None: ... class DeviceInfo(_message.Message): __slots__ = ("encryptedDeviceToken", "deviceName", "deviceStatus", "devicePublicKey", "encryptedDataKeyDoNotUse", "clientVersion", "username", "ipAddress", "approveRequestTime", "encryptedDataKeyPresent", "groupId", "devicePlatform", "clientFormFactor") @@ -87,7 +86,7 @@ class DeviceInfo(_message.Message): groupId: int devicePlatform: str clientFormFactor: _APIRequest_pb2.ClientFormFactor - def __init__(self, encryptedDeviceToken: _Optional[bytes] = ..., deviceName: _Optional[str] = ..., deviceStatus: _Optional[_Union[_APIRequest_pb2.DeviceStatus, str]] = ..., devicePublicKey: _Optional[bytes] = ..., encryptedDataKeyDoNotUse: _Optional[bytes] = ..., clientVersion: _Optional[str] = ..., username: _Optional[str] = ..., ipAddress: _Optional[str] = ..., approveRequestTime: _Optional[int] = ..., encryptedDataKeyPresent: _Optional[bool] = ..., groupId: _Optional[int] = ..., devicePlatform: _Optional[str] = ..., clientFormFactor: _Optional[_Union[_APIRequest_pb2.ClientFormFactor, str]] = ...) -> None: ... + def __init__(self, encryptedDeviceToken: _Optional[bytes] = ..., deviceName: _Optional[str] = ..., deviceStatus: _Optional[_Union[_APIRequest_pb2.DeviceStatus, str]] = ..., devicePublicKey: _Optional[bytes] = ..., encryptedDataKeyDoNotUse: _Optional[bytes] = ..., clientVersion: _Optional[str] = ..., username: _Optional[str] = ..., ipAddress: _Optional[str] = ..., approveRequestTime: _Optional[int] = ..., encryptedDataKeyPresent: bool = ..., groupId: _Optional[int] = ..., devicePlatform: _Optional[str] = ..., clientFormFactor: _Optional[_Union[_APIRequest_pb2.ClientFormFactor, str]] = ...) -> None: ... class KeysInfo(_message.Message): __slots__ = ("encryptionParams", "encryptedDataKey", "dataKeyBackupDate", "userAuthUid", "encryptedPrivateKey", "encryptedEccPrivateKey", "eccPublicKey") @@ -193,7 +192,7 @@ class License(_message.Message): pendingEnterprise: bool isPamEnabled: bool isKsmEnabled: bool - def __init__(self, subscriptionCode: _Optional[str] = ..., productTypeId: _Optional[int] = ..., productTypeName: _Optional[str] = ..., expirationDate: _Optional[str] = ..., secondsUntilExpiration: _Optional[int] = ..., maxDevices: _Optional[int] = ..., filePlanType: _Optional[int] = ..., bytesUsed: _Optional[int] = ..., bytesTotal: _Optional[int] = ..., secondsUntilStorageExpiration: _Optional[int] = ..., storageExpirationDate: _Optional[str] = ..., hasAutoRenewableAppstoreSubscription: _Optional[bool] = ..., accountType: _Optional[int] = ..., uploadsRemaining: _Optional[int] = ..., enterpriseId: _Optional[int] = ..., chatEnabled: _Optional[bool] = ..., auditAndReportingEnabled: _Optional[bool] = ..., breachWatchFeatureDisable: _Optional[bool] = ..., accountUid: _Optional[bytes] = ..., allowPersonalLicense: _Optional[bool] = ..., licensedBy: _Optional[str] = ..., email: _Optional[str] = ..., breachWatchEnabled: _Optional[bool] = ..., breachWatchScanned: _Optional[bool] = ..., breachWatchExpiration: _Optional[int] = ..., breachWatchDateCreated: _Optional[int] = ..., error: _Optional[_Union[Result, _Mapping]] = ..., expiration: _Optional[int] = ..., storageExpiration: _Optional[int] = ..., uploadsCount: _Optional[int] = ..., units: _Optional[int] = ..., pendingEnterprise: _Optional[bool] = ..., isPamEnabled: _Optional[bool] = ..., isKsmEnabled: _Optional[bool] = ...) -> None: ... + def __init__(self, subscriptionCode: _Optional[str] = ..., productTypeId: _Optional[int] = ..., productTypeName: _Optional[str] = ..., expirationDate: _Optional[str] = ..., secondsUntilExpiration: _Optional[int] = ..., maxDevices: _Optional[int] = ..., filePlanType: _Optional[int] = ..., bytesUsed: _Optional[int] = ..., bytesTotal: _Optional[int] = ..., secondsUntilStorageExpiration: _Optional[int] = ..., storageExpirationDate: _Optional[str] = ..., hasAutoRenewableAppstoreSubscription: bool = ..., accountType: _Optional[int] = ..., uploadsRemaining: _Optional[int] = ..., enterpriseId: _Optional[int] = ..., chatEnabled: bool = ..., auditAndReportingEnabled: bool = ..., breachWatchFeatureDisable: bool = ..., accountUid: _Optional[bytes] = ..., allowPersonalLicense: bool = ..., licensedBy: _Optional[str] = ..., email: _Optional[str] = ..., breachWatchEnabled: bool = ..., breachWatchScanned: bool = ..., breachWatchExpiration: _Optional[int] = ..., breachWatchDateCreated: _Optional[int] = ..., error: _Optional[_Union[Result, _Mapping]] = ..., expiration: _Optional[int] = ..., storageExpiration: _Optional[int] = ..., uploadsCount: _Optional[int] = ..., units: _Optional[int] = ..., pendingEnterprise: bool = ..., isPamEnabled: bool = ..., isKsmEnabled: bool = ...) -> None: ... class AddOn(_message.Message): __slots__ = ("licenseKeyId", "name", "expirationDate", "createdDate", "isTrial", "enabled", "scanned", "featureDisable") @@ -213,7 +212,7 @@ class AddOn(_message.Message): enabled: bool scanned: bool featureDisable: bool - def __init__(self, licenseKeyId: _Optional[int] = ..., name: _Optional[str] = ..., expirationDate: _Optional[int] = ..., createdDate: _Optional[int] = ..., isTrial: _Optional[bool] = ..., enabled: _Optional[bool] = ..., scanned: _Optional[bool] = ..., featureDisable: _Optional[bool] = ...) -> None: ... + def __init__(self, licenseKeyId: _Optional[int] = ..., name: _Optional[str] = ..., expirationDate: _Optional[int] = ..., createdDate: _Optional[int] = ..., isTrial: bool = ..., enabled: bool = ..., scanned: bool = ..., featureDisable: bool = ...) -> None: ... class Settings(_message.Message): __slots__ = ("audit", "mustPerformAccountShareBy", "shareAccountTo", "rules", "passwordRulesIntro", "autoBackupDays", "theme", "channel", "channelValue", "rsaConfigured", "emailVerified", "masterPasswordLastModified", "accountFolderKey", "securityKeys", "keyValues", "ssoUser", "onlineAccessOnly", "masterPasswordExpiry", "twoFactorRequired", "disallowExport", "restrictFiles", "restrictAllSharing", "restrictSharing", "restrictSharingIncomingAll", "restrictSharingIncomingEnterprise", "logoutTimer", "persistentLogin", "ipDisableAutoApprove", "shareDataKeyWithEccPublicKey", "shareDataKeyWithDevicePublicKey", "RecordTypesCounter", "RecordTypesEnterpriseCounter", "recordTypesEnabled", "canManageRecordTypes", "recordTypesPAMCounter", "logoutTimerMinutes", "securityKeysNoUserVerify", "channels", "personalUsernames", "maxIpDistance", "maxIpDistanceEffective") @@ -299,7 +298,7 @@ class Settings(_message.Message): personalUsernames: _containers.RepeatedScalarFieldContainer[str] maxIpDistance: int maxIpDistanceEffective: int - def __init__(self, audit: _Optional[bool] = ..., mustPerformAccountShareBy: _Optional[int] = ..., shareAccountTo: _Optional[_Iterable[_Union[MissingAccountShareKey, _Mapping]]] = ..., rules: _Optional[_Iterable[_Union[PasswordRule, _Mapping]]] = ..., passwordRulesIntro: _Optional[str] = ..., autoBackupDays: _Optional[int] = ..., theme: _Optional[str] = ..., channel: _Optional[str] = ..., channelValue: _Optional[str] = ..., rsaConfigured: _Optional[bool] = ..., emailVerified: _Optional[bool] = ..., masterPasswordLastModified: _Optional[float] = ..., accountFolderKey: _Optional[bytes] = ..., securityKeys: _Optional[_Iterable[_Union[SecurityKey, _Mapping]]] = ..., keyValues: _Optional[_Iterable[_Union[KeyValue, _Mapping]]] = ..., ssoUser: _Optional[bool] = ..., onlineAccessOnly: _Optional[bool] = ..., masterPasswordExpiry: _Optional[int] = ..., twoFactorRequired: _Optional[bool] = ..., disallowExport: _Optional[bool] = ..., restrictFiles: _Optional[bool] = ..., restrictAllSharing: _Optional[bool] = ..., restrictSharing: _Optional[bool] = ..., restrictSharingIncomingAll: _Optional[bool] = ..., restrictSharingIncomingEnterprise: _Optional[bool] = ..., logoutTimer: _Optional[int] = ..., persistentLogin: _Optional[bool] = ..., ipDisableAutoApprove: _Optional[bool] = ..., shareDataKeyWithEccPublicKey: _Optional[bool] = ..., shareDataKeyWithDevicePublicKey: _Optional[bool] = ..., RecordTypesCounter: _Optional[int] = ..., RecordTypesEnterpriseCounter: _Optional[int] = ..., recordTypesEnabled: _Optional[bool] = ..., canManageRecordTypes: _Optional[bool] = ..., recordTypesPAMCounter: _Optional[int] = ..., logoutTimerMinutes: _Optional[int] = ..., securityKeysNoUserVerify: _Optional[bool] = ..., channels: _Optional[_Iterable[_Union[_APIRequest_pb2.TwoFactorChannelType, str]]] = ..., personalUsernames: _Optional[_Iterable[str]] = ..., maxIpDistance: _Optional[int] = ..., maxIpDistanceEffective: _Optional[int] = ...) -> None: ... + def __init__(self, audit: bool = ..., mustPerformAccountShareBy: _Optional[int] = ..., shareAccountTo: _Optional[_Iterable[_Union[MissingAccountShareKey, _Mapping]]] = ..., rules: _Optional[_Iterable[_Union[PasswordRule, _Mapping]]] = ..., passwordRulesIntro: _Optional[str] = ..., autoBackupDays: _Optional[int] = ..., theme: _Optional[str] = ..., channel: _Optional[str] = ..., channelValue: _Optional[str] = ..., rsaConfigured: bool = ..., emailVerified: bool = ..., masterPasswordLastModified: _Optional[float] = ..., accountFolderKey: _Optional[bytes] = ..., securityKeys: _Optional[_Iterable[_Union[SecurityKey, _Mapping]]] = ..., keyValues: _Optional[_Iterable[_Union[KeyValue, _Mapping]]] = ..., ssoUser: bool = ..., onlineAccessOnly: bool = ..., masterPasswordExpiry: _Optional[int] = ..., twoFactorRequired: bool = ..., disallowExport: bool = ..., restrictFiles: bool = ..., restrictAllSharing: bool = ..., restrictSharing: bool = ..., restrictSharingIncomingAll: bool = ..., restrictSharingIncomingEnterprise: bool = ..., logoutTimer: _Optional[int] = ..., persistentLogin: bool = ..., ipDisableAutoApprove: bool = ..., shareDataKeyWithEccPublicKey: bool = ..., shareDataKeyWithDevicePublicKey: bool = ..., RecordTypesCounter: _Optional[int] = ..., RecordTypesEnterpriseCounter: _Optional[int] = ..., recordTypesEnabled: bool = ..., canManageRecordTypes: bool = ..., recordTypesPAMCounter: _Optional[int] = ..., logoutTimerMinutes: _Optional[int] = ..., securityKeysNoUserVerify: bool = ..., channels: _Optional[_Iterable[_Union[_APIRequest_pb2.TwoFactorChannelType, str]]] = ..., personalUsernames: _Optional[_Iterable[str]] = ..., maxIpDistance: _Optional[int] = ..., maxIpDistanceEffective: _Optional[int] = ...) -> None: ... class KeyValue(_message.Message): __slots__ = ("key", "value") @@ -315,7 +314,7 @@ class KeyValueBoolean(_message.Message): VALUE_FIELD_NUMBER: _ClassVar[int] key: str value: bool - def __init__(self, key: _Optional[str] = ..., value: _Optional[bool] = ...) -> None: ... + def __init__(self, key: _Optional[str] = ..., value: bool = ...) -> None: ... class KeyValueLong(_message.Message): __slots__ = ("key", "value") @@ -369,7 +368,7 @@ class PasswordRule(_message.Message): minimum: int description: str value: str - def __init__(self, ruleType: _Optional[str] = ..., pattern: _Optional[str] = ..., match: _Optional[bool] = ..., minimum: _Optional[int] = ..., description: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + def __init__(self, ruleType: _Optional[str] = ..., pattern: _Optional[str] = ..., match: bool = ..., minimum: _Optional[int] = ..., description: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... class SecurityKey(_message.Message): __slots__ = ("deviceId", "deviceName", "dateAdded", "isValid", "deviceRegistration") @@ -383,7 +382,7 @@ class SecurityKey(_message.Message): dateAdded: int isValid: bool deviceRegistration: DeviceRegistration - def __init__(self, deviceId: _Optional[int] = ..., deviceName: _Optional[str] = ..., dateAdded: _Optional[int] = ..., isValid: _Optional[bool] = ..., deviceRegistration: _Optional[_Union[DeviceRegistration, _Mapping]] = ...) -> None: ... + def __init__(self, deviceId: _Optional[int] = ..., deviceName: _Optional[str] = ..., dateAdded: _Optional[int] = ..., isValid: bool = ..., deviceRegistration: _Optional[_Union[DeviceRegistration, _Mapping]] = ...) -> None: ... class DeviceRegistration(_message.Message): __slots__ = ("keyHandle", "publicKey", "attestationCert", "counter", "compromised") @@ -397,7 +396,7 @@ class DeviceRegistration(_message.Message): attestationCert: str counter: int compromised: bool - def __init__(self, keyHandle: _Optional[str] = ..., publicKey: _Optional[bytes] = ..., attestationCert: _Optional[str] = ..., counter: _Optional[int] = ..., compromised: _Optional[bool] = ...) -> None: ... + def __init__(self, keyHandle: _Optional[str] = ..., publicKey: _Optional[bytes] = ..., attestationCert: _Optional[str] = ..., counter: _Optional[int] = ..., compromised: bool = ...) -> None: ... class Group(_message.Message): __slots__ = ("admin", "groupVerificationCode", "administrator") @@ -407,7 +406,7 @@ class Group(_message.Message): admin: bool groupVerificationCode: str administrator: Administrator - def __init__(self, admin: _Optional[bool] = ..., groupVerificationCode: _Optional[str] = ..., administrator: _Optional[_Union[Administrator, _Mapping]] = ...) -> None: ... + def __init__(self, admin: bool = ..., groupVerificationCode: _Optional[str] = ..., administrator: _Optional[_Union[Administrator, _Mapping]] = ...) -> None: ... class Administrator(_message.Message): __slots__ = ("firstName", "lastName", "email", "currentNumberOfUsers", "numberOfUsers", "subscriptionCode", "expirationDate", "purchaseDate") diff --git a/keepercommander/proto/BI_pb2.py b/keepercommander/proto/BI_pb2.py index 9d882eab3..367915695 100644 --- a/keepercommander/proto/BI_pb2.py +++ b/keepercommander/proto/BI_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: BI.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'BI.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,20 +25,32 @@ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x08\x42I.proto\x12\x02\x42I\x1a\x1cgoogle/protobuf/struct.proto\"f\n\x1bValidateSessionTokenRequest\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x01 \x01(\x0c\x12\x1c\n\x14returnMcEnterpiseIds\x18\x02 \x01(\x08\x12\n\n\x02ip\x18\x03 \x01(\t\"\xda\x02\n\x1cValidateSessionTokenResponse\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x0e\n\x06userId\x18\x02 \x01(\x05\x12\x18\n\x10\x65nterpriseUserId\x18\x03 \x01(\x03\x12\x37\n\x06status\x18\x04 \x01(\x0e\x32\'.BI.ValidateSessionTokenResponse.Status\x12\x15\n\rstatusMessage\x18\x05 \x01(\t\x12\x17\n\x0fmcEnterpriseIds\x18\x06 \x03(\x05\x12\x18\n\x10hasMSPPermission\x18\x07 \x01(\x08\x12\x1e\n\x16\x64\x65letedMcEnterpriseIds\x18\x08 \x03(\x05\"[\n\x06Status\x12\t\n\x05VALID\x10\x00\x12\r\n\tNOT_VALID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x12\x0e\n\nIP_BLOCKED\x10\x03\x12\x1a\n\x16INVALID_CLIENT_VERSION\x10\x04\"\x1b\n\x19SubscriptionStatusRequest\"\xa6\x03\n\x1aSubscriptionStatusResponse\x12$\n\x0b\x61utoRenewal\x18\x01 \x01(\x0b\x32\x0f.BI.AutoRenewal\x12/\n\x14\x63urrentPaymentMethod\x18\x02 \x01(\x0b\x32\x11.BI.PaymentMethod\x12\x14\n\x0c\x63heckoutLink\x18\x03 \x01(\t\x12\x19\n\x11licenseCreateDate\x18\x04 \x01(\x03\x12\x15\n\risDistributor\x18\x05 \x01(\x08\x12\x13\n\x0bisLegacyMsp\x18\x06 \x01(\x08\x12&\n\x0clicenseStats\x18\x08 \x03(\x0b\x32\x10.BI.LicenseStats\x12\x35\n\x0egradientStatus\x18\t \x01(\x0e\x32\x1d.BI.GradientIntegrationStatus\x12\x17\n\x0fhideTrialBanner\x18\n \x01(\x08\x12\x1c\n\x14gradientLastSyncDate\x18\x0b \x01(\t\x12\x1c\n\x14gradientNextSyncDate\x18\x0c \x01(\t\x12 \n\x18isGradientMappingPending\x18\r \x01(\x08\"\xd7\x01\n\x0cLicenseStats\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.BI.LicenseStats.Type\x12\x11\n\tavailable\x18\x02 \x01(\x05\x12\x0c\n\x04used\x18\x03 \x01(\x05\"\x80\x01\n\x04Type\x12\x18\n\x14LICENSE_STAT_UNKNOWN\x10\x00\x12\x0c\n\x08MSP_BASE\x10\x01\x12\x0f\n\x0bMC_BUSINESS\x10\x02\x12\x14\n\x10MC_BUSINESS_PLUS\x10\x03\x12\x11\n\rMC_ENTERPRISE\x10\x04\x12\x16\n\x12MC_ENTERPRISE_PLUS\x10\x05\"@\n\x0b\x41utoRenewal\x12\x0e\n\x06nextOn\x18\x01 \x01(\x03\x12\x10\n\x08\x64\x61ysLeft\x18\x02 \x01(\x05\x12\x0f\n\x07isTrial\x18\x03 \x01(\x08\"\x84\x04\n\rPaymentMethod\x12$\n\x04type\x18\x01 \x01(\x0e\x32\x16.BI.PaymentMethod.Type\x12$\n\x04\x63\x61rd\x18\x02 \x01(\x0b\x32\x16.BI.PaymentMethod.Card\x12$\n\x04sepa\x18\x03 \x01(\x0b\x32\x16.BI.PaymentMethod.Sepa\x12(\n\x06paypal\x18\x04 \x01(\x0b\x32\x18.BI.PaymentMethod.Paypal\x12\x15\n\rfailedBilling\x18\x05 \x01(\x08\x12(\n\x06vendor\x18\x06 \x01(\x0b\x32\x18.BI.PaymentMethod.Vendor\x12\x36\n\rpurchaseOrder\x18\x07 \x01(\x0b\x32\x1f.BI.PaymentMethod.PurchaseOrder\x1a$\n\x04\x43\x61rd\x12\r\n\x05last4\x18\x01 \x01(\t\x12\r\n\x05\x62rand\x18\x02 \x01(\t\x1a&\n\x04Sepa\x12\r\n\x05last4\x18\x01 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x02 \x01(\t\x1a\x08\n\x06Paypal\x1a\x16\n\x06Vendor\x12\x0c\n\x04name\x18\x01 \x01(\t\x1a\x1d\n\rPurchaseOrder\x12\x0c\n\x04name\x18\x01 \x01(\t\"O\n\x04Type\x12\x08\n\x04\x43\x41RD\x10\x00\x12\x08\n\x04SEPA\x10\x01\x12\n\n\x06PAYPAL\x10\x02\x12\x08\n\x04NONE\x10\x03\x12\n\n\x06VENDOR\x10\x04\x12\x11\n\rPURCHASEORDER\x10\x05\"\x1f\n\x1dSubscriptionMspPricingRequest\"\\\n\x1eSubscriptionMspPricingResponse\x12\x19\n\x06\x61\x64\x64ons\x18\x02 \x03(\x0b\x32\t.BI.Addon\x12\x1f\n\tfilePlans\x18\x03 \x03(\x0b\x32\x0c.BI.FilePlan\"\x1e\n\x1cSubscriptionMcPricingRequest\"|\n\x1dSubscriptionMcPricingResponse\x12\x1f\n\tbasePlans\x18\x01 \x03(\x0b\x32\x0c.BI.BasePlan\x12\x19\n\x06\x61\x64\x64ons\x18\x02 \x03(\x0b\x32\t.BI.Addon\x12\x1f\n\tfilePlans\x18\x03 \x03(\x0b\x32\x0c.BI.FilePlan\".\n\x08\x42\x61sePlan\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x16\n\x04\x63ost\x18\x02 \x01(\x0b\x32\x08.BI.Cost\"C\n\x05\x41\x64\x64on\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x16\n\x04\x63ost\x18\x02 \x01(\x0b\x32\x08.BI.Cost\x12\x16\n\x0e\x61mountConsumed\x18\x03 \x01(\x03\".\n\x08\x46ilePlan\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x16\n\x04\x63ost\x18\x02 \x01(\x0b\x32\x08.BI.Cost\"\xbf\x01\n\x04\x43ost\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x01\x12%\n\tamountPer\x18\x04 \x01(\x0e\x32\x12.BI.Cost.AmountPer\x12\x1e\n\x08\x63urrency\x18\x05 \x01(\x0e\x32\x0c.BI.Currency\"`\n\tAmountPer\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05MONTH\x10\x01\x12\x0e\n\nUSER_MONTH\x10\x02\x12\x17\n\x13USER_CONSUMED_MONTH\x10\x03\x12\x12\n\x0e\x45NDPOINT_MONTH\x10\x04\"\\\n\x14InvoiceSearchRequest\x12\x0c\n\x04size\x18\x01 \x01(\x05\x12\x17\n\x0fstartingAfterId\x18\x02 \x01(\x05\x12\x1d\n\x15\x61llInvoicesUnfiltered\x18\x03 \x01(\x08\"6\n\x15InvoiceSearchResponse\x12\x1d\n\x08invoices\x18\x01 \x03(\x0b\x32\x0b.BI.Invoice\"\xbe\x02\n\x07Invoice\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x15\n\rinvoiceNumber\x18\x02 \x01(\t\x12\x13\n\x0binvoiceDate\x18\x03 \x01(\x03\x12\x14\n\x0clicenseCount\x18\x04 \x01(\x05\x12#\n\ttotalCost\x18\x05 \x01(\x0b\x32\x10.BI.Invoice.Cost\x12%\n\x0binvoiceType\x18\x06 \x01(\x0e\x32\x10.BI.Invoice.Type\x1a\x36\n\x04\x43ost\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x01\x12\x1e\n\x08\x63urrency\x18\x02 \x01(\x0e\x32\x0c.BI.Currency\"a\n\x04Type\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03NEW\x10\x01\x12\x0b\n\x07RENEWAL\x10\x02\x12\x0b\n\x07UPGRADE\x10\x03\x12\x0b\n\x07RESTORE\x10\x04\x12\x0f\n\x0b\x41SSOCIATION\x10\x05\x12\x0b\n\x07OVERAGE\x10\x06\"\x1a\n\x18VaultInvoicesListRequest\"?\n\x19VaultInvoicesListResponse\x12\"\n\x08invoices\x18\x01 \x03(\x0b\x32\x10.BI.VaultInvoice\"\x8f\x01\n\x0cVaultInvoice\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x15\n\rinvoiceNumber\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x61teCreated\x18\x03 \x01(\x03\x12\x1f\n\x05total\x18\x04 \x01(\x0b\x32\x10.BI.Invoice.Cost\x12&\n\x0cpurchaseType\x18\x05 \x01(\x0e\x32\x10.BI.Invoice.Type\"/\n\x16InvoiceDownloadRequest\x12\x15\n\rinvoiceNumber\x18\x01 \x01(\t\"9\n\x17InvoiceDownloadResponse\x12\x0c\n\x04link\x18\x01 \x01(\t\x12\x10\n\x08\x66ileName\x18\x02 \x01(\t\"8\n\x1fVaultInvoiceDownloadLinkRequest\x12\x15\n\rinvoiceNumber\x18\x01 \x01(\t\"B\n VaultInvoiceDownloadLinkResponse\x12\x0c\n\x04link\x18\x01 \x01(\t\x12\x10\n\x08\x66ileName\x18\x02 \x01(\t\"<\n\x1dReportingDailySnapshotRequest\x12\r\n\x05month\x18\x01 \x01(\x05\x12\x0c\n\x04year\x18\x02 \x01(\x05\"v\n\x1eReportingDailySnapshotResponse\x12#\n\x07records\x18\x01 \x03(\x0b\x32\x12.BI.SnapshotRecord\x12/\n\rmcEnterprises\x18\x02 \x03(\x0b\x32\x18.BI.SnapshotMcEnterprise\"\xd7\x01\n\x0eSnapshotRecord\x12\x0c\n\x04\x64\x61te\x18\x01 \x01(\x03\x12\x16\n\x0emcEnterpriseId\x18\x02 \x01(\x05\x12\x17\n\x0fmaxLicenseCount\x18\x04 \x01(\x05\x12\x19\n\x11maxFilePlanTypeId\x18\x05 \x01(\x05\x12\x15\n\rmaxBasePlanId\x18\x06 \x01(\x05\x12(\n\x06\x61\x64\x64ons\x18\x07 \x03(\x0b\x32\x18.BI.SnapshotRecord.Addon\x1a*\n\x05\x41\x64\x64on\x12\x12\n\nmaxAddonId\x18\x01 \x01(\x05\x12\r\n\x05units\x18\x02 \x01(\x03\"0\n\x14SnapshotMcEnterprise\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04name\x18\x02 \x01(\t\"\x16\n\x14MappingAddonsRequest\"\\\n\x15MappingAddonsResponse\x12\x1f\n\x06\x61\x64\x64ons\x18\x01 \x03(\x0b\x32\x0f.BI.MappingItem\x12\"\n\tfilePlans\x18\x02 \x03(\x0b\x32\x0f.BI.MappingItem\"\'\n\x0bMappingItem\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04name\x18\x02 \x01(\t\"1\n\x1aGradientValidateKeyRequest\x12\x13\n\x0bgradientKey\x18\x01 \x01(\t\"?\n\x1bGradientValidateKeyResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"D\n\x13GradientSaveRequest\x12\x13\n\x0bgradientKey\x18\x01 \x01(\t\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\"g\n\x14GradientSaveResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12-\n\x06status\x18\x02 \x01(\x0e\x32\x1d.BI.GradientIntegrationStatus\x12\x0f\n\x07message\x18\x03 \x01(\t\"1\n\x15GradientRemoveRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\":\n\x16GradientRemoveResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"/\n\x13GradientSyncRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\"g\n\x14GradientSyncResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12-\n\x06status\x18\x02 \x01(\x0e\x32\x1d.BI.GradientIntegrationStatus\x12\x0f\n\x07message\x18\x03 \x01(\t\"N\n\'NetPromoterScoreSurveySubmissionRequest\x12\x14\n\x0csurvey_score\x18\x01 \x01(\x05\x12\r\n\x05notes\x18\x02 \x01(\t\"*\n(NetPromoterScoreSurveySubmissionResponse\"&\n$NetPromoterScorePopupScheduleRequest\";\n%NetPromoterScorePopupScheduleResponse\x12\x12\n\nshow_popup\x18\x01 \x01(\x08\"\'\n%NetPromoterScorePopupDismissalRequest\"(\n&NetPromoterScorePopupDismissalResponse\"-\n\x11KCMLicenseRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\"%\n\x12KCMLicenseResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x84\x01\n\x0c\x45ventRequest\x12 \n\teventType\x18\x01 \x01(\x0e\x32\r.BI.EventType\x12\x12\n\neventValue\x18\x02 \x01(\t\x12\x11\n\teventTime\x18\x03 \x01(\x03\x12+\n\nattributes\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\"0\n\rEventsRequest\x12\x1f\n\x05\x65vent\x18\x01 \x03(\x0b\x32\x10.BI.EventRequest\"\x87\x01\n\x16\x43ustomerCaptureRequest\x12\x0f\n\x07pageUrl\x18\x01 \x01(\t\x12\x0c\n\x04tree\x18\x02 \x01(\t\x12\x0c\n\x04hash\x18\x03 \x01(\t\x12\r\n\x05image\x18\x04 \x01(\t\x12\x14\n\x0cpageLoadTime\x18\x05 \x01(\t\x12\r\n\x05keyId\x18\x06 \x01(\t\x12\x0c\n\x04test\x18\x07 \x01(\x08\"\x19\n\x17\x43ustomerCaptureResponse*M\n\x08\x43urrency\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03USD\x10\x01\x12\x07\n\x03GBP\x10\x02\x12\x07\n\x03JPY\x10\x03\x12\x07\n\x03\x45UR\x10\x04\x12\x07\n\x03\x41UD\x10\x05\x12\x07\n\x03\x43\x41\x44\x10\x06*S\n\x19GradientIntegrationStatus\x12\x10\n\x0cNOTCONNECTED\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\r\n\tCONNECTED\x10\x02\x12\x08\n\x04NONE\x10\x03*\xb9\x01\n\tEventType\x12\x1f\n\x1bUNKNOWN_TRACKING_EVENT_TYPE\x10\x00\x12\x1c\n\x18TRACKING_POPUP_DISPLAYED\x10\x01\x12\x1b\n\x17TRACKING_POPUP_ACCEPTED\x10\x02\x12\x1c\n\x18TRACKING_POPUP_DISMISSED\x10\x03\x12\x17\n\x13TRACKING_POPUP_PAID\x10\x04\x12\x19\n\x15TRACKING_PUSH_CLICKED\x10\x05\x42\x1e\n\x18\x63om.keepersecurity.protoB\x02\x42Ib\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x08\x42I.proto\x12\x02\x42I\x1a\x1cgoogle/protobuf/struct.proto\"f\n\x1bValidateSessionTokenRequest\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x01 \x01(\x0c\x12\x1c\n\x14returnMcEnterpiseIds\x18\x02 \x01(\x08\x12\n\n\x02ip\x18\x03 \x01(\t\"\xda\x02\n\x1cValidateSessionTokenResponse\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x0e\n\x06userId\x18\x02 \x01(\x05\x12\x18\n\x10\x65nterpriseUserId\x18\x03 \x01(\x03\x12\x37\n\x06status\x18\x04 \x01(\x0e\x32\'.BI.ValidateSessionTokenResponse.Status\x12\x15\n\rstatusMessage\x18\x05 \x01(\t\x12\x17\n\x0fmcEnterpriseIds\x18\x06 \x03(\x05\x12\x18\n\x10hasMSPPermission\x18\x07 \x01(\x08\x12\x1e\n\x16\x64\x65letedMcEnterpriseIds\x18\x08 \x03(\x05\"[\n\x06Status\x12\t\n\x05VALID\x10\x00\x12\r\n\tNOT_VALID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x12\x0e\n\nIP_BLOCKED\x10\x03\x12\x1a\n\x16INVALID_CLIENT_VERSION\x10\x04\"\x1b\n\x19SubscriptionStatusRequest\"\xfe\x03\n\x1aSubscriptionStatusResponse\x12$\n\x0b\x61utoRenewal\x18\x01 \x01(\x0b\x32\x0f.BI.AutoRenewal\x12/\n\x14\x63urrentPaymentMethod\x18\x02 \x01(\x0b\x32\x11.BI.PaymentMethod\x12\x14\n\x0c\x63heckoutLink\x18\x03 \x01(\t\x12\x19\n\x11licenseCreateDate\x18\x04 \x01(\x03\x12\x15\n\risDistributor\x18\x05 \x01(\x08\x12\x13\n\x0bisLegacyMsp\x18\x06 \x01(\x08\x12&\n\x0clicenseStats\x18\x08 \x03(\x0b\x32\x10.BI.LicenseStats\x12\x35\n\x0egradientStatus\x18\t \x01(\x0e\x32\x1d.BI.GradientIntegrationStatus\x12\x17\n\x0fhideTrialBanner\x18\n \x01(\x08\x12\x1c\n\x14gradientLastSyncDate\x18\x0b \x01(\t\x12\x1c\n\x14gradientNextSyncDate\x18\x0c \x01(\t\x12 \n\x18isGradientMappingPending\x18\r \x01(\x08\x12\x1b\n\x03nhi\x18\x0e \x01(\x0b\x32\x0e.BI.NhiBilling\x12\x1c\n\x14\x66reeKsmApiCallsCount\x18\x0f \x01(\x05\x12\x1b\n\x03ksm\x18\x10 \x01(\x0b\x32\x0e.BI.KsmBilling\"\x95\x01\n\nKsmBilling\x12\x1d\n\x15\x62illingStartTimestamp\x18\x01 \x01(\x03\x12\x1b\n\x13\x62illingEndTimestamp\x18\x02 \x01(\x03\x12\x15\n\rcurrentTierId\x18\x03 \x01(\x05\x12\x18\n\x10\x65nterpriseBlocks\x18\x04 \x01(\x05\x12\x1a\n\x12\x63urrentTierCeiling\x18\x05 \x01(\x05\"\xc3\x01\n\nNhiBilling\x12\x1d\n\x15\x62illingStartTimestamp\x18\x01 \x01(\x03\x12\x1b\n\x13\x62illingEndTimestamp\x18\x02 \x01(\x03\x12\x15\n\rcurrentTierId\x18\x03 \x01(\x05\x12\x18\n\x10\x65nterpriseBlocks\x18\x04 \x01(\x05\x12\x1a\n\x12\x63urrentTierCeiling\x18\x05 \x01(\x05\x12,\n\x0e\x62illingPeriods\x18\x06 \x03(\x0b\x32\x14.BI.NhiBillingPeriod\"@\n\x10NhiBillingPeriod\x12\x16\n\x0estartTimestamp\x18\x01 \x01(\x03\x12\x14\n\x0c\x65ndTimestamp\x18\x02 \x01(\x03\"\x97\x02\n\x0cLicenseStats\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.BI.LicenseStats.Type\x12\x11\n\tavailable\x18\x02 \x01(\x05\x12\x0c\n\x04used\x18\x03 \x01(\x05\"\xc0\x01\n\x04Type\x12\x18\n\x14LICENSE_STAT_UNKNOWN\x10\x00\x12\x0c\n\x08MSP_BASE\x10\x01\x12\x0f\n\x0bMC_BUSINESS\x10\x02\x12\x14\n\x10MC_BUSINESS_PLUS\x10\x03\x12\x11\n\rMC_ENTERPRISE\x10\x04\x12\x16\n\x12MC_ENTERPRISE_PLUS\x10\x05\x12\x18\n\x14\x42\x32\x42_BUSINESS_STARTER\x10\x06\x12\x10\n\x0c\x42\x32\x42_BUSINESS\x10\x07\x12\x12\n\x0e\x42\x32\x42_ENTERPRISE\x10\x08\"@\n\x0b\x41utoRenewal\x12\x0e\n\x06nextOn\x18\x01 \x01(\x03\x12\x10\n\x08\x64\x61ysLeft\x18\x02 \x01(\x05\x12\x0f\n\x07isTrial\x18\x03 \x01(\x08\"\x84\x04\n\rPaymentMethod\x12$\n\x04type\x18\x01 \x01(\x0e\x32\x16.BI.PaymentMethod.Type\x12$\n\x04\x63\x61rd\x18\x02 \x01(\x0b\x32\x16.BI.PaymentMethod.Card\x12$\n\x04sepa\x18\x03 \x01(\x0b\x32\x16.BI.PaymentMethod.Sepa\x12(\n\x06paypal\x18\x04 \x01(\x0b\x32\x18.BI.PaymentMethod.Paypal\x12\x15\n\rfailedBilling\x18\x05 \x01(\x08\x12(\n\x06vendor\x18\x06 \x01(\x0b\x32\x18.BI.PaymentMethod.Vendor\x12\x36\n\rpurchaseOrder\x18\x07 \x01(\x0b\x32\x1f.BI.PaymentMethod.PurchaseOrder\x1a$\n\x04\x43\x61rd\x12\r\n\x05last4\x18\x01 \x01(\t\x12\r\n\x05\x62rand\x18\x02 \x01(\t\x1a&\n\x04Sepa\x12\r\n\x05last4\x18\x01 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x02 \x01(\t\x1a\x08\n\x06Paypal\x1a\x16\n\x06Vendor\x12\x0c\n\x04name\x18\x01 \x01(\t\x1a\x1d\n\rPurchaseOrder\x12\x0c\n\x04name\x18\x01 \x01(\t\"O\n\x04Type\x12\x08\n\x04\x43\x41RD\x10\x00\x12\x08\n\x04SEPA\x10\x01\x12\n\n\x06PAYPAL\x10\x02\x12\x08\n\x04NONE\x10\x03\x12\n\n\x06VENDOR\x10\x04\x12\x11\n\rPURCHASEORDER\x10\x05\"\x1f\n\x1dSubscriptionMspPricingRequest\"\\\n\x1eSubscriptionMspPricingResponse\x12\x19\n\x06\x61\x64\x64ons\x18\x02 \x03(\x0b\x32\t.BI.Addon\x12\x1f\n\tfilePlans\x18\x03 \x03(\x0b\x32\x0c.BI.FilePlan\"\x1e\n\x1cSubscriptionMcPricingRequest\"|\n\x1dSubscriptionMcPricingResponse\x12\x1f\n\tbasePlans\x18\x01 \x03(\x0b\x32\x0c.BI.BasePlan\x12\x19\n\x06\x61\x64\x64ons\x18\x02 \x03(\x0b\x32\t.BI.Addon\x12\x1f\n\tfilePlans\x18\x03 \x03(\x0b\x32\x0c.BI.FilePlan\".\n\x08\x42\x61sePlan\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x16\n\x04\x63ost\x18\x02 \x01(\x0b\x32\x08.BI.Cost\"C\n\x05\x41\x64\x64on\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x16\n\x04\x63ost\x18\x02 \x01(\x0b\x32\x08.BI.Cost\x12\x16\n\x0e\x61mountConsumed\x18\x03 \x01(\x03\".\n\x08\x46ilePlan\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x16\n\x04\x63ost\x18\x02 \x01(\x0b\x32\x08.BI.Cost\"\x9a\x02\n\x04\x43ost\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x01\x12%\n\tamountPer\x18\x04 \x01(\x0e\x32\x12.BI.Cost.AmountPer\x12\x1e\n\x08\x63urrency\x18\x05 \x01(\x0e\x32\x0c.BI.Currency\x12\x14\n\x0c\x63ontactSales\x18\x06 \x01(\x08\"\xa4\x01\n\tAmountPer\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05MONTH\x10\x01\x12\x0e\n\nUSER_MONTH\x10\x02\x12\x17\n\x13USER_CONSUMED_MONTH\x10\x03\x12\x12\n\x0e\x45NDPOINT_MONTH\x10\x04\x12\r\n\tUSER_YEAR\x10\x05\x12\x16\n\x12USER_CONSUMED_YEAR\x10\x06\x12\x08\n\x04YEAR\x10\x07\x12\x11\n\rENDPOINT_YEAR\x10\x08\"\\\n\x14InvoiceSearchRequest\x12\x0c\n\x04size\x18\x01 \x01(\x05\x12\x17\n\x0fstartingAfterId\x18\x02 \x01(\x05\x12\x1d\n\x15\x61llInvoicesUnfiltered\x18\x03 \x01(\x08\"6\n\x15InvoiceSearchResponse\x12\x1d\n\x08invoices\x18\x01 \x03(\x0b\x32\x0b.BI.Invoice\"\xbe\x02\n\x07Invoice\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x15\n\rinvoiceNumber\x18\x02 \x01(\t\x12\x13\n\x0binvoiceDate\x18\x03 \x01(\x03\x12\x14\n\x0clicenseCount\x18\x04 \x01(\x05\x12#\n\ttotalCost\x18\x05 \x01(\x0b\x32\x10.BI.Invoice.Cost\x12%\n\x0binvoiceType\x18\x06 \x01(\x0e\x32\x10.BI.Invoice.Type\x1a\x36\n\x04\x43ost\x12\x0e\n\x06\x61mount\x18\x01 \x01(\x01\x12\x1e\n\x08\x63urrency\x18\x02 \x01(\x0e\x32\x0c.BI.Currency\"a\n\x04Type\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03NEW\x10\x01\x12\x0b\n\x07RENEWAL\x10\x02\x12\x0b\n\x07UPGRADE\x10\x03\x12\x0b\n\x07RESTORE\x10\x04\x12\x0f\n\x0b\x41SSOCIATION\x10\x05\x12\x0b\n\x07OVERAGE\x10\x06\"\x1a\n\x18VaultInvoicesListRequest\"?\n\x19VaultInvoicesListResponse\x12\"\n\x08invoices\x18\x01 \x03(\x0b\x32\x10.BI.VaultInvoice\"\x8f\x01\n\x0cVaultInvoice\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x15\n\rinvoiceNumber\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x61teCreated\x18\x03 \x01(\x03\x12\x1f\n\x05total\x18\x04 \x01(\x0b\x32\x10.BI.Invoice.Cost\x12&\n\x0cpurchaseType\x18\x05 \x01(\x0e\x32\x10.BI.Invoice.Type\"/\n\x16InvoiceDownloadRequest\x12\x15\n\rinvoiceNumber\x18\x01 \x01(\t\"9\n\x17InvoiceDownloadResponse\x12\x0c\n\x04link\x18\x01 \x01(\t\x12\x10\n\x08\x66ileName\x18\x02 \x01(\t\"8\n\x1fVaultInvoiceDownloadLinkRequest\x12\x15\n\rinvoiceNumber\x18\x01 \x01(\t\"B\n VaultInvoiceDownloadLinkResponse\x12\x0c\n\x04link\x18\x01 \x01(\t\x12\x10\n\x08\x66ileName\x18\x02 \x01(\t\"<\n\x1dReportingDailySnapshotRequest\x12\r\n\x05month\x18\x01 \x01(\x05\x12\x0c\n\x04year\x18\x02 \x01(\x05\"v\n\x1eReportingDailySnapshotResponse\x12#\n\x07records\x18\x01 \x03(\x0b\x32\x12.BI.SnapshotRecord\x12/\n\rmcEnterprises\x18\x02 \x03(\x0b\x32\x18.BI.SnapshotMcEnterprise\"\xd7\x01\n\x0eSnapshotRecord\x12\x0c\n\x04\x64\x61te\x18\x01 \x01(\x03\x12\x16\n\x0emcEnterpriseId\x18\x02 \x01(\x05\x12\x17\n\x0fmaxLicenseCount\x18\x04 \x01(\x05\x12\x19\n\x11maxFilePlanTypeId\x18\x05 \x01(\x05\x12\x15\n\rmaxBasePlanId\x18\x06 \x01(\x05\x12(\n\x06\x61\x64\x64ons\x18\x07 \x03(\x0b\x32\x18.BI.SnapshotRecord.Addon\x1a*\n\x05\x41\x64\x64on\x12\x12\n\nmaxAddonId\x18\x01 \x01(\x05\x12\r\n\x05units\x18\x02 \x01(\x03\"0\n\x14SnapshotMcEnterprise\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04name\x18\x02 \x01(\t\"\x16\n\x14MappingAddonsRequest\"\\\n\x15MappingAddonsResponse\x12\x1f\n\x06\x61\x64\x64ons\x18\x01 \x03(\x0b\x32\x0f.BI.MappingItem\x12\"\n\tfilePlans\x18\x02 \x03(\x0b\x32\x0f.BI.MappingItem\"\'\n\x0bMappingItem\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04name\x18\x02 \x01(\t\"1\n\x1aGradientValidateKeyRequest\x12\x13\n\x0bgradientKey\x18\x01 \x01(\t\"?\n\x1bGradientValidateKeyResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"D\n\x13GradientSaveRequest\x12\x13\n\x0bgradientKey\x18\x01 \x01(\t\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\"g\n\x14GradientSaveResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12-\n\x06status\x18\x02 \x01(\x0e\x32\x1d.BI.GradientIntegrationStatus\x12\x0f\n\x07message\x18\x03 \x01(\t\"1\n\x15GradientRemoveRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\":\n\x16GradientRemoveResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"/\n\x13GradientSyncRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\"g\n\x14GradientSyncResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12-\n\x06status\x18\x02 \x01(\x0e\x32\x1d.BI.GradientIntegrationStatus\x12\x0f\n\x07message\x18\x03 \x01(\t\"N\n\'NetPromoterScoreSurveySubmissionRequest\x12\x14\n\x0csurvey_score\x18\x01 \x01(\x05\x12\r\n\x05notes\x18\x02 \x01(\t\"*\n(NetPromoterScoreSurveySubmissionResponse\"&\n$NetPromoterScorePopupScheduleRequest\";\n%NetPromoterScorePopupScheduleResponse\x12\x12\n\nshow_popup\x18\x01 \x01(\x08\"\'\n%NetPromoterScorePopupDismissalRequest\"(\n&NetPromoterScorePopupDismissalResponse\"-\n\x11KCMLicenseRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\"%\n\x12KCMLicenseResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x84\x01\n\x0c\x45ventRequest\x12 \n\teventType\x18\x01 \x01(\x0e\x32\r.BI.EventType\x12\x12\n\neventValue\x18\x02 \x01(\t\x12\x11\n\teventTime\x18\x03 \x01(\x03\x12+\n\nattributes\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\"0\n\rEventsRequest\x12\x1f\n\x05\x65vent\x18\x01 \x03(\x0b\x32\x10.BI.EventRequest\".\n\rEventResponse\x12\r\n\x05index\x18\x01 \x01(\x05\x12\x0e\n\x06status\x18\x02 \x01(\x08\"5\n\x0e\x45ventsResponse\x12#\n\x08response\x18\x01 \x03(\x0b\x32\x11.BI.EventResponse\"\xa9\x01\n\x16\x43ustomerCaptureRequest\x12\x0f\n\x07pageUrl\x18\x01 \x01(\t\x12\x0c\n\x04tree\x18\x02 \x01(\t\x12\x0c\n\x04hash\x18\x03 \x01(\t\x12\r\n\x05image\x18\x04 \x01(\t\x12\x14\n\x0cpageLoadTime\x18\x05 \x01(\t\x12\r\n\x05keyId\x18\x06 \x01(\t\x12\x0c\n\x04test\x18\x07 \x01(\x08\x12\x11\n\tissueType\x18\x08 \x01(\t\x12\r\n\x05notes\x18\t \x01(\t\"\x19\n\x17\x43ustomerCaptureResponse\"|\n\x05\x45rror\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\t\x12%\n\x06\x65xtras\x18\x03 \x03(\x0b\x32\x15.BI.Error.ExtrasEntry\x1a-\n\x0b\x45xtrasEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x96\x01\n\rQuotePurchase\x12\x12\n\nquoteTotal\x18\x01 \x01(\x01\x12\x13\n\x0bincludedTax\x18\x02 \x01(\x08\x12\x1b\n\x13includedOtherAddons\x18\x03 \x01(\x08\x12\x11\n\ttaxAmount\x18\x04 \x01(\x01\x12\x10\n\x08taxLabel\x18\x05 \x01(\t\x12\x1a\n\x12purchaseIdentifier\x18\x06 \x01(\t\"k\n\x0fPurchaseOptions\x12\x16\n\tinConsole\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x1d\n\x10\x65xternalCheckout\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\x0c\n\n_inConsoleB\x13\n\x11_externalCheckout\"\xe5\x06\n\x14\x41\x64\x64onPurchaseOptions\x12)\n\x07storage\x18\x01 \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x00\x88\x01\x01\x12\'\n\x05\x61udit\x18\x02 \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x01\x88\x01\x01\x12-\n\x0b\x62reachwatch\x18\x03 \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x02\x88\x01\x01\x12&\n\x04\x63hat\x18\x04 \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x03\x88\x01\x01\x12,\n\ncompliance\x18\x05 \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x04\x88\x01\x01\x12<\n\x1aprofessionalServicesSilver\x18\x06 \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x05\x88\x01\x01\x12>\n\x1cprofessionalServicesPlatinum\x18\x07 \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x06\x88\x01\x01\x12%\n\x03pam\x18\x08 \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x07\x88\x01\x01\x12%\n\x03\x65pm\x18\t \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x08\x88\x01\x01\x12\x30\n\x0esecretsManager\x18\n \x01(\x0b\x32\x13.BI.PurchaseOptionsH\t\x88\x01\x01\x12\x33\n\x11\x63onnectionManager\x18\x0b \x01(\x0b\x32\x13.BI.PurchaseOptionsH\n\x88\x01\x01\x12\x38\n\x16remoteBrowserIsolation\x18\x0c \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x0b\x88\x01\x01\x12)\n\x07nhiTier\x18\r \x01(\x0b\x32\x13.BI.PurchaseOptionsH\x0c\x88\x01\x01\x42\n\n\x08_storageB\x08\n\x06_auditB\x0e\n\x0c_breachwatchB\x07\n\x05_chatB\r\n\x0b_complianceB\x1d\n\x1b_professionalServicesSilverB\x1f\n\x1d_professionalServicesPlatinumB\x06\n\x04_pamB\x06\n\x04_epmB\x11\n\x0f_secretsManagerB\x14\n\x12_connectionManagerB\x19\n\x17_remoteBrowserIsolationB\n\n\x08_nhiTier\"\x8f\x01\n\x18\x41vailablePurchaseOptions\x12%\n\x08\x62\x61sePlan\x18\x01 \x01(\x0b\x32\x13.BI.PurchaseOptions\x12\"\n\x05users\x18\x02 \x01(\x0b\x32\x13.BI.PurchaseOptions\x12(\n\x06\x61\x64\x64ons\x18\x03 \x01(\x0b\x32\x18.BI.AddonPurchaseOptions\"\x1d\n\x1bUpgradeLicenseStatusRequest\"\x91\x01\n\x1cUpgradeLicenseStatusResponse\x12 \n\x18\x61llowPurchaseFromConsole\x18\x01 \x01(\x08\x12\x35\n\x0fpurchaseOptions\x18\x02 \x01(\x0b\x32\x1c.BI.AvailablePurchaseOptions\x12\x18\n\x05\x65rror\x18\x03 \x01(\x0b\x32\t.BI.Error\"r\n\"UpgradeLicenseQuotePurchaseRequest\x12,\n\x0bproductType\x18\x01 \x01(\x0e\x32\x17.BI.PurchaseProductType\x12\x10\n\x08quantity\x18\x02 \x01(\x05\x12\x0c\n\x04tier\x18\x03 \x01(\x05\"\x93\x01\n#UpgradeLicenseQuotePurchaseResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12(\n\rquotePurchase\x18\x02 \x01(\x0b\x32\x11.BI.QuotePurchase\x12\x17\n\x0fviewSummaryLink\x18\x03 \x01(\t\x12\x18\n\x05\x65rror\x18\x04 \x01(\x0b\x32\t.BI.Error\"\x9f\x01\n%UpgradeLicenseCompletePurchaseRequest\x12,\n\x0bproductType\x18\x01 \x01(\x0e\x32\x17.BI.PurchaseProductType\x12\x10\n\x08quantity\x18\x02 \x01(\x05\x12(\n\rquotePurchase\x18\x03 \x01(\x0b\x32\x11.BI.QuotePurchase\x12\x0c\n\x04tier\x18\x04 \x01(\x05\"\x94\x01\n&UpgradeLicenseCompletePurchaseResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x15\n\rinvoiceNumber\x18\x02 \x01(\t\x12\x18\n\x05\x65rror\x18\x03 \x01(\x0b\x32\t.BI.Error\x12(\n\rquotePurchase\x18\x04 \x01(\x0b\x32\x11.BI.QuotePurchase\"\xd5\x01\n\x12\x45nterpriseBasePlan\x12I\n\x0f\x62\x61seplanVersion\x18\x01 \x01(\x0e\x32\x30.BI.EnterpriseBasePlan.EnterpriseBasePlanVersion\x12\x16\n\x04\x63ost\x18\x02 \x01(\x0b\x32\x08.BI.Cost\"\\\n\x19\x45nterpriseBasePlanVersion\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x14\n\x10\x42USINESS_STARTER\x10\x01\x12\x0c\n\x08\x42USINESS\x10\x02\x12\x0e\n\nENTERPRISE\x10\x03\"&\n$SubscriptionEnterprisePricingRequest\"n\n\x0bNhiTierPlan\x12\x0e\n\x06tierId\x18\x01 \x01(\x05\x12\x12\n\nnhiCeiling\x18\x02 \x01(\x05\x12\x16\n\x04\x63ost\x18\x03 \x01(\x0b\x32\x08.BI.Cost\x12\x11\n\tproductId\x18\x04 \x01(\x05\x12\x10\n\x08nhiFloor\x18\x05 \x01(\x05\"\xb5\x01\n%SubscriptionEnterprisePricingResponse\x12)\n\tbasePlans\x18\x01 \x03(\x0b\x32\x16.BI.EnterpriseBasePlan\x12\x19\n\x06\x61\x64\x64ons\x18\x02 \x03(\x0b\x32\t.BI.Addon\x12\x1f\n\tfilePlans\x18\x03 \x03(\x0b\x32\x0c.BI.FilePlan\x12%\n\x0cnhiTierPlans\x18\x04 \x03(\x0b\x32\x0f.BI.NhiTierPlan\"J\n\x18SingularDeviceIdentifier\x12\n\n\x02id\x18\x01 \x01(\t\x12\"\n\x06idType\x18\x02 \x01(\x0e\x32\x12.BI.IdentifierType\"\xac\x01\n\x12SingularSharedData\x12\x10\n\x08platform\x18\x01 \x01(\t\x12\x11\n\tosVersion\x18\x02 \x01(\t\x12\x0c\n\x04make\x18\x03 \x01(\t\x12\r\n\x05model\x18\x04 \x01(\t\x12\x0e\n\x06locale\x18\x05 \x01(\t\x12\r\n\x05\x62uild\x18\x06 \x01(\t\x12\x15\n\rappIdentifier\x18\x07 \x01(\t\x12\x1e\n\x16\x61ttAuthorizationStatus\x18\x08 \x01(\x05\"\x8b\x03\n\x16SingularSessionRequest\x12\x37\n\x11\x64\x65viceIdentifiers\x18\x01 \x03(\x0b\x32\x1c.BI.SingularDeviceIdentifier\x12*\n\nsharedData\x18\x02 \x01(\x0b\x32\x16.BI.SingularSharedData\x12\x1a\n\x12\x61pplicationVersion\x18\x03 \x01(\t\x12\x0f\n\x07install\x18\x04 \x01(\x08\x12\x13\n\x0binstallTime\x18\x05 \x01(\x03\x12\x12\n\nupdateTime\x18\x06 \x01(\x03\x12\x15\n\rinstallSource\x18\x07 \x01(\t\x12\x16\n\x0einstallReceipt\x18\x08 \x01(\t\x12\x0f\n\x07openuri\x18\t \x01(\t\x12\x12\n\nddlEnabled\x18\n \x01(\x08\x12#\n\x1bsingularLinkResolveRequired\x18\x0b \x01(\x08\x12\x12\n\ninstallRef\x18\x0c \x01(\t\x12\x0f\n\x07metaRef\x18\r \x01(\t\x12\x18\n\x10\x61ttributionToken\x18\x0e \x01(\t\"\x8e\x01\n\x14SingularEventRequest\x12\x37\n\x11\x64\x65viceIdentifiers\x18\x01 \x03(\x0b\x32\x1c.BI.SingularDeviceIdentifier\x12*\n\nsharedData\x18\x02 \x01(\x0b\x32\x16.BI.SingularSharedData\x12\x11\n\teventName\x18\x03 \x01(\t\"-\n\x15\x41\x63tivePamCountRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\"*\n\x16\x41\x63tivePamCountResponse\x12\x10\n\x08pamCount\x18\x01 \x01(\x05\"P\n\x14NhiEnterpriseRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x11\n\tstartTime\x18\x02 \x01(\x03\x12\x0f\n\x07\x65ndTime\x18\x03 \x01(\x03\"\x89\x01\n\x11NhiMetricsRequest\x12\x19\n\renterpriseIds\x18\x01 \x03(\x05\x42\x02\x18\x01\x12\x15\n\tstartTime\x18\x02 \x01(\x03\x42\x02\x18\x01\x12\x13\n\x07\x65ndTime\x18\x03 \x01(\x03\x42\x02\x18\x01\x12-\n\x0b\x65nterprises\x18\x04 \x03(\x0b\x32\x18.BI.NhiEnterpriseRequest*M\n\x08\x43urrency\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03USD\x10\x01\x12\x07\n\x03GBP\x10\x02\x12\x07\n\x03JPY\x10\x03\x12\x07\n\x03\x45UR\x10\x04\x12\x07\n\x03\x41UD\x10\x05\x12\x07\n\x03\x43\x41\x44\x10\x06*S\n\x19GradientIntegrationStatus\x12\x10\n\x0cNOTCONNECTED\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\r\n\tCONNECTED\x10\x02\x12\x08\n\x04NONE\x10\x03*\xdf\x01\n\tEventType\x12\x1f\n\x1bUNKNOWN_TRACKING_EVENT_TYPE\x10\x00\x12\x1c\n\x18TRACKING_POPUP_DISPLAYED\x10\x01\x12\x1b\n\x17TRACKING_POPUP_ACCEPTED\x10\x02\x12\x1c\n\x18TRACKING_POPUP_DISMISSED\x10\x03\x12\x17\n\x13TRACKING_POPUP_PAID\x10\x04\x12\x19\n\x15TRACKING_PUSH_CLICKED\x10\x05\x12\x12\n\x0e\x43ONSOLE_ACTION\x10\x06\x12\x10\n\x0cVAULT_ACTION\x10\x07*\xe1\x01\n\x13PurchaseProductType\x12\x17\n\x13upgradeToEnterprise\x10\x00\x12\x0c\n\x08\x61\x64\x64Users\x10\x01\x12\x0e\n\naddStorage\x10\x02\x12\x0c\n\x08\x61\x64\x64\x41udit\x10\x03\x12\x12\n\x0e\x61\x64\x64\x42reachWatch\x10\x04\x12\x11\n\raddCompliance\x10\x05\x12\x0b\n\x07\x61\x64\x64\x43hat\x10\x06\x12\n\n\x06\x61\x64\x64PAM\x10\x07\x12\x14\n\x10\x61\x64\x64SilverSupport\x10\x08\x12\x16\n\x12\x61\x64\x64PlatinumSupport\x10\t\x12\x0b\n\x07\x61\x64\x64KEPM\x10\n\x12\n\n\x06\x61\x64\x64Nhi\x10\x0b*\xe0\x01\n\x0eIdentifierType\x12\x1b\n\x17UNKNOWN_IDENTIFIER_TYPE\x10\x00\x12\n\n\x06IOS_ID\x10\x01\x12\x1a\n\x16\x41NDROID_GOOGLE_PLAY_ID\x10\x02\x12\x16\n\x12\x41NDROID_APP_SET_ID\x10\x03\x12\x0e\n\nANDROID_ID\x10\x04\x12\x19\n\x15\x41MAZON_ADVERTISING_ID\x10\x05\x12\x17\n\x13OPEN_ADVERTISING_ID\x10\x06\x12\x16\n\x12SINGULAR_DEVICE_ID\x10\x07\x12\x15\n\x11\x43LIENT_DEFINED_ID\x10\x08\x42\x1e\n\x18\x63om.keepersecurity.protoB\x02\x42Ib\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'BI_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\002BI' - _globals['_CURRENCY']._serialized_start=5253 - _globals['_CURRENCY']._serialized_end=5330 - _globals['_GRADIENTINTEGRATIONSTATUS']._serialized_start=5332 - _globals['_GRADIENTINTEGRATIONSTATUS']._serialized_end=5415 - _globals['_EVENTTYPE']._serialized_start=5418 - _globals['_EVENTTYPE']._serialized_end=5603 + _globals['_ERROR_EXTRASENTRY']._loaded_options = None + _globals['_ERROR_EXTRASENTRY']._serialized_options = b'8\001' + _globals['_NHIMETRICSREQUEST'].fields_by_name['enterpriseIds']._loaded_options = None + _globals['_NHIMETRICSREQUEST'].fields_by_name['enterpriseIds']._serialized_options = b'\030\001' + _globals['_NHIMETRICSREQUEST'].fields_by_name['startTime']._loaded_options = None + _globals['_NHIMETRICSREQUEST'].fields_by_name['startTime']._serialized_options = b'\030\001' + _globals['_NHIMETRICSREQUEST'].fields_by_name['endTime']._loaded_options = None + _globals['_NHIMETRICSREQUEST'].fields_by_name['endTime']._serialized_options = b'\030\001' + _globals['_CURRENCY']._serialized_start=9872 + _globals['_CURRENCY']._serialized_end=9949 + _globals['_GRADIENTINTEGRATIONSTATUS']._serialized_start=9951 + _globals['_GRADIENTINTEGRATIONSTATUS']._serialized_end=10034 + _globals['_EVENTTYPE']._serialized_start=10037 + _globals['_EVENTTYPE']._serialized_end=10260 + _globals['_PURCHASEPRODUCTTYPE']._serialized_start=10263 + _globals['_PURCHASEPRODUCTTYPE']._serialized_end=10488 + _globals['_IDENTIFIERTYPE']._serialized_start=10491 + _globals['_IDENTIFIERTYPE']._serialized_end=10715 _globals['_VALIDATESESSIONTOKENREQUEST']._serialized_start=46 _globals['_VALIDATESESSIONTOKENREQUEST']._serialized_end=148 _globals['_VALIDATESESSIONTOKENRESPONSE']._serialized_start=151 @@ -37,123 +60,183 @@ _globals['_SUBSCRIPTIONSTATUSREQUEST']._serialized_start=499 _globals['_SUBSCRIPTIONSTATUSREQUEST']._serialized_end=526 _globals['_SUBSCRIPTIONSTATUSRESPONSE']._serialized_start=529 - _globals['_SUBSCRIPTIONSTATUSRESPONSE']._serialized_end=951 - _globals['_LICENSESTATS']._serialized_start=954 - _globals['_LICENSESTATS']._serialized_end=1169 - _globals['_LICENSESTATS_TYPE']._serialized_start=1041 - _globals['_LICENSESTATS_TYPE']._serialized_end=1169 - _globals['_AUTORENEWAL']._serialized_start=1171 - _globals['_AUTORENEWAL']._serialized_end=1235 - _globals['_PAYMENTMETHOD']._serialized_start=1238 - _globals['_PAYMENTMETHOD']._serialized_end=1754 - _globals['_PAYMENTMETHOD_CARD']._serialized_start=1532 - _globals['_PAYMENTMETHOD_CARD']._serialized_end=1568 - _globals['_PAYMENTMETHOD_SEPA']._serialized_start=1570 - _globals['_PAYMENTMETHOD_SEPA']._serialized_end=1608 - _globals['_PAYMENTMETHOD_PAYPAL']._serialized_start=1610 - _globals['_PAYMENTMETHOD_PAYPAL']._serialized_end=1618 - _globals['_PAYMENTMETHOD_VENDOR']._serialized_start=1620 - _globals['_PAYMENTMETHOD_VENDOR']._serialized_end=1642 - _globals['_PAYMENTMETHOD_PURCHASEORDER']._serialized_start=1644 - _globals['_PAYMENTMETHOD_PURCHASEORDER']._serialized_end=1673 - _globals['_PAYMENTMETHOD_TYPE']._serialized_start=1675 - _globals['_PAYMENTMETHOD_TYPE']._serialized_end=1754 - _globals['_SUBSCRIPTIONMSPPRICINGREQUEST']._serialized_start=1756 - _globals['_SUBSCRIPTIONMSPPRICINGREQUEST']._serialized_end=1787 - _globals['_SUBSCRIPTIONMSPPRICINGRESPONSE']._serialized_start=1789 - _globals['_SUBSCRIPTIONMSPPRICINGRESPONSE']._serialized_end=1881 - _globals['_SUBSCRIPTIONMCPRICINGREQUEST']._serialized_start=1883 - _globals['_SUBSCRIPTIONMCPRICINGREQUEST']._serialized_end=1913 - _globals['_SUBSCRIPTIONMCPRICINGRESPONSE']._serialized_start=1915 - _globals['_SUBSCRIPTIONMCPRICINGRESPONSE']._serialized_end=2039 - _globals['_BASEPLAN']._serialized_start=2041 - _globals['_BASEPLAN']._serialized_end=2087 - _globals['_ADDON']._serialized_start=2089 - _globals['_ADDON']._serialized_end=2156 - _globals['_FILEPLAN']._serialized_start=2158 - _globals['_FILEPLAN']._serialized_end=2204 - _globals['_COST']._serialized_start=2207 - _globals['_COST']._serialized_end=2398 - _globals['_COST_AMOUNTPER']._serialized_start=2302 - _globals['_COST_AMOUNTPER']._serialized_end=2398 - _globals['_INVOICESEARCHREQUEST']._serialized_start=2400 - _globals['_INVOICESEARCHREQUEST']._serialized_end=2492 - _globals['_INVOICESEARCHRESPONSE']._serialized_start=2494 - _globals['_INVOICESEARCHRESPONSE']._serialized_end=2548 - _globals['_INVOICE']._serialized_start=2551 - _globals['_INVOICE']._serialized_end=2869 - _globals['_INVOICE_COST']._serialized_start=2716 - _globals['_INVOICE_COST']._serialized_end=2770 - _globals['_INVOICE_TYPE']._serialized_start=2772 - _globals['_INVOICE_TYPE']._serialized_end=2869 - _globals['_VAULTINVOICESLISTREQUEST']._serialized_start=2871 - _globals['_VAULTINVOICESLISTREQUEST']._serialized_end=2897 - _globals['_VAULTINVOICESLISTRESPONSE']._serialized_start=2899 - _globals['_VAULTINVOICESLISTRESPONSE']._serialized_end=2962 - _globals['_VAULTINVOICE']._serialized_start=2965 - _globals['_VAULTINVOICE']._serialized_end=3108 - _globals['_INVOICEDOWNLOADREQUEST']._serialized_start=3110 - _globals['_INVOICEDOWNLOADREQUEST']._serialized_end=3157 - _globals['_INVOICEDOWNLOADRESPONSE']._serialized_start=3159 - _globals['_INVOICEDOWNLOADRESPONSE']._serialized_end=3216 - _globals['_VAULTINVOICEDOWNLOADLINKREQUEST']._serialized_start=3218 - _globals['_VAULTINVOICEDOWNLOADLINKREQUEST']._serialized_end=3274 - _globals['_VAULTINVOICEDOWNLOADLINKRESPONSE']._serialized_start=3276 - _globals['_VAULTINVOICEDOWNLOADLINKRESPONSE']._serialized_end=3342 - _globals['_REPORTINGDAILYSNAPSHOTREQUEST']._serialized_start=3344 - _globals['_REPORTINGDAILYSNAPSHOTREQUEST']._serialized_end=3404 - _globals['_REPORTINGDAILYSNAPSHOTRESPONSE']._serialized_start=3406 - _globals['_REPORTINGDAILYSNAPSHOTRESPONSE']._serialized_end=3524 - _globals['_SNAPSHOTRECORD']._serialized_start=3527 - _globals['_SNAPSHOTRECORD']._serialized_end=3742 - _globals['_SNAPSHOTRECORD_ADDON']._serialized_start=3700 - _globals['_SNAPSHOTRECORD_ADDON']._serialized_end=3742 - _globals['_SNAPSHOTMCENTERPRISE']._serialized_start=3744 - _globals['_SNAPSHOTMCENTERPRISE']._serialized_end=3792 - _globals['_MAPPINGADDONSREQUEST']._serialized_start=3794 - _globals['_MAPPINGADDONSREQUEST']._serialized_end=3816 - _globals['_MAPPINGADDONSRESPONSE']._serialized_start=3818 - _globals['_MAPPINGADDONSRESPONSE']._serialized_end=3910 - _globals['_MAPPINGITEM']._serialized_start=3912 - _globals['_MAPPINGITEM']._serialized_end=3951 - _globals['_GRADIENTVALIDATEKEYREQUEST']._serialized_start=3953 - _globals['_GRADIENTVALIDATEKEYREQUEST']._serialized_end=4002 - _globals['_GRADIENTVALIDATEKEYRESPONSE']._serialized_start=4004 - _globals['_GRADIENTVALIDATEKEYRESPONSE']._serialized_end=4067 - _globals['_GRADIENTSAVEREQUEST']._serialized_start=4069 - _globals['_GRADIENTSAVEREQUEST']._serialized_end=4137 - _globals['_GRADIENTSAVERESPONSE']._serialized_start=4139 - _globals['_GRADIENTSAVERESPONSE']._serialized_end=4242 - _globals['_GRADIENTREMOVEREQUEST']._serialized_start=4244 - _globals['_GRADIENTREMOVEREQUEST']._serialized_end=4293 - _globals['_GRADIENTREMOVERESPONSE']._serialized_start=4295 - _globals['_GRADIENTREMOVERESPONSE']._serialized_end=4353 - _globals['_GRADIENTSYNCREQUEST']._serialized_start=4355 - _globals['_GRADIENTSYNCREQUEST']._serialized_end=4402 - _globals['_GRADIENTSYNCRESPONSE']._serialized_start=4404 - _globals['_GRADIENTSYNCRESPONSE']._serialized_end=4507 - _globals['_NETPROMOTERSCORESURVEYSUBMISSIONREQUEST']._serialized_start=4509 - _globals['_NETPROMOTERSCORESURVEYSUBMISSIONREQUEST']._serialized_end=4587 - _globals['_NETPROMOTERSCORESURVEYSUBMISSIONRESPONSE']._serialized_start=4589 - _globals['_NETPROMOTERSCORESURVEYSUBMISSIONRESPONSE']._serialized_end=4631 - _globals['_NETPROMOTERSCOREPOPUPSCHEDULEREQUEST']._serialized_start=4633 - _globals['_NETPROMOTERSCOREPOPUPSCHEDULEREQUEST']._serialized_end=4671 - _globals['_NETPROMOTERSCOREPOPUPSCHEDULERESPONSE']._serialized_start=4673 - _globals['_NETPROMOTERSCOREPOPUPSCHEDULERESPONSE']._serialized_end=4732 - _globals['_NETPROMOTERSCOREPOPUPDISMISSALREQUEST']._serialized_start=4734 - _globals['_NETPROMOTERSCOREPOPUPDISMISSALREQUEST']._serialized_end=4773 - _globals['_NETPROMOTERSCOREPOPUPDISMISSALRESPONSE']._serialized_start=4775 - _globals['_NETPROMOTERSCOREPOPUPDISMISSALRESPONSE']._serialized_end=4815 - _globals['_KCMLICENSEREQUEST']._serialized_start=4817 - _globals['_KCMLICENSEREQUEST']._serialized_end=4862 - _globals['_KCMLICENSERESPONSE']._serialized_start=4864 - _globals['_KCMLICENSERESPONSE']._serialized_end=4901 - _globals['_EVENTREQUEST']._serialized_start=4904 - _globals['_EVENTREQUEST']._serialized_end=5036 - _globals['_EVENTSREQUEST']._serialized_start=5038 - _globals['_EVENTSREQUEST']._serialized_end=5086 - _globals['_CUSTOMERCAPTUREREQUEST']._serialized_start=5089 - _globals['_CUSTOMERCAPTUREREQUEST']._serialized_end=5224 - _globals['_CUSTOMERCAPTURERESPONSE']._serialized_start=5226 - _globals['_CUSTOMERCAPTURERESPONSE']._serialized_end=5251 + _globals['_SUBSCRIPTIONSTATUSRESPONSE']._serialized_end=1039 + _globals['_KSMBILLING']._serialized_start=1042 + _globals['_KSMBILLING']._serialized_end=1191 + _globals['_NHIBILLING']._serialized_start=1194 + _globals['_NHIBILLING']._serialized_end=1389 + _globals['_NHIBILLINGPERIOD']._serialized_start=1391 + _globals['_NHIBILLINGPERIOD']._serialized_end=1455 + _globals['_LICENSESTATS']._serialized_start=1458 + _globals['_LICENSESTATS']._serialized_end=1737 + _globals['_LICENSESTATS_TYPE']._serialized_start=1545 + _globals['_LICENSESTATS_TYPE']._serialized_end=1737 + _globals['_AUTORENEWAL']._serialized_start=1739 + _globals['_AUTORENEWAL']._serialized_end=1803 + _globals['_PAYMENTMETHOD']._serialized_start=1806 + _globals['_PAYMENTMETHOD']._serialized_end=2322 + _globals['_PAYMENTMETHOD_CARD']._serialized_start=2100 + _globals['_PAYMENTMETHOD_CARD']._serialized_end=2136 + _globals['_PAYMENTMETHOD_SEPA']._serialized_start=2138 + _globals['_PAYMENTMETHOD_SEPA']._serialized_end=2176 + _globals['_PAYMENTMETHOD_PAYPAL']._serialized_start=2178 + _globals['_PAYMENTMETHOD_PAYPAL']._serialized_end=2186 + _globals['_PAYMENTMETHOD_VENDOR']._serialized_start=2188 + _globals['_PAYMENTMETHOD_VENDOR']._serialized_end=2210 + _globals['_PAYMENTMETHOD_PURCHASEORDER']._serialized_start=2212 + _globals['_PAYMENTMETHOD_PURCHASEORDER']._serialized_end=2241 + _globals['_PAYMENTMETHOD_TYPE']._serialized_start=2243 + _globals['_PAYMENTMETHOD_TYPE']._serialized_end=2322 + _globals['_SUBSCRIPTIONMSPPRICINGREQUEST']._serialized_start=2324 + _globals['_SUBSCRIPTIONMSPPRICINGREQUEST']._serialized_end=2355 + _globals['_SUBSCRIPTIONMSPPRICINGRESPONSE']._serialized_start=2357 + _globals['_SUBSCRIPTIONMSPPRICINGRESPONSE']._serialized_end=2449 + _globals['_SUBSCRIPTIONMCPRICINGREQUEST']._serialized_start=2451 + _globals['_SUBSCRIPTIONMCPRICINGREQUEST']._serialized_end=2481 + _globals['_SUBSCRIPTIONMCPRICINGRESPONSE']._serialized_start=2483 + _globals['_SUBSCRIPTIONMCPRICINGRESPONSE']._serialized_end=2607 + _globals['_BASEPLAN']._serialized_start=2609 + _globals['_BASEPLAN']._serialized_end=2655 + _globals['_ADDON']._serialized_start=2657 + _globals['_ADDON']._serialized_end=2724 + _globals['_FILEPLAN']._serialized_start=2726 + _globals['_FILEPLAN']._serialized_end=2772 + _globals['_COST']._serialized_start=2775 + _globals['_COST']._serialized_end=3057 + _globals['_COST_AMOUNTPER']._serialized_start=2893 + _globals['_COST_AMOUNTPER']._serialized_end=3057 + _globals['_INVOICESEARCHREQUEST']._serialized_start=3059 + _globals['_INVOICESEARCHREQUEST']._serialized_end=3151 + _globals['_INVOICESEARCHRESPONSE']._serialized_start=3153 + _globals['_INVOICESEARCHRESPONSE']._serialized_end=3207 + _globals['_INVOICE']._serialized_start=3210 + _globals['_INVOICE']._serialized_end=3528 + _globals['_INVOICE_COST']._serialized_start=3375 + _globals['_INVOICE_COST']._serialized_end=3429 + _globals['_INVOICE_TYPE']._serialized_start=3431 + _globals['_INVOICE_TYPE']._serialized_end=3528 + _globals['_VAULTINVOICESLISTREQUEST']._serialized_start=3530 + _globals['_VAULTINVOICESLISTREQUEST']._serialized_end=3556 + _globals['_VAULTINVOICESLISTRESPONSE']._serialized_start=3558 + _globals['_VAULTINVOICESLISTRESPONSE']._serialized_end=3621 + _globals['_VAULTINVOICE']._serialized_start=3624 + _globals['_VAULTINVOICE']._serialized_end=3767 + _globals['_INVOICEDOWNLOADREQUEST']._serialized_start=3769 + _globals['_INVOICEDOWNLOADREQUEST']._serialized_end=3816 + _globals['_INVOICEDOWNLOADRESPONSE']._serialized_start=3818 + _globals['_INVOICEDOWNLOADRESPONSE']._serialized_end=3875 + _globals['_VAULTINVOICEDOWNLOADLINKREQUEST']._serialized_start=3877 + _globals['_VAULTINVOICEDOWNLOADLINKREQUEST']._serialized_end=3933 + _globals['_VAULTINVOICEDOWNLOADLINKRESPONSE']._serialized_start=3935 + _globals['_VAULTINVOICEDOWNLOADLINKRESPONSE']._serialized_end=4001 + _globals['_REPORTINGDAILYSNAPSHOTREQUEST']._serialized_start=4003 + _globals['_REPORTINGDAILYSNAPSHOTREQUEST']._serialized_end=4063 + _globals['_REPORTINGDAILYSNAPSHOTRESPONSE']._serialized_start=4065 + _globals['_REPORTINGDAILYSNAPSHOTRESPONSE']._serialized_end=4183 + _globals['_SNAPSHOTRECORD']._serialized_start=4186 + _globals['_SNAPSHOTRECORD']._serialized_end=4401 + _globals['_SNAPSHOTRECORD_ADDON']._serialized_start=4359 + _globals['_SNAPSHOTRECORD_ADDON']._serialized_end=4401 + _globals['_SNAPSHOTMCENTERPRISE']._serialized_start=4403 + _globals['_SNAPSHOTMCENTERPRISE']._serialized_end=4451 + _globals['_MAPPINGADDONSREQUEST']._serialized_start=4453 + _globals['_MAPPINGADDONSREQUEST']._serialized_end=4475 + _globals['_MAPPINGADDONSRESPONSE']._serialized_start=4477 + _globals['_MAPPINGADDONSRESPONSE']._serialized_end=4569 + _globals['_MAPPINGITEM']._serialized_start=4571 + _globals['_MAPPINGITEM']._serialized_end=4610 + _globals['_GRADIENTVALIDATEKEYREQUEST']._serialized_start=4612 + _globals['_GRADIENTVALIDATEKEYREQUEST']._serialized_end=4661 + _globals['_GRADIENTVALIDATEKEYRESPONSE']._serialized_start=4663 + _globals['_GRADIENTVALIDATEKEYRESPONSE']._serialized_end=4726 + _globals['_GRADIENTSAVEREQUEST']._serialized_start=4728 + _globals['_GRADIENTSAVEREQUEST']._serialized_end=4796 + _globals['_GRADIENTSAVERESPONSE']._serialized_start=4798 + _globals['_GRADIENTSAVERESPONSE']._serialized_end=4901 + _globals['_GRADIENTREMOVEREQUEST']._serialized_start=4903 + _globals['_GRADIENTREMOVEREQUEST']._serialized_end=4952 + _globals['_GRADIENTREMOVERESPONSE']._serialized_start=4954 + _globals['_GRADIENTREMOVERESPONSE']._serialized_end=5012 + _globals['_GRADIENTSYNCREQUEST']._serialized_start=5014 + _globals['_GRADIENTSYNCREQUEST']._serialized_end=5061 + _globals['_GRADIENTSYNCRESPONSE']._serialized_start=5063 + _globals['_GRADIENTSYNCRESPONSE']._serialized_end=5166 + _globals['_NETPROMOTERSCORESURVEYSUBMISSIONREQUEST']._serialized_start=5168 + _globals['_NETPROMOTERSCORESURVEYSUBMISSIONREQUEST']._serialized_end=5246 + _globals['_NETPROMOTERSCORESURVEYSUBMISSIONRESPONSE']._serialized_start=5248 + _globals['_NETPROMOTERSCORESURVEYSUBMISSIONRESPONSE']._serialized_end=5290 + _globals['_NETPROMOTERSCOREPOPUPSCHEDULEREQUEST']._serialized_start=5292 + _globals['_NETPROMOTERSCOREPOPUPSCHEDULEREQUEST']._serialized_end=5330 + _globals['_NETPROMOTERSCOREPOPUPSCHEDULERESPONSE']._serialized_start=5332 + _globals['_NETPROMOTERSCOREPOPUPSCHEDULERESPONSE']._serialized_end=5391 + _globals['_NETPROMOTERSCOREPOPUPDISMISSALREQUEST']._serialized_start=5393 + _globals['_NETPROMOTERSCOREPOPUPDISMISSALREQUEST']._serialized_end=5432 + _globals['_NETPROMOTERSCOREPOPUPDISMISSALRESPONSE']._serialized_start=5434 + _globals['_NETPROMOTERSCOREPOPUPDISMISSALRESPONSE']._serialized_end=5474 + _globals['_KCMLICENSEREQUEST']._serialized_start=5476 + _globals['_KCMLICENSEREQUEST']._serialized_end=5521 + _globals['_KCMLICENSERESPONSE']._serialized_start=5523 + _globals['_KCMLICENSERESPONSE']._serialized_end=5560 + _globals['_EVENTREQUEST']._serialized_start=5563 + _globals['_EVENTREQUEST']._serialized_end=5695 + _globals['_EVENTSREQUEST']._serialized_start=5697 + _globals['_EVENTSREQUEST']._serialized_end=5745 + _globals['_EVENTRESPONSE']._serialized_start=5747 + _globals['_EVENTRESPONSE']._serialized_end=5793 + _globals['_EVENTSRESPONSE']._serialized_start=5795 + _globals['_EVENTSRESPONSE']._serialized_end=5848 + _globals['_CUSTOMERCAPTUREREQUEST']._serialized_start=5851 + _globals['_CUSTOMERCAPTUREREQUEST']._serialized_end=6020 + _globals['_CUSTOMERCAPTURERESPONSE']._serialized_start=6022 + _globals['_CUSTOMERCAPTURERESPONSE']._serialized_end=6047 + _globals['_ERROR']._serialized_start=6049 + _globals['_ERROR']._serialized_end=6173 + _globals['_ERROR_EXTRASENTRY']._serialized_start=6128 + _globals['_ERROR_EXTRASENTRY']._serialized_end=6173 + _globals['_QUOTEPURCHASE']._serialized_start=6176 + _globals['_QUOTEPURCHASE']._serialized_end=6326 + _globals['_PURCHASEOPTIONS']._serialized_start=6328 + _globals['_PURCHASEOPTIONS']._serialized_end=6435 + _globals['_ADDONPURCHASEOPTIONS']._serialized_start=6438 + _globals['_ADDONPURCHASEOPTIONS']._serialized_end=7307 + _globals['_AVAILABLEPURCHASEOPTIONS']._serialized_start=7310 + _globals['_AVAILABLEPURCHASEOPTIONS']._serialized_end=7453 + _globals['_UPGRADELICENSESTATUSREQUEST']._serialized_start=7455 + _globals['_UPGRADELICENSESTATUSREQUEST']._serialized_end=7484 + _globals['_UPGRADELICENSESTATUSRESPONSE']._serialized_start=7487 + _globals['_UPGRADELICENSESTATUSRESPONSE']._serialized_end=7632 + _globals['_UPGRADELICENSEQUOTEPURCHASEREQUEST']._serialized_start=7634 + _globals['_UPGRADELICENSEQUOTEPURCHASEREQUEST']._serialized_end=7748 + _globals['_UPGRADELICENSEQUOTEPURCHASERESPONSE']._serialized_start=7751 + _globals['_UPGRADELICENSEQUOTEPURCHASERESPONSE']._serialized_end=7898 + _globals['_UPGRADELICENSECOMPLETEPURCHASEREQUEST']._serialized_start=7901 + _globals['_UPGRADELICENSECOMPLETEPURCHASEREQUEST']._serialized_end=8060 + _globals['_UPGRADELICENSECOMPLETEPURCHASERESPONSE']._serialized_start=8063 + _globals['_UPGRADELICENSECOMPLETEPURCHASERESPONSE']._serialized_end=8211 + _globals['_ENTERPRISEBASEPLAN']._serialized_start=8214 + _globals['_ENTERPRISEBASEPLAN']._serialized_end=8427 + _globals['_ENTERPRISEBASEPLAN_ENTERPRISEBASEPLANVERSION']._serialized_start=8335 + _globals['_ENTERPRISEBASEPLAN_ENTERPRISEBASEPLANVERSION']._serialized_end=8427 + _globals['_SUBSCRIPTIONENTERPRISEPRICINGREQUEST']._serialized_start=8429 + _globals['_SUBSCRIPTIONENTERPRISEPRICINGREQUEST']._serialized_end=8467 + _globals['_NHITIERPLAN']._serialized_start=8469 + _globals['_NHITIERPLAN']._serialized_end=8579 + _globals['_SUBSCRIPTIONENTERPRISEPRICINGRESPONSE']._serialized_start=8582 + _globals['_SUBSCRIPTIONENTERPRISEPRICINGRESPONSE']._serialized_end=8763 + _globals['_SINGULARDEVICEIDENTIFIER']._serialized_start=8765 + _globals['_SINGULARDEVICEIDENTIFIER']._serialized_end=8839 + _globals['_SINGULARSHAREDDATA']._serialized_start=8842 + _globals['_SINGULARSHAREDDATA']._serialized_end=9014 + _globals['_SINGULARSESSIONREQUEST']._serialized_start=9017 + _globals['_SINGULARSESSIONREQUEST']._serialized_end=9412 + _globals['_SINGULAREVENTREQUEST']._serialized_start=9415 + _globals['_SINGULAREVENTREQUEST']._serialized_end=9557 + _globals['_ACTIVEPAMCOUNTREQUEST']._serialized_start=9559 + _globals['_ACTIVEPAMCOUNTREQUEST']._serialized_end=9604 + _globals['_ACTIVEPAMCOUNTRESPONSE']._serialized_start=9606 + _globals['_ACTIVEPAMCOUNTRESPONSE']._serialized_end=9648 + _globals['_NHIENTERPRISEREQUEST']._serialized_start=9650 + _globals['_NHIENTERPRISEREQUEST']._serialized_end=9730 + _globals['_NHIMETRICSREQUEST']._serialized_start=9733 + _globals['_NHIMETRICSREQUEST']._serialized_end=9870 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/BI_pb2.pyi b/keepercommander/proto/BI_pb2.pyi index 65defd3da..3cb87a69a 100644 --- a/keepercommander/proto/BI_pb2.pyi +++ b/keepercommander/proto/BI_pb2.pyi @@ -8,7 +8,7 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class Currency(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () UNKNOWN: _ClassVar[Currency] USD: _ClassVar[Currency] GBP: _ClassVar[Currency] @@ -18,20 +18,49 @@ class Currency(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): CAD: _ClassVar[Currency] class GradientIntegrationStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () NOTCONNECTED: _ClassVar[GradientIntegrationStatus] PENDING: _ClassVar[GradientIntegrationStatus] CONNECTED: _ClassVar[GradientIntegrationStatus] NONE: _ClassVar[GradientIntegrationStatus] class EventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () UNKNOWN_TRACKING_EVENT_TYPE: _ClassVar[EventType] TRACKING_POPUP_DISPLAYED: _ClassVar[EventType] TRACKING_POPUP_ACCEPTED: _ClassVar[EventType] TRACKING_POPUP_DISMISSED: _ClassVar[EventType] TRACKING_POPUP_PAID: _ClassVar[EventType] TRACKING_PUSH_CLICKED: _ClassVar[EventType] + CONSOLE_ACTION: _ClassVar[EventType] + VAULT_ACTION: _ClassVar[EventType] + +class PurchaseProductType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + upgradeToEnterprise: _ClassVar[PurchaseProductType] + addUsers: _ClassVar[PurchaseProductType] + addStorage: _ClassVar[PurchaseProductType] + addAudit: _ClassVar[PurchaseProductType] + addBreachWatch: _ClassVar[PurchaseProductType] + addCompliance: _ClassVar[PurchaseProductType] + addChat: _ClassVar[PurchaseProductType] + addPAM: _ClassVar[PurchaseProductType] + addSilverSupport: _ClassVar[PurchaseProductType] + addPlatinumSupport: _ClassVar[PurchaseProductType] + addKEPM: _ClassVar[PurchaseProductType] + addNhi: _ClassVar[PurchaseProductType] + +class IdentifierType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + UNKNOWN_IDENTIFIER_TYPE: _ClassVar[IdentifierType] + IOS_ID: _ClassVar[IdentifierType] + ANDROID_GOOGLE_PLAY_ID: _ClassVar[IdentifierType] + ANDROID_APP_SET_ID: _ClassVar[IdentifierType] + ANDROID_ID: _ClassVar[IdentifierType] + AMAZON_ADVERTISING_ID: _ClassVar[IdentifierType] + OPEN_ADVERTISING_ID: _ClassVar[IdentifierType] + SINGULAR_DEVICE_ID: _ClassVar[IdentifierType] + CLIENT_DEFINED_ID: _ClassVar[IdentifierType] UNKNOWN: Currency USD: Currency GBP: Currency @@ -49,9 +78,32 @@ TRACKING_POPUP_ACCEPTED: EventType TRACKING_POPUP_DISMISSED: EventType TRACKING_POPUP_PAID: EventType TRACKING_PUSH_CLICKED: EventType +CONSOLE_ACTION: EventType +VAULT_ACTION: EventType +upgradeToEnterprise: PurchaseProductType +addUsers: PurchaseProductType +addStorage: PurchaseProductType +addAudit: PurchaseProductType +addBreachWatch: PurchaseProductType +addCompliance: PurchaseProductType +addChat: PurchaseProductType +addPAM: PurchaseProductType +addSilverSupport: PurchaseProductType +addPlatinumSupport: PurchaseProductType +addKEPM: PurchaseProductType +addNhi: PurchaseProductType +UNKNOWN_IDENTIFIER_TYPE: IdentifierType +IOS_ID: IdentifierType +ANDROID_GOOGLE_PLAY_ID: IdentifierType +ANDROID_APP_SET_ID: IdentifierType +ANDROID_ID: IdentifierType +AMAZON_ADVERTISING_ID: IdentifierType +OPEN_ADVERTISING_ID: IdentifierType +SINGULAR_DEVICE_ID: IdentifierType +CLIENT_DEFINED_ID: IdentifierType class ValidateSessionTokenRequest(_message.Message): - __slots__ = ["encryptedSessionToken", "returnMcEnterpiseIds", "ip"] + __slots__ = ("encryptedSessionToken", "returnMcEnterpiseIds", "ip") ENCRYPTEDSESSIONTOKEN_FIELD_NUMBER: _ClassVar[int] RETURNMCENTERPISEIDS_FIELD_NUMBER: _ClassVar[int] IP_FIELD_NUMBER: _ClassVar[int] @@ -61,9 +113,9 @@ class ValidateSessionTokenRequest(_message.Message): def __init__(self, encryptedSessionToken: _Optional[bytes] = ..., returnMcEnterpiseIds: bool = ..., ip: _Optional[str] = ...) -> None: ... class ValidateSessionTokenResponse(_message.Message): - __slots__ = ["username", "userId", "enterpriseUserId", "status", "statusMessage", "mcEnterpriseIds", "hasMSPPermission", "deletedMcEnterpriseIds"] + __slots__ = ("username", "userId", "enterpriseUserId", "status", "statusMessage", "mcEnterpriseIds", "hasMSPPermission", "deletedMcEnterpriseIds") class Status(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () VALID: _ClassVar[ValidateSessionTokenResponse.Status] NOT_VALID: _ClassVar[ValidateSessionTokenResponse.Status] EXPIRED: _ClassVar[ValidateSessionTokenResponse.Status] @@ -93,11 +145,11 @@ class ValidateSessionTokenResponse(_message.Message): def __init__(self, username: _Optional[str] = ..., userId: _Optional[int] = ..., enterpriseUserId: _Optional[int] = ..., status: _Optional[_Union[ValidateSessionTokenResponse.Status, str]] = ..., statusMessage: _Optional[str] = ..., mcEnterpriseIds: _Optional[_Iterable[int]] = ..., hasMSPPermission: bool = ..., deletedMcEnterpriseIds: _Optional[_Iterable[int]] = ...) -> None: ... class SubscriptionStatusRequest(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class SubscriptionStatusResponse(_message.Message): - __slots__ = ["autoRenewal", "currentPaymentMethod", "checkoutLink", "licenseCreateDate", "isDistributor", "isLegacyMsp", "licenseStats", "gradientStatus", "hideTrialBanner", "gradientLastSyncDate", "gradientNextSyncDate", "isGradientMappingPending"] + __slots__ = ("autoRenewal", "currentPaymentMethod", "checkoutLink", "licenseCreateDate", "isDistributor", "isLegacyMsp", "licenseStats", "gradientStatus", "hideTrialBanner", "gradientLastSyncDate", "gradientNextSyncDate", "isGradientMappingPending", "nhi", "freeKsmApiCallsCount", "ksm") AUTORENEWAL_FIELD_NUMBER: _ClassVar[int] CURRENTPAYMENTMETHOD_FIELD_NUMBER: _ClassVar[int] CHECKOUTLINK_FIELD_NUMBER: _ClassVar[int] @@ -110,6 +162,9 @@ class SubscriptionStatusResponse(_message.Message): GRADIENTLASTSYNCDATE_FIELD_NUMBER: _ClassVar[int] GRADIENTNEXTSYNCDATE_FIELD_NUMBER: _ClassVar[int] ISGRADIENTMAPPINGPENDING_FIELD_NUMBER: _ClassVar[int] + NHI_FIELD_NUMBER: _ClassVar[int] + FREEKSMAPICALLSCOUNT_FIELD_NUMBER: _ClassVar[int] + KSM_FIELD_NUMBER: _ClassVar[int] autoRenewal: AutoRenewal currentPaymentMethod: PaymentMethod checkoutLink: str @@ -122,24 +177,71 @@ class SubscriptionStatusResponse(_message.Message): gradientLastSyncDate: str gradientNextSyncDate: str isGradientMappingPending: bool - def __init__(self, autoRenewal: _Optional[_Union[AutoRenewal, _Mapping]] = ..., currentPaymentMethod: _Optional[_Union[PaymentMethod, _Mapping]] = ..., checkoutLink: _Optional[str] = ..., licenseCreateDate: _Optional[int] = ..., isDistributor: bool = ..., isLegacyMsp: bool = ..., licenseStats: _Optional[_Iterable[_Union[LicenseStats, _Mapping]]] = ..., gradientStatus: _Optional[_Union[GradientIntegrationStatus, str]] = ..., hideTrialBanner: bool = ..., gradientLastSyncDate: _Optional[str] = ..., gradientNextSyncDate: _Optional[str] = ..., isGradientMappingPending: bool = ...) -> None: ... + nhi: NhiBilling + freeKsmApiCallsCount: int + ksm: KsmBilling + def __init__(self, autoRenewal: _Optional[_Union[AutoRenewal, _Mapping]] = ..., currentPaymentMethod: _Optional[_Union[PaymentMethod, _Mapping]] = ..., checkoutLink: _Optional[str] = ..., licenseCreateDate: _Optional[int] = ..., isDistributor: bool = ..., isLegacyMsp: bool = ..., licenseStats: _Optional[_Iterable[_Union[LicenseStats, _Mapping]]] = ..., gradientStatus: _Optional[_Union[GradientIntegrationStatus, str]] = ..., hideTrialBanner: bool = ..., gradientLastSyncDate: _Optional[str] = ..., gradientNextSyncDate: _Optional[str] = ..., isGradientMappingPending: bool = ..., nhi: _Optional[_Union[NhiBilling, _Mapping]] = ..., freeKsmApiCallsCount: _Optional[int] = ..., ksm: _Optional[_Union[KsmBilling, _Mapping]] = ...) -> None: ... + +class KsmBilling(_message.Message): + __slots__ = ("billingStartTimestamp", "billingEndTimestamp", "currentTierId", "enterpriseBlocks", "currentTierCeiling") + BILLINGSTARTTIMESTAMP_FIELD_NUMBER: _ClassVar[int] + BILLINGENDTIMESTAMP_FIELD_NUMBER: _ClassVar[int] + CURRENTTIERID_FIELD_NUMBER: _ClassVar[int] + ENTERPRISEBLOCKS_FIELD_NUMBER: _ClassVar[int] + CURRENTTIERCEILING_FIELD_NUMBER: _ClassVar[int] + billingStartTimestamp: int + billingEndTimestamp: int + currentTierId: int + enterpriseBlocks: int + currentTierCeiling: int + def __init__(self, billingStartTimestamp: _Optional[int] = ..., billingEndTimestamp: _Optional[int] = ..., currentTierId: _Optional[int] = ..., enterpriseBlocks: _Optional[int] = ..., currentTierCeiling: _Optional[int] = ...) -> None: ... + +class NhiBilling(_message.Message): + __slots__ = ("billingStartTimestamp", "billingEndTimestamp", "currentTierId", "enterpriseBlocks", "currentTierCeiling", "billingPeriods") + BILLINGSTARTTIMESTAMP_FIELD_NUMBER: _ClassVar[int] + BILLINGENDTIMESTAMP_FIELD_NUMBER: _ClassVar[int] + CURRENTTIERID_FIELD_NUMBER: _ClassVar[int] + ENTERPRISEBLOCKS_FIELD_NUMBER: _ClassVar[int] + CURRENTTIERCEILING_FIELD_NUMBER: _ClassVar[int] + BILLINGPERIODS_FIELD_NUMBER: _ClassVar[int] + billingStartTimestamp: int + billingEndTimestamp: int + currentTierId: int + enterpriseBlocks: int + currentTierCeiling: int + billingPeriods: _containers.RepeatedCompositeFieldContainer[NhiBillingPeriod] + def __init__(self, billingStartTimestamp: _Optional[int] = ..., billingEndTimestamp: _Optional[int] = ..., currentTierId: _Optional[int] = ..., enterpriseBlocks: _Optional[int] = ..., currentTierCeiling: _Optional[int] = ..., billingPeriods: _Optional[_Iterable[_Union[NhiBillingPeriod, _Mapping]]] = ...) -> None: ... + +class NhiBillingPeriod(_message.Message): + __slots__ = ("startTimestamp", "endTimestamp") + STARTTIMESTAMP_FIELD_NUMBER: _ClassVar[int] + ENDTIMESTAMP_FIELD_NUMBER: _ClassVar[int] + startTimestamp: int + endTimestamp: int + def __init__(self, startTimestamp: _Optional[int] = ..., endTimestamp: _Optional[int] = ...) -> None: ... class LicenseStats(_message.Message): - __slots__ = ["type", "available", "used"] + __slots__ = ("type", "available", "used") class Type(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () LICENSE_STAT_UNKNOWN: _ClassVar[LicenseStats.Type] MSP_BASE: _ClassVar[LicenseStats.Type] MC_BUSINESS: _ClassVar[LicenseStats.Type] MC_BUSINESS_PLUS: _ClassVar[LicenseStats.Type] MC_ENTERPRISE: _ClassVar[LicenseStats.Type] MC_ENTERPRISE_PLUS: _ClassVar[LicenseStats.Type] + B2B_BUSINESS_STARTER: _ClassVar[LicenseStats.Type] + B2B_BUSINESS: _ClassVar[LicenseStats.Type] + B2B_ENTERPRISE: _ClassVar[LicenseStats.Type] LICENSE_STAT_UNKNOWN: LicenseStats.Type MSP_BASE: LicenseStats.Type MC_BUSINESS: LicenseStats.Type MC_BUSINESS_PLUS: LicenseStats.Type MC_ENTERPRISE: LicenseStats.Type MC_ENTERPRISE_PLUS: LicenseStats.Type + B2B_BUSINESS_STARTER: LicenseStats.Type + B2B_BUSINESS: LicenseStats.Type + B2B_ENTERPRISE: LicenseStats.Type TYPE_FIELD_NUMBER: _ClassVar[int] AVAILABLE_FIELD_NUMBER: _ClassVar[int] USED_FIELD_NUMBER: _ClassVar[int] @@ -149,7 +251,7 @@ class LicenseStats(_message.Message): def __init__(self, type: _Optional[_Union[LicenseStats.Type, str]] = ..., available: _Optional[int] = ..., used: _Optional[int] = ...) -> None: ... class AutoRenewal(_message.Message): - __slots__ = ["nextOn", "daysLeft", "isTrial"] + __slots__ = ("nextOn", "daysLeft", "isTrial") NEXTON_FIELD_NUMBER: _ClassVar[int] DAYSLEFT_FIELD_NUMBER: _ClassVar[int] ISTRIAL_FIELD_NUMBER: _ClassVar[int] @@ -159,9 +261,9 @@ class AutoRenewal(_message.Message): def __init__(self, nextOn: _Optional[int] = ..., daysLeft: _Optional[int] = ..., isTrial: bool = ...) -> None: ... class PaymentMethod(_message.Message): - __slots__ = ["type", "card", "sepa", "paypal", "failedBilling", "vendor", "purchaseOrder"] + __slots__ = ("type", "card", "sepa", "paypal", "failedBilling", "vendor", "purchaseOrder") class Type(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () CARD: _ClassVar[PaymentMethod.Type] SEPA: _ClassVar[PaymentMethod.Type] PAYPAL: _ClassVar[PaymentMethod.Type] @@ -175,29 +277,29 @@ class PaymentMethod(_message.Message): VENDOR: PaymentMethod.Type PURCHASEORDER: PaymentMethod.Type class Card(_message.Message): - __slots__ = ["last4", "brand"] + __slots__ = ("last4", "brand") LAST4_FIELD_NUMBER: _ClassVar[int] BRAND_FIELD_NUMBER: _ClassVar[int] last4: str brand: str def __init__(self, last4: _Optional[str] = ..., brand: _Optional[str] = ...) -> None: ... class Sepa(_message.Message): - __slots__ = ["last4", "country"] + __slots__ = ("last4", "country") LAST4_FIELD_NUMBER: _ClassVar[int] COUNTRY_FIELD_NUMBER: _ClassVar[int] last4: str country: str def __init__(self, last4: _Optional[str] = ..., country: _Optional[str] = ...) -> None: ... class Paypal(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class Vendor(_message.Message): - __slots__ = ["name"] + __slots__ = ("name",) NAME_FIELD_NUMBER: _ClassVar[int] name: str def __init__(self, name: _Optional[str] = ...) -> None: ... class PurchaseOrder(_message.Message): - __slots__ = ["name"] + __slots__ = ("name",) NAME_FIELD_NUMBER: _ClassVar[int] name: str def __init__(self, name: _Optional[str] = ...) -> None: ... @@ -218,11 +320,11 @@ class PaymentMethod(_message.Message): def __init__(self, type: _Optional[_Union[PaymentMethod.Type, str]] = ..., card: _Optional[_Union[PaymentMethod.Card, _Mapping]] = ..., sepa: _Optional[_Union[PaymentMethod.Sepa, _Mapping]] = ..., paypal: _Optional[_Union[PaymentMethod.Paypal, _Mapping]] = ..., failedBilling: bool = ..., vendor: _Optional[_Union[PaymentMethod.Vendor, _Mapping]] = ..., purchaseOrder: _Optional[_Union[PaymentMethod.PurchaseOrder, _Mapping]] = ...) -> None: ... class SubscriptionMspPricingRequest(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class SubscriptionMspPricingResponse(_message.Message): - __slots__ = ["addons", "filePlans"] + __slots__ = ("addons", "filePlans") ADDONS_FIELD_NUMBER: _ClassVar[int] FILEPLANS_FIELD_NUMBER: _ClassVar[int] addons: _containers.RepeatedCompositeFieldContainer[Addon] @@ -230,11 +332,11 @@ class SubscriptionMspPricingResponse(_message.Message): def __init__(self, addons: _Optional[_Iterable[_Union[Addon, _Mapping]]] = ..., filePlans: _Optional[_Iterable[_Union[FilePlan, _Mapping]]] = ...) -> None: ... class SubscriptionMcPricingRequest(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class SubscriptionMcPricingResponse(_message.Message): - __slots__ = ["basePlans", "addons", "filePlans"] + __slots__ = ("basePlans", "addons", "filePlans") BASEPLANS_FIELD_NUMBER: _ClassVar[int] ADDONS_FIELD_NUMBER: _ClassVar[int] FILEPLANS_FIELD_NUMBER: _ClassVar[int] @@ -244,7 +346,7 @@ class SubscriptionMcPricingResponse(_message.Message): def __init__(self, basePlans: _Optional[_Iterable[_Union[BasePlan, _Mapping]]] = ..., addons: _Optional[_Iterable[_Union[Addon, _Mapping]]] = ..., filePlans: _Optional[_Iterable[_Union[FilePlan, _Mapping]]] = ...) -> None: ... class BasePlan(_message.Message): - __slots__ = ["id", "cost"] + __slots__ = ("id", "cost") ID_FIELD_NUMBER: _ClassVar[int] COST_FIELD_NUMBER: _ClassVar[int] id: int @@ -252,7 +354,7 @@ class BasePlan(_message.Message): def __init__(self, id: _Optional[int] = ..., cost: _Optional[_Union[Cost, _Mapping]] = ...) -> None: ... class Addon(_message.Message): - __slots__ = ["id", "cost", "amountConsumed"] + __slots__ = ("id", "cost", "amountConsumed") ID_FIELD_NUMBER: _ClassVar[int] COST_FIELD_NUMBER: _ClassVar[int] AMOUNTCONSUMED_FIELD_NUMBER: _ClassVar[int] @@ -262,7 +364,7 @@ class Addon(_message.Message): def __init__(self, id: _Optional[int] = ..., cost: _Optional[_Union[Cost, _Mapping]] = ..., amountConsumed: _Optional[int] = ...) -> None: ... class FilePlan(_message.Message): - __slots__ = ["id", "cost"] + __slots__ = ("id", "cost") ID_FIELD_NUMBER: _ClassVar[int] COST_FIELD_NUMBER: _ClassVar[int] id: int @@ -270,29 +372,39 @@ class FilePlan(_message.Message): def __init__(self, id: _Optional[int] = ..., cost: _Optional[_Union[Cost, _Mapping]] = ...) -> None: ... class Cost(_message.Message): - __slots__ = ["amount", "amountPer", "currency"] + __slots__ = ("amount", "amountPer", "currency", "contactSales") class AmountPer(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () UNKNOWN: _ClassVar[Cost.AmountPer] MONTH: _ClassVar[Cost.AmountPer] USER_MONTH: _ClassVar[Cost.AmountPer] USER_CONSUMED_MONTH: _ClassVar[Cost.AmountPer] ENDPOINT_MONTH: _ClassVar[Cost.AmountPer] + USER_YEAR: _ClassVar[Cost.AmountPer] + USER_CONSUMED_YEAR: _ClassVar[Cost.AmountPer] + YEAR: _ClassVar[Cost.AmountPer] + ENDPOINT_YEAR: _ClassVar[Cost.AmountPer] UNKNOWN: Cost.AmountPer MONTH: Cost.AmountPer USER_MONTH: Cost.AmountPer USER_CONSUMED_MONTH: Cost.AmountPer ENDPOINT_MONTH: Cost.AmountPer + USER_YEAR: Cost.AmountPer + USER_CONSUMED_YEAR: Cost.AmountPer + YEAR: Cost.AmountPer + ENDPOINT_YEAR: Cost.AmountPer AMOUNT_FIELD_NUMBER: _ClassVar[int] AMOUNTPER_FIELD_NUMBER: _ClassVar[int] CURRENCY_FIELD_NUMBER: _ClassVar[int] + CONTACTSALES_FIELD_NUMBER: _ClassVar[int] amount: float amountPer: Cost.AmountPer currency: Currency - def __init__(self, amount: _Optional[float] = ..., amountPer: _Optional[_Union[Cost.AmountPer, str]] = ..., currency: _Optional[_Union[Currency, str]] = ...) -> None: ... + contactSales: bool + def __init__(self, amount: _Optional[float] = ..., amountPer: _Optional[_Union[Cost.AmountPer, str]] = ..., currency: _Optional[_Union[Currency, str]] = ..., contactSales: bool = ...) -> None: ... class InvoiceSearchRequest(_message.Message): - __slots__ = ["size", "startingAfterId", "allInvoicesUnfiltered"] + __slots__ = ("size", "startingAfterId", "allInvoicesUnfiltered") SIZE_FIELD_NUMBER: _ClassVar[int] STARTINGAFTERID_FIELD_NUMBER: _ClassVar[int] ALLINVOICESUNFILTERED_FIELD_NUMBER: _ClassVar[int] @@ -302,15 +414,15 @@ class InvoiceSearchRequest(_message.Message): def __init__(self, size: _Optional[int] = ..., startingAfterId: _Optional[int] = ..., allInvoicesUnfiltered: bool = ...) -> None: ... class InvoiceSearchResponse(_message.Message): - __slots__ = ["invoices"] + __slots__ = ("invoices",) INVOICES_FIELD_NUMBER: _ClassVar[int] invoices: _containers.RepeatedCompositeFieldContainer[Invoice] def __init__(self, invoices: _Optional[_Iterable[_Union[Invoice, _Mapping]]] = ...) -> None: ... class Invoice(_message.Message): - __slots__ = ["id", "invoiceNumber", "invoiceDate", "licenseCount", "totalCost", "invoiceType"] + __slots__ = ("id", "invoiceNumber", "invoiceDate", "licenseCount", "totalCost", "invoiceType") class Type(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () UNKNOWN: _ClassVar[Invoice.Type] NEW: _ClassVar[Invoice.Type] RENEWAL: _ClassVar[Invoice.Type] @@ -326,7 +438,7 @@ class Invoice(_message.Message): ASSOCIATION: Invoice.Type OVERAGE: Invoice.Type class Cost(_message.Message): - __slots__ = ["amount", "currency"] + __slots__ = ("amount", "currency") AMOUNT_FIELD_NUMBER: _ClassVar[int] CURRENCY_FIELD_NUMBER: _ClassVar[int] amount: float @@ -347,17 +459,17 @@ class Invoice(_message.Message): def __init__(self, id: _Optional[int] = ..., invoiceNumber: _Optional[str] = ..., invoiceDate: _Optional[int] = ..., licenseCount: _Optional[int] = ..., totalCost: _Optional[_Union[Invoice.Cost, _Mapping]] = ..., invoiceType: _Optional[_Union[Invoice.Type, str]] = ...) -> None: ... class VaultInvoicesListRequest(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class VaultInvoicesListResponse(_message.Message): - __slots__ = ["invoices"] + __slots__ = ("invoices",) INVOICES_FIELD_NUMBER: _ClassVar[int] invoices: _containers.RepeatedCompositeFieldContainer[VaultInvoice] def __init__(self, invoices: _Optional[_Iterable[_Union[VaultInvoice, _Mapping]]] = ...) -> None: ... class VaultInvoice(_message.Message): - __slots__ = ["id", "invoiceNumber", "dateCreated", "total", "purchaseType"] + __slots__ = ("id", "invoiceNumber", "dateCreated", "total", "purchaseType") ID_FIELD_NUMBER: _ClassVar[int] INVOICENUMBER_FIELD_NUMBER: _ClassVar[int] DATECREATED_FIELD_NUMBER: _ClassVar[int] @@ -371,13 +483,13 @@ class VaultInvoice(_message.Message): def __init__(self, id: _Optional[int] = ..., invoiceNumber: _Optional[str] = ..., dateCreated: _Optional[int] = ..., total: _Optional[_Union[Invoice.Cost, _Mapping]] = ..., purchaseType: _Optional[_Union[Invoice.Type, str]] = ...) -> None: ... class InvoiceDownloadRequest(_message.Message): - __slots__ = ["invoiceNumber"] + __slots__ = ("invoiceNumber",) INVOICENUMBER_FIELD_NUMBER: _ClassVar[int] invoiceNumber: str def __init__(self, invoiceNumber: _Optional[str] = ...) -> None: ... class InvoiceDownloadResponse(_message.Message): - __slots__ = ["link", "fileName"] + __slots__ = ("link", "fileName") LINK_FIELD_NUMBER: _ClassVar[int] FILENAME_FIELD_NUMBER: _ClassVar[int] link: str @@ -385,13 +497,13 @@ class InvoiceDownloadResponse(_message.Message): def __init__(self, link: _Optional[str] = ..., fileName: _Optional[str] = ...) -> None: ... class VaultInvoiceDownloadLinkRequest(_message.Message): - __slots__ = ["invoiceNumber"] + __slots__ = ("invoiceNumber",) INVOICENUMBER_FIELD_NUMBER: _ClassVar[int] invoiceNumber: str def __init__(self, invoiceNumber: _Optional[str] = ...) -> None: ... class VaultInvoiceDownloadLinkResponse(_message.Message): - __slots__ = ["link", "fileName"] + __slots__ = ("link", "fileName") LINK_FIELD_NUMBER: _ClassVar[int] FILENAME_FIELD_NUMBER: _ClassVar[int] link: str @@ -399,7 +511,7 @@ class VaultInvoiceDownloadLinkResponse(_message.Message): def __init__(self, link: _Optional[str] = ..., fileName: _Optional[str] = ...) -> None: ... class ReportingDailySnapshotRequest(_message.Message): - __slots__ = ["month", "year"] + __slots__ = ("month", "year") MONTH_FIELD_NUMBER: _ClassVar[int] YEAR_FIELD_NUMBER: _ClassVar[int] month: int @@ -407,7 +519,7 @@ class ReportingDailySnapshotRequest(_message.Message): def __init__(self, month: _Optional[int] = ..., year: _Optional[int] = ...) -> None: ... class ReportingDailySnapshotResponse(_message.Message): - __slots__ = ["records", "mcEnterprises"] + __slots__ = ("records", "mcEnterprises") RECORDS_FIELD_NUMBER: _ClassVar[int] MCENTERPRISES_FIELD_NUMBER: _ClassVar[int] records: _containers.RepeatedCompositeFieldContainer[SnapshotRecord] @@ -415,9 +527,9 @@ class ReportingDailySnapshotResponse(_message.Message): def __init__(self, records: _Optional[_Iterable[_Union[SnapshotRecord, _Mapping]]] = ..., mcEnterprises: _Optional[_Iterable[_Union[SnapshotMcEnterprise, _Mapping]]] = ...) -> None: ... class SnapshotRecord(_message.Message): - __slots__ = ["date", "mcEnterpriseId", "maxLicenseCount", "maxFilePlanTypeId", "maxBasePlanId", "addons"] + __slots__ = ("date", "mcEnterpriseId", "maxLicenseCount", "maxFilePlanTypeId", "maxBasePlanId", "addons") class Addon(_message.Message): - __slots__ = ["maxAddonId", "units"] + __slots__ = ("maxAddonId", "units") MAXADDONID_FIELD_NUMBER: _ClassVar[int] UNITS_FIELD_NUMBER: _ClassVar[int] maxAddonId: int @@ -438,7 +550,7 @@ class SnapshotRecord(_message.Message): def __init__(self, date: _Optional[int] = ..., mcEnterpriseId: _Optional[int] = ..., maxLicenseCount: _Optional[int] = ..., maxFilePlanTypeId: _Optional[int] = ..., maxBasePlanId: _Optional[int] = ..., addons: _Optional[_Iterable[_Union[SnapshotRecord.Addon, _Mapping]]] = ...) -> None: ... class SnapshotMcEnterprise(_message.Message): - __slots__ = ["id", "name"] + __slots__ = ("id", "name") ID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] id: int @@ -446,11 +558,11 @@ class SnapshotMcEnterprise(_message.Message): def __init__(self, id: _Optional[int] = ..., name: _Optional[str] = ...) -> None: ... class MappingAddonsRequest(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class MappingAddonsResponse(_message.Message): - __slots__ = ["addons", "filePlans"] + __slots__ = ("addons", "filePlans") ADDONS_FIELD_NUMBER: _ClassVar[int] FILEPLANS_FIELD_NUMBER: _ClassVar[int] addons: _containers.RepeatedCompositeFieldContainer[MappingItem] @@ -458,7 +570,7 @@ class MappingAddonsResponse(_message.Message): def __init__(self, addons: _Optional[_Iterable[_Union[MappingItem, _Mapping]]] = ..., filePlans: _Optional[_Iterable[_Union[MappingItem, _Mapping]]] = ...) -> None: ... class MappingItem(_message.Message): - __slots__ = ["id", "name"] + __slots__ = ("id", "name") ID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] id: int @@ -466,13 +578,13 @@ class MappingItem(_message.Message): def __init__(self, id: _Optional[int] = ..., name: _Optional[str] = ...) -> None: ... class GradientValidateKeyRequest(_message.Message): - __slots__ = ["gradientKey"] + __slots__ = ("gradientKey",) GRADIENTKEY_FIELD_NUMBER: _ClassVar[int] gradientKey: str def __init__(self, gradientKey: _Optional[str] = ...) -> None: ... class GradientValidateKeyResponse(_message.Message): - __slots__ = ["success", "message"] + __slots__ = ("success", "message") SUCCESS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] success: bool @@ -480,7 +592,7 @@ class GradientValidateKeyResponse(_message.Message): def __init__(self, success: bool = ..., message: _Optional[str] = ...) -> None: ... class GradientSaveRequest(_message.Message): - __slots__ = ["gradientKey", "enterpriseUserId"] + __slots__ = ("gradientKey", "enterpriseUserId") GRADIENTKEY_FIELD_NUMBER: _ClassVar[int] ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] gradientKey: str @@ -488,7 +600,7 @@ class GradientSaveRequest(_message.Message): def __init__(self, gradientKey: _Optional[str] = ..., enterpriseUserId: _Optional[int] = ...) -> None: ... class GradientSaveResponse(_message.Message): - __slots__ = ["success", "status", "message"] + __slots__ = ("success", "status", "message") SUCCESS_FIELD_NUMBER: _ClassVar[int] STATUS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -498,13 +610,13 @@ class GradientSaveResponse(_message.Message): def __init__(self, success: bool = ..., status: _Optional[_Union[GradientIntegrationStatus, str]] = ..., message: _Optional[str] = ...) -> None: ... class GradientRemoveRequest(_message.Message): - __slots__ = ["enterpriseUserId"] + __slots__ = ("enterpriseUserId",) ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] enterpriseUserId: int def __init__(self, enterpriseUserId: _Optional[int] = ...) -> None: ... class GradientRemoveResponse(_message.Message): - __slots__ = ["success", "message"] + __slots__ = ("success", "message") SUCCESS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] success: bool @@ -512,13 +624,13 @@ class GradientRemoveResponse(_message.Message): def __init__(self, success: bool = ..., message: _Optional[str] = ...) -> None: ... class GradientSyncRequest(_message.Message): - __slots__ = ["enterpriseUserId"] + __slots__ = ("enterpriseUserId",) ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] enterpriseUserId: int def __init__(self, enterpriseUserId: _Optional[int] = ...) -> None: ... class GradientSyncResponse(_message.Message): - __slots__ = ["success", "status", "message"] + __slots__ = ("success", "status", "message") SUCCESS_FIELD_NUMBER: _ClassVar[int] STATUS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -528,7 +640,7 @@ class GradientSyncResponse(_message.Message): def __init__(self, success: bool = ..., status: _Optional[_Union[GradientIntegrationStatus, str]] = ..., message: _Optional[str] = ...) -> None: ... class NetPromoterScoreSurveySubmissionRequest(_message.Message): - __slots__ = ["survey_score", "notes"] + __slots__ = ("survey_score", "notes") SURVEY_SCORE_FIELD_NUMBER: _ClassVar[int] NOTES_FIELD_NUMBER: _ClassVar[int] survey_score: int @@ -536,41 +648,41 @@ class NetPromoterScoreSurveySubmissionRequest(_message.Message): def __init__(self, survey_score: _Optional[int] = ..., notes: _Optional[str] = ...) -> None: ... class NetPromoterScoreSurveySubmissionResponse(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class NetPromoterScorePopupScheduleRequest(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class NetPromoterScorePopupScheduleResponse(_message.Message): - __slots__ = ["show_popup"] + __slots__ = ("show_popup",) SHOW_POPUP_FIELD_NUMBER: _ClassVar[int] show_popup: bool def __init__(self, show_popup: bool = ...) -> None: ... class NetPromoterScorePopupDismissalRequest(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class NetPromoterScorePopupDismissalResponse(_message.Message): - __slots__ = [] + __slots__ = () def __init__(self) -> None: ... class KCMLicenseRequest(_message.Message): - __slots__ = ["enterpriseUserId"] + __slots__ = ("enterpriseUserId",) ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] enterpriseUserId: int def __init__(self, enterpriseUserId: _Optional[int] = ...) -> None: ... class KCMLicenseResponse(_message.Message): - __slots__ = ["message"] + __slots__ = ("message",) MESSAGE_FIELD_NUMBER: _ClassVar[int] message: str def __init__(self, message: _Optional[str] = ...) -> None: ... class EventRequest(_message.Message): - __slots__ = ["eventType", "eventValue", "eventTime", "attributes"] + __slots__ = ("eventType", "eventValue", "eventTime", "attributes") EVENTTYPE_FIELD_NUMBER: _ClassVar[int] EVENTVALUE_FIELD_NUMBER: _ClassVar[int] EVENTTIME_FIELD_NUMBER: _ClassVar[int] @@ -582,13 +694,27 @@ class EventRequest(_message.Message): def __init__(self, eventType: _Optional[_Union[EventType, str]] = ..., eventValue: _Optional[str] = ..., eventTime: _Optional[int] = ..., attributes: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... class EventsRequest(_message.Message): - __slots__ = ["event"] + __slots__ = ("event",) EVENT_FIELD_NUMBER: _ClassVar[int] event: _containers.RepeatedCompositeFieldContainer[EventRequest] def __init__(self, event: _Optional[_Iterable[_Union[EventRequest, _Mapping]]] = ...) -> None: ... +class EventResponse(_message.Message): + __slots__ = ("index", "status") + INDEX_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + index: int + status: bool + def __init__(self, index: _Optional[int] = ..., status: bool = ...) -> None: ... + +class EventsResponse(_message.Message): + __slots__ = ("response",) + RESPONSE_FIELD_NUMBER: _ClassVar[int] + response: _containers.RepeatedCompositeFieldContainer[EventResponse] + def __init__(self, response: _Optional[_Iterable[_Union[EventResponse, _Mapping]]] = ...) -> None: ... + class CustomerCaptureRequest(_message.Message): - __slots__ = ["pageUrl", "tree", "hash", "image", "pageLoadTime", "keyId", "test"] + __slots__ = ("pageUrl", "tree", "hash", "image", "pageLoadTime", "keyId", "test", "issueType", "notes") PAGEURL_FIELD_NUMBER: _ClassVar[int] TREE_FIELD_NUMBER: _ClassVar[int] HASH_FIELD_NUMBER: _ClassVar[int] @@ -596,6 +722,8 @@ class CustomerCaptureRequest(_message.Message): PAGELOADTIME_FIELD_NUMBER: _ClassVar[int] KEYID_FIELD_NUMBER: _ClassVar[int] TEST_FIELD_NUMBER: _ClassVar[int] + ISSUETYPE_FIELD_NUMBER: _ClassVar[int] + NOTES_FIELD_NUMBER: _ClassVar[int] pageUrl: str tree: str hash: str @@ -603,8 +731,303 @@ class CustomerCaptureRequest(_message.Message): pageLoadTime: str keyId: str test: bool - def __init__(self, pageUrl: _Optional[str] = ..., tree: _Optional[str] = ..., hash: _Optional[str] = ..., image: _Optional[str] = ..., pageLoadTime: _Optional[str] = ..., keyId: _Optional[str] = ..., test: bool = ...) -> None: ... + issueType: str + notes: str + def __init__(self, pageUrl: _Optional[str] = ..., tree: _Optional[str] = ..., hash: _Optional[str] = ..., image: _Optional[str] = ..., pageLoadTime: _Optional[str] = ..., keyId: _Optional[str] = ..., test: bool = ..., issueType: _Optional[str] = ..., notes: _Optional[str] = ...) -> None: ... class CustomerCaptureResponse(_message.Message): - __slots__ = [] + __slots__ = () + def __init__(self) -> None: ... + +class Error(_message.Message): + __slots__ = ("code", "message", "extras") + class ExtrasEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + CODE_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + EXTRAS_FIELD_NUMBER: _ClassVar[int] + code: str + message: str + extras: _containers.ScalarMap[str, str] + def __init__(self, code: _Optional[str] = ..., message: _Optional[str] = ..., extras: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class QuotePurchase(_message.Message): + __slots__ = ("quoteTotal", "includedTax", "includedOtherAddons", "taxAmount", "taxLabel", "purchaseIdentifier") + QUOTETOTAL_FIELD_NUMBER: _ClassVar[int] + INCLUDEDTAX_FIELD_NUMBER: _ClassVar[int] + INCLUDEDOTHERADDONS_FIELD_NUMBER: _ClassVar[int] + TAXAMOUNT_FIELD_NUMBER: _ClassVar[int] + TAXLABEL_FIELD_NUMBER: _ClassVar[int] + PURCHASEIDENTIFIER_FIELD_NUMBER: _ClassVar[int] + quoteTotal: float + includedTax: bool + includedOtherAddons: bool + taxAmount: float + taxLabel: str + purchaseIdentifier: str + def __init__(self, quoteTotal: _Optional[float] = ..., includedTax: bool = ..., includedOtherAddons: bool = ..., taxAmount: _Optional[float] = ..., taxLabel: _Optional[str] = ..., purchaseIdentifier: _Optional[str] = ...) -> None: ... + +class PurchaseOptions(_message.Message): + __slots__ = ("inConsole", "externalCheckout") + INCONSOLE_FIELD_NUMBER: _ClassVar[int] + EXTERNALCHECKOUT_FIELD_NUMBER: _ClassVar[int] + inConsole: bool + externalCheckout: bool + def __init__(self, inConsole: bool = ..., externalCheckout: bool = ...) -> None: ... + +class AddonPurchaseOptions(_message.Message): + __slots__ = ("storage", "audit", "breachwatch", "chat", "compliance", "professionalServicesSilver", "professionalServicesPlatinum", "pam", "epm", "secretsManager", "connectionManager", "remoteBrowserIsolation", "nhiTier") + STORAGE_FIELD_NUMBER: _ClassVar[int] + AUDIT_FIELD_NUMBER: _ClassVar[int] + BREACHWATCH_FIELD_NUMBER: _ClassVar[int] + CHAT_FIELD_NUMBER: _ClassVar[int] + COMPLIANCE_FIELD_NUMBER: _ClassVar[int] + PROFESSIONALSERVICESSILVER_FIELD_NUMBER: _ClassVar[int] + PROFESSIONALSERVICESPLATINUM_FIELD_NUMBER: _ClassVar[int] + PAM_FIELD_NUMBER: _ClassVar[int] + EPM_FIELD_NUMBER: _ClassVar[int] + SECRETSMANAGER_FIELD_NUMBER: _ClassVar[int] + CONNECTIONMANAGER_FIELD_NUMBER: _ClassVar[int] + REMOTEBROWSERISOLATION_FIELD_NUMBER: _ClassVar[int] + NHITIER_FIELD_NUMBER: _ClassVar[int] + storage: PurchaseOptions + audit: PurchaseOptions + breachwatch: PurchaseOptions + chat: PurchaseOptions + compliance: PurchaseOptions + professionalServicesSilver: PurchaseOptions + professionalServicesPlatinum: PurchaseOptions + pam: PurchaseOptions + epm: PurchaseOptions + secretsManager: PurchaseOptions + connectionManager: PurchaseOptions + remoteBrowserIsolation: PurchaseOptions + nhiTier: PurchaseOptions + def __init__(self, storage: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., audit: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., breachwatch: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., chat: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., compliance: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., professionalServicesSilver: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., professionalServicesPlatinum: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., pam: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., epm: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., secretsManager: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., connectionManager: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., remoteBrowserIsolation: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., nhiTier: _Optional[_Union[PurchaseOptions, _Mapping]] = ...) -> None: ... + +class AvailablePurchaseOptions(_message.Message): + __slots__ = ("basePlan", "users", "addons") + BASEPLAN_FIELD_NUMBER: _ClassVar[int] + USERS_FIELD_NUMBER: _ClassVar[int] + ADDONS_FIELD_NUMBER: _ClassVar[int] + basePlan: PurchaseOptions + users: PurchaseOptions + addons: AddonPurchaseOptions + def __init__(self, basePlan: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., users: _Optional[_Union[PurchaseOptions, _Mapping]] = ..., addons: _Optional[_Union[AddonPurchaseOptions, _Mapping]] = ...) -> None: ... + +class UpgradeLicenseStatusRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class UpgradeLicenseStatusResponse(_message.Message): + __slots__ = ("allowPurchaseFromConsole", "purchaseOptions", "error") + ALLOWPURCHASEFROMCONSOLE_FIELD_NUMBER: _ClassVar[int] + PURCHASEOPTIONS_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + allowPurchaseFromConsole: bool + purchaseOptions: AvailablePurchaseOptions + error: Error + def __init__(self, allowPurchaseFromConsole: bool = ..., purchaseOptions: _Optional[_Union[AvailablePurchaseOptions, _Mapping]] = ..., error: _Optional[_Union[Error, _Mapping]] = ...) -> None: ... + +class UpgradeLicenseQuotePurchaseRequest(_message.Message): + __slots__ = ("productType", "quantity", "tier") + PRODUCTTYPE_FIELD_NUMBER: _ClassVar[int] + QUANTITY_FIELD_NUMBER: _ClassVar[int] + TIER_FIELD_NUMBER: _ClassVar[int] + productType: PurchaseProductType + quantity: int + tier: int + def __init__(self, productType: _Optional[_Union[PurchaseProductType, str]] = ..., quantity: _Optional[int] = ..., tier: _Optional[int] = ...) -> None: ... + +class UpgradeLicenseQuotePurchaseResponse(_message.Message): + __slots__ = ("success", "quotePurchase", "viewSummaryLink", "error") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + QUOTEPURCHASE_FIELD_NUMBER: _ClassVar[int] + VIEWSUMMARYLINK_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + success: bool + quotePurchase: QuotePurchase + viewSummaryLink: str + error: Error + def __init__(self, success: bool = ..., quotePurchase: _Optional[_Union[QuotePurchase, _Mapping]] = ..., viewSummaryLink: _Optional[str] = ..., error: _Optional[_Union[Error, _Mapping]] = ...) -> None: ... + +class UpgradeLicenseCompletePurchaseRequest(_message.Message): + __slots__ = ("productType", "quantity", "quotePurchase", "tier") + PRODUCTTYPE_FIELD_NUMBER: _ClassVar[int] + QUANTITY_FIELD_NUMBER: _ClassVar[int] + QUOTEPURCHASE_FIELD_NUMBER: _ClassVar[int] + TIER_FIELD_NUMBER: _ClassVar[int] + productType: PurchaseProductType + quantity: int + quotePurchase: QuotePurchase + tier: int + def __init__(self, productType: _Optional[_Union[PurchaseProductType, str]] = ..., quantity: _Optional[int] = ..., quotePurchase: _Optional[_Union[QuotePurchase, _Mapping]] = ..., tier: _Optional[int] = ...) -> None: ... + +class UpgradeLicenseCompletePurchaseResponse(_message.Message): + __slots__ = ("success", "invoiceNumber", "error", "quotePurchase") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + INVOICENUMBER_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + QUOTEPURCHASE_FIELD_NUMBER: _ClassVar[int] + success: bool + invoiceNumber: str + error: Error + quotePurchase: QuotePurchase + def __init__(self, success: bool = ..., invoiceNumber: _Optional[str] = ..., error: _Optional[_Union[Error, _Mapping]] = ..., quotePurchase: _Optional[_Union[QuotePurchase, _Mapping]] = ...) -> None: ... + +class EnterpriseBasePlan(_message.Message): + __slots__ = ("baseplanVersion", "cost") + class EnterpriseBasePlanVersion(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + UNKNOWN: _ClassVar[EnterpriseBasePlan.EnterpriseBasePlanVersion] + BUSINESS_STARTER: _ClassVar[EnterpriseBasePlan.EnterpriseBasePlanVersion] + BUSINESS: _ClassVar[EnterpriseBasePlan.EnterpriseBasePlanVersion] + ENTERPRISE: _ClassVar[EnterpriseBasePlan.EnterpriseBasePlanVersion] + UNKNOWN: EnterpriseBasePlan.EnterpriseBasePlanVersion + BUSINESS_STARTER: EnterpriseBasePlan.EnterpriseBasePlanVersion + BUSINESS: EnterpriseBasePlan.EnterpriseBasePlanVersion + ENTERPRISE: EnterpriseBasePlan.EnterpriseBasePlanVersion + BASEPLANVERSION_FIELD_NUMBER: _ClassVar[int] + COST_FIELD_NUMBER: _ClassVar[int] + baseplanVersion: EnterpriseBasePlan.EnterpriseBasePlanVersion + cost: Cost + def __init__(self, baseplanVersion: _Optional[_Union[EnterpriseBasePlan.EnterpriseBasePlanVersion, str]] = ..., cost: _Optional[_Union[Cost, _Mapping]] = ...) -> None: ... + +class SubscriptionEnterprisePricingRequest(_message.Message): + __slots__ = () def __init__(self) -> None: ... + +class NhiTierPlan(_message.Message): + __slots__ = ("tierId", "nhiCeiling", "cost", "productId", "nhiFloor") + TIERID_FIELD_NUMBER: _ClassVar[int] + NHICEILING_FIELD_NUMBER: _ClassVar[int] + COST_FIELD_NUMBER: _ClassVar[int] + PRODUCTID_FIELD_NUMBER: _ClassVar[int] + NHIFLOOR_FIELD_NUMBER: _ClassVar[int] + tierId: int + nhiCeiling: int + cost: Cost + productId: int + nhiFloor: int + def __init__(self, tierId: _Optional[int] = ..., nhiCeiling: _Optional[int] = ..., cost: _Optional[_Union[Cost, _Mapping]] = ..., productId: _Optional[int] = ..., nhiFloor: _Optional[int] = ...) -> None: ... + +class SubscriptionEnterprisePricingResponse(_message.Message): + __slots__ = ("basePlans", "addons", "filePlans", "nhiTierPlans") + BASEPLANS_FIELD_NUMBER: _ClassVar[int] + ADDONS_FIELD_NUMBER: _ClassVar[int] + FILEPLANS_FIELD_NUMBER: _ClassVar[int] + NHITIERPLANS_FIELD_NUMBER: _ClassVar[int] + basePlans: _containers.RepeatedCompositeFieldContainer[EnterpriseBasePlan] + addons: _containers.RepeatedCompositeFieldContainer[Addon] + filePlans: _containers.RepeatedCompositeFieldContainer[FilePlan] + nhiTierPlans: _containers.RepeatedCompositeFieldContainer[NhiTierPlan] + def __init__(self, basePlans: _Optional[_Iterable[_Union[EnterpriseBasePlan, _Mapping]]] = ..., addons: _Optional[_Iterable[_Union[Addon, _Mapping]]] = ..., filePlans: _Optional[_Iterable[_Union[FilePlan, _Mapping]]] = ..., nhiTierPlans: _Optional[_Iterable[_Union[NhiTierPlan, _Mapping]]] = ...) -> None: ... + +class SingularDeviceIdentifier(_message.Message): + __slots__ = ("id", "idType") + ID_FIELD_NUMBER: _ClassVar[int] + IDTYPE_FIELD_NUMBER: _ClassVar[int] + id: str + idType: IdentifierType + def __init__(self, id: _Optional[str] = ..., idType: _Optional[_Union[IdentifierType, str]] = ...) -> None: ... + +class SingularSharedData(_message.Message): + __slots__ = ("platform", "osVersion", "make", "model", "locale", "build", "appIdentifier", "attAuthorizationStatus") + PLATFORM_FIELD_NUMBER: _ClassVar[int] + OSVERSION_FIELD_NUMBER: _ClassVar[int] + MAKE_FIELD_NUMBER: _ClassVar[int] + MODEL_FIELD_NUMBER: _ClassVar[int] + LOCALE_FIELD_NUMBER: _ClassVar[int] + BUILD_FIELD_NUMBER: _ClassVar[int] + APPIDENTIFIER_FIELD_NUMBER: _ClassVar[int] + ATTAUTHORIZATIONSTATUS_FIELD_NUMBER: _ClassVar[int] + platform: str + osVersion: str + make: str + model: str + locale: str + build: str + appIdentifier: str + attAuthorizationStatus: int + def __init__(self, platform: _Optional[str] = ..., osVersion: _Optional[str] = ..., make: _Optional[str] = ..., model: _Optional[str] = ..., locale: _Optional[str] = ..., build: _Optional[str] = ..., appIdentifier: _Optional[str] = ..., attAuthorizationStatus: _Optional[int] = ...) -> None: ... + +class SingularSessionRequest(_message.Message): + __slots__ = ("deviceIdentifiers", "sharedData", "applicationVersion", "install", "installTime", "updateTime", "installSource", "installReceipt", "openuri", "ddlEnabled", "singularLinkResolveRequired", "installRef", "metaRef", "attributionToken") + DEVICEIDENTIFIERS_FIELD_NUMBER: _ClassVar[int] + SHAREDDATA_FIELD_NUMBER: _ClassVar[int] + APPLICATIONVERSION_FIELD_NUMBER: _ClassVar[int] + INSTALL_FIELD_NUMBER: _ClassVar[int] + INSTALLTIME_FIELD_NUMBER: _ClassVar[int] + UPDATETIME_FIELD_NUMBER: _ClassVar[int] + INSTALLSOURCE_FIELD_NUMBER: _ClassVar[int] + INSTALLRECEIPT_FIELD_NUMBER: _ClassVar[int] + OPENURI_FIELD_NUMBER: _ClassVar[int] + DDLENABLED_FIELD_NUMBER: _ClassVar[int] + SINGULARLINKRESOLVEREQUIRED_FIELD_NUMBER: _ClassVar[int] + INSTALLREF_FIELD_NUMBER: _ClassVar[int] + METAREF_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTIONTOKEN_FIELD_NUMBER: _ClassVar[int] + deviceIdentifiers: _containers.RepeatedCompositeFieldContainer[SingularDeviceIdentifier] + sharedData: SingularSharedData + applicationVersion: str + install: bool + installTime: int + updateTime: int + installSource: str + installReceipt: str + openuri: str + ddlEnabled: bool + singularLinkResolveRequired: bool + installRef: str + metaRef: str + attributionToken: str + def __init__(self, deviceIdentifiers: _Optional[_Iterable[_Union[SingularDeviceIdentifier, _Mapping]]] = ..., sharedData: _Optional[_Union[SingularSharedData, _Mapping]] = ..., applicationVersion: _Optional[str] = ..., install: bool = ..., installTime: _Optional[int] = ..., updateTime: _Optional[int] = ..., installSource: _Optional[str] = ..., installReceipt: _Optional[str] = ..., openuri: _Optional[str] = ..., ddlEnabled: bool = ..., singularLinkResolveRequired: bool = ..., installRef: _Optional[str] = ..., metaRef: _Optional[str] = ..., attributionToken: _Optional[str] = ...) -> None: ... + +class SingularEventRequest(_message.Message): + __slots__ = ("deviceIdentifiers", "sharedData", "eventName") + DEVICEIDENTIFIERS_FIELD_NUMBER: _ClassVar[int] + SHAREDDATA_FIELD_NUMBER: _ClassVar[int] + EVENTNAME_FIELD_NUMBER: _ClassVar[int] + deviceIdentifiers: _containers.RepeatedCompositeFieldContainer[SingularDeviceIdentifier] + sharedData: SingularSharedData + eventName: str + def __init__(self, deviceIdentifiers: _Optional[_Iterable[_Union[SingularDeviceIdentifier, _Mapping]]] = ..., sharedData: _Optional[_Union[SingularSharedData, _Mapping]] = ..., eventName: _Optional[str] = ...) -> None: ... + +class ActivePamCountRequest(_message.Message): + __slots__ = ("enterpriseId",) + ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] + enterpriseId: int + def __init__(self, enterpriseId: _Optional[int] = ...) -> None: ... + +class ActivePamCountResponse(_message.Message): + __slots__ = ("pamCount",) + PAMCOUNT_FIELD_NUMBER: _ClassVar[int] + pamCount: int + def __init__(self, pamCount: _Optional[int] = ...) -> None: ... + +class NhiEnterpriseRequest(_message.Message): + __slots__ = ("enterpriseId", "startTime", "endTime") + ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] + STARTTIME_FIELD_NUMBER: _ClassVar[int] + ENDTIME_FIELD_NUMBER: _ClassVar[int] + enterpriseId: int + startTime: int + endTime: int + def __init__(self, enterpriseId: _Optional[int] = ..., startTime: _Optional[int] = ..., endTime: _Optional[int] = ...) -> None: ... + +class NhiMetricsRequest(_message.Message): + __slots__ = ("enterpriseIds", "startTime", "endTime", "enterprises") + ENTERPRISEIDS_FIELD_NUMBER: _ClassVar[int] + STARTTIME_FIELD_NUMBER: _ClassVar[int] + ENDTIME_FIELD_NUMBER: _ClassVar[int] + ENTERPRISES_FIELD_NUMBER: _ClassVar[int] + enterpriseIds: _containers.RepeatedScalarFieldContainer[int] + startTime: int + endTime: int + enterprises: _containers.RepeatedCompositeFieldContainer[NhiEnterpriseRequest] + def __init__(self, enterpriseIds: _Optional[_Iterable[int]] = ..., startTime: _Optional[int] = ..., endTime: _Optional[int] = ..., enterprises: _Optional[_Iterable[_Union[NhiEnterpriseRequest, _Mapping]]] = ...) -> None: ... diff --git a/keepercommander/proto/DeviceManagement_pb2.py b/keepercommander/proto/DeviceManagement_pb2.py index c3cf71721..992e95283 100644 --- a/keepercommander/proto/DeviceManagement_pb2.py +++ b/keepercommander/proto/DeviceManagement_pb2.py @@ -2,13 +2,21 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: DeviceManagement.proto -# Protobuf Python Version: 5.29.3 +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder - +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'DeviceManagement.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -69,4 +77,4 @@ _globals['_DEVICEADMINACTIONRESPONSE']._serialized_end=2110 _globals['_DEVICEADMINACTIONRESULT']._serialized_start=2113 _globals['_DEVICEADMINACTIONRESULT']._serialized_end=2322 -# @@protoc_insertion_point(module_scope) \ No newline at end of file +# @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/GraphSync_pb2.py b/keepercommander/proto/GraphSync_pb2.py index 892f43c4f..1599095b6 100644 --- a/keepercommander/proto/GraphSync_pb2.py +++ b/keepercommander/proto/GraphSync_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: GraphSync.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'GraphSync.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/GraphSync_pb2.pyi b/keepercommander/proto/GraphSync_pb2.pyi index 26c41dc37..594a4eecc 100644 --- a/keepercommander/proto/GraphSync_pb2.pyi +++ b/keepercommander/proto/GraphSync_pb2.pyi @@ -2,8 +2,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -138,7 +137,7 @@ 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: _Optional[bool] = ...) -> None: ... + def __init__(self, streamId: _Optional[bytes] = ..., syncPoint: _Optional[int] = ..., data: _Optional[_Iterable[_Union[GraphSyncDataPlus, _Mapping]]] = ..., hasMore: bool = ...) -> None: ... class GraphSyncMultiQuery(_message.Message): __slots__ = ("queries",) diff --git a/keepercommander/proto/NotificationCenter_pb2.py b/keepercommander/proto/NotificationCenter_pb2.py index eef3b73fc..583169f23 100644 --- a/keepercommander/proto/NotificationCenter_pb2.py +++ b/keepercommander/proto/NotificationCenter_pb2.py @@ -1,18 +1,31 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: NotificationCenter.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'NotificationCenter.proto' +) +# @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() + from . import GraphSync_pb2 as GraphSync__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18NotificationCenter.proto\x12\x12NotificationCenter\x1a\x0fGraphSync.proto\".\n\rEncryptedData\x12\x0f\n\x07version\x18\x01 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"2\n\x15NotificationParameter\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"\xe2\x02\n\x0cNotification\x12\x32\n\x04type\x18\x01 \x01(\x0e\x32$.NotificationCenter.NotificationType\x12>\n\x08\x63\x61tegory\x18\x02 \x01(\x0e\x32(.NotificationCenter.NotificationCategoryB\x02\x18\x01\x12\'\n\x06sender\x18\x03 \x01(\x0b\x32\x17.GraphSync.GraphSyncRef\x12\x16\n\x0esenderFullName\x18\x04 \x01(\t\x12\x38\n\rencryptedData\x18\x05 \x01(\x0b\x32!.NotificationCenter.EncryptedData\x12%\n\x04refs\x18\x06 \x03(\x0b\x32\x17.GraphSync.GraphSyncRef\x12<\n\ncategories\x18\x07 \x03(\x0e\x32(.NotificationCenter.NotificationCategory\"\x97\x01\n\x14NotificationReadMark\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x1c\n\x14notification_edge_id\x18\x02 \x01(\x03\x12\x14\n\x0cmark_edge_id\x18\x03 \x01(\x03\x12>\n\nreadStatus\x18\x04 \x01(\x0e\x32*.NotificationCenter.NotificationReadStatus\"\xa6\x02\n\x13NotificationContent\x12\x38\n\x0cnotification\x18\x01 \x01(\x0b\x32 .NotificationCenter.NotificationH\x00\x12@\n\nreadStatus\x18\x02 \x01(\x0e\x32*.NotificationCenter.NotificationReadStatusH\x00\x12H\n\x0e\x61pprovalStatus\x18\x03 \x01(\x0e\x32..NotificationCenter.NotificationApprovalStatusH\x00\x12\x17\n\rtrimmingPoint\x18\x04 \x01(\x08H\x00\x12\x15\n\rclientTypeIDs\x18\x05 \x03(\x05\x12\x11\n\tdeviceIDs\x18\x06 \x03(\x03\x42\x06\n\x04type\"o\n\x13NotificationWrapper\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x38\n\x07\x63ontent\x18\x02 \x01(\x0b\x32\'.NotificationCenter.NotificationContent\x12\x11\n\ttimestamp\x18\x03 \x01(\x03\"m\n\x10NotificationSync\x12\x35\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\'.NotificationCenter.NotificationWrapper\x12\x11\n\tsyncPoint\x18\x02 \x01(\x03\x12\x0f\n\x07hasMore\x18\x03 \x01(\x08\"g\n\x10ReadStatusUpdate\x12\x17\n\x0fnotificationUid\x18\x01 \x01(\x0c\x12:\n\x06status\x18\x02 \x01(\x0e\x32*.NotificationCenter.NotificationReadStatus\"o\n\x14\x41pprovalStatusUpdate\x12\x17\n\x0fnotificationUid\x18\x01 \x01(\x0c\x12>\n\x06status\x18\x02 \x01(\x0e\x32..NotificationCenter.NotificationApprovalStatus\"^\n\x1cProcessMarkReadEventsRequest\x12>\n\x10readStatusUpdate\x18\x01 \x03(\x0b\x32$.NotificationCenter.ReadStatusUpdate\"\xa8\x01\n\x17NotificationSendRequest\x12+\n\nrecipients\x18\x01 \x03(\x0b\x32\x17.GraphSync.GraphSyncRef\x12\x36\n\x0cnotification\x18\x02 \x01(\x0b\x32 .NotificationCenter.Notification\x12\x15\n\rclientTypeIDs\x18\x03 \x03(\x05\x12\x11\n\tdeviceIDs\x18\x04 \x03(\x03\"^\n\x18NotificationsSendRequest\x12\x42\n\rnotifications\x18\x01 \x03(\x0b\x32+.NotificationCenter.NotificationSendRequest\",\n\x17NotificationSyncRequest\x12\x11\n\tsyncPoint\x18\x01 \x01(\x03\"e\n(NotificationsApprovalStatusUpdateRequest\x12\x39\n\x07updates\x18\x01 \x03(\x0b\x32(.NotificationCenter.ApprovalStatusUpdate*\x9f\x01\n\x14NotificationCategory\x12\x12\n\x0eNC_UNSPECIFIED\x10\x00\x12\x0e\n\nNC_ACCOUNT\x10\x01\x12\x0e\n\nNC_SHARING\x10\x02\x12\x11\n\rNC_ENTERPRISE\x10\x03\x12\x0f\n\x0bNC_SECURITY\x10\x04\x12\x0e\n\nNC_REQUEST\x10\x05\x12\r\n\tNC_SYSTEM\x10\x06\x12\x10\n\x0cNC_PROMOTION\x10\x07*\x9e\x04\n\x10NotificationType\x12\x12\n\x0eNT_UNSPECIFIED\x10\x00\x12\x0c\n\x08NT_ALERT\x10\x01\x12\x16\n\x12NT_DEVICE_APPROVAL\x10\x02\x12\x1a\n\x16NT_MASTER_PASS_UPDATED\x10\x03\x12\x15\n\x11NT_SHARE_APPROVAL\x10\x04\x12\x1e\n\x1aNT_SHARE_APPROVAL_APPROVED\x10\x05\x12\r\n\tNT_SHARED\x10\x06\x12\x12\n\x0eNT_TRANSFERRED\x10\x07\x12\x1c\n\x18NT_LICENSE_LIMIT_REACHED\x10\x08\x12\x17\n\x13NT_APPROVAL_REQUEST\x10\t\x12\x18\n\x14NT_APPROVED_RESPONSE\x10\n\x12\x16\n\x12NT_DENIED_RESPONSE\x10\x0b\x12\x15\n\x11NT_2FA_CONFIGURED\x10\x0c\x12\x1c\n\x18NT_SHARE_APPROVAL_DENIED\x10\r\x12\x1f\n\x1bNT_DEVICE_APPROVAL_APPROVED\x10\x0e\x12\x1d\n\x19NT_DEVICE_APPROVAL_DENIED\x10\x0f\x12\x16\n\x12NT_ACCOUNT_CREATED\x10\x10\x12\x12\n\x0eNT_2FA_ENABLED\x10\x11\x12\x13\n\x0fNT_2FA_DISABLED\x10\x12\x12\x1c\n\x18NT_SECURITY_KEYS_ENABLED\x10\x13\x12\x1d\n\x19NT_SECURITY_KEYS_DISABLED\x10\x14*Y\n\x16NotificationReadStatus\x12\x13\n\x0fNRS_UNSPECIFIED\x10\x00\x12\x0c\n\x08NRS_LAST\x10\x01\x12\x0c\n\x08NRS_READ\x10\x02\x12\x0e\n\nNRS_UNREAD\x10\x03*\x99\x01\n\x1aNotificationApprovalStatus\x12\x13\n\x0fNAS_UNSPECIFIED\x10\x00\x12\x10\n\x0cNAS_APPROVED\x10\x01\x12\x0e\n\nNAS_DENIED\x10\x02\x12\x1c\n\x18NAS_LOST_APPROVAL_RIGHTS\x10\x03\x12\x13\n\x0fNAS_LOST_ACCESS\x10\x04\x12\x11\n\rNAS_ESCALATED\x10\x05\x42.\n\x18\x63om.keepersecurity.protoB\x12NotificationCenterb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18NotificationCenter.proto\x12\x12NotificationCenter\x1a\x0fGraphSync.proto\".\n\rEncryptedData\x12\x0f\n\x07version\x18\x01 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"2\n\x15NotificationParameter\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"\xa1\x03\n\x0cNotification\x12\x32\n\x04type\x18\x01 \x01(\x0e\x32$.NotificationCenter.NotificationType\x12>\n\x08\x63\x61tegory\x18\x02 \x01(\x0e\x32(.NotificationCenter.NotificationCategoryB\x02\x18\x01\x12\'\n\x06sender\x18\x03 \x01(\x0b\x32\x17.GraphSync.GraphSyncRef\x12\x16\n\x0esenderFullName\x18\x04 \x01(\t\x12\x38\n\rencryptedData\x18\x05 \x01(\x0b\x32!.NotificationCenter.EncryptedData\x12%\n\x04refs\x18\x06 \x03(\x0b\x32\x17.GraphSync.GraphSyncRef\x12<\n\ncategories\x18\x07 \x03(\x0e\x32(.NotificationCenter.NotificationCategory\x12=\n\nparameters\x18\x08 \x03(\x0b\x32).NotificationCenter.NotificationParameter\"\x97\x01\n\x14NotificationReadMark\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x1c\n\x14notification_edge_id\x18\x02 \x01(\x03\x12\x14\n\x0cmark_edge_id\x18\x03 \x01(\x03\x12>\n\nreadStatus\x18\x04 \x01(\x0e\x32*.NotificationCenter.NotificationReadStatus\"\xa6\x02\n\x13NotificationContent\x12\x38\n\x0cnotification\x18\x01 \x01(\x0b\x32 .NotificationCenter.NotificationH\x00\x12@\n\nreadStatus\x18\x02 \x01(\x0e\x32*.NotificationCenter.NotificationReadStatusH\x00\x12H\n\x0e\x61pprovalStatus\x18\x03 \x01(\x0e\x32..NotificationCenter.NotificationApprovalStatusH\x00\x12\x17\n\rtrimmingPoint\x18\x04 \x01(\x08H\x00\x12\x15\n\rclientTypeIDs\x18\x05 \x03(\x05\x12\x11\n\tdeviceIDs\x18\x06 \x03(\x03\x42\x06\n\x04type\"o\n\x13NotificationWrapper\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x38\n\x07\x63ontent\x18\x02 \x01(\x0b\x32\'.NotificationCenter.NotificationContent\x12\x11\n\ttimestamp\x18\x03 \x01(\x03\"m\n\x10NotificationSync\x12\x35\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\'.NotificationCenter.NotificationWrapper\x12\x11\n\tsyncPoint\x18\x02 \x01(\x03\x12\x0f\n\x07hasMore\x18\x03 \x01(\x08\"g\n\x10ReadStatusUpdate\x12\x17\n\x0fnotificationUid\x18\x01 \x01(\x0c\x12:\n\x06status\x18\x02 \x01(\x0e\x32*.NotificationCenter.NotificationReadStatus\"o\n\x14\x41pprovalStatusUpdate\x12\x17\n\x0fnotificationUid\x18\x01 \x01(\x0c\x12>\n\x06status\x18\x02 \x01(\x0e\x32..NotificationCenter.NotificationApprovalStatus\"^\n\x1cProcessMarkReadEventsRequest\x12>\n\x10readStatusUpdate\x18\x01 \x03(\x0b\x32$.NotificationCenter.ReadStatusUpdate\"\xd6\x01\n\x17NotificationSendRequest\x12+\n\nrecipients\x18\x01 \x03(\x0b\x32\x17.GraphSync.GraphSyncRef\x12\x36\n\x0cnotification\x18\x02 \x01(\x0b\x32 .NotificationCenter.Notification\x12\x15\n\rclientTypeIDs\x18\x03 \x03(\x05\x12\x11\n\tdeviceIDs\x18\x04 \x03(\x03\x12\x1a\n\rpredefinedUid\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x42\x10\n\x0e_predefinedUid\"^\n\x18NotificationsSendRequest\x12\x42\n\rnotifications\x18\x01 \x03(\x0b\x32+.NotificationCenter.NotificationSendRequest\",\n\x17NotificationSyncRequest\x12\x11\n\tsyncPoint\x18\x01 \x01(\x03\"9\n\x10SentNotification\x12\x0c\n\x04user\x18\x01 \x01(\x05\x12\x17\n\x0fnotificationUid\x18\x02 \x01(\x0c\"\xa7\x01\n(NotificationsApprovalStatusUpdateRequest\x12>\n\x06status\x18\x01 \x01(\x0e\x32..NotificationCenter.NotificationApprovalStatus\x12;\n\rnotifications\x18\x02 \x03(\x0b\x32$.NotificationCenter.SentNotification*\x9f\x01\n\x14NotificationCategory\x12\x12\n\x0eNC_UNSPECIFIED\x10\x00\x12\x0e\n\nNC_ACCOUNT\x10\x01\x12\x0e\n\nNC_SHARING\x10\x02\x12\x11\n\rNC_ENTERPRISE\x10\x03\x12\x0f\n\x0bNC_SECURITY\x10\x04\x12\x0e\n\nNC_REQUEST\x10\x05\x12\r\n\tNC_SYSTEM\x10\x06\x12\x10\n\x0cNC_PROMOTION\x10\x07*\xe3\x04\n\x10NotificationType\x12\x12\n\x0eNT_UNSPECIFIED\x10\x00\x12\x0c\n\x08NT_ALERT\x10\x01\x12\x16\n\x12NT_DEVICE_APPROVAL\x10\x02\x12\x1a\n\x16NT_MASTER_PASS_UPDATED\x10\x03\x12\x15\n\x11NT_SHARE_APPROVAL\x10\x04\x12\x1e\n\x1aNT_SHARE_APPROVAL_APPROVED\x10\x05\x12\r\n\tNT_SHARED\x10\x06\x12\x12\n\x0eNT_TRANSFERRED\x10\x07\x12\x1c\n\x18NT_LICENSE_LIMIT_REACHED\x10\x08\x12\x17\n\x13NT_APPROVAL_REQUEST\x10\t\x12\x18\n\x14NT_APPROVED_RESPONSE\x10\n\x12\x16\n\x12NT_DENIED_RESPONSE\x10\x0b\x12\x15\n\x11NT_2FA_CONFIGURED\x10\x0c\x12\x1c\n\x18NT_SHARE_APPROVAL_DENIED\x10\r\x12\x1f\n\x1bNT_DEVICE_APPROVAL_APPROVED\x10\x0e\x12\x1d\n\x19NT_DEVICE_APPROVAL_DENIED\x10\x0f\x12\x16\n\x12NT_ACCOUNT_CREATED\x10\x10\x12\x12\n\x0eNT_2FA_ENABLED\x10\x11\x12\x13\n\x0fNT_2FA_DISABLED\x10\x12\x12\x1c\n\x18NT_SECURITY_KEYS_ENABLED\x10\x13\x12\x1d\n\x19NT_SECURITY_KEYS_DISABLED\x10\x14\x12#\n\x1fNT_SSL_CERTIFICATE_EXPIRES_SOON\x10\x15\x12\x1e\n\x1aNT_SSL_CERTIFICATE_EXPIRED\x10\x16*Y\n\x16NotificationReadStatus\x12\x13\n\x0fNRS_UNSPECIFIED\x10\x00\x12\x0c\n\x08NRS_LAST\x10\x01\x12\x0c\n\x08NRS_READ\x10\x02\x12\x0e\n\nNRS_UNREAD\x10\x03*\x86\x01\n\x1aNotificationApprovalStatus\x12\x13\n\x0fNAS_UNSPECIFIED\x10\x00\x12\x10\n\x0cNAS_APPROVED\x10\x01\x12\x0e\n\nNAS_DENIED\x10\x02\x12\x1c\n\x18NAS_LOST_APPROVAL_RIGHTS\x10\x03\x12\x13\n\x0fNAS_LOST_ACCESS\x10\x04\x42.\n\x18\x63om.keepersecurity.protoB\x12NotificationCenterb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -22,40 +35,42 @@ _globals['DESCRIPTOR']._serialized_options = b'\n\030com.keepersecurity.protoB\022NotificationCenter' _globals['_NOTIFICATION'].fields_by_name['category']._loaded_options = None _globals['_NOTIFICATION'].fields_by_name['category']._serialized_options = b'\030\001' - _globals['_NOTIFICATIONCATEGORY']._serialized_start=1928 - _globals['_NOTIFICATIONCATEGORY']._serialized_end=2087 - _globals['_NOTIFICATIONTYPE']._serialized_start=2090 - _globals['_NOTIFICATIONTYPE']._serialized_end=2632 - _globals['_NOTIFICATIONREADSTATUS']._serialized_start=2634 - _globals['_NOTIFICATIONREADSTATUS']._serialized_end=2723 - _globals['_NOTIFICATIONAPPROVALSTATUS']._serialized_start=2726 - _globals['_NOTIFICATIONAPPROVALSTATUS']._serialized_end=2879 + _globals['_NOTIFICATIONCATEGORY']._serialized_start=2163 + _globals['_NOTIFICATIONCATEGORY']._serialized_end=2322 + _globals['_NOTIFICATIONTYPE']._serialized_start=2325 + _globals['_NOTIFICATIONTYPE']._serialized_end=2936 + _globals['_NOTIFICATIONREADSTATUS']._serialized_start=2938 + _globals['_NOTIFICATIONREADSTATUS']._serialized_end=3027 + _globals['_NOTIFICATIONAPPROVALSTATUS']._serialized_start=3030 + _globals['_NOTIFICATIONAPPROVALSTATUS']._serialized_end=3164 _globals['_ENCRYPTEDDATA']._serialized_start=65 _globals['_ENCRYPTEDDATA']._serialized_end=111 _globals['_NOTIFICATIONPARAMETER']._serialized_start=113 _globals['_NOTIFICATIONPARAMETER']._serialized_end=163 _globals['_NOTIFICATION']._serialized_start=166 - _globals['_NOTIFICATION']._serialized_end=520 - _globals['_NOTIFICATIONREADMARK']._serialized_start=523 - _globals['_NOTIFICATIONREADMARK']._serialized_end=674 - _globals['_NOTIFICATIONCONTENT']._serialized_start=677 - _globals['_NOTIFICATIONCONTENT']._serialized_end=971 - _globals['_NOTIFICATIONWRAPPER']._serialized_start=973 - _globals['_NOTIFICATIONWRAPPER']._serialized_end=1084 - _globals['_NOTIFICATIONSYNC']._serialized_start=1086 - _globals['_NOTIFICATIONSYNC']._serialized_end=1195 - _globals['_READSTATUSUPDATE']._serialized_start=1197 - _globals['_READSTATUSUPDATE']._serialized_end=1300 - _globals['_APPROVALSTATUSUPDATE']._serialized_start=1302 - _globals['_APPROVALSTATUSUPDATE']._serialized_end=1413 - _globals['_PROCESSMARKREADEVENTSREQUEST']._serialized_start=1415 - _globals['_PROCESSMARKREADEVENTSREQUEST']._serialized_end=1509 - _globals['_NOTIFICATIONSENDREQUEST']._serialized_start=1512 - _globals['_NOTIFICATIONSENDREQUEST']._serialized_end=1680 - _globals['_NOTIFICATIONSSENDREQUEST']._serialized_start=1682 - _globals['_NOTIFICATIONSSENDREQUEST']._serialized_end=1776 - _globals['_NOTIFICATIONSYNCREQUEST']._serialized_start=1778 - _globals['_NOTIFICATIONSYNCREQUEST']._serialized_end=1822 - _globals['_NOTIFICATIONSAPPROVALSTATUSUPDATEREQUEST']._serialized_start=1824 - _globals['_NOTIFICATIONSAPPROVALSTATUSUPDATEREQUEST']._serialized_end=1925 + _globals['_NOTIFICATION']._serialized_end=583 + _globals['_NOTIFICATIONREADMARK']._serialized_start=586 + _globals['_NOTIFICATIONREADMARK']._serialized_end=737 + _globals['_NOTIFICATIONCONTENT']._serialized_start=740 + _globals['_NOTIFICATIONCONTENT']._serialized_end=1034 + _globals['_NOTIFICATIONWRAPPER']._serialized_start=1036 + _globals['_NOTIFICATIONWRAPPER']._serialized_end=1147 + _globals['_NOTIFICATIONSYNC']._serialized_start=1149 + _globals['_NOTIFICATIONSYNC']._serialized_end=1258 + _globals['_READSTATUSUPDATE']._serialized_start=1260 + _globals['_READSTATUSUPDATE']._serialized_end=1363 + _globals['_APPROVALSTATUSUPDATE']._serialized_start=1365 + _globals['_APPROVALSTATUSUPDATE']._serialized_end=1476 + _globals['_PROCESSMARKREADEVENTSREQUEST']._serialized_start=1478 + _globals['_PROCESSMARKREADEVENTSREQUEST']._serialized_end=1572 + _globals['_NOTIFICATIONSENDREQUEST']._serialized_start=1575 + _globals['_NOTIFICATIONSENDREQUEST']._serialized_end=1789 + _globals['_NOTIFICATIONSSENDREQUEST']._serialized_start=1791 + _globals['_NOTIFICATIONSSENDREQUEST']._serialized_end=1885 + _globals['_NOTIFICATIONSYNCREQUEST']._serialized_start=1887 + _globals['_NOTIFICATIONSYNCREQUEST']._serialized_end=1931 + _globals['_SENTNOTIFICATION']._serialized_start=1933 + _globals['_SENTNOTIFICATION']._serialized_end=1990 + _globals['_NOTIFICATIONSAPPROVALSTATUSUPDATEREQUEST']._serialized_start=1993 + _globals['_NOTIFICATIONSAPPROVALSTATUSUPDATEREQUEST']._serialized_end=2160 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/NotificationCenter_pb2.pyi b/keepercommander/proto/NotificationCenter_pb2.pyi index dfcecea36..ea89c059d 100644 --- a/keepercommander/proto/NotificationCenter_pb2.pyi +++ b/keepercommander/proto/NotificationCenter_pb2.pyi @@ -3,8 +3,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -42,6 +41,8 @@ class NotificationType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): NT_2FA_DISABLED: _ClassVar[NotificationType] NT_SECURITY_KEYS_ENABLED: _ClassVar[NotificationType] NT_SECURITY_KEYS_DISABLED: _ClassVar[NotificationType] + NT_SSL_CERTIFICATE_EXPIRES_SOON: _ClassVar[NotificationType] + NT_SSL_CERTIFICATE_EXPIRED: _ClassVar[NotificationType] class NotificationReadStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -86,6 +87,8 @@ NT_2FA_ENABLED: NotificationType NT_2FA_DISABLED: NotificationType NT_SECURITY_KEYS_ENABLED: NotificationType NT_SECURITY_KEYS_DISABLED: NotificationType +NT_SSL_CERTIFICATE_EXPIRES_SOON: NotificationType +NT_SSL_CERTIFICATE_EXPIRED: NotificationType NRS_UNSPECIFIED: NotificationReadStatus NRS_LAST: NotificationReadStatus NRS_READ: NotificationReadStatus @@ -158,7 +161,7 @@ class NotificationContent(_message.Message): trimmingPoint: bool clientTypeIDs: _containers.RepeatedScalarFieldContainer[int] deviceIDs: _containers.RepeatedScalarFieldContainer[int] - def __init__(self, notification: _Optional[_Union[Notification, _Mapping]] = ..., readStatus: _Optional[_Union[NotificationReadStatus, str]] = ..., approvalStatus: _Optional[_Union[NotificationApprovalStatus, str]] = ..., trimmingPoint: _Optional[bool] = ..., clientTypeIDs: _Optional[_Iterable[int]] = ..., deviceIDs: _Optional[_Iterable[int]] = ...) -> None: ... + def __init__(self, notification: _Optional[_Union[Notification, _Mapping]] = ..., readStatus: _Optional[_Union[NotificationReadStatus, str]] = ..., approvalStatus: _Optional[_Union[NotificationApprovalStatus, str]] = ..., trimmingPoint: bool = ..., clientTypeIDs: _Optional[_Iterable[int]] = ..., deviceIDs: _Optional[_Iterable[int]] = ...) -> None: ... class NotificationWrapper(_message.Message): __slots__ = ("uid", "content", "timestamp") @@ -178,7 +181,7 @@ class NotificationSync(_message.Message): data: _containers.RepeatedCompositeFieldContainer[NotificationWrapper] syncPoint: int hasMore: bool - def __init__(self, data: _Optional[_Iterable[_Union[NotificationWrapper, _Mapping]]] = ..., syncPoint: _Optional[int] = ..., hasMore: _Optional[bool] = ...) -> None: ... + def __init__(self, data: _Optional[_Iterable[_Union[NotificationWrapper, _Mapping]]] = ..., syncPoint: _Optional[int] = ..., hasMore: bool = ...) -> None: ... class ReadStatusUpdate(_message.Message): __slots__ = ("notificationUid", "status") diff --git a/keepercommander/proto/SyncDown_pb2.py b/keepercommander/proto/SyncDown_pb2.py index 17ac2d397..a3f99608a 100644 --- a/keepercommander/proto/SyncDown_pb2.py +++ b/keepercommander/proto/SyncDown_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: SyncDown.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'SyncDown.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/SyncDown_pb2.pyi b/keepercommander/proto/SyncDown_pb2.pyi index de75c9162..8c1b62221 100644 --- a/keepercommander/proto/SyncDown_pb2.pyi +++ b/keepercommander/proto/SyncDown_pb2.pyi @@ -10,8 +10,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -41,7 +40,7 @@ class SyncDownRequest(_message.Message): continuationToken: bytes dataVersion: int debug: bool - def __init__(self, continuationToken: _Optional[bytes] = ..., dataVersion: _Optional[int] = ..., debug: _Optional[bool] = ...) -> None: ... + def __init__(self, continuationToken: _Optional[bytes] = ..., dataVersion: _Optional[int] = ..., debug: bool = ...) -> None: ... class SyncDownResponse(_message.Message): __slots__ = ("continuationToken", "hasMore", "cacheStatus", "userFolders", "sharedFolders", "userFolderSharedFolders", "sharedFolderFolders", "records", "recordMetaData", "nonSharedData", "recordLinks", "userFolderRecords", "sharedFolderRecords", "sharedFolderFolderRecords", "sharedFolderUsers", "sharedFolderTeams", "recordAddAuditData", "teams", "sharingChanges", "profile", "profilePic", "pendingTeamMembers", "breachWatchRecords", "userAuths", "breachWatchSecurityData", "reusedPasswords", "removedUserFolders", "removedSharedFolders", "removedUserFolderSharedFolders", "removedSharedFolderFolders", "removedRecords", "removedRecordLinks", "removedUserFolderRecords", "removedSharedFolderRecords", "removedSharedFolderFolderRecords", "removedSharedFolderUsers", "removedSharedFolderTeams", "removedTeams", "ksmAppShares", "ksmAppClients", "shareInvitations", "diagnostics", "recordRotations", "users", "removedUsers", "securityScoreData", "notificationSync", "keeperDriveData") @@ -141,7 +140,7 @@ class SyncDownResponse(_message.Message): securityScoreData: _containers.RepeatedCompositeFieldContainer[SecurityScoreData] notificationSync: _containers.RepeatedCompositeFieldContainer[_NotificationCenter_pb2.NotificationWrapper] keeperDriveData: KeeperDriveData - def __init__(self, continuationToken: _Optional[bytes] = ..., hasMore: _Optional[bool] = ..., cacheStatus: _Optional[_Union[CacheStatus, str]] = ..., userFolders: _Optional[_Iterable[_Union[UserFolder, _Mapping]]] = ..., sharedFolders: _Optional[_Iterable[_Union[SharedFolder, _Mapping]]] = ..., userFolderSharedFolders: _Optional[_Iterable[_Union[UserFolderSharedFolder, _Mapping]]] = ..., sharedFolderFolders: _Optional[_Iterable[_Union[SharedFolderFolder, _Mapping]]] = ..., records: _Optional[_Iterable[_Union[Record, _Mapping]]] = ..., recordMetaData: _Optional[_Iterable[_Union[RecordMetaData, _Mapping]]] = ..., nonSharedData: _Optional[_Iterable[_Union[NonSharedData, _Mapping]]] = ..., recordLinks: _Optional[_Iterable[_Union[RecordLink, _Mapping]]] = ..., userFolderRecords: _Optional[_Iterable[_Union[UserFolderRecord, _Mapping]]] = ..., sharedFolderRecords: _Optional[_Iterable[_Union[SharedFolderRecord, _Mapping]]] = ..., sharedFolderFolderRecords: _Optional[_Iterable[_Union[SharedFolderFolderRecord, _Mapping]]] = ..., sharedFolderUsers: _Optional[_Iterable[_Union[SharedFolderUser, _Mapping]]] = ..., sharedFolderTeams: _Optional[_Iterable[_Union[SharedFolderTeam, _Mapping]]] = ..., recordAddAuditData: _Optional[_Iterable[bytes]] = ..., teams: _Optional[_Iterable[_Union[Team, _Mapping]]] = ..., sharingChanges: _Optional[_Iterable[_Union[SharingChange, _Mapping]]] = ..., profile: _Optional[_Union[Profile, _Mapping]] = ..., profilePic: _Optional[_Union[ProfilePic, _Mapping]] = ..., pendingTeamMembers: _Optional[_Iterable[_Union[PendingTeamMember, _Mapping]]] = ..., breachWatchRecords: _Optional[_Iterable[_Union[BreachWatchRecord, _Mapping]]] = ..., userAuths: _Optional[_Iterable[_Union[UserAuth, _Mapping]]] = ..., breachWatchSecurityData: _Optional[_Iterable[_Union[BreachWatchSecurityData, _Mapping]]] = ..., reusedPasswords: _Optional[_Union[ReusedPasswords, _Mapping]] = ..., removedUserFolders: _Optional[_Iterable[bytes]] = ..., removedSharedFolders: _Optional[_Iterable[bytes]] = ..., removedUserFolderSharedFolders: _Optional[_Iterable[_Union[UserFolderSharedFolder, _Mapping]]] = ..., removedSharedFolderFolders: _Optional[_Iterable[_Union[SharedFolderFolder, _Mapping]]] = ..., removedRecords: _Optional[_Iterable[bytes]] = ..., removedRecordLinks: _Optional[_Iterable[_Union[RecordLink, _Mapping]]] = ..., removedUserFolderRecords: _Optional[_Iterable[_Union[UserFolderRecord, _Mapping]]] = ..., removedSharedFolderRecords: _Optional[_Iterable[_Union[SharedFolderRecord, _Mapping]]] = ..., removedSharedFolderFolderRecords: _Optional[_Iterable[_Union[SharedFolderFolderRecord, _Mapping]]] = ..., removedSharedFolderUsers: _Optional[_Iterable[_Union[SharedFolderUser, _Mapping]]] = ..., removedSharedFolderTeams: _Optional[_Iterable[_Union[SharedFolderTeam, _Mapping]]] = ..., removedTeams: _Optional[_Iterable[bytes]] = ..., ksmAppShares: _Optional[_Iterable[_Union[KsmChange, _Mapping]]] = ..., ksmAppClients: _Optional[_Iterable[_Union[KsmChange, _Mapping]]] = ..., shareInvitations: _Optional[_Iterable[_Union[ShareInvitation, _Mapping]]] = ..., diagnostics: _Optional[_Union[SyncDiagnostics, _Mapping]] = ..., recordRotations: _Optional[_Iterable[_Union[RecordRotation, _Mapping]]] = ..., users: _Optional[_Iterable[_Union[User, _Mapping]]] = ..., removedUsers: _Optional[_Iterable[bytes]] = ..., securityScoreData: _Optional[_Iterable[_Union[SecurityScoreData, _Mapping]]] = ..., notificationSync: _Optional[_Iterable[_Union[_NotificationCenter_pb2.NotificationWrapper, _Mapping]]] = ..., keeperDriveData: _Optional[_Union[KeeperDriveData, _Mapping]] = ...) -> None: ... + def __init__(self, continuationToken: _Optional[bytes] = ..., hasMore: bool = ..., cacheStatus: _Optional[_Union[CacheStatus, str]] = ..., userFolders: _Optional[_Iterable[_Union[UserFolder, _Mapping]]] = ..., sharedFolders: _Optional[_Iterable[_Union[SharedFolder, _Mapping]]] = ..., userFolderSharedFolders: _Optional[_Iterable[_Union[UserFolderSharedFolder, _Mapping]]] = ..., sharedFolderFolders: _Optional[_Iterable[_Union[SharedFolderFolder, _Mapping]]] = ..., records: _Optional[_Iterable[_Union[Record, _Mapping]]] = ..., recordMetaData: _Optional[_Iterable[_Union[RecordMetaData, _Mapping]]] = ..., nonSharedData: _Optional[_Iterable[_Union[NonSharedData, _Mapping]]] = ..., recordLinks: _Optional[_Iterable[_Union[RecordLink, _Mapping]]] = ..., userFolderRecords: _Optional[_Iterable[_Union[UserFolderRecord, _Mapping]]] = ..., sharedFolderRecords: _Optional[_Iterable[_Union[SharedFolderRecord, _Mapping]]] = ..., sharedFolderFolderRecords: _Optional[_Iterable[_Union[SharedFolderFolderRecord, _Mapping]]] = ..., sharedFolderUsers: _Optional[_Iterable[_Union[SharedFolderUser, _Mapping]]] = ..., sharedFolderTeams: _Optional[_Iterable[_Union[SharedFolderTeam, _Mapping]]] = ..., recordAddAuditData: _Optional[_Iterable[bytes]] = ..., teams: _Optional[_Iterable[_Union[Team, _Mapping]]] = ..., sharingChanges: _Optional[_Iterable[_Union[SharingChange, _Mapping]]] = ..., profile: _Optional[_Union[Profile, _Mapping]] = ..., profilePic: _Optional[_Union[ProfilePic, _Mapping]] = ..., pendingTeamMembers: _Optional[_Iterable[_Union[PendingTeamMember, _Mapping]]] = ..., breachWatchRecords: _Optional[_Iterable[_Union[BreachWatchRecord, _Mapping]]] = ..., userAuths: _Optional[_Iterable[_Union[UserAuth, _Mapping]]] = ..., breachWatchSecurityData: _Optional[_Iterable[_Union[BreachWatchSecurityData, _Mapping]]] = ..., reusedPasswords: _Optional[_Union[ReusedPasswords, _Mapping]] = ..., removedUserFolders: _Optional[_Iterable[bytes]] = ..., removedSharedFolders: _Optional[_Iterable[bytes]] = ..., removedUserFolderSharedFolders: _Optional[_Iterable[_Union[UserFolderSharedFolder, _Mapping]]] = ..., removedSharedFolderFolders: _Optional[_Iterable[_Union[SharedFolderFolder, _Mapping]]] = ..., removedRecords: _Optional[_Iterable[bytes]] = ..., removedRecordLinks: _Optional[_Iterable[_Union[RecordLink, _Mapping]]] = ..., removedUserFolderRecords: _Optional[_Iterable[_Union[UserFolderRecord, _Mapping]]] = ..., removedSharedFolderRecords: _Optional[_Iterable[_Union[SharedFolderRecord, _Mapping]]] = ..., removedSharedFolderFolderRecords: _Optional[_Iterable[_Union[SharedFolderFolderRecord, _Mapping]]] = ..., removedSharedFolderUsers: _Optional[_Iterable[_Union[SharedFolderUser, _Mapping]]] = ..., removedSharedFolderTeams: _Optional[_Iterable[_Union[SharedFolderTeam, _Mapping]]] = ..., removedTeams: _Optional[_Iterable[bytes]] = ..., ksmAppShares: _Optional[_Iterable[_Union[KsmChange, _Mapping]]] = ..., ksmAppClients: _Optional[_Iterable[_Union[KsmChange, _Mapping]]] = ..., shareInvitations: _Optional[_Iterable[_Union[ShareInvitation, _Mapping]]] = ..., diagnostics: _Optional[_Union[SyncDiagnostics, _Mapping]] = ..., recordRotations: _Optional[_Iterable[_Union[RecordRotation, _Mapping]]] = ..., users: _Optional[_Iterable[_Union[User, _Mapping]]] = ..., removedUsers: _Optional[_Iterable[bytes]] = ..., securityScoreData: _Optional[_Iterable[_Union[SecurityScoreData, _Mapping]]] = ..., notificationSync: _Optional[_Iterable[_Union[_NotificationCenter_pb2.NotificationWrapper, _Mapping]]] = ..., keeperDriveData: _Optional[_Union[KeeperDriveData, _Mapping]] = ...) -> None: ... class DriveRecord(_message.Message): __slots__ = ("recordUid", "revision", "version", "shared", "clientModifiedTime", "fileSize", "thumbnailSize") @@ -159,7 +158,7 @@ class DriveRecord(_message.Message): clientModifiedTime: int fileSize: int thumbnailSize: int - def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., version: _Optional[int] = ..., shared: _Optional[bool] = ..., clientModifiedTime: _Optional[int] = ..., fileSize: _Optional[int] = ..., thumbnailSize: _Optional[int] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., version: _Optional[int] = ..., shared: bool = ..., clientModifiedTime: _Optional[int] = ..., fileSize: _Optional[int] = ..., thumbnailSize: _Optional[int] = ...) -> None: ... class FolderSharingState(_message.Message): __slots__ = ("folderUid", "shared", "count") @@ -169,7 +168,7 @@ class FolderSharingState(_message.Message): folderUid: bytes shared: bool count: int - def __init__(self, folderUid: _Optional[bytes] = ..., shared: _Optional[bool] = ..., count: _Optional[int] = ...) -> None: ... + def __init__(self, folderUid: _Optional[bytes] = ..., shared: bool = ..., count: _Optional[int] = ...) -> None: ... class KeeperDriveData(_message.Message): __slots__ = ("folders", "folderKeys", "folderAccesses", "revokedFolderAccesses", "recordData", "nonSharedData", "recordAccesses", "revokedRecordAccesses", "recordSharingStates", "recordLinks", "removedRecordLinks", "breachWatchRecords", "securityScoreData", "breachWatchSecurityData", "removedFolders", "removedFolderRecords", "folderRecords", "recordRotationData", "records", "folderSharingState", "rawDagData") @@ -261,7 +260,7 @@ class SharedFolder(_message.Message): owner: str ownerAccountUid: bytes name: bytes - def __init__(self, sharedFolderUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., sharedFolderKey: _Optional[bytes] = ..., keyType: _Optional[_Union[_record_pb2.RecordKeyType, str]] = ..., data: _Optional[bytes] = ..., defaultManageRecords: _Optional[bool] = ..., defaultManageUsers: _Optional[bool] = ..., defaultCanEdit: _Optional[bool] = ..., defaultCanReshare: _Optional[bool] = ..., cacheStatus: _Optional[_Union[CacheStatus, str]] = ..., owner: _Optional[str] = ..., ownerAccountUid: _Optional[bytes] = ..., name: _Optional[bytes] = ...) -> None: ... + def __init__(self, sharedFolderUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., sharedFolderKey: _Optional[bytes] = ..., keyType: _Optional[_Union[_record_pb2.RecordKeyType, str]] = ..., data: _Optional[bytes] = ..., defaultManageRecords: bool = ..., defaultManageUsers: bool = ..., defaultCanEdit: bool = ..., defaultCanReshare: bool = ..., cacheStatus: _Optional[_Union[CacheStatus, str]] = ..., owner: _Optional[str] = ..., ownerAccountUid: _Optional[bytes] = ..., name: _Optional[bytes] = ...) -> None: ... class UserFolderSharedFolder(_message.Message): __slots__ = ("folderUid", "sharedFolderUid", "revision") @@ -327,7 +326,7 @@ class Team(_message.Message): sharedFolderKeys: _containers.RepeatedCompositeFieldContainer[SharedFolderKey] teamEccPrivateKey: bytes teamEccPublicKey: bytes - def __init__(self, teamUid: _Optional[bytes] = ..., name: _Optional[str] = ..., teamKey: _Optional[bytes] = ..., teamKeyType: _Optional[_Union[_record_pb2.RecordKeyType, str]] = ..., teamPrivateKey: _Optional[bytes] = ..., restrictEdit: _Optional[bool] = ..., restrictShare: _Optional[bool] = ..., restrictView: _Optional[bool] = ..., removedSharedFolders: _Optional[_Iterable[bytes]] = ..., sharedFolderKeys: _Optional[_Iterable[_Union[SharedFolderKey, _Mapping]]] = ..., teamEccPrivateKey: _Optional[bytes] = ..., teamEccPublicKey: _Optional[bytes] = ...) -> None: ... + def __init__(self, teamUid: _Optional[bytes] = ..., name: _Optional[str] = ..., teamKey: _Optional[bytes] = ..., teamKeyType: _Optional[_Union[_record_pb2.RecordKeyType, str]] = ..., teamPrivateKey: _Optional[bytes] = ..., restrictEdit: bool = ..., restrictShare: bool = ..., restrictView: bool = ..., removedSharedFolders: _Optional[_Iterable[bytes]] = ..., sharedFolderKeys: _Optional[_Iterable[_Union[SharedFolderKey, _Mapping]]] = ..., teamEccPrivateKey: _Optional[bytes] = ..., teamEccPublicKey: _Optional[bytes] = ...) -> None: ... class Record(_message.Message): __slots__ = ("recordUid", "revision", "version", "shared", "clientModifiedTime", "data", "extra", "udata", "fileSize", "thumbnailSize") @@ -351,7 +350,7 @@ class Record(_message.Message): udata: str fileSize: int thumbnailSize: int - def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., version: _Optional[int] = ..., shared: _Optional[bool] = ..., clientModifiedTime: _Optional[int] = ..., data: _Optional[bytes] = ..., extra: _Optional[bytes] = ..., udata: _Optional[str] = ..., fileSize: _Optional[int] = ..., thumbnailSize: _Optional[int] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., version: _Optional[int] = ..., shared: bool = ..., clientModifiedTime: _Optional[int] = ..., data: _Optional[bytes] = ..., extra: _Optional[bytes] = ..., udata: _Optional[str] = ..., fileSize: _Optional[int] = ..., thumbnailSize: _Optional[int] = ...) -> None: ... class RecordLink(_message.Message): __slots__ = ("parentRecordUid", "childRecordUid", "recordKey", "revision") @@ -417,7 +416,7 @@ class RecordMetaData(_message.Message): expiration: int expirationNotificationType: _record_pb2.TimerNotificationType ownerUsername: str - def __init__(self, recordUid: _Optional[bytes] = ..., owner: _Optional[bool] = ..., recordKey: _Optional[bytes] = ..., recordKeyType: _Optional[_Union[_record_pb2.RecordKeyType, str]] = ..., canShare: _Optional[bool] = ..., canEdit: _Optional[bool] = ..., ownerAccountUid: _Optional[bytes] = ..., expiration: _Optional[int] = ..., expirationNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., ownerUsername: _Optional[str] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., owner: bool = ..., recordKey: _Optional[bytes] = ..., recordKeyType: _Optional[_Union[_record_pb2.RecordKeyType, str]] = ..., canShare: bool = ..., canEdit: bool = ..., ownerAccountUid: _Optional[bytes] = ..., expiration: _Optional[int] = ..., expirationNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., ownerUsername: _Optional[str] = ...) -> None: ... class SharingChange(_message.Message): __slots__ = ("recordUid", "shared") @@ -425,7 +424,7 @@ class SharingChange(_message.Message): SHARED_FIELD_NUMBER: _ClassVar[int] recordUid: bytes shared: bool - def __init__(self, recordUid: _Optional[bytes] = ..., shared: _Optional[bool] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., shared: bool = ...) -> None: ... class Profile(_message.Message): __slots__ = ("data", "profileName", "revision") @@ -491,7 +490,7 @@ class UserAuth(_message.Message): encryptedClientKey: bytes revision: int name: str - def __init__(self, uid: _Optional[bytes] = ..., loginType: _Optional[_Union[_APIRequest_pb2.LoginType, str]] = ..., deleted: _Optional[bool] = ..., iterations: _Optional[int] = ..., salt: _Optional[bytes] = ..., encryptedClientKey: _Optional[bytes] = ..., revision: _Optional[int] = ..., name: _Optional[str] = ...) -> None: ... + def __init__(self, uid: _Optional[bytes] = ..., loginType: _Optional[_Union[_APIRequest_pb2.LoginType, str]] = ..., deleted: bool = ..., iterations: _Optional[int] = ..., salt: _Optional[bytes] = ..., encryptedClientKey: _Optional[bytes] = ..., revision: _Optional[int] = ..., name: _Optional[str] = ...) -> None: ... class BreachWatchSecurityData(_message.Message): __slots__ = ("recordUid", "revision", "removed") @@ -501,7 +500,7 @@ class BreachWatchSecurityData(_message.Message): recordUid: bytes revision: int removed: bool - def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., removed: _Optional[bool] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., removed: bool = ...) -> None: ... class ReusedPasswords(_message.Message): __slots__ = ("count", "revision") @@ -535,7 +534,7 @@ class SharedFolderRecord(_message.Message): expirationNotificationType: _record_pb2.TimerNotificationType ownerUsername: str rotateOnExpiration: bool - def __init__(self, sharedFolderUid: _Optional[bytes] = ..., recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., canShare: _Optional[bool] = ..., canEdit: _Optional[bool] = ..., ownerAccountUid: _Optional[bytes] = ..., expiration: _Optional[int] = ..., owner: _Optional[bool] = ..., expirationNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., ownerUsername: _Optional[str] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, sharedFolderUid: _Optional[bytes] = ..., recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., canShare: bool = ..., canEdit: bool = ..., ownerAccountUid: _Optional[bytes] = ..., expiration: _Optional[int] = ..., owner: bool = ..., expirationNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., ownerUsername: _Optional[str] = ..., rotateOnExpiration: bool = ...) -> None: ... class SharedFolderUser(_message.Message): __slots__ = ("sharedFolderUid", "username", "manageRecords", "manageUsers", "accountUid", "expiration", "expirationNotificationType", "rotateOnExpiration") @@ -555,7 +554,7 @@ class SharedFolderUser(_message.Message): expiration: int expirationNotificationType: _record_pb2.TimerNotificationType rotateOnExpiration: bool - def __init__(self, sharedFolderUid: _Optional[bytes] = ..., username: _Optional[str] = ..., manageRecords: _Optional[bool] = ..., manageUsers: _Optional[bool] = ..., accountUid: _Optional[bytes] = ..., expiration: _Optional[int] = ..., expirationNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, sharedFolderUid: _Optional[bytes] = ..., username: _Optional[str] = ..., manageRecords: bool = ..., manageUsers: bool = ..., accountUid: _Optional[bytes] = ..., expiration: _Optional[int] = ..., expirationNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., rotateOnExpiration: bool = ...) -> None: ... class SharedFolderTeam(_message.Message): __slots__ = ("sharedFolderUid", "teamUid", "name", "manageRecords", "manageUsers", "expiration", "expirationNotificationType", "rotateOnExpiration") @@ -575,7 +574,7 @@ class SharedFolderTeam(_message.Message): expiration: int expirationNotificationType: _record_pb2.TimerNotificationType rotateOnExpiration: bool - def __init__(self, sharedFolderUid: _Optional[bytes] = ..., teamUid: _Optional[bytes] = ..., name: _Optional[str] = ..., manageRecords: _Optional[bool] = ..., manageUsers: _Optional[bool] = ..., expiration: _Optional[int] = ..., expirationNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, sharedFolderUid: _Optional[bytes] = ..., teamUid: _Optional[bytes] = ..., name: _Optional[str] = ..., manageRecords: bool = ..., manageUsers: bool = ..., expiration: _Optional[int] = ..., expirationNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., rotateOnExpiration: bool = ...) -> None: ... class KsmChange(_message.Message): __slots__ = ("appRecordUid", "detailId", "removed", "appClientType", "expiration") @@ -589,7 +588,7 @@ class KsmChange(_message.Message): removed: bool appClientType: _enterprise_pb2.AppClientType expiration: int - def __init__(self, appRecordUid: _Optional[bytes] = ..., detailId: _Optional[bytes] = ..., removed: _Optional[bool] = ..., appClientType: _Optional[_Union[_enterprise_pb2.AppClientType, str]] = ..., expiration: _Optional[int] = ...) -> None: ... + def __init__(self, appRecordUid: _Optional[bytes] = ..., detailId: _Optional[bytes] = ..., removed: bool = ..., appClientType: _Optional[_Union[_enterprise_pb2.AppClientType, str]] = ..., expiration: _Optional[int] = ...) -> None: ... class ShareInvitation(_message.Message): __slots__ = ("username",) @@ -639,7 +638,7 @@ class RecordRotation(_message.Message): resourceUid: bytes lastRotation: int lastRotationStatus: RecordRotationStatus - def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., configurationUid: _Optional[bytes] = ..., schedule: _Optional[str] = ..., pwdComplexity: _Optional[bytes] = ..., disabled: _Optional[bool] = ..., resourceUid: _Optional[bytes] = ..., lastRotation: _Optional[int] = ..., lastRotationStatus: _Optional[_Union[RecordRotationStatus, str]] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., revision: _Optional[int] = ..., configurationUid: _Optional[bytes] = ..., schedule: _Optional[str] = ..., pwdComplexity: _Optional[bytes] = ..., disabled: bool = ..., resourceUid: _Optional[bytes] = ..., lastRotation: _Optional[int] = ..., lastRotationStatus: _Optional[_Union[RecordRotationStatus, str]] = ...) -> None: ... class SecurityScoreData(_message.Message): __slots__ = ("recordUid", "data", "revision") diff --git a/keepercommander/proto/automator_pb2.py b/keepercommander/proto/automator_pb2.py index b7fe6391c..cbf11324a 100644 --- a/keepercommander/proto/automator_pb2.py +++ b/keepercommander/proto/automator_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: automator.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'automator.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,22 +27,24 @@ from . import version_pb2 as version__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x61utomator.proto\x12\tAutomator\x1a\x0essocloud.proto\x1a\x10\x65nterprise.proto\x1a\rversion.proto\"\xbf\x02\n\x15\x41utomatorSettingValue\x12\x11\n\tsettingId\x18\x01 \x01(\x03\x12\x15\n\rsettingTypeId\x18\x02 \x01(\x05\x12\x12\n\nsettingTag\x18\x03 \x01(\t\x12\x13\n\x0bsettingName\x18\x04 \x01(\t\x12\x14\n\x0csettingValue\x18\x05 \x01(\t\x12$\n\x08\x64\x61taType\x18\x06 \x01(\x0e\x32\x12.SsoCloud.DataType\x12\x14\n\x0clastModified\x18\x07 \x01(\t\x12\x10\n\x08\x66romFile\x18\x08 \x01(\x08\x12\x11\n\tencrypted\x18\t \x01(\x08\x12\x0f\n\x07\x65ncoded\x18\n \x01(\x08\x12\x10\n\x08\x65\x64itable\x18\x0b \x01(\x08\x12\x12\n\ntranslated\x18\x0c \x01(\x08\x12\x13\n\x0buserVisible\x18\r \x01(\x08\x12\x10\n\x08required\x18\x0e \x01(\x08\"\xee\x02\n\x14\x41pproveDeviceRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12O\n\x1dssoAuthenticationProtocolType\x18\x02 \x01(\x0e\x32(.Automator.SsoAuthenticationProtocolType\x12\x13\n\x0b\x61uthMessage\x18\x03 \x01(\t\x12\r\n\x05\x65mail\x18\x04 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x05 \x01(\x0c\x12\x1c\n\x14serverEccPublicKeyId\x18\x06 \x01(\x05\x12\x1c\n\x14userEncryptedDataKey\x18\x07 \x01(\x0c\x12>\n\x18userEncryptedDataKeyType\x18\x08 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x11\n\tipAddress\x18\t \x01(\t\x12\x11\n\tisTesting\x18\n \x01(\x08\x12\x11\n\tisEccOnly\x18\x0b \x01(\x08\"\xa9\x02\n\x0cSetupRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x1c\n\x14serverEccPublicKeyId\x18\x02 \x01(\x05\x12\x31\n\x0e\x61utomatorState\x18\x03 \x01(\x0e\x32\x19.Automator.AutomatorState\x12(\n encryptedEnterprisePrivateEccKey\x18\x04 \x01(\x0c\x12(\n encryptedEnterprisePrivateRsaKey\x18\x05 \x01(\x0c\x12\x32\n\x0f\x61utomatorSkills\x18\x06 \x03(\x0b\x32\x19.Automator.AutomatorSkill\x12\x18\n\x10\x65ncryptedTreeKey\x18\x07 \x01(\x0c\x12\x11\n\tisEccOnly\x18\x08 \x01(\x08\"U\n\rStatusRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x1c\n\x14serverEccPublicKeyId\x18\x02 \x01(\x05\x12\x11\n\tisEccOnly\x18\x03 \x01(\x08\"\xa3\x04\n\x11InitializeRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x13\n\x0bidpMetadata\x18\x02 \x01(\t\x12\x1d\n\x15idpSigningCertificate\x18\x03 \x01(\x0c\x12\x13\n\x0bssoEntityId\x18\x04 \x01(\t\x12\x14\n\x0c\x65mailMapping\x18\x05 \x01(\t\x12\x18\n\x10\x66irstnameMapping\x18\x06 \x01(\t\x12\x17\n\x0flastnameMapping\x18\x07 \x01(\t\x12\x10\n\x08\x64isabled\x18\x08 \x01(\x08\x12\x1c\n\x14serverEccPublicKeyId\x18\t \x01(\x05\x12\x0e\n\x06\x63onfig\x18\n \x01(\x0c\x12\x0f\n\x07sslMode\x18\x0b \x01(\t\x12\x14\n\x0cpersistState\x18\x0c \x01(\x08\x12\x17\n\x0f\x64isableSniCheck\x18\r \x01(\x08\x12\x1e\n\x16sslCertificateFilename\x18\x0e \x01(\t\x12\"\n\x1asslCertificateFilePassword\x18\x0f \x01(\t\x12!\n\x19sslCertificateKeyPassword\x18\x10 \x01(\t\x12\x1e\n\x16sslCertificateContents\x18\x11 \x01(\x0c\x12\x15\n\rautomatorHost\x18\x12 \x01(\t\x12\x15\n\rautomatorPort\x18\x13 \x01(\t\x12\x0f\n\x07ipAllow\x18\x14 \x01(\t\x12\x0e\n\x06ipDeny\x18\x15 \x01(\t\x12\x11\n\tisEccOnly\x18\x16 \x01(\x08\"\xa6\x02\n\x16NotInitializedResponse\x12 \n\x18\x61utomatorTransmissionKey\x18\x01 \x01(\x0c\x12\x1a\n\x12signingCertificate\x18\x02 \x01(\x0c\x12\"\n\x1asigningCertificateFilename\x18\x03 \x01(\t\x12\"\n\x1asigningCertificatePassword\x18\x04 \x01(\t\x12\x1a\n\x12signingKeyPassword\x18\x05 \x01(\t\x12>\n\x18signingCertificateFormat\x18\x06 \x01(\x0e\x32\x1c.Automator.CertificateFormat\x12\x1a\n\x12\x61utomatorPublicKey\x18\x07 \x01(\x0c\x12\x0e\n\x06\x63onfig\x18\x08 \x01(\x0c\"\xa5\x04\n\x11\x41utomatorResponse\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x11\n\ttimestamp\x18\x03 \x01(\x03\x12\x39\n\rapproveDevice\x18\x04 \x01(\x0b\x32 .Automator.ApproveDeviceResponseH\x00\x12+\n\x06status\x18\x05 \x01(\x0b\x32\x19.Automator.StatusResponseH\x00\x12;\n\x0enotInitialized\x18\x06 \x01(\x0b\x32!.Automator.NotInitializedResponseH\x00\x12)\n\x05\x65rror\x18\x07 \x01(\x0b\x32\x18.Automator.ErrorResponseH\x00\x12\x45\n\x13\x61pproveTeamsForUser\x18\n \x01(\x0b\x32&.Automator.ApproveTeamsForUserResponseH\x00\x12\x37\n\x0c\x61pproveTeams\x18\x0b \x01(\x0b\x32\x1f.Automator.ApproveTeamsResponseH\x00\x12\x31\n\x0e\x61utomatorState\x18\x08 \x01(\x0e\x32\x19.Automator.AutomatorState\x12\x1d\n\x15\x61utomatorPublicEccKey\x18\t \x01(\x0c\x12)\n\x07version\x18\x0c \x01(\x0b\x32\x18.SemanticVersion.VersionB\n\n\x08response\"\x98\x01\n\x15\x41pproveDeviceResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x1c\n\x14\x65ncryptedUserDataKey\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\t\x12>\n\x18\x65ncryptedUserDataKeyType\x18\x04 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"\xd0\x03\n\x0eStatusResponse\x12\x13\n\x0binitialized\x18\x01 \x01(\x08\x12\x18\n\x10\x65nabledTimestamp\x18\x02 \x01(\x03\x12\x1c\n\x14initializedTimestamp\x18\x03 \x01(\x03\x12\x18\n\x10updatedTimestamp\x18\x04 \x01(\x03\x12\x1f\n\x17numberOfDevicesApproved\x18\x05 \x01(\x03\x12\x1d\n\x15numberOfDevicesDenied\x18\x06 \x01(\x03\x12\x16\n\x0enumberOfErrors\x18\x07 \x01(\x03\x12 \n\x18sslCertificateExpiration\x18\x08 \x01(\x03\x12\x41\n\x16notInitializedResponse\x18\t \x01(\x0b\x32!.Automator.NotInitializedResponse\x12\x0e\n\x06\x63onfig\x18\n \x01(\x0c\x12\'\n\x1fnumberOfTeamMembershipsApproved\x18\x0b \x01(\x03\x12%\n\x1dnumberOfTeamMembershipsDenied\x18\x0c \x01(\x03\x12\x1d\n\x15numberOfTeamsApproved\x18\r \x01(\x03\x12\x1b\n\x13numberOfTeamsDenied\x18\x0e \x01(\x03\" \n\rErrorResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"X\n\x08LogEntry\x12\x12\n\nserverTime\x18\x01 \x01(\t\x12\x14\n\x0cmessageLevel\x18\x02 \x01(\t\x12\x11\n\tcomponent\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\"b\n\rAdminResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12/\n\rautomatorInfo\x18\x03 \x03(\x0b\x32\x18.Automator.AutomatorInfo\"\xee\x02\n\rAutomatorInfo\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x0b\n\x03url\x18\x05 \x01(\t\x12\x32\n\x0f\x61utomatorSkills\x18\x06 \x03(\x0b\x32\x19.Automator.AutomatorSkill\x12@\n\x16\x61utomatorSettingValues\x18\x07 \x03(\x0b\x32 .Automator.AutomatorSettingValue\x12)\n\x06status\x18\x08 \x01(\x0b\x32\x19.Automator.StatusResponse\x12\'\n\nlogEntries\x18\t \x03(\x0b\x32\x13.Automator.LogEntry\x12\x31\n\x0e\x61utomatorState\x18\n \x01(\x0e\x32\x19.Automator.AutomatorState\x12\x0f\n\x07version\x18\x0b \x01(\t\"e\n\x1b\x41\x64minCreateAutomatorRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12(\n\x05skill\x18\x03 \x01(\x0b\x32\x19.Automator.AutomatorSkill\"2\n\x1b\x41\x64minDeleteAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"1\n\x1f\x41\x64minGetAutomatorsOnNodeRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\">\n&AdminGetAutomatorsForEnterpriseRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\"/\n\x18\x41\x64minGetAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"C\n\x1b\x41\x64minEnableAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\"\xc8\x01\n\x19\x41\x64minEditAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x03 \x01(\x08\x12\x0b\n\x03url\x18\x04 \x01(\t\x12(\n\nskillTypes\x18\x05 \x03(\x0e\x32\x14.Automator.SkillType\x12@\n\x16\x61utomatorSettingValues\x18\x06 \x03(\x0b\x32 .Automator.AutomatorSettingValue\"\xfc\x01\n\x1a\x41\x64minSetupAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x31\n\x0e\x61utomatorState\x18\x02 \x01(\x0e\x32\x19.Automator.AutomatorState\x12(\n encryptedEccEnterprisePrivateKey\x18\x03 \x01(\x0c\x12(\n encryptedRsaEnterprisePrivateKey\x18\x04 \x01(\x0c\x12(\n\nskillTypes\x18\x05 \x03(\x0e\x32\x14.Automator.SkillType\x12\x18\n\x10\x65ncryptedTreeKey\x18\x06 \x01(\x0c\"\xa6\x01\n\x1b\x41\x64minSetupAutomatorResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x13\n\x0b\x61utomatorId\x18\x03 \x01(\x03\x12\x31\n\x0e\x61utomatorState\x18\x04 \x01(\x0e\x32\x19.Automator.AutomatorState\x12\x1d\n\x15\x61utomatorEccPublicKey\x18\x05 \x01(\x0c\"2\n\x1b\x41\x64minAutomatorSkillsRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"_\n\x0e\x41utomatorSkill\x12\'\n\tskillType\x18\x01 \x01(\x0e\x32\x14.Automator.SkillType\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0etranslatedName\x18\x03 \x01(\t\"t\n\x1c\x41\x64minAutomatorSkillsResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x32\n\x0f\x61utomatorSkills\x18\x03 \x03(\x0b\x32\x19.Automator.AutomatorSkill\"1\n\x1a\x41\x64minResetAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"6\n\x1f\x41\x64minInitializeAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"/\n\x18\x41\x64minAutomatorLogRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"4\n\x1d\x41\x64minAutomatorLogClearRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"\xe3\x02\n\x1a\x41pproveTeamsForUserRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12O\n\x1dssoAuthenticationProtocolType\x18\x02 \x01(\x0e\x32(.Automator.SsoAuthenticationProtocolType\x12\x13\n\x0b\x61uthMessage\x18\x03 \x01(\t\x12\r\n\x05\x65mail\x18\x04 \x01(\t\x12\x1c\n\x14serverEccPublicKeyId\x18\x05 \x01(\x05\x12\x11\n\tipAddress\x18\x06 \x01(\t\x12\x15\n\ruserPublicKey\x18\x07 \x01(\x0c\x12\x33\n\x0fteamDescription\x18\x08 \x03(\x0b\x32\x1a.Automator.TeamDescription\x12\x11\n\tisTesting\x18\t \x01(\x08\x12\x11\n\tisEccOnly\x18\n \x01(\x08\x12\x18\n\x10userPublicKeyEcc\x18\x0b \x01(\x0c\"\x8a\x01\n\x0fTeamDescription\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x10\n\x08teamName\x18\x02 \x01(\t\x12\x18\n\x10\x65ncryptedTeamKey\x18\x03 \x01(\x0c\x12:\n\x14\x65ncryptedTeamKeyType\x18\x04 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"\x99\x01\n\x1b\x41pproveTeamsForUserResponse\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x45\n\x13\x61pproveTeamResponse\x18\x04 \x03(\x0b\x32(.Automator.ApproveOneTeamForUserResponse\"\xab\x02\n\x1d\x41pproveOneTeamForUserResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0f\n\x07teamUid\x18\x03 \x01(\x0c\x12\x10\n\x08teamName\x18\x04 \x01(\t\x12\x1c\n\x14userEncryptedTeamKey\x18\x05 \x01(\x0c\x12>\n\x18userEncryptedTeamKeyType\x18\x06 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12!\n\x19userEncryptedTeamKeyByEcc\x18\x07 \x01(\x0c\x12\x43\n\x1duserEncryptedTeamKeyByEccType\x18\x08 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"\xab\x02\n\x13\x41pproveTeamsRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12O\n\x1dssoAuthenticationProtocolType\x18\x02 \x01(\x0e\x32(.Automator.SsoAuthenticationProtocolType\x12\x13\n\x0b\x61uthMessage\x18\x03 \x01(\t\x12\r\n\x05\x65mail\x18\x04 \x01(\t\x12\x1c\n\x14serverEccPublicKeyId\x18\x05 \x01(\x05\x12\x11\n\tipAddress\x18\x06 \x01(\t\x12\x33\n\x0fteamDescription\x18\x07 \x03(\x0b\x32\x1a.Automator.TeamDescription\x12\x11\n\tisEccOnly\x18\x08 \x01(\x08\x12\x11\n\tisTesting\x18\t \x01(\x08\"|\n\x14\x41pproveTeamsResponse\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0f\n\x07message\x18\x02 \x01(\t\x12>\n\x13\x61pproveTeamResponse\x18\x03 \x03(\x0b\x32!.Automator.ApproveOneTeamResponse\"\x9e\x04\n\x16\x41pproveOneTeamResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0f\n\x07teamUid\x18\x03 \x01(\x0c\x12\x10\n\x08teamName\x18\x04 \x01(\t\x12\x1b\n\x13\x65ncryptedTeamKeyCbc\x18\x05 \x01(\x0c\x12=\n\x17\x65ncryptedTeamKeyCbcType\x18\x06 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x1b\n\x13\x65ncryptedTeamKeyGcm\x18\x07 \x01(\x0c\x12=\n\x17\x65ncryptedTeamKeyGcmType\x18\x08 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x18\n\x10teamPublicKeyRsa\x18\t \x01(\x0c\x12\"\n\x1a\x65ncryptedTeamPrivateKeyRsa\x18\n \x01(\x0c\x12\x44\n\x1e\x65ncryptedTeamPrivateKeyRsaType\x18\x0b \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x18\n\x10teamPublicKeyEcc\x18\x0c \x01(\x0c\x12\"\n\x1a\x65ncryptedTeamPrivateKeyEcc\x18\r \x01(\x0c\x12\x44\n\x1e\x65ncryptedTeamPrivateKeyEccType\x18\x0e \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType*@\n\x1dSsoAuthenticationProtocolType\x12\x14\n\x10UNKNOWN_PROTOCOL\x10\x00\x12\t\n\x05SAML2\x10\x01*<\n\x11\x43\x65rtificateFormat\x12\x12\n\x0eUNKNOWN_FORMAT\x10\x00\x12\n\n\x06PKCS12\x10\x01\x12\x07\n\x03JKS\x10\x02*g\n\tSkillType\x12\x16\n\x12UNKNOWN_SKILL_TYPE\x10\x00\x12\x13\n\x0f\x44\x45VICE_APPROVAL\x10\x01\x12\x11\n\rTEAM_APPROVAL\x10\x02\x12\x1a\n\x16TEAM_FOR_USER_APPROVAL\x10\x03*\x87\x01\n\x0e\x41utomatorState\x12\x11\n\rUNKNOWN_STATE\x10\x00\x12\x0b\n\x07RUNNING\x10\x01\x12\t\n\x05\x45RROR\x10\x02\x12\x18\n\x14NEEDS_INITIALIZATION\x10\x03\x12\x17\n\x13NEEDS_CRYPTO_STEP_1\x10\x04\x12\x17\n\x13NEEDS_CRYPTO_STEP_2\x10\x05\x42%\n\x18\x63om.keepersecurity.protoB\tAutomatorb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x61utomator.proto\x12\tAutomator\x1a\x0essocloud.proto\x1a\x10\x65nterprise.proto\x1a\rversion.proto\"\xbf\x02\n\x15\x41utomatorSettingValue\x12\x11\n\tsettingId\x18\x01 \x01(\x03\x12\x15\n\rsettingTypeId\x18\x02 \x01(\x05\x12\x12\n\nsettingTag\x18\x03 \x01(\t\x12\x13\n\x0bsettingName\x18\x04 \x01(\t\x12\x14\n\x0csettingValue\x18\x05 \x01(\t\x12$\n\x08\x64\x61taType\x18\x06 \x01(\x0e\x32\x12.SsoCloud.DataType\x12\x14\n\x0clastModified\x18\x07 \x01(\t\x12\x10\n\x08\x66romFile\x18\x08 \x01(\x08\x12\x11\n\tencrypted\x18\t \x01(\x08\x12\x0f\n\x07\x65ncoded\x18\n \x01(\x08\x12\x10\n\x08\x65\x64itable\x18\x0b \x01(\x08\x12\x12\n\ntranslated\x18\x0c \x01(\x08\x12\x13\n\x0buserVisible\x18\r \x01(\x08\x12\x10\n\x08required\x18\x0e \x01(\x08\"\xee\x02\n\x14\x41pproveDeviceRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12O\n\x1dssoAuthenticationProtocolType\x18\x02 \x01(\x0e\x32(.Automator.SsoAuthenticationProtocolType\x12\x13\n\x0b\x61uthMessage\x18\x03 \x01(\t\x12\r\n\x05\x65mail\x18\x04 \x01(\t\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x05 \x01(\x0c\x12\x1c\n\x14serverEccPublicKeyId\x18\x06 \x01(\x05\x12\x1c\n\x14userEncryptedDataKey\x18\x07 \x01(\x0c\x12>\n\x18userEncryptedDataKeyType\x18\x08 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x11\n\tipAddress\x18\t \x01(\t\x12\x11\n\tisTesting\x18\n \x01(\x08\x12\x11\n\tisEccOnly\x18\x0b \x01(\x08\"\xa9\x02\n\x0cSetupRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x1c\n\x14serverEccPublicKeyId\x18\x02 \x01(\x05\x12\x31\n\x0e\x61utomatorState\x18\x03 \x01(\x0e\x32\x19.Automator.AutomatorState\x12(\n encryptedEnterprisePrivateEccKey\x18\x04 \x01(\x0c\x12(\n encryptedEnterprisePrivateRsaKey\x18\x05 \x01(\x0c\x12\x32\n\x0f\x61utomatorSkills\x18\x06 \x03(\x0b\x32\x19.Automator.AutomatorSkill\x12\x18\n\x10\x65ncryptedTreeKey\x18\x07 \x01(\x0c\x12\x11\n\tisEccOnly\x18\x08 \x01(\x08\"U\n\rStatusRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x1c\n\x14serverEccPublicKeyId\x18\x02 \x01(\x05\x12\x11\n\tisEccOnly\x18\x03 \x01(\x08\"\xa3\x04\n\x11InitializeRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x13\n\x0bidpMetadata\x18\x02 \x01(\t\x12\x1d\n\x15idpSigningCertificate\x18\x03 \x01(\x0c\x12\x13\n\x0bssoEntityId\x18\x04 \x01(\t\x12\x14\n\x0c\x65mailMapping\x18\x05 \x01(\t\x12\x18\n\x10\x66irstnameMapping\x18\x06 \x01(\t\x12\x17\n\x0flastnameMapping\x18\x07 \x01(\t\x12\x10\n\x08\x64isabled\x18\x08 \x01(\x08\x12\x1c\n\x14serverEccPublicKeyId\x18\t \x01(\x05\x12\x0e\n\x06\x63onfig\x18\n \x01(\x0c\x12\x0f\n\x07sslMode\x18\x0b \x01(\t\x12\x14\n\x0cpersistState\x18\x0c \x01(\x08\x12\x17\n\x0f\x64isableSniCheck\x18\r \x01(\x08\x12\x1e\n\x16sslCertificateFilename\x18\x0e \x01(\t\x12\"\n\x1asslCertificateFilePassword\x18\x0f \x01(\t\x12!\n\x19sslCertificateKeyPassword\x18\x10 \x01(\t\x12\x1e\n\x16sslCertificateContents\x18\x11 \x01(\x0c\x12\x15\n\rautomatorHost\x18\x12 \x01(\t\x12\x15\n\rautomatorPort\x18\x13 \x01(\t\x12\x0f\n\x07ipAllow\x18\x14 \x01(\t\x12\x0e\n\x06ipDeny\x18\x15 \x01(\t\x12\x11\n\tisEccOnly\x18\x16 \x01(\x08\"\xa6\x02\n\x16NotInitializedResponse\x12 \n\x18\x61utomatorTransmissionKey\x18\x01 \x01(\x0c\x12\x1a\n\x12signingCertificate\x18\x02 \x01(\x0c\x12\"\n\x1asigningCertificateFilename\x18\x03 \x01(\t\x12\"\n\x1asigningCertificatePassword\x18\x04 \x01(\t\x12\x1a\n\x12signingKeyPassword\x18\x05 \x01(\t\x12>\n\x18signingCertificateFormat\x18\x06 \x01(\x0e\x32\x1c.Automator.CertificateFormat\x12\x1a\n\x12\x61utomatorPublicKey\x18\x07 \x01(\x0c\x12\x0e\n\x06\x63onfig\x18\x08 \x01(\x0c\"\xa5\x04\n\x11\x41utomatorResponse\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x11\n\ttimestamp\x18\x03 \x01(\x03\x12\x39\n\rapproveDevice\x18\x04 \x01(\x0b\x32 .Automator.ApproveDeviceResponseH\x00\x12+\n\x06status\x18\x05 \x01(\x0b\x32\x19.Automator.StatusResponseH\x00\x12;\n\x0enotInitialized\x18\x06 \x01(\x0b\x32!.Automator.NotInitializedResponseH\x00\x12)\n\x05\x65rror\x18\x07 \x01(\x0b\x32\x18.Automator.ErrorResponseH\x00\x12\x45\n\x13\x61pproveTeamsForUser\x18\n \x01(\x0b\x32&.Automator.ApproveTeamsForUserResponseH\x00\x12\x37\n\x0c\x61pproveTeams\x18\x0b \x01(\x0b\x32\x1f.Automator.ApproveTeamsResponseH\x00\x12\x31\n\x0e\x61utomatorState\x18\x08 \x01(\x0e\x32\x19.Automator.AutomatorState\x12\x1d\n\x15\x61utomatorPublicEccKey\x18\t \x01(\x0c\x12)\n\x07version\x18\x0c \x01(\x0b\x32\x18.SemanticVersion.VersionB\n\n\x08response\"\x98\x01\n\x15\x41pproveDeviceResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x1c\n\x14\x65ncryptedUserDataKey\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\t\x12>\n\x18\x65ncryptedUserDataKeyType\x18\x04 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"\x8f\x04\n\x0eStatusResponse\x12\x13\n\x0binitialized\x18\x01 \x01(\x08\x12\x18\n\x10\x65nabledTimestamp\x18\x02 \x01(\x03\x12\x1c\n\x14initializedTimestamp\x18\x03 \x01(\x03\x12\x18\n\x10updatedTimestamp\x18\x04 \x01(\x03\x12\x1f\n\x17numberOfDevicesApproved\x18\x05 \x01(\x03\x12\x1d\n\x15numberOfDevicesDenied\x18\x06 \x01(\x03\x12\x16\n\x0enumberOfErrors\x18\x07 \x01(\x03\x12$\n\x18sslCertificateExpiration\x18\x08 \x01(\x03\x42\x02\x18\x01\x12\x41\n\x16notInitializedResponse\x18\t \x01(\x0b\x32!.Automator.NotInitializedResponse\x12\x0e\n\x06\x63onfig\x18\n \x01(\x0c\x12\'\n\x1fnumberOfTeamMembershipsApproved\x18\x0b \x01(\x03\x12%\n\x1dnumberOfTeamMembershipsDenied\x18\x0c \x01(\x03\x12\x1d\n\x15numberOfTeamsApproved\x18\r \x01(\x03\x12\x1b\n\x13numberOfTeamsDenied\x18\x0e \x01(\x03\x12\x39\n\x12sslCertificateInfo\x18\x0f \x03(\x0b\x32\x1d.Automator.SSLCertificateInfo\" \n\rErrorResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"X\n\x08LogEntry\x12\x12\n\nserverTime\x18\x01 \x01(\t\x12\x14\n\x0cmessageLevel\x18\x02 \x01(\t\x12\x11\n\tcomponent\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\"b\n\rAdminResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12/\n\rautomatorInfo\x18\x03 \x03(\x0b\x32\x18.Automator.AutomatorInfo\"\x94\x03\n\rAutomatorInfo\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x0b\n\x03url\x18\x05 \x01(\t\x12\x32\n\x0f\x61utomatorSkills\x18\x06 \x03(\x0b\x32\x19.Automator.AutomatorSkill\x12@\n\x16\x61utomatorSettingValues\x18\x07 \x03(\x0b\x32 .Automator.AutomatorSettingValue\x12)\n\x06status\x18\x08 \x01(\x0b\x32\x19.Automator.StatusResponse\x12\'\n\nlogEntries\x18\t \x03(\x0b\x32\x13.Automator.LogEntry\x12\x31\n\x0e\x61utomatorState\x18\n \x01(\x0e\x32\x19.Automator.AutomatorState\x12\x0f\n\x07version\x18\x0b \x01(\t\x12$\n\x1csslCertificateExpirationDate\x18\x0c \x01(\t\"e\n\x1b\x41\x64minCreateAutomatorRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12(\n\x05skill\x18\x03 \x01(\x0b\x32\x19.Automator.AutomatorSkill\"2\n\x1b\x41\x64minDeleteAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"1\n\x1f\x41\x64minGetAutomatorsOnNodeRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\">\n&AdminGetAutomatorsForEnterpriseRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\"/\n\x18\x41\x64minGetAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"C\n\x1b\x41\x64minEnableAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\"\xc8\x01\n\x19\x41\x64minEditAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x03 \x01(\x08\x12\x0b\n\x03url\x18\x04 \x01(\t\x12(\n\nskillTypes\x18\x05 \x03(\x0e\x32\x14.Automator.SkillType\x12@\n\x16\x61utomatorSettingValues\x18\x06 \x03(\x0b\x32 .Automator.AutomatorSettingValue\"\xfc\x01\n\x1a\x41\x64minSetupAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x31\n\x0e\x61utomatorState\x18\x02 \x01(\x0e\x32\x19.Automator.AutomatorState\x12(\n encryptedEccEnterprisePrivateKey\x18\x03 \x01(\x0c\x12(\n encryptedRsaEnterprisePrivateKey\x18\x04 \x01(\x0c\x12(\n\nskillTypes\x18\x05 \x03(\x0e\x32\x14.Automator.SkillType\x12\x18\n\x10\x65ncryptedTreeKey\x18\x06 \x01(\x0c\"\xa6\x01\n\x1b\x41\x64minSetupAutomatorResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x13\n\x0b\x61utomatorId\x18\x03 \x01(\x03\x12\x31\n\x0e\x61utomatorState\x18\x04 \x01(\x0e\x32\x19.Automator.AutomatorState\x12\x1d\n\x15\x61utomatorEccPublicKey\x18\x05 \x01(\x0c\"2\n\x1b\x41\x64minAutomatorSkillsRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"_\n\x0e\x41utomatorSkill\x12\'\n\tskillType\x18\x01 \x01(\x0e\x32\x14.Automator.SkillType\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\x0etranslatedName\x18\x03 \x01(\t\"t\n\x1c\x41\x64minAutomatorSkillsResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x32\n\x0f\x61utomatorSkills\x18\x03 \x03(\x0b\x32\x19.Automator.AutomatorSkill\"1\n\x1a\x41\x64minResetAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"6\n\x1f\x41\x64minInitializeAutomatorRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"/\n\x18\x41\x64minAutomatorLogRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"4\n\x1d\x41\x64minAutomatorLogClearRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\"\xe3\x02\n\x1a\x41pproveTeamsForUserRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12O\n\x1dssoAuthenticationProtocolType\x18\x02 \x01(\x0e\x32(.Automator.SsoAuthenticationProtocolType\x12\x13\n\x0b\x61uthMessage\x18\x03 \x01(\t\x12\r\n\x05\x65mail\x18\x04 \x01(\t\x12\x1c\n\x14serverEccPublicKeyId\x18\x05 \x01(\x05\x12\x11\n\tipAddress\x18\x06 \x01(\t\x12\x15\n\ruserPublicKey\x18\x07 \x01(\x0c\x12\x33\n\x0fteamDescription\x18\x08 \x03(\x0b\x32\x1a.Automator.TeamDescription\x12\x11\n\tisTesting\x18\t \x01(\x08\x12\x11\n\tisEccOnly\x18\n \x01(\x08\x12\x18\n\x10userPublicKeyEcc\x18\x0b \x01(\x0c\"\x8a\x01\n\x0fTeamDescription\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x10\n\x08teamName\x18\x02 \x01(\t\x12\x18\n\x10\x65ncryptedTeamKey\x18\x03 \x01(\x0c\x12:\n\x14\x65ncryptedTeamKeyType\x18\x04 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"\x99\x01\n\x1b\x41pproveTeamsForUserResponse\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x45\n\x13\x61pproveTeamResponse\x18\x04 \x03(\x0b\x32(.Automator.ApproveOneTeamForUserResponse\"\xab\x02\n\x1d\x41pproveOneTeamForUserResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0f\n\x07teamUid\x18\x03 \x01(\x0c\x12\x10\n\x08teamName\x18\x04 \x01(\t\x12\x1c\n\x14userEncryptedTeamKey\x18\x05 \x01(\x0c\x12>\n\x18userEncryptedTeamKeyType\x18\x06 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12!\n\x19userEncryptedTeamKeyByEcc\x18\x07 \x01(\x0c\x12\x43\n\x1duserEncryptedTeamKeyByEccType\x18\x08 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"\xab\x02\n\x13\x41pproveTeamsRequest\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12O\n\x1dssoAuthenticationProtocolType\x18\x02 \x01(\x0e\x32(.Automator.SsoAuthenticationProtocolType\x12\x13\n\x0b\x61uthMessage\x18\x03 \x01(\t\x12\r\n\x05\x65mail\x18\x04 \x01(\t\x12\x1c\n\x14serverEccPublicKeyId\x18\x05 \x01(\x05\x12\x11\n\tipAddress\x18\x06 \x01(\t\x12\x33\n\x0fteamDescription\x18\x07 \x03(\x0b\x32\x1a.Automator.TeamDescription\x12\x11\n\tisEccOnly\x18\x08 \x01(\x08\x12\x11\n\tisTesting\x18\t \x01(\x08\"|\n\x14\x41pproveTeamsResponse\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x03\x12\x0f\n\x07message\x18\x02 \x01(\t\x12>\n\x13\x61pproveTeamResponse\x18\x03 \x03(\x0b\x32!.Automator.ApproveOneTeamResponse\"\x9e\x04\n\x16\x41pproveOneTeamResponse\x12\x10\n\x08\x61pproved\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0f\n\x07teamUid\x18\x03 \x01(\x0c\x12\x10\n\x08teamName\x18\x04 \x01(\t\x12\x1b\n\x13\x65ncryptedTeamKeyCbc\x18\x05 \x01(\x0c\x12=\n\x17\x65ncryptedTeamKeyCbcType\x18\x06 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x1b\n\x13\x65ncryptedTeamKeyGcm\x18\x07 \x01(\x0c\x12=\n\x17\x65ncryptedTeamKeyGcmType\x18\x08 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x18\n\x10teamPublicKeyRsa\x18\t \x01(\x0c\x12\"\n\x1a\x65ncryptedTeamPrivateKeyRsa\x18\n \x01(\x0c\x12\x44\n\x1e\x65ncryptedTeamPrivateKeyRsaType\x18\x0b \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x18\n\x10teamPublicKeyEcc\x18\x0c \x01(\x0c\x12\"\n\x1a\x65ncryptedTeamPrivateKeyEcc\x18\r \x01(\x0c\x12\x44\n\x1e\x65ncryptedTeamPrivateKeyEccType\x18\x0e \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"\x93\x01\n\x12SSLCertificateInfo\x12\x13\n\x0b\x61utomatorId\x18\x01 \x01(\x04\x12\x0f\n\x07hostUrl\x18\x02 \x01(\t\x12\x0f\n\x07subject\x18\x03 \x01(\t\x12\x0e\n\x06issuer\x18\x04 \x01(\t\x12\x10\n\x08issuedOn\x18\x05 \x01(\x04\x12\x11\n\texpiresOn\x18\x06 \x01(\x04\x12\x11\n\tcheckedOn\x18\x07 \x01(\x04*I\n\x1dSsoAuthenticationProtocolType\x12\x14\n\x10UNKNOWN_PROTOCOL\x10\x00\x12\t\n\x05SAML2\x10\x01\x12\x07\n\x03JWT\x10\x02*<\n\x11\x43\x65rtificateFormat\x12\x12\n\x0eUNKNOWN_FORMAT\x10\x00\x12\n\n\x06PKCS12\x10\x01\x12\x07\n\x03JKS\x10\x02*g\n\tSkillType\x12\x16\n\x12UNKNOWN_SKILL_TYPE\x10\x00\x12\x13\n\x0f\x44\x45VICE_APPROVAL\x10\x01\x12\x11\n\rTEAM_APPROVAL\x10\x02\x12\x1a\n\x16TEAM_FOR_USER_APPROVAL\x10\x03*\x87\x01\n\x0e\x41utomatorState\x12\x11\n\rUNKNOWN_STATE\x10\x00\x12\x0b\n\x07RUNNING\x10\x01\x12\t\n\x05\x45RROR\x10\x02\x12\x18\n\x14NEEDS_INITIALIZATION\x10\x03\x12\x17\n\x13NEEDS_CRYPTO_STEP_1\x10\x04\x12\x17\n\x13NEEDS_CRYPTO_STEP_2\x10\x05\x42%\n\x18\x63om.keepersecurity.protoB\tAutomatorb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'automator_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\tAutomator' - _globals['_SSOAUTHENTICATIONPROTOCOLTYPE']._serialized_start=7193 - _globals['_SSOAUTHENTICATIONPROTOCOLTYPE']._serialized_end=7257 - _globals['_CERTIFICATEFORMAT']._serialized_start=7259 - _globals['_CERTIFICATEFORMAT']._serialized_end=7319 - _globals['_SKILLTYPE']._serialized_start=7321 - _globals['_SKILLTYPE']._serialized_end=7424 - _globals['_AUTOMATORSTATE']._serialized_start=7427 - _globals['_AUTOMATORSTATE']._serialized_end=7562 + _globals['_STATUSRESPONSE'].fields_by_name['sslCertificateExpiration']._loaded_options = None + _globals['_STATUSRESPONSE'].fields_by_name['sslCertificateExpiration']._serialized_options = b'\030\001' + _globals['_SSOAUTHENTICATIONPROTOCOLTYPE']._serialized_start=7444 + _globals['_SSOAUTHENTICATIONPROTOCOLTYPE']._serialized_end=7517 + _globals['_CERTIFICATEFORMAT']._serialized_start=7519 + _globals['_CERTIFICATEFORMAT']._serialized_end=7579 + _globals['_SKILLTYPE']._serialized_start=7581 + _globals['_SKILLTYPE']._serialized_end=7684 + _globals['_AUTOMATORSTATE']._serialized_start=7687 + _globals['_AUTOMATORSTATE']._serialized_end=7822 _globals['_AUTOMATORSETTINGVALUE']._serialized_start=80 _globals['_AUTOMATORSETTINGVALUE']._serialized_end=399 _globals['_APPROVEDEVICEREQUEST']._serialized_start=402 @@ -49,59 +62,61 @@ _globals['_APPROVEDEVICERESPONSE']._serialized_start=2557 _globals['_APPROVEDEVICERESPONSE']._serialized_end=2709 _globals['_STATUSRESPONSE']._serialized_start=2712 - _globals['_STATUSRESPONSE']._serialized_end=3176 - _globals['_ERRORRESPONSE']._serialized_start=3178 - _globals['_ERRORRESPONSE']._serialized_end=3210 - _globals['_LOGENTRY']._serialized_start=3212 - _globals['_LOGENTRY']._serialized_end=3300 - _globals['_ADMINRESPONSE']._serialized_start=3302 - _globals['_ADMINRESPONSE']._serialized_end=3400 - _globals['_AUTOMATORINFO']._serialized_start=3403 - _globals['_AUTOMATORINFO']._serialized_end=3769 - _globals['_ADMINCREATEAUTOMATORREQUEST']._serialized_start=3771 - _globals['_ADMINCREATEAUTOMATORREQUEST']._serialized_end=3872 - _globals['_ADMINDELETEAUTOMATORREQUEST']._serialized_start=3874 - _globals['_ADMINDELETEAUTOMATORREQUEST']._serialized_end=3924 - _globals['_ADMINGETAUTOMATORSONNODEREQUEST']._serialized_start=3926 - _globals['_ADMINGETAUTOMATORSONNODEREQUEST']._serialized_end=3975 - _globals['_ADMINGETAUTOMATORSFORENTERPRISEREQUEST']._serialized_start=3977 - _globals['_ADMINGETAUTOMATORSFORENTERPRISEREQUEST']._serialized_end=4039 - _globals['_ADMINGETAUTOMATORREQUEST']._serialized_start=4041 - _globals['_ADMINGETAUTOMATORREQUEST']._serialized_end=4088 - _globals['_ADMINENABLEAUTOMATORREQUEST']._serialized_start=4090 - _globals['_ADMINENABLEAUTOMATORREQUEST']._serialized_end=4157 - _globals['_ADMINEDITAUTOMATORREQUEST']._serialized_start=4160 - _globals['_ADMINEDITAUTOMATORREQUEST']._serialized_end=4360 - _globals['_ADMINSETUPAUTOMATORREQUEST']._serialized_start=4363 - _globals['_ADMINSETUPAUTOMATORREQUEST']._serialized_end=4615 - _globals['_ADMINSETUPAUTOMATORRESPONSE']._serialized_start=4618 - _globals['_ADMINSETUPAUTOMATORRESPONSE']._serialized_end=4784 - _globals['_ADMINAUTOMATORSKILLSREQUEST']._serialized_start=4786 - _globals['_ADMINAUTOMATORSKILLSREQUEST']._serialized_end=4836 - _globals['_AUTOMATORSKILL']._serialized_start=4838 - _globals['_AUTOMATORSKILL']._serialized_end=4933 - _globals['_ADMINAUTOMATORSKILLSRESPONSE']._serialized_start=4935 - _globals['_ADMINAUTOMATORSKILLSRESPONSE']._serialized_end=5051 - _globals['_ADMINRESETAUTOMATORREQUEST']._serialized_start=5053 - _globals['_ADMINRESETAUTOMATORREQUEST']._serialized_end=5102 - _globals['_ADMININITIALIZEAUTOMATORREQUEST']._serialized_start=5104 - _globals['_ADMININITIALIZEAUTOMATORREQUEST']._serialized_end=5158 - _globals['_ADMINAUTOMATORLOGREQUEST']._serialized_start=5160 - _globals['_ADMINAUTOMATORLOGREQUEST']._serialized_end=5207 - _globals['_ADMINAUTOMATORLOGCLEARREQUEST']._serialized_start=5209 - _globals['_ADMINAUTOMATORLOGCLEARREQUEST']._serialized_end=5261 - _globals['_APPROVETEAMSFORUSERREQUEST']._serialized_start=5264 - _globals['_APPROVETEAMSFORUSERREQUEST']._serialized_end=5619 - _globals['_TEAMDESCRIPTION']._serialized_start=5622 - _globals['_TEAMDESCRIPTION']._serialized_end=5760 - _globals['_APPROVETEAMSFORUSERRESPONSE']._serialized_start=5763 - _globals['_APPROVETEAMSFORUSERRESPONSE']._serialized_end=5916 - _globals['_APPROVEONETEAMFORUSERRESPONSE']._serialized_start=5919 - _globals['_APPROVEONETEAMFORUSERRESPONSE']._serialized_end=6218 - _globals['_APPROVETEAMSREQUEST']._serialized_start=6221 - _globals['_APPROVETEAMSREQUEST']._serialized_end=6520 - _globals['_APPROVETEAMSRESPONSE']._serialized_start=6522 - _globals['_APPROVETEAMSRESPONSE']._serialized_end=6646 - _globals['_APPROVEONETEAMRESPONSE']._serialized_start=6649 - _globals['_APPROVEONETEAMRESPONSE']._serialized_end=7191 + _globals['_STATUSRESPONSE']._serialized_end=3239 + _globals['_ERRORRESPONSE']._serialized_start=3241 + _globals['_ERRORRESPONSE']._serialized_end=3273 + _globals['_LOGENTRY']._serialized_start=3275 + _globals['_LOGENTRY']._serialized_end=3363 + _globals['_ADMINRESPONSE']._serialized_start=3365 + _globals['_ADMINRESPONSE']._serialized_end=3463 + _globals['_AUTOMATORINFO']._serialized_start=3466 + _globals['_AUTOMATORINFO']._serialized_end=3870 + _globals['_ADMINCREATEAUTOMATORREQUEST']._serialized_start=3872 + _globals['_ADMINCREATEAUTOMATORREQUEST']._serialized_end=3973 + _globals['_ADMINDELETEAUTOMATORREQUEST']._serialized_start=3975 + _globals['_ADMINDELETEAUTOMATORREQUEST']._serialized_end=4025 + _globals['_ADMINGETAUTOMATORSONNODEREQUEST']._serialized_start=4027 + _globals['_ADMINGETAUTOMATORSONNODEREQUEST']._serialized_end=4076 + _globals['_ADMINGETAUTOMATORSFORENTERPRISEREQUEST']._serialized_start=4078 + _globals['_ADMINGETAUTOMATORSFORENTERPRISEREQUEST']._serialized_end=4140 + _globals['_ADMINGETAUTOMATORREQUEST']._serialized_start=4142 + _globals['_ADMINGETAUTOMATORREQUEST']._serialized_end=4189 + _globals['_ADMINENABLEAUTOMATORREQUEST']._serialized_start=4191 + _globals['_ADMINENABLEAUTOMATORREQUEST']._serialized_end=4258 + _globals['_ADMINEDITAUTOMATORREQUEST']._serialized_start=4261 + _globals['_ADMINEDITAUTOMATORREQUEST']._serialized_end=4461 + _globals['_ADMINSETUPAUTOMATORREQUEST']._serialized_start=4464 + _globals['_ADMINSETUPAUTOMATORREQUEST']._serialized_end=4716 + _globals['_ADMINSETUPAUTOMATORRESPONSE']._serialized_start=4719 + _globals['_ADMINSETUPAUTOMATORRESPONSE']._serialized_end=4885 + _globals['_ADMINAUTOMATORSKILLSREQUEST']._serialized_start=4887 + _globals['_ADMINAUTOMATORSKILLSREQUEST']._serialized_end=4937 + _globals['_AUTOMATORSKILL']._serialized_start=4939 + _globals['_AUTOMATORSKILL']._serialized_end=5034 + _globals['_ADMINAUTOMATORSKILLSRESPONSE']._serialized_start=5036 + _globals['_ADMINAUTOMATORSKILLSRESPONSE']._serialized_end=5152 + _globals['_ADMINRESETAUTOMATORREQUEST']._serialized_start=5154 + _globals['_ADMINRESETAUTOMATORREQUEST']._serialized_end=5203 + _globals['_ADMININITIALIZEAUTOMATORREQUEST']._serialized_start=5205 + _globals['_ADMININITIALIZEAUTOMATORREQUEST']._serialized_end=5259 + _globals['_ADMINAUTOMATORLOGREQUEST']._serialized_start=5261 + _globals['_ADMINAUTOMATORLOGREQUEST']._serialized_end=5308 + _globals['_ADMINAUTOMATORLOGCLEARREQUEST']._serialized_start=5310 + _globals['_ADMINAUTOMATORLOGCLEARREQUEST']._serialized_end=5362 + _globals['_APPROVETEAMSFORUSERREQUEST']._serialized_start=5365 + _globals['_APPROVETEAMSFORUSERREQUEST']._serialized_end=5720 + _globals['_TEAMDESCRIPTION']._serialized_start=5723 + _globals['_TEAMDESCRIPTION']._serialized_end=5861 + _globals['_APPROVETEAMSFORUSERRESPONSE']._serialized_start=5864 + _globals['_APPROVETEAMSFORUSERRESPONSE']._serialized_end=6017 + _globals['_APPROVEONETEAMFORUSERRESPONSE']._serialized_start=6020 + _globals['_APPROVEONETEAMFORUSERRESPONSE']._serialized_end=6319 + _globals['_APPROVETEAMSREQUEST']._serialized_start=6322 + _globals['_APPROVETEAMSREQUEST']._serialized_end=6621 + _globals['_APPROVETEAMSRESPONSE']._serialized_start=6623 + _globals['_APPROVETEAMSRESPONSE']._serialized_end=6747 + _globals['_APPROVEONETEAMRESPONSE']._serialized_start=6750 + _globals['_APPROVEONETEAMRESPONSE']._serialized_end=7292 + _globals['_SSLCERTIFICATEINFO']._serialized_start=7295 + _globals['_SSLCERTIFICATEINFO']._serialized_end=7442 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/automator_pb2.pyi b/keepercommander/proto/automator_pb2.pyi index ad40672ee..c83a01aed 100644 --- a/keepercommander/proto/automator_pb2.pyi +++ b/keepercommander/proto/automator_pb2.pyi @@ -10,25 +10,26 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class SsoAuthenticationProtocolType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () UNKNOWN_PROTOCOL: _ClassVar[SsoAuthenticationProtocolType] SAML2: _ClassVar[SsoAuthenticationProtocolType] + JWT: _ClassVar[SsoAuthenticationProtocolType] class CertificateFormat(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () UNKNOWN_FORMAT: _ClassVar[CertificateFormat] PKCS12: _ClassVar[CertificateFormat] JKS: _ClassVar[CertificateFormat] class SkillType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () UNKNOWN_SKILL_TYPE: _ClassVar[SkillType] DEVICE_APPROVAL: _ClassVar[SkillType] TEAM_APPROVAL: _ClassVar[SkillType] TEAM_FOR_USER_APPROVAL: _ClassVar[SkillType] class AutomatorState(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () UNKNOWN_STATE: _ClassVar[AutomatorState] RUNNING: _ClassVar[AutomatorState] ERROR: _ClassVar[AutomatorState] @@ -37,6 +38,7 @@ class AutomatorState(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): NEEDS_CRYPTO_STEP_2: _ClassVar[AutomatorState] UNKNOWN_PROTOCOL: SsoAuthenticationProtocolType SAML2: SsoAuthenticationProtocolType +JWT: SsoAuthenticationProtocolType UNKNOWN_FORMAT: CertificateFormat PKCS12: CertificateFormat JKS: CertificateFormat @@ -52,7 +54,7 @@ NEEDS_CRYPTO_STEP_1: AutomatorState NEEDS_CRYPTO_STEP_2: AutomatorState class AutomatorSettingValue(_message.Message): - __slots__ = ["settingId", "settingTypeId", "settingTag", "settingName", "settingValue", "dataType", "lastModified", "fromFile", "encrypted", "encoded", "editable", "translated", "userVisible", "required"] + __slots__ = ("settingId", "settingTypeId", "settingTag", "settingName", "settingValue", "dataType", "lastModified", "fromFile", "encrypted", "encoded", "editable", "translated", "userVisible", "required") SETTINGID_FIELD_NUMBER: _ClassVar[int] SETTINGTYPEID_FIELD_NUMBER: _ClassVar[int] SETTINGTAG_FIELD_NUMBER: _ClassVar[int] @@ -84,7 +86,7 @@ class AutomatorSettingValue(_message.Message): def __init__(self, settingId: _Optional[int] = ..., settingTypeId: _Optional[int] = ..., settingTag: _Optional[str] = ..., settingName: _Optional[str] = ..., settingValue: _Optional[str] = ..., dataType: _Optional[_Union[_ssocloud_pb2.DataType, str]] = ..., lastModified: _Optional[str] = ..., fromFile: bool = ..., encrypted: bool = ..., encoded: bool = ..., editable: bool = ..., translated: bool = ..., userVisible: bool = ..., required: bool = ...) -> None: ... class ApproveDeviceRequest(_message.Message): - __slots__ = ["automatorId", "ssoAuthenticationProtocolType", "authMessage", "email", "devicePublicKey", "serverEccPublicKeyId", "userEncryptedDataKey", "userEncryptedDataKeyType", "ipAddress", "isTesting", "isEccOnly"] + __slots__ = ("automatorId", "ssoAuthenticationProtocolType", "authMessage", "email", "devicePublicKey", "serverEccPublicKeyId", "userEncryptedDataKey", "userEncryptedDataKeyType", "ipAddress", "isTesting", "isEccOnly") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] SSOAUTHENTICATIONPROTOCOLTYPE_FIELD_NUMBER: _ClassVar[int] AUTHMESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -110,7 +112,7 @@ class ApproveDeviceRequest(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., ssoAuthenticationProtocolType: _Optional[_Union[SsoAuthenticationProtocolType, str]] = ..., authMessage: _Optional[str] = ..., email: _Optional[str] = ..., devicePublicKey: _Optional[bytes] = ..., serverEccPublicKeyId: _Optional[int] = ..., userEncryptedDataKey: _Optional[bytes] = ..., userEncryptedDataKeyType: _Optional[_Union[_enterprise_pb2.EncryptedKeyType, str]] = ..., ipAddress: _Optional[str] = ..., isTesting: bool = ..., isEccOnly: bool = ...) -> None: ... class SetupRequest(_message.Message): - __slots__ = ["automatorId", "serverEccPublicKeyId", "automatorState", "encryptedEnterprisePrivateEccKey", "encryptedEnterprisePrivateRsaKey", "automatorSkills", "encryptedTreeKey", "isEccOnly"] + __slots__ = ("automatorId", "serverEccPublicKeyId", "automatorState", "encryptedEnterprisePrivateEccKey", "encryptedEnterprisePrivateRsaKey", "automatorSkills", "encryptedTreeKey", "isEccOnly") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] SERVERECCPUBLICKEYID_FIELD_NUMBER: _ClassVar[int] AUTOMATORSTATE_FIELD_NUMBER: _ClassVar[int] @@ -130,7 +132,7 @@ class SetupRequest(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., serverEccPublicKeyId: _Optional[int] = ..., automatorState: _Optional[_Union[AutomatorState, str]] = ..., encryptedEnterprisePrivateEccKey: _Optional[bytes] = ..., encryptedEnterprisePrivateRsaKey: _Optional[bytes] = ..., automatorSkills: _Optional[_Iterable[_Union[AutomatorSkill, _Mapping]]] = ..., encryptedTreeKey: _Optional[bytes] = ..., isEccOnly: bool = ...) -> None: ... class StatusRequest(_message.Message): - __slots__ = ["automatorId", "serverEccPublicKeyId", "isEccOnly"] + __slots__ = ("automatorId", "serverEccPublicKeyId", "isEccOnly") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] SERVERECCPUBLICKEYID_FIELD_NUMBER: _ClassVar[int] ISECCONLY_FIELD_NUMBER: _ClassVar[int] @@ -140,7 +142,7 @@ class StatusRequest(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., serverEccPublicKeyId: _Optional[int] = ..., isEccOnly: bool = ...) -> None: ... class InitializeRequest(_message.Message): - __slots__ = ["automatorId", "idpMetadata", "idpSigningCertificate", "ssoEntityId", "emailMapping", "firstnameMapping", "lastnameMapping", "disabled", "serverEccPublicKeyId", "config", "sslMode", "persistState", "disableSniCheck", "sslCertificateFilename", "sslCertificateFilePassword", "sslCertificateKeyPassword", "sslCertificateContents", "automatorHost", "automatorPort", "ipAllow", "ipDeny", "isEccOnly"] + __slots__ = ("automatorId", "idpMetadata", "idpSigningCertificate", "ssoEntityId", "emailMapping", "firstnameMapping", "lastnameMapping", "disabled", "serverEccPublicKeyId", "config", "sslMode", "persistState", "disableSniCheck", "sslCertificateFilename", "sslCertificateFilePassword", "sslCertificateKeyPassword", "sslCertificateContents", "automatorHost", "automatorPort", "ipAllow", "ipDeny", "isEccOnly") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] IDPMETADATA_FIELD_NUMBER: _ClassVar[int] IDPSIGNINGCERTIFICATE_FIELD_NUMBER: _ClassVar[int] @@ -188,7 +190,7 @@ class InitializeRequest(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., idpMetadata: _Optional[str] = ..., idpSigningCertificate: _Optional[bytes] = ..., ssoEntityId: _Optional[str] = ..., emailMapping: _Optional[str] = ..., firstnameMapping: _Optional[str] = ..., lastnameMapping: _Optional[str] = ..., disabled: bool = ..., serverEccPublicKeyId: _Optional[int] = ..., config: _Optional[bytes] = ..., sslMode: _Optional[str] = ..., persistState: bool = ..., disableSniCheck: bool = ..., sslCertificateFilename: _Optional[str] = ..., sslCertificateFilePassword: _Optional[str] = ..., sslCertificateKeyPassword: _Optional[str] = ..., sslCertificateContents: _Optional[bytes] = ..., automatorHost: _Optional[str] = ..., automatorPort: _Optional[str] = ..., ipAllow: _Optional[str] = ..., ipDeny: _Optional[str] = ..., isEccOnly: bool = ...) -> None: ... class NotInitializedResponse(_message.Message): - __slots__ = ["automatorTransmissionKey", "signingCertificate", "signingCertificateFilename", "signingCertificatePassword", "signingKeyPassword", "signingCertificateFormat", "automatorPublicKey", "config"] + __slots__ = ("automatorTransmissionKey", "signingCertificate", "signingCertificateFilename", "signingCertificatePassword", "signingKeyPassword", "signingCertificateFormat", "automatorPublicKey", "config") AUTOMATORTRANSMISSIONKEY_FIELD_NUMBER: _ClassVar[int] SIGNINGCERTIFICATE_FIELD_NUMBER: _ClassVar[int] SIGNINGCERTIFICATEFILENAME_FIELD_NUMBER: _ClassVar[int] @@ -208,7 +210,7 @@ class NotInitializedResponse(_message.Message): def __init__(self, automatorTransmissionKey: _Optional[bytes] = ..., signingCertificate: _Optional[bytes] = ..., signingCertificateFilename: _Optional[str] = ..., signingCertificatePassword: _Optional[str] = ..., signingKeyPassword: _Optional[str] = ..., signingCertificateFormat: _Optional[_Union[CertificateFormat, str]] = ..., automatorPublicKey: _Optional[bytes] = ..., config: _Optional[bytes] = ...) -> None: ... class AutomatorResponse(_message.Message): - __slots__ = ["automatorId", "enabled", "timestamp", "approveDevice", "status", "notInitialized", "error", "approveTeamsForUser", "approveTeams", "automatorState", "automatorPublicEccKey", "version"] + __slots__ = ("automatorId", "enabled", "timestamp", "approveDevice", "status", "notInitialized", "error", "approveTeamsForUser", "approveTeams", "automatorState", "automatorPublicEccKey", "version") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] ENABLED_FIELD_NUMBER: _ClassVar[int] TIMESTAMP_FIELD_NUMBER: _ClassVar[int] @@ -236,7 +238,7 @@ class AutomatorResponse(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., enabled: bool = ..., timestamp: _Optional[int] = ..., approveDevice: _Optional[_Union[ApproveDeviceResponse, _Mapping]] = ..., status: _Optional[_Union[StatusResponse, _Mapping]] = ..., notInitialized: _Optional[_Union[NotInitializedResponse, _Mapping]] = ..., error: _Optional[_Union[ErrorResponse, _Mapping]] = ..., approveTeamsForUser: _Optional[_Union[ApproveTeamsForUserResponse, _Mapping]] = ..., approveTeams: _Optional[_Union[ApproveTeamsResponse, _Mapping]] = ..., automatorState: _Optional[_Union[AutomatorState, str]] = ..., automatorPublicEccKey: _Optional[bytes] = ..., version: _Optional[_Union[_version_pb2.Version, _Mapping]] = ...) -> None: ... class ApproveDeviceResponse(_message.Message): - __slots__ = ["approved", "encryptedUserDataKey", "message", "encryptedUserDataKeyType"] + __slots__ = ("approved", "encryptedUserDataKey", "message", "encryptedUserDataKeyType") APPROVED_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDUSERDATAKEY_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -248,7 +250,7 @@ class ApproveDeviceResponse(_message.Message): def __init__(self, approved: bool = ..., encryptedUserDataKey: _Optional[bytes] = ..., message: _Optional[str] = ..., encryptedUserDataKeyType: _Optional[_Union[_enterprise_pb2.EncryptedKeyType, str]] = ...) -> None: ... class StatusResponse(_message.Message): - __slots__ = ["initialized", "enabledTimestamp", "initializedTimestamp", "updatedTimestamp", "numberOfDevicesApproved", "numberOfDevicesDenied", "numberOfErrors", "sslCertificateExpiration", "notInitializedResponse", "config", "numberOfTeamMembershipsApproved", "numberOfTeamMembershipsDenied", "numberOfTeamsApproved", "numberOfTeamsDenied"] + __slots__ = ("initialized", "enabledTimestamp", "initializedTimestamp", "updatedTimestamp", "numberOfDevicesApproved", "numberOfDevicesDenied", "numberOfErrors", "sslCertificateExpiration", "notInitializedResponse", "config", "numberOfTeamMembershipsApproved", "numberOfTeamMembershipsDenied", "numberOfTeamsApproved", "numberOfTeamsDenied", "sslCertificateInfo") INITIALIZED_FIELD_NUMBER: _ClassVar[int] ENABLEDTIMESTAMP_FIELD_NUMBER: _ClassVar[int] INITIALIZEDTIMESTAMP_FIELD_NUMBER: _ClassVar[int] @@ -263,6 +265,7 @@ class StatusResponse(_message.Message): NUMBEROFTEAMMEMBERSHIPSDENIED_FIELD_NUMBER: _ClassVar[int] NUMBEROFTEAMSAPPROVED_FIELD_NUMBER: _ClassVar[int] NUMBEROFTEAMSDENIED_FIELD_NUMBER: _ClassVar[int] + SSLCERTIFICATEINFO_FIELD_NUMBER: _ClassVar[int] initialized: bool enabledTimestamp: int initializedTimestamp: int @@ -277,16 +280,17 @@ class StatusResponse(_message.Message): numberOfTeamMembershipsDenied: int numberOfTeamsApproved: int numberOfTeamsDenied: int - def __init__(self, initialized: bool = ..., enabledTimestamp: _Optional[int] = ..., initializedTimestamp: _Optional[int] = ..., updatedTimestamp: _Optional[int] = ..., numberOfDevicesApproved: _Optional[int] = ..., numberOfDevicesDenied: _Optional[int] = ..., numberOfErrors: _Optional[int] = ..., sslCertificateExpiration: _Optional[int] = ..., notInitializedResponse: _Optional[_Union[NotInitializedResponse, _Mapping]] = ..., config: _Optional[bytes] = ..., numberOfTeamMembershipsApproved: _Optional[int] = ..., numberOfTeamMembershipsDenied: _Optional[int] = ..., numberOfTeamsApproved: _Optional[int] = ..., numberOfTeamsDenied: _Optional[int] = ...) -> None: ... + sslCertificateInfo: _containers.RepeatedCompositeFieldContainer[SSLCertificateInfo] + def __init__(self, initialized: bool = ..., enabledTimestamp: _Optional[int] = ..., initializedTimestamp: _Optional[int] = ..., updatedTimestamp: _Optional[int] = ..., numberOfDevicesApproved: _Optional[int] = ..., numberOfDevicesDenied: _Optional[int] = ..., numberOfErrors: _Optional[int] = ..., sslCertificateExpiration: _Optional[int] = ..., notInitializedResponse: _Optional[_Union[NotInitializedResponse, _Mapping]] = ..., config: _Optional[bytes] = ..., numberOfTeamMembershipsApproved: _Optional[int] = ..., numberOfTeamMembershipsDenied: _Optional[int] = ..., numberOfTeamsApproved: _Optional[int] = ..., numberOfTeamsDenied: _Optional[int] = ..., sslCertificateInfo: _Optional[_Iterable[_Union[SSLCertificateInfo, _Mapping]]] = ...) -> None: ... class ErrorResponse(_message.Message): - __slots__ = ["message"] + __slots__ = ("message",) MESSAGE_FIELD_NUMBER: _ClassVar[int] message: str def __init__(self, message: _Optional[str] = ...) -> None: ... class LogEntry(_message.Message): - __slots__ = ["serverTime", "messageLevel", "component", "message"] + __slots__ = ("serverTime", "messageLevel", "component", "message") SERVERTIME_FIELD_NUMBER: _ClassVar[int] MESSAGELEVEL_FIELD_NUMBER: _ClassVar[int] COMPONENT_FIELD_NUMBER: _ClassVar[int] @@ -298,7 +302,7 @@ class LogEntry(_message.Message): def __init__(self, serverTime: _Optional[str] = ..., messageLevel: _Optional[str] = ..., component: _Optional[str] = ..., message: _Optional[str] = ...) -> None: ... class AdminResponse(_message.Message): - __slots__ = ["success", "message", "automatorInfo"] + __slots__ = ("success", "message", "automatorInfo") SUCCESS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] AUTOMATORINFO_FIELD_NUMBER: _ClassVar[int] @@ -308,7 +312,7 @@ class AdminResponse(_message.Message): def __init__(self, success: bool = ..., message: _Optional[str] = ..., automatorInfo: _Optional[_Iterable[_Union[AutomatorInfo, _Mapping]]] = ...) -> None: ... class AutomatorInfo(_message.Message): - __slots__ = ["automatorId", "nodeId", "name", "enabled", "url", "automatorSkills", "automatorSettingValues", "status", "logEntries", "automatorState", "version"] + __slots__ = ("automatorId", "nodeId", "name", "enabled", "url", "automatorSkills", "automatorSettingValues", "status", "logEntries", "automatorState", "version", "sslCertificateExpirationDate") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] NODEID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] @@ -320,6 +324,7 @@ class AutomatorInfo(_message.Message): LOGENTRIES_FIELD_NUMBER: _ClassVar[int] AUTOMATORSTATE_FIELD_NUMBER: _ClassVar[int] VERSION_FIELD_NUMBER: _ClassVar[int] + SSLCERTIFICATEEXPIRATIONDATE_FIELD_NUMBER: _ClassVar[int] automatorId: int nodeId: int name: str @@ -331,10 +336,11 @@ class AutomatorInfo(_message.Message): logEntries: _containers.RepeatedCompositeFieldContainer[LogEntry] automatorState: AutomatorState version: str - def __init__(self, automatorId: _Optional[int] = ..., nodeId: _Optional[int] = ..., name: _Optional[str] = ..., enabled: bool = ..., url: _Optional[str] = ..., automatorSkills: _Optional[_Iterable[_Union[AutomatorSkill, _Mapping]]] = ..., automatorSettingValues: _Optional[_Iterable[_Union[AutomatorSettingValue, _Mapping]]] = ..., status: _Optional[_Union[StatusResponse, _Mapping]] = ..., logEntries: _Optional[_Iterable[_Union[LogEntry, _Mapping]]] = ..., automatorState: _Optional[_Union[AutomatorState, str]] = ..., version: _Optional[str] = ...) -> None: ... + sslCertificateExpirationDate: str + def __init__(self, automatorId: _Optional[int] = ..., nodeId: _Optional[int] = ..., name: _Optional[str] = ..., enabled: bool = ..., url: _Optional[str] = ..., automatorSkills: _Optional[_Iterable[_Union[AutomatorSkill, _Mapping]]] = ..., automatorSettingValues: _Optional[_Iterable[_Union[AutomatorSettingValue, _Mapping]]] = ..., status: _Optional[_Union[StatusResponse, _Mapping]] = ..., logEntries: _Optional[_Iterable[_Union[LogEntry, _Mapping]]] = ..., automatorState: _Optional[_Union[AutomatorState, str]] = ..., version: _Optional[str] = ..., sslCertificateExpirationDate: _Optional[str] = ...) -> None: ... class AdminCreateAutomatorRequest(_message.Message): - __slots__ = ["nodeId", "name", "skill"] + __slots__ = ("nodeId", "name", "skill") NODEID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] SKILL_FIELD_NUMBER: _ClassVar[int] @@ -344,31 +350,31 @@ class AdminCreateAutomatorRequest(_message.Message): def __init__(self, nodeId: _Optional[int] = ..., name: _Optional[str] = ..., skill: _Optional[_Union[AutomatorSkill, _Mapping]] = ...) -> None: ... class AdminDeleteAutomatorRequest(_message.Message): - __slots__ = ["automatorId"] + __slots__ = ("automatorId",) AUTOMATORID_FIELD_NUMBER: _ClassVar[int] automatorId: int def __init__(self, automatorId: _Optional[int] = ...) -> None: ... class AdminGetAutomatorsOnNodeRequest(_message.Message): - __slots__ = ["nodeId"] + __slots__ = ("nodeId",) NODEID_FIELD_NUMBER: _ClassVar[int] nodeId: int def __init__(self, nodeId: _Optional[int] = ...) -> None: ... class AdminGetAutomatorsForEnterpriseRequest(_message.Message): - __slots__ = ["enterpriseId"] + __slots__ = ("enterpriseId",) ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] enterpriseId: int def __init__(self, enterpriseId: _Optional[int] = ...) -> None: ... class AdminGetAutomatorRequest(_message.Message): - __slots__ = ["automatorId"] + __slots__ = ("automatorId",) AUTOMATORID_FIELD_NUMBER: _ClassVar[int] automatorId: int def __init__(self, automatorId: _Optional[int] = ...) -> None: ... class AdminEnableAutomatorRequest(_message.Message): - __slots__ = ["automatorId", "enabled"] + __slots__ = ("automatorId", "enabled") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] ENABLED_FIELD_NUMBER: _ClassVar[int] automatorId: int @@ -376,7 +382,7 @@ class AdminEnableAutomatorRequest(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., enabled: bool = ...) -> None: ... class AdminEditAutomatorRequest(_message.Message): - __slots__ = ["automatorId", "name", "enabled", "url", "skillTypes", "automatorSettingValues"] + __slots__ = ("automatorId", "name", "enabled", "url", "skillTypes", "automatorSettingValues") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] ENABLED_FIELD_NUMBER: _ClassVar[int] @@ -392,7 +398,7 @@ class AdminEditAutomatorRequest(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., name: _Optional[str] = ..., enabled: bool = ..., url: _Optional[str] = ..., skillTypes: _Optional[_Iterable[_Union[SkillType, str]]] = ..., automatorSettingValues: _Optional[_Iterable[_Union[AutomatorSettingValue, _Mapping]]] = ...) -> None: ... class AdminSetupAutomatorRequest(_message.Message): - __slots__ = ["automatorId", "automatorState", "encryptedEccEnterprisePrivateKey", "encryptedRsaEnterprisePrivateKey", "skillTypes", "encryptedTreeKey"] + __slots__ = ("automatorId", "automatorState", "encryptedEccEnterprisePrivateKey", "encryptedRsaEnterprisePrivateKey", "skillTypes", "encryptedTreeKey") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] AUTOMATORSTATE_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDECCENTERPRISEPRIVATEKEY_FIELD_NUMBER: _ClassVar[int] @@ -408,7 +414,7 @@ class AdminSetupAutomatorRequest(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., automatorState: _Optional[_Union[AutomatorState, str]] = ..., encryptedEccEnterprisePrivateKey: _Optional[bytes] = ..., encryptedRsaEnterprisePrivateKey: _Optional[bytes] = ..., skillTypes: _Optional[_Iterable[_Union[SkillType, str]]] = ..., encryptedTreeKey: _Optional[bytes] = ...) -> None: ... class AdminSetupAutomatorResponse(_message.Message): - __slots__ = ["success", "message", "automatorId", "automatorState", "automatorEccPublicKey"] + __slots__ = ("success", "message", "automatorId", "automatorState", "automatorEccPublicKey") SUCCESS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] AUTOMATORID_FIELD_NUMBER: _ClassVar[int] @@ -422,13 +428,13 @@ class AdminSetupAutomatorResponse(_message.Message): def __init__(self, success: bool = ..., message: _Optional[str] = ..., automatorId: _Optional[int] = ..., automatorState: _Optional[_Union[AutomatorState, str]] = ..., automatorEccPublicKey: _Optional[bytes] = ...) -> None: ... class AdminAutomatorSkillsRequest(_message.Message): - __slots__ = ["automatorId"] + __slots__ = ("automatorId",) AUTOMATORID_FIELD_NUMBER: _ClassVar[int] automatorId: int def __init__(self, automatorId: _Optional[int] = ...) -> None: ... class AutomatorSkill(_message.Message): - __slots__ = ["skillType", "name", "translatedName"] + __slots__ = ("skillType", "name", "translatedName") SKILLTYPE_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] TRANSLATEDNAME_FIELD_NUMBER: _ClassVar[int] @@ -438,7 +444,7 @@ class AutomatorSkill(_message.Message): def __init__(self, skillType: _Optional[_Union[SkillType, str]] = ..., name: _Optional[str] = ..., translatedName: _Optional[str] = ...) -> None: ... class AdminAutomatorSkillsResponse(_message.Message): - __slots__ = ["success", "message", "automatorSkills"] + __slots__ = ("success", "message", "automatorSkills") SUCCESS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] AUTOMATORSKILLS_FIELD_NUMBER: _ClassVar[int] @@ -448,31 +454,31 @@ class AdminAutomatorSkillsResponse(_message.Message): def __init__(self, success: bool = ..., message: _Optional[str] = ..., automatorSkills: _Optional[_Iterable[_Union[AutomatorSkill, _Mapping]]] = ...) -> None: ... class AdminResetAutomatorRequest(_message.Message): - __slots__ = ["automatorId"] + __slots__ = ("automatorId",) AUTOMATORID_FIELD_NUMBER: _ClassVar[int] automatorId: int def __init__(self, automatorId: _Optional[int] = ...) -> None: ... class AdminInitializeAutomatorRequest(_message.Message): - __slots__ = ["automatorId"] + __slots__ = ("automatorId",) AUTOMATORID_FIELD_NUMBER: _ClassVar[int] automatorId: int def __init__(self, automatorId: _Optional[int] = ...) -> None: ... class AdminAutomatorLogRequest(_message.Message): - __slots__ = ["automatorId"] + __slots__ = ("automatorId",) AUTOMATORID_FIELD_NUMBER: _ClassVar[int] automatorId: int def __init__(self, automatorId: _Optional[int] = ...) -> None: ... class AdminAutomatorLogClearRequest(_message.Message): - __slots__ = ["automatorId"] + __slots__ = ("automatorId",) AUTOMATORID_FIELD_NUMBER: _ClassVar[int] automatorId: int def __init__(self, automatorId: _Optional[int] = ...) -> None: ... class ApproveTeamsForUserRequest(_message.Message): - __slots__ = ["automatorId", "ssoAuthenticationProtocolType", "authMessage", "email", "serverEccPublicKeyId", "ipAddress", "userPublicKey", "teamDescription", "isTesting", "isEccOnly", "userPublicKeyEcc"] + __slots__ = ("automatorId", "ssoAuthenticationProtocolType", "authMessage", "email", "serverEccPublicKeyId", "ipAddress", "userPublicKey", "teamDescription", "isTesting", "isEccOnly", "userPublicKeyEcc") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] SSOAUTHENTICATIONPROTOCOLTYPE_FIELD_NUMBER: _ClassVar[int] AUTHMESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -498,7 +504,7 @@ class ApproveTeamsForUserRequest(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., ssoAuthenticationProtocolType: _Optional[_Union[SsoAuthenticationProtocolType, str]] = ..., authMessage: _Optional[str] = ..., email: _Optional[str] = ..., serverEccPublicKeyId: _Optional[int] = ..., ipAddress: _Optional[str] = ..., userPublicKey: _Optional[bytes] = ..., teamDescription: _Optional[_Iterable[_Union[TeamDescription, _Mapping]]] = ..., isTesting: bool = ..., isEccOnly: bool = ..., userPublicKeyEcc: _Optional[bytes] = ...) -> None: ... class TeamDescription(_message.Message): - __slots__ = ["teamUid", "teamName", "encryptedTeamKey", "encryptedTeamKeyType"] + __slots__ = ("teamUid", "teamName", "encryptedTeamKey", "encryptedTeamKeyType") TEAMUID_FIELD_NUMBER: _ClassVar[int] TEAMNAME_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDTEAMKEY_FIELD_NUMBER: _ClassVar[int] @@ -510,7 +516,7 @@ class TeamDescription(_message.Message): def __init__(self, teamUid: _Optional[bytes] = ..., teamName: _Optional[str] = ..., encryptedTeamKey: _Optional[bytes] = ..., encryptedTeamKeyType: _Optional[_Union[_enterprise_pb2.EncryptedKeyType, str]] = ...) -> None: ... class ApproveTeamsForUserResponse(_message.Message): - __slots__ = ["automatorId", "email", "message", "approveTeamResponse"] + __slots__ = ("automatorId", "email", "message", "approveTeamResponse") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] EMAIL_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -522,7 +528,7 @@ class ApproveTeamsForUserResponse(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., email: _Optional[str] = ..., message: _Optional[str] = ..., approveTeamResponse: _Optional[_Iterable[_Union[ApproveOneTeamForUserResponse, _Mapping]]] = ...) -> None: ... class ApproveOneTeamForUserResponse(_message.Message): - __slots__ = ["approved", "message", "teamUid", "teamName", "userEncryptedTeamKey", "userEncryptedTeamKeyType", "userEncryptedTeamKeyByEcc", "userEncryptedTeamKeyByEccType"] + __slots__ = ("approved", "message", "teamUid", "teamName", "userEncryptedTeamKey", "userEncryptedTeamKeyType", "userEncryptedTeamKeyByEcc", "userEncryptedTeamKeyByEccType") APPROVED_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] TEAMUID_FIELD_NUMBER: _ClassVar[int] @@ -542,7 +548,7 @@ class ApproveOneTeamForUserResponse(_message.Message): def __init__(self, approved: bool = ..., message: _Optional[str] = ..., teamUid: _Optional[bytes] = ..., teamName: _Optional[str] = ..., userEncryptedTeamKey: _Optional[bytes] = ..., userEncryptedTeamKeyType: _Optional[_Union[_enterprise_pb2.EncryptedKeyType, str]] = ..., userEncryptedTeamKeyByEcc: _Optional[bytes] = ..., userEncryptedTeamKeyByEccType: _Optional[_Union[_enterprise_pb2.EncryptedKeyType, str]] = ...) -> None: ... class ApproveTeamsRequest(_message.Message): - __slots__ = ["automatorId", "ssoAuthenticationProtocolType", "authMessage", "email", "serverEccPublicKeyId", "ipAddress", "teamDescription", "isEccOnly", "isTesting"] + __slots__ = ("automatorId", "ssoAuthenticationProtocolType", "authMessage", "email", "serverEccPublicKeyId", "ipAddress", "teamDescription", "isEccOnly", "isTesting") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] SSOAUTHENTICATIONPROTOCOLTYPE_FIELD_NUMBER: _ClassVar[int] AUTHMESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -564,7 +570,7 @@ class ApproveTeamsRequest(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., ssoAuthenticationProtocolType: _Optional[_Union[SsoAuthenticationProtocolType, str]] = ..., authMessage: _Optional[str] = ..., email: _Optional[str] = ..., serverEccPublicKeyId: _Optional[int] = ..., ipAddress: _Optional[str] = ..., teamDescription: _Optional[_Iterable[_Union[TeamDescription, _Mapping]]] = ..., isEccOnly: bool = ..., isTesting: bool = ...) -> None: ... class ApproveTeamsResponse(_message.Message): - __slots__ = ["automatorId", "message", "approveTeamResponse"] + __slots__ = ("automatorId", "message", "approveTeamResponse") AUTOMATORID_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] APPROVETEAMRESPONSE_FIELD_NUMBER: _ClassVar[int] @@ -574,7 +580,7 @@ class ApproveTeamsResponse(_message.Message): def __init__(self, automatorId: _Optional[int] = ..., message: _Optional[str] = ..., approveTeamResponse: _Optional[_Iterable[_Union[ApproveOneTeamResponse, _Mapping]]] = ...) -> None: ... class ApproveOneTeamResponse(_message.Message): - __slots__ = ["approved", "message", "teamUid", "teamName", "encryptedTeamKeyCbc", "encryptedTeamKeyCbcType", "encryptedTeamKeyGcm", "encryptedTeamKeyGcmType", "teamPublicKeyRsa", "encryptedTeamPrivateKeyRsa", "encryptedTeamPrivateKeyRsaType", "teamPublicKeyEcc", "encryptedTeamPrivateKeyEcc", "encryptedTeamPrivateKeyEccType"] + __slots__ = ("approved", "message", "teamUid", "teamName", "encryptedTeamKeyCbc", "encryptedTeamKeyCbcType", "encryptedTeamKeyGcm", "encryptedTeamKeyGcmType", "teamPublicKeyRsa", "encryptedTeamPrivateKeyRsa", "encryptedTeamPrivateKeyRsaType", "teamPublicKeyEcc", "encryptedTeamPrivateKeyEcc", "encryptedTeamPrivateKeyEccType") APPROVED_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] TEAMUID_FIELD_NUMBER: _ClassVar[int] @@ -604,3 +610,21 @@ class ApproveOneTeamResponse(_message.Message): encryptedTeamPrivateKeyEcc: bytes encryptedTeamPrivateKeyEccType: _enterprise_pb2.EncryptedKeyType def __init__(self, approved: bool = ..., message: _Optional[str] = ..., teamUid: _Optional[bytes] = ..., teamName: _Optional[str] = ..., encryptedTeamKeyCbc: _Optional[bytes] = ..., encryptedTeamKeyCbcType: _Optional[_Union[_enterprise_pb2.EncryptedKeyType, str]] = ..., encryptedTeamKeyGcm: _Optional[bytes] = ..., encryptedTeamKeyGcmType: _Optional[_Union[_enterprise_pb2.EncryptedKeyType, str]] = ..., teamPublicKeyRsa: _Optional[bytes] = ..., encryptedTeamPrivateKeyRsa: _Optional[bytes] = ..., encryptedTeamPrivateKeyRsaType: _Optional[_Union[_enterprise_pb2.EncryptedKeyType, str]] = ..., teamPublicKeyEcc: _Optional[bytes] = ..., encryptedTeamPrivateKeyEcc: _Optional[bytes] = ..., encryptedTeamPrivateKeyEccType: _Optional[_Union[_enterprise_pb2.EncryptedKeyType, str]] = ...) -> None: ... + +class SSLCertificateInfo(_message.Message): + __slots__ = ("automatorId", "hostUrl", "subject", "issuer", "issuedOn", "expiresOn", "checkedOn") + AUTOMATORID_FIELD_NUMBER: _ClassVar[int] + HOSTURL_FIELD_NUMBER: _ClassVar[int] + SUBJECT_FIELD_NUMBER: _ClassVar[int] + ISSUER_FIELD_NUMBER: _ClassVar[int] + ISSUEDON_FIELD_NUMBER: _ClassVar[int] + EXPIRESON_FIELD_NUMBER: _ClassVar[int] + CHECKEDON_FIELD_NUMBER: _ClassVar[int] + automatorId: int + hostUrl: str + subject: str + issuer: str + issuedOn: int + expiresOn: int + checkedOn: int + def __init__(self, automatorId: _Optional[int] = ..., hostUrl: _Optional[str] = ..., subject: _Optional[str] = ..., issuer: _Optional[str] = ..., issuedOn: _Optional[int] = ..., expiresOn: _Optional[int] = ..., checkedOn: _Optional[int] = ...) -> None: ... diff --git a/keepercommander/proto/breachwatch_pb2.py b/keepercommander/proto/breachwatch_pb2.py index 75ff7ee2b..340d282a2 100644 --- a/keepercommander/proto/breachwatch_pb2.py +++ b/keepercommander/proto/breachwatch_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: breachwatch.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'breachwatch.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,8 +29,8 @@ _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'breachwatch_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\013BreachWatch' _globals['_BREACHWATCHINFOTYPE']._serialized_start=1772 _globals['_BREACHWATCHINFOTYPE']._serialized_end=1829 diff --git a/keepercommander/proto/breachwatch_pb2.pyi b/keepercommander/proto/breachwatch_pb2.pyi index d9a0f8182..c888e6f2a 100644 --- a/keepercommander/proto/breachwatch_pb2.pyi +++ b/keepercommander/proto/breachwatch_pb2.pyi @@ -7,14 +7,14 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class BreachWatchInfoType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () RECORD: _ClassVar[BreachWatchInfoType] ALTERNATE_PASSWORD: _ClassVar[BreachWatchInfoType] RECORD: BreachWatchInfoType ALTERNATE_PASSWORD: BreachWatchInfoType class BreachWatchRecordRequest(_message.Message): - __slots__ = ["recordUid", "encryptedData", "breachWatchInfoType", "updateUserWhoScanned"] + __slots__ = ("recordUid", "encryptedData", "breachWatchInfoType", "updateUserWhoScanned") RECORDUID_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] BREACHWATCHINFOTYPE_FIELD_NUMBER: _ClassVar[int] @@ -26,7 +26,7 @@ class BreachWatchRecordRequest(_message.Message): def __init__(self, recordUid: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., breachWatchInfoType: _Optional[_Union[BreachWatchInfoType, str]] = ..., updateUserWhoScanned: bool = ...) -> None: ... class BreachWatchUpdateRequest(_message.Message): - __slots__ = ["breachWatchRecordRequest", "encryptedData"] + __slots__ = ("breachWatchRecordRequest", "encryptedData") BREACHWATCHRECORDREQUEST_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] breachWatchRecordRequest: _containers.RepeatedCompositeFieldContainer[BreachWatchRecordRequest] @@ -34,7 +34,7 @@ class BreachWatchUpdateRequest(_message.Message): def __init__(self, breachWatchRecordRequest: _Optional[_Iterable[_Union[BreachWatchRecordRequest, _Mapping]]] = ..., encryptedData: _Optional[bytes] = ...) -> None: ... class BreachWatchRecordStatus(_message.Message): - __slots__ = ["recordUid", "status", "reason"] + __slots__ = ("recordUid", "status", "reason") RECORDUID_FIELD_NUMBER: _ClassVar[int] STATUS_FIELD_NUMBER: _ClassVar[int] REASON_FIELD_NUMBER: _ClassVar[int] @@ -44,19 +44,19 @@ class BreachWatchRecordStatus(_message.Message): def __init__(self, recordUid: _Optional[bytes] = ..., status: _Optional[str] = ..., reason: _Optional[str] = ...) -> None: ... class BreachWatchUpdateResponse(_message.Message): - __slots__ = ["breachWatchRecordStatus"] + __slots__ = ("breachWatchRecordStatus",) BREACHWATCHRECORDSTATUS_FIELD_NUMBER: _ClassVar[int] breachWatchRecordStatus: _containers.RepeatedCompositeFieldContainer[BreachWatchRecordStatus] def __init__(self, breachWatchRecordStatus: _Optional[_Iterable[_Union[BreachWatchRecordStatus, _Mapping]]] = ...) -> None: ... class BreachWatchTokenRequest(_message.Message): - __slots__ = ["breachWatchToken"] + __slots__ = ("breachWatchToken",) BREACHWATCHTOKEN_FIELD_NUMBER: _ClassVar[int] breachWatchToken: bytes def __init__(self, breachWatchToken: _Optional[bytes] = ...) -> None: ... class BreachWatchTokenResponse(_message.Message): - __slots__ = ["breachWatchToken", "clientEncrypted"] + __slots__ = ("breachWatchToken", "clientEncrypted") BREACHWATCHTOKEN_FIELD_NUMBER: _ClassVar[int] CLIENTENCRYPTED_FIELD_NUMBER: _ClassVar[int] breachWatchToken: bytes @@ -64,7 +64,7 @@ class BreachWatchTokenResponse(_message.Message): def __init__(self, breachWatchToken: _Optional[bytes] = ..., clientEncrypted: bool = ...) -> None: ... class AnonymizedTokenResponse(_message.Message): - __slots__ = ["domainToken", "emailToken", "passwordToken"] + __slots__ = ("domainToken", "emailToken", "passwordToken") DOMAINTOKEN_FIELD_NUMBER: _ClassVar[int] EMAILTOKEN_FIELD_NUMBER: _ClassVar[int] PASSWORDTOKEN_FIELD_NUMBER: _ClassVar[int] @@ -74,7 +74,7 @@ class AnonymizedTokenResponse(_message.Message): def __init__(self, domainToken: _Optional[bytes] = ..., emailToken: _Optional[bytes] = ..., passwordToken: _Optional[bytes] = ...) -> None: ... class HashCheck(_message.Message): - __slots__ = ["hash1", "euid"] + __slots__ = ("hash1", "euid") HASH1_FIELD_NUMBER: _ClassVar[int] EUID_FIELD_NUMBER: _ClassVar[int] hash1: bytes @@ -82,7 +82,7 @@ class HashCheck(_message.Message): def __init__(self, hash1: _Optional[bytes] = ..., euid: _Optional[bytes] = ...) -> None: ... class BreachWatchStatusRequest(_message.Message): - __slots__ = ["anonymizedToken", "hashCheck", "removedEuid"] + __slots__ = ("anonymizedToken", "hashCheck", "removedEuid") ANONYMIZEDTOKEN_FIELD_NUMBER: _ClassVar[int] HASHCHECK_FIELD_NUMBER: _ClassVar[int] REMOVEDEUID_FIELD_NUMBER: _ClassVar[int] @@ -92,7 +92,7 @@ class BreachWatchStatusRequest(_message.Message): def __init__(self, anonymizedToken: _Optional[bytes] = ..., hashCheck: _Optional[_Iterable[_Union[HashCheck, _Mapping]]] = ..., removedEuid: _Optional[_Iterable[bytes]] = ...) -> None: ... class HashStatus(_message.Message): - __slots__ = ["hash1", "euid", "breachDetected"] + __slots__ = ("hash1", "euid", "breachDetected") HASH1_FIELD_NUMBER: _ClassVar[int] EUID_FIELD_NUMBER: _ClassVar[int] BREACHDETECTED_FIELD_NUMBER: _ClassVar[int] @@ -102,13 +102,13 @@ class HashStatus(_message.Message): def __init__(self, hash1: _Optional[bytes] = ..., euid: _Optional[bytes] = ..., breachDetected: bool = ...) -> None: ... class BreachWatchStatusResponse(_message.Message): - __slots__ = ["hashStatus"] + __slots__ = ("hashStatus",) HASHSTATUS_FIELD_NUMBER: _ClassVar[int] hashStatus: _containers.RepeatedCompositeFieldContainer[HashStatus] def __init__(self, hashStatus: _Optional[_Iterable[_Union[HashStatus, _Mapping]]] = ...) -> None: ... class EnterprisePublicKeyResponse(_message.Message): - __slots__ = ["enterprisePublicKey", "enterpriseECCPublicKey"] + __slots__ = ("enterprisePublicKey", "enterpriseECCPublicKey") ENTERPRISEPUBLICKEY_FIELD_NUMBER: _ClassVar[int] ENTERPRISEECCPUBLICKEY_FIELD_NUMBER: _ClassVar[int] enterprisePublicKey: bytes @@ -116,13 +116,13 @@ class EnterprisePublicKeyResponse(_message.Message): def __init__(self, enterprisePublicKey: _Optional[bytes] = ..., enterpriseECCPublicKey: _Optional[bytes] = ...) -> None: ... class FreeScanRequest(_message.Message): - __slots__ = ["hashedEmail"] + __slots__ = ("hashedEmail",) HASHEDEMAIL_FIELD_NUMBER: _ClassVar[int] hashedEmail: bytes def __init__(self, hashedEmail: _Optional[bytes] = ...) -> None: ... class FreeScanResponse(_message.Message): - __slots__ = ["emailBreaches", "passwordBreaches"] + __slots__ = ("emailBreaches", "passwordBreaches") EMAILBREACHES_FIELD_NUMBER: _ClassVar[int] PASSWORDBREACHES_FIELD_NUMBER: _ClassVar[int] emailBreaches: int @@ -130,31 +130,31 @@ class FreeScanResponse(_message.Message): def __init__(self, emailBreaches: _Optional[int] = ..., passwordBreaches: _Optional[int] = ...) -> None: ... class PaidUserRequest(_message.Message): - __slots__ = ["email"] + __slots__ = ("email",) EMAIL_FIELD_NUMBER: _ClassVar[int] email: str def __init__(self, email: _Optional[str] = ...) -> None: ... class PaidUserResponse(_message.Message): - __slots__ = ["paidUser"] + __slots__ = ("paidUser",) PAIDUSER_FIELD_NUMBER: _ClassVar[int] paidUser: bool def __init__(self, paidUser: bool = ...) -> None: ... class DetailedScanRequest(_message.Message): - __slots__ = ["email"] + __slots__ = ("email",) EMAIL_FIELD_NUMBER: _ClassVar[int] email: str def __init__(self, email: _Optional[str] = ...) -> None: ... class UseOneTimeTokenRequest(_message.Message): - __slots__ = ["token"] + __slots__ = ("token",) TOKEN_FIELD_NUMBER: _ClassVar[int] token: bytes def __init__(self, token: _Optional[bytes] = ...) -> None: ... class BreachEvent(_message.Message): - __slots__ = ["site", "email", "passwordInBreach", "date", "description"] + __slots__ = ("site", "email", "passwordInBreach", "date", "description") SITE_FIELD_NUMBER: _ClassVar[int] EMAIL_FIELD_NUMBER: _ClassVar[int] PASSWORDINBREACH_FIELD_NUMBER: _ClassVar[int] @@ -168,7 +168,7 @@ class BreachEvent(_message.Message): def __init__(self, site: _Optional[str] = ..., email: _Optional[str] = ..., passwordInBreach: bool = ..., date: _Optional[str] = ..., description: _Optional[str] = ...) -> None: ... class UseOneTimeTokenResponse(_message.Message): - __slots__ = ["emailBreaches", "passwordBreaches", "breachEvents", "email"] + __slots__ = ("emailBreaches", "passwordBreaches", "breachEvents", "email") EMAILBREACHES_FIELD_NUMBER: _ClassVar[int] PASSWORDBREACHES_FIELD_NUMBER: _ClassVar[int] BREACHEVENTS_FIELD_NUMBER: _ClassVar[int] @@ -180,7 +180,7 @@ class UseOneTimeTokenResponse(_message.Message): def __init__(self, emailBreaches: _Optional[int] = ..., passwordBreaches: _Optional[int] = ..., breachEvents: _Optional[_Iterable[_Union[BreachEvent, _Mapping]]] = ..., email: _Optional[str] = ...) -> None: ... class OneTimeUseToken(_message.Message): - __slots__ = ["email", "pad"] + __slots__ = ("email", "pad") EMAIL_FIELD_NUMBER: _ClassVar[int] PAD_FIELD_NUMBER: _ClassVar[int] email: str @@ -188,13 +188,13 @@ class OneTimeUseToken(_message.Message): def __init__(self, email: _Optional[str] = ..., pad: _Optional[str] = ...) -> None: ... class FreePasswordScanRequest(_message.Message): - __slots__ = ["hashedPassword"] + __slots__ = ("hashedPassword",) HASHEDPASSWORD_FIELD_NUMBER: _ClassVar[int] hashedPassword: bytes def __init__(self, hashedPassword: _Optional[bytes] = ...) -> None: ... class FreePasswordScanResponse(_message.Message): - __slots__ = ["passwordBreaches"] + __slots__ = ("passwordBreaches",) PASSWORDBREACHES_FIELD_NUMBER: _ClassVar[int] passwordBreaches: int def __init__(self, passwordBreaches: _Optional[int] = ...) -> None: ... diff --git a/keepercommander/proto/client_pb2.py b/keepercommander/proto/client_pb2.py index 2477e9d56..1d008178f 100644 --- a/keepercommander/proto/client_pb2.py +++ b/keepercommander/proto/client_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: client.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'client.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,8 +29,8 @@ _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'client_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\006Client' _globals['_BREACHWATCHINFOTYPE']._serialized_start=529 _globals['_BREACHWATCHINFOTYPE']._serialized_end=586 diff --git a/keepercommander/proto/client_pb2.pyi b/keepercommander/proto/client_pb2.pyi index 4d914af03..d04a2e6d5 100644 --- a/keepercommander/proto/client_pb2.pyi +++ b/keepercommander/proto/client_pb2.pyi @@ -7,12 +7,12 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class BreachWatchInfoType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () RECORD: _ClassVar[BreachWatchInfoType] ALTERNATE_PASSWORD: _ClassVar[BreachWatchInfoType] class BWStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () GOOD: _ClassVar[BWStatus] CHANGED: _ClassVar[BWStatus] WEAK: _ClassVar[BWStatus] @@ -27,7 +27,7 @@ BREACHED: BWStatus IGNORE: BWStatus class BreachWatchUpdateRequest(_message.Message): - __slots__ = ["breachWatchRecordRequest", "encryptedData"] + __slots__ = ("breachWatchRecordRequest", "encryptedData") BREACHWATCHRECORDREQUEST_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] breachWatchRecordRequest: _containers.RepeatedCompositeFieldContainer[BreachWatchRecordRequest] @@ -35,7 +35,7 @@ class BreachWatchUpdateRequest(_message.Message): def __init__(self, breachWatchRecordRequest: _Optional[_Iterable[_Union[BreachWatchRecordRequest, _Mapping]]] = ..., encryptedData: _Optional[bytes] = ...) -> None: ... class BreachWatchRecordRequest(_message.Message): - __slots__ = ["recordUid", "encryptedData", "breachWatchInfoType", "updateUserWhoScanned"] + __slots__ = ("recordUid", "encryptedData", "breachWatchInfoType", "updateUserWhoScanned") RECORDUID_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] BREACHWATCHINFOTYPE_FIELD_NUMBER: _ClassVar[int] @@ -47,7 +47,7 @@ class BreachWatchRecordRequest(_message.Message): def __init__(self, recordUid: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., breachWatchInfoType: _Optional[_Union[BreachWatchInfoType, str]] = ..., updateUserWhoScanned: bool = ...) -> None: ... class BreachWatchData(_message.Message): - __slots__ = ["passwords", "emails", "domains"] + __slots__ = ("passwords", "emails", "domains") PASSWORDS_FIELD_NUMBER: _ClassVar[int] EMAILS_FIELD_NUMBER: _ClassVar[int] DOMAINS_FIELD_NUMBER: _ClassVar[int] @@ -57,7 +57,7 @@ class BreachWatchData(_message.Message): def __init__(self, passwords: _Optional[_Iterable[_Union[BWPassword, _Mapping]]] = ..., emails: _Optional[_Iterable[_Union[BWPassword, _Mapping]]] = ..., domains: _Optional[_Iterable[_Union[BWPassword, _Mapping]]] = ...) -> None: ... class BWPassword(_message.Message): - __slots__ = ["value", "resolved", "status", "euid"] + __slots__ = ("value", "resolved", "status", "euid") VALUE_FIELD_NUMBER: _ClassVar[int] RESOLVED_FIELD_NUMBER: _ClassVar[int] STATUS_FIELD_NUMBER: _ClassVar[int] diff --git a/keepercommander/proto/dag_pb2.py b/keepercommander/proto/dag_pb2.py index 40889c765..2b7c9d437 100644 --- a/keepercommander/proto/dag_pb2.py +++ b/keepercommander/proto/dag_pb2.py @@ -1,11 +1,23 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: dag.proto +# Protobuf Python Version: 5.29.5 +"""Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder - +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'dag.proto' +) +# @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/dag_pb2.pyi b/keepercommander/proto/dag_pb2.pyi index 026e97603..8738ef405 100644 --- a/keepercommander/proto/dag_pb2.pyi +++ b/keepercommander/proto/dag_pb2.pyi @@ -2,8 +2,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -79,7 +78,7 @@ class SyncData(_message.Message): data: _containers.RepeatedCompositeFieldContainer[Data] syncPoint: int hasMore: bool - def __init__(self, data: _Optional[_Iterable[_Union[Data, _Mapping]]] = ..., syncPoint: _Optional[int] = ..., hasMore: _Optional[bool] = ...) -> None: ... + def __init__(self, data: _Optional[_Iterable[_Union[Data, _Mapping]]] = ..., syncPoint: _Optional[int] = ..., hasMore: bool = ...) -> None: ... class DebugData(_message.Message): __slots__ = ("dataType", "path", "ref", "parentRef") diff --git a/keepercommander/proto/enterprise_pb2.py b/keepercommander/proto/enterprise_pb2.py index 709b672fd..63c28ba73 100644 --- a/keepercommander/proto/enterprise_pb2.py +++ b/keepercommander/proto/enterprise_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: enterprise.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'enterprise.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -13,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x65nterprise.proto\x12\nEnterprise\"\x84\x01\n\x18\x45nterpriseKeyPairRequest\x12\x1b\n\x13\x65nterprisePublicKey\x18\x01 \x01(\x0c\x12%\n\x1d\x65ncryptedEnterprisePrivateKey\x18\x02 \x01(\x0c\x12$\n\x07keyType\x18\x03 \x01(\x0e\x32\x13.Enterprise.KeyType\"\'\n\x14GetTeamMemberRequest\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\"}\n\x0e\x45nterpriseUser\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x1a\n\x12\x65nterpriseUsername\x18\x03 \x01(\t\x12\x14\n\x0cisShareAdmin\x18\x04 \x01(\x08\x12\x10\n\x08username\x18\x05 \x01(\t\"K\n\x15GetTeamMemberResponse\x12\x32\n\x0e\x65nterpriseUser\x18\x01 \x03(\x0b\x32\x1a.Enterprise.EnterpriseUser\"-\n\x11\x45nterpriseUserIds\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x03(\x03\"B\n\x19\x45nterprisePersonalAccount\x12\r\n\x05\x65mail\x18\x01 \x01(\t\x12\x16\n\x0eOBSOLETE_FIELD\x18\x02 \x01(\x0c\"S\n\x17\x45ncryptedTeamKeyRequest\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x18\n\x10\x65ncryptedTeamKey\x18\x02 \x01(\x0c\x12\r\n\x05\x66orce\x18\x03 \x01(\x08\"+\n\x0fReEncryptedData\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"?\n\x12ReEncryptedRoleKey\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x18\n\x10\x65ncryptedRoleKey\x18\x02 \x01(\x0c\"P\n\x16ReEncryptedUserDataKey\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14userEncryptedDataKey\x18\x02 \x01(\x0c\"\xd8\x02\n\x1bNodeToManagedCompanyRequest\x12\x11\n\tcompanyId\x18\x01 \x01(\x05\x12*\n\x05nodes\x18\x02 \x03(\x0b\x32\x1b.Enterprise.ReEncryptedData\x12*\n\x05roles\x18\x03 \x03(\x0b\x32\x1b.Enterprise.ReEncryptedData\x12*\n\x05users\x18\x04 \x03(\x0b\x32\x1b.Enterprise.ReEncryptedData\x12\x30\n\x08roleKeys\x18\x05 \x03(\x0b\x32\x1e.Enterprise.ReEncryptedRoleKey\x12\x35\n\x08teamKeys\x18\x06 \x03(\x0b\x32#.Enterprise.EncryptedTeamKeyRequest\x12\x39\n\rusersDataKeys\x18\x07 \x03(\x0b\x32\".Enterprise.ReEncryptedUserDataKey\",\n\x08RoleTeam\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x0f\n\x07teamUid\x18\x02 \x01(\x0c\"4\n\tRoleTeams\x12\'\n\trole_team\x18\x01 \x03(\x0b\x32\x14.Enterprise.RoleTeam\"/\n\x0bTeamsByRole\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x0f\n\x07teamUid\x18\x02 \x03(\x0c\"<\n\x12ManagedNodesByRole\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x15\n\rmanagedNodeId\x18\x02 \x03(\x03\"R\n\x0fRoleUserAddKeys\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0f\n\x07treeKey\x18\x02 \x01(\t\x12\x14\n\x0croleAdminKey\x18\x03 \x01(\t\"T\n\x0bRoleUserAdd\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x34\n\x0froleUserAddKeys\x18\x02 \x03(\x0b\x32\x1b.Enterprise.RoleUserAddKeys\"D\n\x13RoleUsersAddRequest\x12-\n\x0croleUserAdds\x18\x01 \x03(\x0b\x32\x17.Enterprise.RoleUserAdd\"\x80\x01\n\x11RoleUserAddResult\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x30\n\x06status\x18\x03 \x01(\x0e\x32 .Enterprise.RoleUserModifyStatus\x12\x0f\n\x07message\x18\x04 \x01(\t\"F\n\x14RoleUsersAddResponse\x12.\n\x07results\x18\x01 \x03(\x0b\x32\x1d.Enterprise.RoleUserAddResult\"<\n\x0eRoleUserRemove\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x19\n\x11\x65nterpriseUserIds\x18\x02 \x03(\x03\"M\n\x16RoleUsersRemoveRequest\x12\x33\n\x0froleUserRemoves\x18\x01 \x03(\x0b\x32\x1a.Enterprise.RoleUserRemove\"\x83\x01\n\x14RoleUserRemoveResult\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x30\n\x06status\x18\x03 \x01(\x0e\x32 .Enterprise.RoleUserModifyStatus\x12\x0f\n\x07message\x18\x04 \x01(\t\"L\n\x17RoleUsersRemoveResponse\x12\x31\n\x07results\x18\x01 \x03(\x0b\x32 .Enterprise.RoleUserRemoveResult\"\xa0\x04\n\x16\x45nterpriseRegistration\x12\x18\n\x10\x65ncryptedTreeKey\x18\x01 \x01(\x0c\x12\x16\n\x0e\x65nterpriseName\x18\x02 \x01(\t\x12\x14\n\x0crootNodeData\x18\x03 \x01(\x0c\x12\x15\n\radminUserData\x18\x04 \x01(\x0c\x12\x11\n\tadminName\x18\x05 \x01(\t\x12\x10\n\x08roleData\x18\x06 \x01(\x0c\x12\x38\n\nrsaKeyPair\x18\x07 \x01(\x0b\x32$.Enterprise.EnterpriseKeyPairRequest\x12\x13\n\x0bnumberSeats\x18\x08 \x01(\x05\x12\x32\n\x0e\x65nterpriseType\x18\t \x01(\x0e\x32\x1a.Enterprise.EnterpriseType\x12\x15\n\rrolePublicKey\x18\n \x01(\x0c\x12*\n\"rolePrivateKeyEncryptedWithRoleKey\x18\x0b \x01(\x0c\x12#\n\x1broleKeyEncryptedWithTreeKey\x18\x0c \x01(\x0c\x12\x38\n\neccKeyPair\x18\r \x01(\x0b\x32$.Enterprise.EnterpriseKeyPairRequest\x12\x18\n\x10\x61llUsersRoleData\x18\x0e \x01(\x0c\x12)\n!roleKeyEncryptedWithUserPublicKey\x18\x0f \x01(\x0c\x12\x18\n\x10\x61pproverRoleData\x18\x10 \x01(\x0c\"H\n\x1a\x44omainPasswordRulesRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x18\n\x10verificationCode\x18\x02 \x01(\t\"\\\n\x19\x44omainPasswordRulesFields\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07minimum\x18\x02 \x01(\x05\x12\x0f\n\x07maximum\x18\x03 \x01(\x05\x12\x0f\n\x07\x61llowed\x18\x04 \x01(\x08\"E\n\x10LoginToMcRequest\x12\x16\n\x0emcEnterpriseId\x18\x01 \x01(\x05\x12\x19\n\x11messageSessionUid\x18\x02 \x01(\x0c\"d\n\x11LoginToMcResponse\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x01 \x01(\x0c\x12\x18\n\x10\x65ncryptedTreeKey\x18\x02 \x01(\t\x12\x16\n\x0e\x66orbidKeyType2\x18\x04 \x01(\x08\"g\n\x1b\x44omainPasswordRulesResponse\x12H\n\x19\x64omainPasswordRulesFields\x18\x01 \x03(\x0b\x32%.Enterprise.DomainPasswordRulesFields\"\x88\x01\n\x18\x41pproveUserDeviceRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x02 \x01(\x0c\x12\x1e\n\x16\x65ncryptedDeviceDataKey\x18\x03 \x01(\x0c\x12\x14\n\x0c\x64\x65nyApproval\x18\x04 \x01(\x08\"t\n\x19\x41pproveUserDeviceResponse\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x02 \x01(\x0c\x12\x0e\n\x06\x66\x61iled\x18\x03 \x01(\x08\x12\x0f\n\x07message\x18\x04 \x01(\t\"Y\n\x19\x41pproveUserDevicesRequest\x12<\n\x0e\x64\x65viceRequests\x18\x01 \x03(\x0b\x32$.Enterprise.ApproveUserDeviceRequest\"\\\n\x1a\x41pproveUserDevicesResponse\x12>\n\x0f\x64\x65viceResponses\x18\x01 \x03(\x0b\x32%.Enterprise.ApproveUserDeviceResponse\"\x87\x01\n\x15\x45nterpriseUserDataKey\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14userEncryptedDataKey\x18\x02 \x01(\x0c\x12\x11\n\tkeyTypeId\x18\x03 \x01(\x05\x12\x0f\n\x07roleKey\x18\x04 \x01(\x0c\x12\x12\n\nprivateKey\x18\x05 \x01(\x0c\"I\n\x16\x45nterpriseUserDataKeys\x12/\n\x04keys\x18\x01 \x03(\x0b\x32!.Enterprise.EnterpriseUserDataKey\"g\n\x1a\x45nterpriseUserDataKeyLight\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14userEncryptedDataKey\x18\x02 \x01(\x0c\x12\x11\n\tkeyTypeId\x18\x03 \x01(\x05\"d\n\x1c\x45nterpriseUserDataKeysByNode\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x34\n\x04keys\x18\x02 \x03(\x0b\x32&.Enterprise.EnterpriseUserDataKeyLight\"^\n$EnterpriseUserDataKeysByNodeResponse\x12\x36\n\x04keys\x18\x01 \x03(\x0b\x32(.Enterprise.EnterpriseUserDataKeysByNode\"2\n\x15\x45nterpriseDataRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\"0\n\x13SpecialProvisioning\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"\x84\x02\n\x11GeneralDataEntity\x12\x16\n\x0e\x65nterpriseName\x18\x01 \x01(\t\x12\x1a\n\x12restrictVisibility\x18\x02 \x01(\x08\x12<\n\x13specialProvisioning\x18\x04 \x01(\x0b\x32\x1f.Enterprise.SpecialProvisioning\x12\x30\n\ruserPrivilege\x18\x07 \x01(\x0b\x32\x19.Enterprise.UserPrivilege\x12\x13\n\x0b\x64istributor\x18\x08 \x01(\x08\x12\x1d\n\x15\x66orbidAccountTransfer\x18\t \x01(\x08\x12\x17\n\x0fshowUserOnboard\x18\n \x01(\x08\"\xfd\x01\n\x04Node\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x10\n\x08parentId\x18\x02 \x01(\x03\x12\x10\n\x08\x62ridgeId\x18\x03 \x01(\x03\x12\x0e\n\x06scimId\x18\x04 \x01(\x03\x12\x11\n\tlicenseId\x18\x05 \x01(\x03\x12\x15\n\rencryptedData\x18\x06 \x01(\t\x12\x12\n\nduoEnabled\x18\x07 \x01(\x08\x12\x12\n\nrsaEnabled\x18\x08 \x01(\x08\x12 \n\x14ssoServiceProviderId\x18\t \x01(\x03\x42\x02\x18\x01\x12\x1a\n\x12restrictVisibility\x18\n \x01(\x08\x12!\n\x15ssoServiceProviderIds\x18\x0b \x03(\x03\x42\x02\x10\x01\"\x8e\x01\n\x04Role\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\t\x12\x0f\n\x07keyType\x18\x04 \x01(\t\x12\x14\n\x0cvisibleBelow\x18\x05 \x01(\x08\x12\x16\n\x0enewUserInherit\x18\x06 \x01(\x08\x12\x10\n\x08roleType\x18\x07 \x01(\t\"\xb8\x02\n\x04User\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\t\x12\x0f\n\x07keyType\x18\x04 \x01(\t\x12\x10\n\x08username\x18\x05 \x01(\t\x12\x0e\n\x06status\x18\x06 \x01(\t\x12\x0c\n\x04lock\x18\x07 \x01(\x05\x12\x0e\n\x06userId\x18\x08 \x01(\x05\x12\x1e\n\x16\x61\x63\x63ountShareExpiration\x18\t \x01(\x03\x12\x10\n\x08\x66ullName\x18\n \x01(\t\x12\x10\n\x08jobTitle\x18\x0b \x01(\t\x12\x12\n\ntfaEnabled\x18\x0c \x01(\x08\x12\x46\n\x18transferAcceptanceStatus\x18\r \x01(\x0e\x32$.Enterprise.TransferAcceptanceStatus\"7\n\tUserAlias\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08username\x18\x02 \x01(\t\"\xac\x01\n\x18\x43omplianceReportMetaData\x12\x11\n\treportUid\x18\x01 \x01(\x0c\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x12\n\nreportName\x18\x03 \x01(\t\x12\x15\n\rdateGenerated\x18\x04 \x01(\x03\x12\x11\n\trunByName\x18\x05 \x01(\t\x12\x16\n\x0enumberOfOwners\x18\x07 \x01(\x05\x12\x17\n\x0fnumberOfRecords\x18\x08 \x01(\x05\"S\n\x0bManagedNode\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x15\n\rmanagedNodeId\x18\x02 \x01(\x03\x12\x1d\n\x15\x63\x61scadeNodeManagement\x18\x03 \x01(\x08\"T\n\x0fUserManagedNode\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x1d\n\x15\x63\x61scadeNodeManagement\x18\x02 \x01(\x08\x12\x12\n\nprivileges\x18\x03 \x03(\t\"w\n\rUserPrivilege\x12\x35\n\x10userManagedNodes\x18\x01 \x03(\x0b\x32\x1b.Enterprise.UserManagedNode\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\t\"4\n\x08RoleUser\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\"M\n\rRolePrivilege\x12\x15\n\rmanagedNodeId\x18\x01 \x01(\x03\x12\x0e\n\x06roleId\x18\x02 \x01(\x03\x12\x15\n\rprivilegeType\x18\x03 \x01(\t\"T\n\x17PrivilegesByManagedNode\x12\x15\n\rmanagedNodeId\x18\x01 \x01(\x03\x12\x0e\n\x06roleId\x18\x02 \x01(\x03\x12\x12\n\nprivileges\x18\x03 \x03(\t\"I\n\x0fRoleEnforcement\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x17\n\x0f\x65nforcementType\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\"\xa9\x01\n\x04Team\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x03\x12\x14\n\x0crestrictEdit\x18\x04 \x01(\x08\x12\x15\n\rrestrictShare\x18\x05 \x01(\x08\x12\x14\n\x0crestrictView\x18\x06 \x01(\x08\x12\x15\n\rencryptedData\x18\x07 \x01(\t\x12\x18\n\x10\x65ncryptedTeamKey\x18\x08 \x01(\t\"G\n\x08TeamUser\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x10\n\x08userType\x18\x03 \x01(\t\"K\n\x1aGetDistributorInfoResponse\x12-\n\x0c\x64istributors\x18\x01 \x03(\x0b\x32\x17.Enterprise.Distributor\"B\n\x0b\x44istributor\x12\x0c\n\x04name\x18\x01 \x01(\t\x12%\n\x08mspInfos\x18\x02 \x03(\x0b\x32\x13.Enterprise.MspInfo\"\x9d\x02\n\x07MspInfo\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x16\n\x0e\x65nterpriseName\x18\x02 \x01(\t\x12\x19\n\x11\x61llocatedLicenses\x18\x03 \x01(\x05\x12\x19\n\x11\x61llowedMcProducts\x18\x04 \x03(\t\x12\x15\n\rallowedAddOns\x18\x05 \x03(\t\x12\x17\n\x0fmaxFilePlanType\x18\x06 \x01(\t\x12\x34\n\x10managedCompanies\x18\x07 \x03(\x0b\x32\x1a.Enterprise.ManagedCompany\x12\x1e\n\x16\x61llowUnlimitedLicenses\x18\x08 \x01(\x08\x12(\n\x06\x61\x64\x64Ons\x18\t \x03(\x0b\x32\x18.Enterprise.LicenseAddOn\"\x91\x02\n\x0eManagedCompany\x12\x16\n\x0emcEnterpriseId\x18\x01 \x01(\x05\x12\x18\n\x10mcEnterpriseName\x18\x02 \x01(\t\x12\x11\n\tmspNodeId\x18\x03 \x01(\x03\x12\x15\n\rnumberOfSeats\x18\x04 \x01(\x05\x12\x15\n\rnumberOfUsers\x18\x05 \x01(\x05\x12\x11\n\tproductId\x18\x06 \x01(\t\x12\x11\n\tisExpired\x18\x07 \x01(\x08\x12\x0f\n\x07treeKey\x18\x08 \x01(\t\x12\x15\n\rtree_key_role\x18\t \x01(\x03\x12\x14\n\x0c\x66ilePlanType\x18\n \x01(\t\x12(\n\x06\x61\x64\x64Ons\x18\x0b \x03(\x0b\x32\x18.Enterprise.LicenseAddOn\"R\n\x07MSPPool\x12\x11\n\tproductId\x18\x01 \x01(\t\x12\r\n\x05seats\x18\x02 \x01(\x05\x12\x16\n\x0e\x61vailableSeats\x18\x03 \x01(\x05\x12\r\n\x05stash\x18\x04 \x01(\x05\":\n\nMSPContact\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x16\n\x0e\x65nterpriseName\x18\x02 \x01(\t\"\xec\x01\n\x0cLicenseAddOn\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x0f\n\x07isTrial\x18\x03 \x01(\x08\x12\x12\n\nexpiration\x18\x04 \x01(\x03\x12\x0f\n\x07\x63reated\x18\x05 \x01(\x03\x12\r\n\x05seats\x18\x06 \x01(\x05\x12\x16\n\x0e\x61\x63tivationTime\x18\x07 \x01(\x03\x12\x19\n\x11includedInProduct\x18\x08 \x01(\x08\x12\x14\n\x0c\x61piCallCount\x18\t \x01(\x05\x12\x17\n\x0ftierDescription\x18\n \x01(\t\x12\x16\n\x0eseatsAllocated\x18\x0b \x01(\x05\"s\n\tMCDefault\x12\x11\n\tmcProduct\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x64\x64Ons\x18\x02 \x03(\t\x12\x14\n\x0c\x66ilePlanType\x18\x03 \x01(\t\x12\x13\n\x0bmaxLicenses\x18\x04 \x01(\x05\x12\x18\n\x10\x66ixedMaxLicenses\x18\x05 \x01(\x08\"\xd2\x01\n\nMSPPermits\x12\x12\n\nrestricted\x18\x01 \x01(\x08\x12\x1a\n\x12maxAllowedLicenses\x18\x02 \x01(\x05\x12\x19\n\x11\x61llowedMcProducts\x18\x03 \x03(\t\x12\x15\n\rallowedAddOns\x18\x04 \x03(\t\x12\x17\n\x0fmaxFilePlanType\x18\x05 \x01(\t\x12\x1e\n\x16\x61llowUnlimitedLicenses\x18\x06 \x01(\x08\x12)\n\nmcDefaults\x18\x07 \x03(\x0b\x32\x15.Enterprise.MCDefault\"\xa0\x04\n\x07License\x12\x0c\n\x04paid\x18\x01 \x01(\x08\x12\x15\n\rnumberOfSeats\x18\x02 \x01(\x05\x12\x12\n\nexpiration\x18\x03 \x01(\x03\x12\x14\n\x0clicenseKeyId\x18\x04 \x01(\x05\x12\x15\n\rproductTypeId\x18\x05 \x01(\x05\x12\x0c\n\x04name\x18\x06 \x01(\t\x12\x1b\n\x13\x65nterpriseLicenseId\x18\x07 \x01(\x03\x12\x16\n\x0eseatsAllocated\x18\x08 \x01(\x05\x12\x14\n\x0cseatsPending\x18\t \x01(\x05\x12\x0c\n\x04tier\x18\n \x01(\x05\x12\x16\n\x0e\x66ilePlanTypeId\x18\x0b \x01(\x05\x12\x10\n\x08maxBytes\x18\x0c \x01(\x03\x12\x19\n\x11storageExpiration\x18\r \x01(\x03\x12\x15\n\rlicenseStatus\x18\x0e \x01(\t\x12$\n\x07mspPool\x18\x0f \x03(\x0b\x32\x13.Enterprise.MSPPool\x12)\n\tmanagedBy\x18\x10 \x01(\x0b\x32\x16.Enterprise.MSPContact\x12(\n\x06\x61\x64\x64Ons\x18\x11 \x03(\x0b\x32\x18.Enterprise.LicenseAddOn\x12\x17\n\x0fnextBillingDate\x18\x12 \x01(\x03\x12\x17\n\x0fhasMSPLegacyLog\x18\x13 \x01(\x08\x12*\n\nmspPermits\x18\x14 \x01(\x0b\x32\x16.Enterprise.MSPPermits\x12\x13\n\x0b\x64istributor\x18\x15 \x01(\x08\"n\n\x06\x42ridge\x12\x10\n\x08\x62ridgeId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x18\n\x10wanIpEnforcement\x18\x03 \x01(\t\x12\x18\n\x10lanIpEnforcement\x18\x04 \x01(\t\x12\x0e\n\x06status\x18\x05 \x01(\t\"t\n\x04Scim\x12\x0e\n\x06scimId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x12\n\nlastSynced\x18\x04 \x01(\x03\x12\x12\n\nrolePrefix\x18\x05 \x01(\t\x12\x14\n\x0cuniqueGroups\x18\x06 \x01(\x08\"L\n\x0e\x45mailProvision\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x0e\n\x06\x64omain\x18\x03 \x01(\t\x12\x0e\n\x06method\x18\x04 \x01(\t\"R\n\nQueuedTeam\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x03\x12\x15\n\rencryptedData\x18\x04 \x01(\t\"0\n\x0eQueuedTeamUser\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\r\n\x05users\x18\x02 \x03(\x03\"\xa4\x01\n\x0eTeamsAddResult\x12\x34\n\x11successfulTeamAdd\x18\x01 \x03(\x0b\x32\x19.Enterprise.TeamAddResult\x12\x36\n\x13unsuccessfulTeamAdd\x18\x02 \x03(\x0b\x32\x19.Enterprise.TeamAddResult\x12\x0e\n\x06result\x18\x03 \x01(\t\x12\x14\n\x0c\x65rrorMessage\x18\x04 \x01(\t\"U\n\rTeamAddResult\x12\x1e\n\x04team\x18\x01 \x01(\x0b\x32\x10.Enterprise.Team\x12\x0e\n\x06result\x18\x02 \x01(\t\x12\x14\n\x0c\x65rrorMessage\x18\x03 \x01(\t\"\x91\x01\n\nSsoService\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0e\n\x06sp_url\x18\x04 \x01(\t\x12\x16\n\x0einviteNewUsers\x18\x05 \x01(\x08\x12\x0e\n\x06\x61\x63tive\x18\x06 \x01(\x08\x12\x0f\n\x07isCloud\x18\x07 \x01(\x08\"1\n\x10ReportFilterUser\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\r\n\x05\x65mail\x18\x02 \x01(\t\"\x97\x02\n\x1d\x44\x65viceRequestForAdminApproval\x12\x10\n\x08\x64\x65viceId\x18\x01 \x01(\x03\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x03 \x01(\x0c\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x04 \x01(\x0c\x12\x12\n\ndeviceName\x18\x05 \x01(\t\x12\x15\n\rclientVersion\x18\x06 \x01(\t\x12\x12\n\ndeviceType\x18\x07 \x01(\t\x12\x0c\n\x04\x64\x61te\x18\x08 \x01(\x03\x12\x11\n\tipAddress\x18\t \x01(\t\x12\x10\n\x08location\x18\n \x01(\t\x12\r\n\x05\x65mail\x18\x0b \x01(\t\x12\x12\n\naccountUid\x18\x0c \x01(\x0c\"`\n\x0e\x45nterpriseData\x12\x30\n\x06\x65ntity\x18\x01 \x01(\x0e\x32 .Enterprise.EnterpriseDataEntity\x12\x0e\n\x06\x64\x65lete\x18\x02 \x01(\x08\x12\x0c\n\x04\x64\x61ta\x18\x03 \x03(\x0c\"\xd0\x01\n\x16\x45nterpriseDataResponse\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\x12\x0f\n\x07hasMore\x18\x02 \x01(\x08\x12,\n\x0b\x63\x61\x63heStatus\x18\x03 \x01(\x0e\x32\x17.Enterprise.CacheStatus\x12(\n\x04\x64\x61ta\x18\x04 \x03(\x0b\x32\x1a.Enterprise.EnterpriseData\x12\x32\n\x0bgeneralData\x18\x05 \x01(\x0b\x32\x1d.Enterprise.GeneralDataEntity\"*\n\rBackupRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\"\x98\x01\n\x0c\x42\x61\x63kupRecord\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x0b\n\x03key\x18\x03 \x01(\x0c\x12*\n\x07keyType\x18\x04 \x01(\x0e\x32\x19.Enterprise.BackupKeyType\x12\x0f\n\x07version\x18\x05 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x06 \x01(\x0c\x12\r\n\x05\x65xtra\x18\x07 \x01(\x0c\".\n\tBackupKey\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x11\n\tbackupKey\x18\x02 \x01(\x0c\"\x8d\x02\n\nBackupUser\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x10\n\x08userName\x18\x02 \x01(\t\x12\x0f\n\x07\x64\x61taKey\x18\x03 \x01(\x0c\x12\x36\n\x0b\x64\x61taKeyType\x18\x04 \x01(\x0e\x32!.Enterprise.BackupUserDataKeyType\x12\x12\n\nprivateKey\x18\x05 \x01(\x0c\x12\x0f\n\x07treeKey\x18\x06 \x01(\x0c\x12.\n\x0btreeKeyType\x18\x07 \x01(\x0e\x32\x19.Enterprise.BackupKeyType\x12)\n\nbackupKeys\x18\x08 \x03(\x0b\x32\x15.Enterprise.BackupKey\x12\x14\n\x0cprivateECKey\x18\t \x01(\x0c\"\x9e\x01\n\x0e\x42\x61\x63kupResponse\x12\x1f\n\x17\x65nterpriseEccPrivateKey\x18\x01 \x01(\x0c\x12%\n\x05users\x18\x02 \x03(\x0b\x32\x16.Enterprise.BackupUser\x12)\n\x07records\x18\x03 \x03(\x0b\x32\x18.Enterprise.BackupRecord\x12\x19\n\x11\x63ontinuationToken\x18\x04 \x01(\x0c\"e\n\nBackupFile\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x11\n\tbackupUid\x18\x02 \x01(\x0c\x12\x10\n\x08\x66ileName\x18\x03 \x01(\t\x12\x0f\n\x07\x63reated\x18\x04 \x01(\x03\x12\x13\n\x0b\x64ownloadUrl\x18\x05 \x01(\t\"8\n\x0f\x42\x61\x63kupsResponse\x12%\n\x05\x66iles\x18\x01 \x03(\x0b\x32\x16.Enterprise.BackupFile\".\n\x1cGetEnterpriseDataKeysRequest\x12\x0e\n\x06roleId\x18\x01 \x03(\x03\"\xff\x01\n\x1dGetEnterpriseDataKeysResponse\x12:\n\x12reEncryptedRoleKey\x18\x01 \x03(\x0b\x32\x1e.Enterprise.ReEncryptedRoleKey\x12$\n\x07roleKey\x18\x02 \x03(\x0b\x32\x13.Enterprise.RoleKey\x12\"\n\x06mspKey\x18\x03 \x01(\x0b\x32\x12.Enterprise.MspKey\x12\x32\n\x0e\x65nterpriseKeys\x18\x04 \x01(\x0b\x32\x1a.Enterprise.EnterpriseKeys\x12$\n\x07treeKey\x18\x05 \x01(\x0b\x32\x13.Enterprise.TreeKey\"^\n\x07RoleKey\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x14\n\x0c\x65ncryptedKey\x18\x02 \x01(\t\x12-\n\x07keyType\x18\x03 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"d\n\x06MspKey\x12\x1b\n\x13\x65ncryptedMspTreeKey\x18\x01 \x01(\t\x12=\n\x17\x65ncryptedMspTreeKeyType\x18\x02 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"|\n\x0e\x45nterpriseKeys\x12\x14\n\x0crsaPublicKey\x18\x01 \x01(\x0c\x12\x1e\n\x16rsaEncryptedPrivateKey\x18\x02 \x01(\x0c\x12\x14\n\x0c\x65\x63\x63PublicKey\x18\x03 \x01(\x0c\x12\x1e\n\x16\x65\x63\x63\x45ncryptedPrivateKey\x18\x04 \x01(\x0c\"H\n\x07TreeKey\x12\x0f\n\x07treeKey\x18\x01 \x01(\t\x12,\n\tkeyTypeId\x18\x02 \x01(\x0e\x32\x19.Enterprise.BackupKeyType\"E\n\x14SharedRecordResponse\x12-\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1d.Enterprise.SharedRecordEvent\"p\n\x11SharedRecordEvent\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08userName\x18\x02 \x01(\t\x12\x0f\n\x07\x63\x61nEdit\x18\x03 \x01(\x08\x12\x12\n\ncanReshare\x18\x04 \x01(\x08\x12\x11\n\tshareFrom\x18\x05 \x01(\x05\".\n\x1cSetRestrictVisibilityRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\"\xd0\x01\n\x0eUserAddRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12-\n\x07keyType\x18\x04 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x10\n\x08\x66ullName\x18\x05 \x01(\t\x12\x10\n\x08jobTitle\x18\x06 \x01(\t\x12\r\n\x05\x65mail\x18\x07 \x01(\t\x12\x1b\n\x13suppressEmailInvite\x18\x08 \x01(\x08\":\n\x11UserUpdateRequest\x12%\n\x05users\x18\x01 \x03(\x0b\x32\x16.Enterprise.UserUpdate\"\xaf\x01\n\nUserUpdate\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12-\n\x07keyType\x18\x04 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x10\n\x08\x66ullName\x18\x05 \x01(\t\x12\x10\n\x08jobTitle\x18\x06 \x01(\t\x12\r\n\x05\x65mail\x18\x07 \x01(\t\"A\n\x12UserUpdateResponse\x12+\n\x05users\x18\x01 \x03(\x0b\x32\x1c.Enterprise.UserUpdateResult\"Z\n\x10UserUpdateResult\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12,\n\x06status\x18\x02 \x01(\x0e\x32\x1c.Enterprise.UserUpdateStatus\"J\n\x1d\x43omplianceRecordOwnersRequest\x12\x0f\n\x07nodeIds\x18\x01 \x03(\x03\x12\x18\n\x10includeNonShared\x18\x02 \x01(\x08\"O\n\x1e\x43omplianceRecordOwnersResponse\x12-\n\x0crecordOwners\x18\x01 \x03(\x0b\x32\x17.Enterprise.RecordOwner\"7\n\x0bRecordOwner\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06shared\x18\x02 \x01(\x08\"\xa6\x01\n PreliminaryComplianceDataRequest\x12\x19\n\x11\x65nterpriseUserIds\x18\x01 \x03(\x03\x12\x18\n\x10includeNonShared\x18\x02 \x01(\x08\x12\x19\n\x11\x63ontinuationToken\x18\x03 \x01(\x0c\x12\x32\n*includeTotalMatchingRecordsInFirstResponse\x18\x04 \x01(\x08\"\x9f\x01\n!PreliminaryComplianceDataResponse\x12\x30\n\rauditUserData\x18\x01 \x03(\x0b\x32\x19.Enterprise.AuditUserData\x12\x19\n\x11\x63ontinuationToken\x18\x02 \x01(\x0c\x12\x0f\n\x07hasMore\x18\x03 \x01(\x08\x12\x1c\n\x14totalMatchingRecords\x18\x04 \x01(\x05\"K\n\x0f\x41uditUserRecord\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x15\n\rencryptedData\x18\x02 \x01(\x0c\x12\x0e\n\x06shared\x18\x03 \x01(\x08\"\x8d\x01\n\rAuditUserData\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x35\n\x10\x61uditUserRecords\x18\x02 \x03(\x0b\x32\x1b.Enterprise.AuditUserRecord\x12+\n\x06status\x18\x03 \x01(\x0e\x32\x1b.Enterprise.AuditUserStatus\"\x7f\n\x17\x43omplianceReportFilters\x12\x14\n\x0crecordTitles\x18\x01 \x03(\t\x12\x12\n\nrecordUids\x18\x02 \x03(\x0c\x12\x11\n\tjobTitles\x18\x03 \x03(\x03\x12\x0c\n\x04urls\x18\x04 \x03(\t\x12\x19\n\x11\x65nterpriseUserIds\x18\x05 \x03(\x03\"\x7f\n\x17\x43omplianceReportRequest\x12<\n\x13\x63omplianceReportRun\x18\x01 \x01(\x0b\x32\x1f.Enterprise.ComplianceReportRun\x12\x12\n\nreportName\x18\x02 \x01(\t\x12\x12\n\nsaveReport\x18\x03 \x01(\x08\"\x85\x01\n\x13\x43omplianceReportRun\x12N\n\x17reportCriteriaAndFilter\x18\x01 \x01(\x0b\x32-.Enterprise.ComplianceReportCriteriaAndFilter\x12\r\n\x05users\x18\x02 \x03(\x03\x12\x0f\n\x07records\x18\x03 \x03(\x0c\"\xfc\x01\n!ComplianceReportCriteriaAndFilter\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x13\n\x0b\x63riteriaUid\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63riteriaName\x18\x03 \x01(\t\x12\x36\n\x08\x63riteria\x18\x04 \x01(\x0b\x32$.Enterprise.ComplianceReportCriteria\x12\x33\n\x07\x66ilters\x18\x05 \x03(\x0b\x32\".Enterprise.ComplianceReportFilter\x12\x14\n\x0clastModified\x18\x06 \x01(\x03\x12\x19\n\x11nodeEncryptedData\x18\x07 \x01(\x0c\"b\n\x18\x43omplianceReportCriteria\x12\x11\n\tjobTitles\x18\x01 \x03(\t\x12\x19\n\x11\x65nterpriseUserIds\x18\x02 \x03(\x03\x12\x18\n\x10includeNonShared\x18\x03 \x01(\x08\"x\n\x16\x43omplianceReportFilter\x12\x14\n\x0crecordTitles\x18\x01 \x03(\t\x12\x12\n\nrecordUids\x18\x02 \x03(\x0c\x12\x11\n\tjobTitles\x18\x03 \x03(\t\x12\x0c\n\x04urls\x18\x04 \x03(\t\x12\x13\n\x0brecordTypes\x18\x05 \x03(\t\"\xa1\x05\n\x18\x43omplianceReportResponse\x12\x15\n\rdateGenerated\x18\x01 \x01(\x03\x12\x15\n\rrunByUserName\x18\x02 \x01(\t\x12\x12\n\nreportName\x18\x03 \x01(\t\x12\x11\n\treportUid\x18\x04 \x01(\x0c\x12<\n\x13\x63omplianceReportRun\x18\x05 \x01(\x0b\x32\x1f.Enterprise.ComplianceReportRun\x12-\n\x0cuserProfiles\x18\x06 \x03(\x0b\x32\x17.Enterprise.UserProfile\x12)\n\nauditTeams\x18\x07 \x03(\x0b\x32\x15.Enterprise.AuditTeam\x12-\n\x0c\x61uditRecords\x18\x08 \x03(\x0b\x32\x17.Enterprise.AuditRecord\x12+\n\x0buserRecords\x18\t \x03(\x0b\x32\x16.Enterprise.UserRecord\x12;\n\x13sharedFolderRecords\x18\n \x03(\x0b\x32\x1e.Enterprise.SharedFolderRecord\x12\x37\n\x11sharedFolderUsers\x18\x0b \x03(\x0b\x32\x1c.Enterprise.SharedFolderUser\x12\x37\n\x11sharedFolderTeams\x18\x0c \x03(\x0b\x32\x1c.Enterprise.SharedFolderTeam\x12\x31\n\x0e\x61uditTeamUsers\x18\r \x03(\x0b\x32\x19.Enterprise.AuditTeamUser\x12)\n\nauditRoles\x18\x0e \x03(\x0b\x32\x15.Enterprise.AuditRole\x12/\n\rlinkedRecords\x18\x0f \x03(\x0b\x32\x18.Enterprise.LinkedRecord\"\x81\x01\n\x0b\x41uditRecord\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x11\n\tauditData\x18\x02 \x01(\x0c\x12\x16\n\x0ehasAttachments\x18\x03 \x01(\x08\x12\x0f\n\x07inTrash\x18\x04 \x01(\x08\x12\x10\n\x08treeLeft\x18\x05 \x01(\x05\x12\x11\n\ttreeRight\x18\x06 \x01(\x05\"\x80\x02\n\tAuditRole\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x15\n\rencryptedData\x18\x02 \x01(\x0c\x12&\n\x1erestrictShareOutsideEnterprise\x18\x03 \x01(\x08\x12\x18\n\x10restrictShareAll\x18\x04 \x01(\x08\x12\"\n\x1arestrictShareOfAttachments\x18\x05 \x01(\x08\x12)\n!restrictMaskPasswordsWhileEditing\x18\x06 \x01(\x08\x12;\n\x13roleNodeManagements\x18\x07 \x03(\x0b\x32\x1e.Enterprise.RoleNodeManagement\"^\n\x12RoleNodeManagement\x12\x10\n\x08treeLeft\x18\x01 \x01(\x05\x12\x11\n\ttreeRight\x18\x02 \x01(\x05\x12\x0f\n\x07\x63\x61scade\x18\x03 \x01(\x08\x12\x12\n\nprivileges\x18\x04 \x01(\x05\"k\n\x0bUserProfile\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08\x66ullName\x18\x02 \x01(\t\x12\x10\n\x08jobTitle\x18\x03 \x01(\t\x12\r\n\x05\x65mail\x18\x04 \x01(\t\x12\x0f\n\x07roleIds\x18\x05 \x03(\x03\"=\n\x10RecordPermission\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x16\n\x0epermissionBits\x18\x02 \x01(\x05\"_\n\nUserRecord\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x37\n\x11recordPermissions\x18\x02 \x03(\x0b\x32\x1c.Enterprise.RecordPermission\"[\n\tAuditTeam\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x10\n\x08teamName\x18\x02 \x01(\t\x12\x14\n\x0crestrictEdit\x18\x03 \x01(\x08\x12\x15\n\rrestrictShare\x18\x04 \x01(\x08\";\n\rAuditTeamUser\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x19\n\x11\x65nterpriseUserIds\x18\x02 \x03(\x03\"\x9f\x01\n\x12SharedFolderRecord\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x37\n\x11recordPermissions\x18\x02 \x03(\x0b\x32\x1c.Enterprise.RecordPermission\x12\x37\n\x11shareAdminRecords\x18\x03 \x03(\x0b\x32\x1c.Enterprise.ShareAdminRecord\"M\n\x10ShareAdminRecord\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1f\n\x17recordPermissionIndexes\x18\x02 \x03(\x05\"F\n\x10SharedFolderUser\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x19\n\x11\x65nterpriseUserIds\x18\x02 \x03(\x03\"=\n\x10SharedFolderTeam\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x10\n\x08teamUids\x18\x02 \x03(\x0c\"/\n\x1aGetComplianceReportRequest\x12\x11\n\treportUid\x18\x01 \x01(\x0c\"2\n\x1bGetComplianceReportResponse\x12\x13\n\x0b\x64ownloadUrl\x18\x01 \x01(\t\"6\n\x1f\x43omplianceReportCriteriaRequest\x12\x13\n\x0b\x63riteriaUid\x18\x01 \x01(\x0c\";\n$SaveComplianceReportCriteriaResponse\x12\x13\n\x0b\x63riteriaUid\x18\x01 \x01(\x0c\"4\n\x0cLinkedRecord\x12\x10\n\x08ownerUid\x18\x01 \x01(\x0c\x12\x12\n\nrecordUids\x18\x02 \x03(\x0c\"W\n\x17GetSharingAdminsRequest\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x10\n\x08username\x18\x03 \x01(\t\"\xe0\x01\n\x0eUserProfileExt\x12\r\n\x05\x65mail\x18\x01 \x01(\t\x12\x10\n\x08\x66ullName\x18\x02 \x01(\t\x12\x10\n\x08jobTitle\x18\x03 \x01(\t\x12\x14\n\x0cisMSPMCAdmin\x18\x04 \x01(\x08\x12\x18\n\x10isInSharedFolder\x18\x05 \x01(\x08\x12&\n\x1eisShareAdminForRequestedObject\x18\x06 \x01(\x08\x12(\n isShareAdminForSharedFolderOwner\x18\x07 \x01(\x08\x12\x19\n\x11hasAccessToObject\x18\x08 \x01(\x08\"O\n\x18GetSharingAdminsResponse\x12\x33\n\x0fuserProfileExts\x18\x01 \x03(\x0b\x32\x1a.Enterprise.UserProfileExt\"_\n\x1eTeamsEnterpriseUsersAddRequest\x12=\n\x05teams\x18\x01 \x03(\x0b\x32..Enterprise.TeamsEnterpriseUsersAddTeamRequest\"t\n\"TeamsEnterpriseUsersAddTeamRequest\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12=\n\x05users\x18\x02 \x03(\x0b\x32..Enterprise.TeamsEnterpriseUsersAddUserRequest\"\xab\x01\n\"TeamsEnterpriseUsersAddUserRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12*\n\x08userType\x18\x02 \x01(\x0e\x32\x18.Enterprise.TeamUserType\x12\x13\n\x07teamKey\x18\x03 \x01(\tB\x02\x18\x01\x12*\n\x0ctypedTeamKey\x18\x04 \x01(\x0b\x32\x14.Enterprise.TypedKey\"F\n\x08TypedKey\x12\x0b\n\x03key\x18\x01 \x01(\x0c\x12-\n\x07keyType\x18\x02 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"s\n\x1fTeamsEnterpriseUsersAddResponse\x12>\n\x05teams\x18\x01 \x03(\x0b\x32/.Enterprise.TeamsEnterpriseUsersAddTeamResponse\x12\x10\n\x08revision\x18\x02 \x01(\x03\"\xc4\x01\n#TeamsEnterpriseUsersAddTeamResponse\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12>\n\x05users\x18\x02 \x03(\x0b\x32/.Enterprise.TeamsEnterpriseUsersAddUserResponse\x12\x0f\n\x07success\x18\x03 \x01(\x08\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x12\n\nresultCode\x18\x05 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x06 \x01(\t\"\x9f\x01\n#TeamsEnterpriseUsersAddUserResponse\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x0f\n\x07success\x18\x03 \x01(\x08\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x12\n\nresultCode\x18\x05 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x06 \x01(\t\"E\n\x18TeamEnterpriseUserRemove\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\"j\n TeamEnterpriseUserRemovesRequest\x12\x46\n\x18teamEnterpriseUserRemove\x18\x01 \x03(\x0b\x32$.Enterprise.TeamEnterpriseUserRemove\"{\n!TeamEnterpriseUserRemovesResponse\x12V\n teamEnterpriseUserRemoveResponse\x18\x01 \x03(\x0b\x32,.Enterprise.TeamEnterpriseUserRemoveResponse\"\xb8\x01\n TeamEnterpriseUserRemoveResponse\x12\x46\n\x18teamEnterpriseUserRemove\x18\x01 \x01(\x0b\x32$.Enterprise.TeamEnterpriseUserRemove\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x12\n\nresultCode\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x05 \x01(\t\"M\n\x0b\x44omainAlias\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\x0e\n\x06status\x18\x03 \x01(\x05\x12\x0f\n\x07message\x18\x04 \x01(\t\"B\n\x12\x44omainAliasRequest\x12,\n\x0b\x64omainAlias\x18\x01 \x03(\x0b\x32\x17.Enterprise.DomainAlias\"C\n\x13\x44omainAliasResponse\x12,\n\x0b\x64omainAlias\x18\x01 \x03(\x0b\x32\x17.Enterprise.DomainAlias\"m\n\x1f\x45nterpriseUsersProvisionRequest\x12\x33\n\x05users\x18\x01 \x03(\x0b\x32$.Enterprise.EnterpriseUsersProvision\x12\x15\n\rclientVersion\x18\x02 \x01(\t\"\xb6\x03\n\x18\x45nterpriseUsersProvision\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x03\x12\x15\n\rencryptedData\x18\x04 \x01(\t\x12-\n\x07keyType\x18\x05 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x10\n\x08\x66ullName\x18\x06 \x01(\t\x12\x10\n\x08jobTitle\x18\x07 \x01(\t\x12\x1e\n\x16\x65nterpriseUsersDataKey\x18\x08 \x01(\x0c\x12\x14\n\x0c\x61uthVerifier\x18\t \x01(\x0c\x12\x18\n\x10\x65ncryptionParams\x18\n \x01(\x0c\x12\x14\n\x0crsaPublicKey\x18\x0b \x01(\x0c\x12\x1e\n\x16rsaEncryptedPrivateKey\x18\x0c \x01(\x0c\x12\x14\n\x0c\x65\x63\x63PublicKey\x18\r \x01(\x0c\x12\x1e\n\x16\x65\x63\x63\x45ncryptedPrivateKey\x18\x0e \x01(\x0c\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x0f \x01(\x0c\x12\x1a\n\x12\x65ncryptedClientKey\x18\x10 \x01(\x0c\"_\n EnterpriseUsersProvisionResponse\x12;\n\x07results\x18\x01 \x03(\x0b\x32*.Enterprise.EnterpriseUsersProvisionResult\"q\n\x1e\x45nterpriseUsersProvisionResult\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x04 \x01(\t\"a\n\x19\x45nterpriseUsersAddRequest\x12-\n\x05users\x18\x01 \x03(\x0b\x32\x1e.Enterprise.EnterpriseUsersAdd\x12\x15\n\rclientVersion\x18\x02 \x01(\t\"\x8c\x02\n\x12\x45nterpriseUsersAdd\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x03\x12\x15\n\rencryptedData\x18\x04 \x01(\t\x12-\n\x07keyType\x18\x05 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x10\n\x08\x66ullName\x18\x06 \x01(\t\x12\x10\n\x08jobTitle\x18\x07 \x01(\t\x12\x1b\n\x13suppressEmailInvite\x18\x08 \x01(\x08\x12\x15\n\rinviteeLocale\x18\t \x01(\t\x12\x0c\n\x04move\x18\n \x01(\x08\x12\x0e\n\x06roleId\x18\x0b \x01(\x03\"\x9b\x01\n\x1a\x45nterpriseUsersAddResponse\x12\x35\n\x07results\x18\x01 \x03(\x0b\x32$.Enterprise.EnterpriseUsersAddResult\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x0c\n\x04\x63ode\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x05 \x01(\t\"\x96\x01\n\x18\x45nterpriseUsersAddResult\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x18\n\x10verificationCode\x18\x03 \x01(\t\x12\x0c\n\x04\x63ode\x18\x04 \x01(\t\x12\x0f\n\x07message\x18\x05 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x06 \x01(\t\"\xb9\x01\n\x17UpdateMSPPermitsRequest\x12\x17\n\x0fmspEnterpriseId\x18\x01 \x01(\x05\x12\x1a\n\x12maxAllowedLicenses\x18\x02 \x01(\x05\x12\x19\n\x11\x61llowedMcProducts\x18\x03 \x03(\t\x12\x15\n\rallowedAddOns\x18\x04 \x03(\t\x12\x17\n\x0fmaxFilePlanType\x18\x05 \x01(\t\x12\x1e\n\x16\x61llowUnlimitedLicenses\x18\x06 \x01(\x08\"9\n\x1c\x44\x65leteEnterpriseUsersRequest\x12\x19\n\x11\x65nterpriseUserIds\x18\x01 \x03(\x03\"o\n\x1a\x44\x65leteEnterpriseUserStatus\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.Enterprise.DeleteEnterpriseUsersResult\"]\n\x1d\x44\x65leteEnterpriseUsersResponse\x12<\n\x0c\x64\x65leteStatus\x18\x01 \x03(\x0b\x32&.Enterprise.DeleteEnterpriseUserStatus\"w\n\x18\x43learSecurityDataRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x03(\x03\x12\x10\n\x08\x61llUsers\x18\x02 \x01(\x08\x12/\n\x04type\x18\x03 \x01(\x0e\x32!.Enterprise.ClearSecurityDataType\"%\n\x13ListDomainsResponse\x12\x0e\n\x06\x64omain\x18\x01 \x03(\t\"d\n\x14ReserveDomainRequest\x12<\n\x13reserveDomainAction\x18\x01 \x01(\x0e\x32\x1f.Enterprise.ReserveDomainAction\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\"&\n\x15ReserveDomainResponse\x12\r\n\x05token\x18\x01 \x01(\t\".\n\x0bRolesByTeam\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x0e\n\x06roleId\x18\x02 \x03(\x03\"\x8d\x01\n\x10LockUsersRequest\x12\x1d\n\x15lockEnterpriseUserIds\x18\x01 \x03(\x03\x12 \n\x18\x64isableEnterpriseUserIds\x18\x02 \x03(\x03\x12\x1f\n\x17unlockEnterpriseUserIds\x18\x03 \x03(\x03\x12\x17\n\x0f\x64\x65leteIfPending\x18\x04 \x01(\x08\"C\n\x11LockUsersResponse\x12.\n\x08response\x18\x01 \x03(\x0b\x32\x1c.Enterprise.LockUserResponse\"n\n\x10LockUserResponse\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.Enterprise.UserLockStatus\x12\x14\n\x0c\x65rrorMessage\x18\x03 \x01(\t*\x1b\n\x07KeyType\x12\x07\n\x03RSA\x10\x00\x12\x07\n\x03\x45\x43\x43\x10\x01*\x9a\x02\n\x14RoleUserModifyStatus\x12\x0f\n\x0bROLE_EXISTS\x10\x00\x12\x14\n\x10MISSING_TREE_KEY\x10\x01\x12\x14\n\x10MISSING_ROLE_KEY\x10\x02\x12\x1e\n\x1aINVALID_ENTERPRISE_USER_ID\x10\x03\x12\x1b\n\x17PENDING_ENTERPRISE_USER\x10\x04\x12\x13\n\x0fINVALID_NODE_ID\x10\x05\x12!\n\x1dMAY_NOT_REMOVE_SELF_FROM_ROLE\x10\x06\x12\x1c\n\x18MUST_HAVE_ONE_USER_ADMIN\x10\x07\x12\x13\n\x0fINVALID_ROLE_ID\x10\x08\x12\x1d\n\x19PAM_LICENSE_SEAT_EXCEEDED\x10\t*=\n\x0e\x45nterpriseType\x12\x17\n\x13\x45NTERPRISE_STANDARD\x10\x00\x12\x12\n\x0e\x45NTERPRISE_MSP\x10\x01*s\n\x18TransferAcceptanceStatus\x12\r\n\tUNDEFINED\x10\x00\x12\x10\n\x0cNOT_REQUIRED\x10\x01\x12\x10\n\x0cNOT_ACCEPTED\x10\x02\x12\x16\n\x12PARTIALLY_ACCEPTED\x10\x03\x12\x0c\n\x08\x41\x43\x43\x45PTED\x10\x04*\xe1\x03\n\x14\x45nterpriseDataEntity\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05NODES\x10\x01\x12\t\n\x05ROLES\x10\x02\x12\t\n\x05USERS\x10\x03\x12\t\n\x05TEAMS\x10\x04\x12\x0e\n\nTEAM_USERS\x10\x05\x12\x0e\n\nROLE_USERS\x10\x06\x12\x13\n\x0fROLE_PRIVILEGES\x10\x07\x12\x15\n\x11ROLE_ENFORCEMENTS\x10\x08\x12\x0e\n\nROLE_TEAMS\x10\t\x12\x0c\n\x08LICENSES\x10\n\x12\x11\n\rMANAGED_NODES\x10\x0b\x12\x15\n\x11MANAGED_COMPANIES\x10\x0c\x12\x0b\n\x07\x42RIDGES\x10\r\x12\t\n\x05SCIMS\x10\x0e\x12\x13\n\x0f\x45MAIL_PROVISION\x10\x0f\x12\x10\n\x0cQUEUED_TEAMS\x10\x10\x12\x15\n\x11QUEUED_TEAM_USERS\x10\x11\x12\x10\n\x0cSSO_SERVICES\x10\x12\x12\x17\n\x13REPORT_FILTER_USERS\x10\x13\x12&\n\"DEVICES_REQUEST_FOR_ADMIN_APPROVAL\x10\x14\x12\x10\n\x0cUSER_ALIASES\x10\x15\x12)\n%COMPLIANCE_REPORT_CRITERIA_AND_FILTER\x10\x16\x12\x16\n\x12\x43OMPLIANCE_REPORTS\x10\x17*\"\n\x0b\x43\x61\x63heStatus\x12\x08\n\x04KEEP\x10\x00\x12\t\n\x05\x43LEAR\x10\x01*\x93\x01\n\rBackupKeyType\x12\n\n\x06NO_KEY\x10\x00\x12\x19\n\x15\x45NCRYPTED_BY_DATA_KEY\x10\x01\x12\x1b\n\x17\x45NCRYPTED_BY_PUBLIC_KEY\x10\x02\x12\x1d\n\x19\x45NCRYPTED_BY_DATA_KEY_GCM\x10\x03\x12\x1f\n\x1b\x45NCRYPTED_BY_PUBLIC_KEY_ECC\x10\x04*:\n\x15\x42\x61\x63kupUserDataKeyType\x12\x07\n\x03OWN\x10\x00\x12\x18\n\x14SHARED_TO_ENTERPRISE\x10\x01*\xa5\x01\n\x10\x45ncryptedKeyType\x12\r\n\tKT_NO_KEY\x10\x00\x12\x1c\n\x18KT_ENCRYPTED_BY_DATA_KEY\x10\x01\x12\x1e\n\x1aKT_ENCRYPTED_BY_PUBLIC_KEY\x10\x02\x12 \n\x1cKT_ENCRYPTED_BY_DATA_KEY_GCM\x10\x03\x12\"\n\x1eKT_ENCRYPTED_BY_PUBLIC_KEY_ECC\x10\x04*\xb7\x02\n\x12\x45nterpriseFlagType\x12\x0b\n\x07INVALID\x10\x00\x12\x1a\n\x16\x41LLOW_PERSONAL_LICENSE\x10\x01\x12\x18\n\x14SPECIAL_PROVISIONING\x10\x02\x12\x10\n\x0cRECORD_TYPES\x10\x03\x12\x13\n\x0fSECRETS_MANAGER\x10\x04\x12\x15\n\x11\x45NTERPRISE_LOCKED\x10\x05\x12\x15\n\x11\x46ORBID_KEY_TYPE_2\x10\x06\x12\x15\n\x11\x43ONSOLE_ONBOARDED\x10\x07\x12\x1b\n\x17\x46ORBID_ACCOUNT_TRANSFER\x10\x08\x12\x15\n\x11NPS_POPUP_OPT_OUT\x10\t\x12\x15\n\x11SHOW_USER_ONBOARD\x10\n\x12\x15\n\x11\x46ORBID_KEY_TYPE_1\x10\x0b\x12\x10\n\x0cKEEPER_DRIVE\x10\x0c*E\n\x10UserUpdateStatus\x12\x12\n\x0eUSER_UPDATE_OK\x10\x00\x12\x1d\n\x19USER_UPDATE_ACCESS_DENIED\x10\x01*I\n\x0f\x41uditUserStatus\x12\x06\n\x02OK\x10\x00\x12\x11\n\rACCESS_DENIED\x10\x01\x12\x1b\n\x17NO_LONGER_IN_ENTERPRISE\x10\x02*3\n\x0cTeamUserType\x12\x08\n\x04USER\x10\x00\x12\t\n\x05\x41\x44MIN\x10\x01\x12\x0e\n\nADMIN_ONLY\x10\x02*x\n\rAppClientType\x12\x0c\n\x08NOT_USED\x10\x00\x12\x0b\n\x07GENERAL\x10\x01\x12%\n!DISCOVERY_AND_ROTATION_CONTROLLER\x10\x02\x12\x12\n\x0eKCM_CONTROLLER\x10\x03\x12\x11\n\rSELF_DESTRUCT\x10\x04*\x8f\x01\n\x1b\x44\x65leteEnterpriseUsersResult\x12\x0b\n\x07SUCCESS\x10\x00\x12\x1a\n\x16NOT_AN_ENTERPRISE_USER\x10\x01\x12\x16\n\x12\x43\x41NNOT_DELETE_SELF\x10\x02\x12$\n BRIDGE_CANNOT_DELETE_ACTIVE_USER\x10\x03\x12\t\n\x05\x45RROR\x10\x04*\x87\x01\n\x15\x43learSecurityDataType\x12\x1e\n\x1aRECALCULATE_SUMMARY_REPORT\x10\x00\x12\'\n#FORCE_CLIENT_CHECK_FOR_MISSING_DATA\x10\x01\x12%\n!FORCE_CLIENT_RESEND_SECURITY_DATA\x10\x02*J\n\x13ReserveDomainAction\x12\x10\n\x0c\x44OMAIN_TOKEN\x10\x00\x12\x0e\n\nDOMAIN_ADD\x10\x01\x12\x11\n\rDOMAIN_DELETE\x10\x02*s\n\x0eUserLockStatus\x12\x17\n\x13UNKNOWN_LOCK_STATUS\x10\x00\x12\n\n\x06LOCKED\x10\x01\x12\x0c\n\x08\x44ISABLED\x10\x02\x12\x0c\n\x08UNLOCKED\x10\x03\x12\x0b\n\x07\x44\x45LETED\x10\x04\x12\x13\n\x0f\x43\x41NT_BE_PENDING\x10\x05\x42&\n\x18\x63om.keepersecurity.protoB\nEnterpriseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x65nterprise.proto\x12\nEnterprise\"\x84\x01\n\x18\x45nterpriseKeyPairRequest\x12\x1b\n\x13\x65nterprisePublicKey\x18\x01 \x01(\x0c\x12%\n\x1d\x65ncryptedEnterprisePrivateKey\x18\x02 \x01(\x0c\x12$\n\x07keyType\x18\x03 \x01(\x0e\x32\x13.Enterprise.KeyType\"\'\n\x14GetTeamMemberRequest\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\"}\n\x0e\x45nterpriseUser\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x1a\n\x12\x65nterpriseUsername\x18\x03 \x01(\t\x12\x14\n\x0cisShareAdmin\x18\x04 \x01(\x08\x12\x10\n\x08username\x18\x05 \x01(\t\"K\n\x15GetTeamMemberResponse\x12\x32\n\x0e\x65nterpriseUser\x18\x01 \x03(\x0b\x32\x1a.Enterprise.EnterpriseUser\"-\n\x11\x45nterpriseUserIds\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x03(\x03\"B\n\x19\x45nterprisePersonalAccount\x12\r\n\x05\x65mail\x18\x01 \x01(\t\x12\x16\n\x0eOBSOLETE_FIELD\x18\x02 \x01(\x0c\"S\n\x17\x45ncryptedTeamKeyRequest\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x18\n\x10\x65ncryptedTeamKey\x18\x02 \x01(\x0c\x12\r\n\x05\x66orce\x18\x03 \x01(\x08\"+\n\x0fReEncryptedData\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"?\n\x12ReEncryptedRoleKey\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x18\n\x10\x65ncryptedRoleKey\x18\x02 \x01(\x0c\"P\n\x16ReEncryptedUserDataKey\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14userEncryptedDataKey\x18\x02 \x01(\x0c\"\xd8\x02\n\x1bNodeToManagedCompanyRequest\x12\x11\n\tcompanyId\x18\x01 \x01(\x05\x12*\n\x05nodes\x18\x02 \x03(\x0b\x32\x1b.Enterprise.ReEncryptedData\x12*\n\x05roles\x18\x03 \x03(\x0b\x32\x1b.Enterprise.ReEncryptedData\x12*\n\x05users\x18\x04 \x03(\x0b\x32\x1b.Enterprise.ReEncryptedData\x12\x30\n\x08roleKeys\x18\x05 \x03(\x0b\x32\x1e.Enterprise.ReEncryptedRoleKey\x12\x35\n\x08teamKeys\x18\x06 \x03(\x0b\x32#.Enterprise.EncryptedTeamKeyRequest\x12\x39\n\rusersDataKeys\x18\x07 \x03(\x0b\x32\".Enterprise.ReEncryptedUserDataKey\",\n\x08RoleTeam\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x0f\n\x07teamUid\x18\x02 \x01(\x0c\"4\n\tRoleTeams\x12\'\n\trole_team\x18\x01 \x03(\x0b\x32\x14.Enterprise.RoleTeam\"/\n\x0bTeamsByRole\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x0f\n\x07teamUid\x18\x02 \x03(\x0c\"<\n\x12ManagedNodesByRole\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x15\n\rmanagedNodeId\x18\x02 \x03(\x03\"R\n\x0fRoleUserAddKeys\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0f\n\x07treeKey\x18\x02 \x01(\t\x12\x14\n\x0croleAdminKey\x18\x03 \x01(\t\"T\n\x0bRoleUserAdd\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x34\n\x0froleUserAddKeys\x18\x02 \x03(\x0b\x32\x1b.Enterprise.RoleUserAddKeys\"D\n\x13RoleUsersAddRequest\x12-\n\x0croleUserAdds\x18\x01 \x03(\x0b\x32\x17.Enterprise.RoleUserAdd\"\x80\x01\n\x11RoleUserAddResult\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x30\n\x06status\x18\x03 \x01(\x0e\x32 .Enterprise.RoleUserModifyStatus\x12\x0f\n\x07message\x18\x04 \x01(\t\"F\n\x14RoleUsersAddResponse\x12.\n\x07results\x18\x01 \x03(\x0b\x32\x1d.Enterprise.RoleUserAddResult\"<\n\x0eRoleUserRemove\x12\x0f\n\x07role_id\x18\x01 \x01(\x03\x12\x19\n\x11\x65nterpriseUserIds\x18\x02 \x03(\x03\"M\n\x16RoleUsersRemoveRequest\x12\x33\n\x0froleUserRemoves\x18\x01 \x03(\x0b\x32\x1a.Enterprise.RoleUserRemove\"\x83\x01\n\x14RoleUserRemoveResult\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x30\n\x06status\x18\x03 \x01(\x0e\x32 .Enterprise.RoleUserModifyStatus\x12\x0f\n\x07message\x18\x04 \x01(\t\"L\n\x17RoleUsersRemoveResponse\x12\x31\n\x07results\x18\x01 \x03(\x0b\x32 .Enterprise.RoleUserRemoveResult\"\xa0\x04\n\x16\x45nterpriseRegistration\x12\x18\n\x10\x65ncryptedTreeKey\x18\x01 \x01(\x0c\x12\x16\n\x0e\x65nterpriseName\x18\x02 \x01(\t\x12\x14\n\x0crootNodeData\x18\x03 \x01(\x0c\x12\x15\n\radminUserData\x18\x04 \x01(\x0c\x12\x11\n\tadminName\x18\x05 \x01(\t\x12\x10\n\x08roleData\x18\x06 \x01(\x0c\x12\x38\n\nrsaKeyPair\x18\x07 \x01(\x0b\x32$.Enterprise.EnterpriseKeyPairRequest\x12\x13\n\x0bnumberSeats\x18\x08 \x01(\x05\x12\x32\n\x0e\x65nterpriseType\x18\t \x01(\x0e\x32\x1a.Enterprise.EnterpriseType\x12\x15\n\rrolePublicKey\x18\n \x01(\x0c\x12*\n\"rolePrivateKeyEncryptedWithRoleKey\x18\x0b \x01(\x0c\x12#\n\x1broleKeyEncryptedWithTreeKey\x18\x0c \x01(\x0c\x12\x38\n\neccKeyPair\x18\r \x01(\x0b\x32$.Enterprise.EnterpriseKeyPairRequest\x12\x18\n\x10\x61llUsersRoleData\x18\x0e \x01(\x0c\x12)\n!roleKeyEncryptedWithUserPublicKey\x18\x0f \x01(\x0c\x12\x18\n\x10\x61pproverRoleData\x18\x10 \x01(\x0c\"H\n\x1a\x44omainPasswordRulesRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x18\n\x10verificationCode\x18\x02 \x01(\t\"\\\n\x19\x44omainPasswordRulesFields\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07minimum\x18\x02 \x01(\x05\x12\x0f\n\x07maximum\x18\x03 \x01(\x05\x12\x0f\n\x07\x61llowed\x18\x04 \x01(\x08\"E\n\x10LoginToMcRequest\x12\x16\n\x0emcEnterpriseId\x18\x01 \x01(\x05\x12\x19\n\x11messageSessionUid\x18\x02 \x01(\x0c\"w\n\x11LoginToMcResponse\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x01 \x01(\x0c\x12\x18\n\x10\x65ncryptedTreeKey\x18\x02 \x01(\t\x12\x11\n\tkeyTypeId\x18\x03 \x01(\x05\x12\x16\n\x0e\x66orbidKeyType2\x18\x04 \x01(\x08\"g\n\x1b\x44omainPasswordRulesResponse\x12H\n\x19\x64omainPasswordRulesFields\x18\x01 \x03(\x0b\x32%.Enterprise.DomainPasswordRulesFields\"\x88\x01\n\x18\x41pproveUserDeviceRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x02 \x01(\x0c\x12\x1e\n\x16\x65ncryptedDeviceDataKey\x18\x03 \x01(\x0c\x12\x14\n\x0c\x64\x65nyApproval\x18\x04 \x01(\x08\"t\n\x19\x41pproveUserDeviceResponse\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x02 \x01(\x0c\x12\x0e\n\x06\x66\x61iled\x18\x03 \x01(\x08\x12\x0f\n\x07message\x18\x04 \x01(\t\"Y\n\x19\x41pproveUserDevicesRequest\x12<\n\x0e\x64\x65viceRequests\x18\x01 \x03(\x0b\x32$.Enterprise.ApproveUserDeviceRequest\"\\\n\x1a\x41pproveUserDevicesResponse\x12>\n\x0f\x64\x65viceResponses\x18\x01 \x03(\x0b\x32%.Enterprise.ApproveUserDeviceResponse\"\x87\x01\n\x15\x45nterpriseUserDataKey\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14userEncryptedDataKey\x18\x02 \x01(\x0c\x12\x11\n\tkeyTypeId\x18\x03 \x01(\x05\x12\x0f\n\x07roleKey\x18\x04 \x01(\x0c\x12\x12\n\nprivateKey\x18\x05 \x01(\x0c\"I\n\x16\x45nterpriseUserDataKeys\x12/\n\x04keys\x18\x01 \x03(\x0b\x32!.Enterprise.EnterpriseUserDataKey\"g\n\x1a\x45nterpriseUserDataKeyLight\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1c\n\x14userEncryptedDataKey\x18\x02 \x01(\x0c\x12\x11\n\tkeyTypeId\x18\x03 \x01(\x05\"d\n\x1c\x45nterpriseUserDataKeysByNode\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x34\n\x04keys\x18\x02 \x03(\x0b\x32&.Enterprise.EnterpriseUserDataKeyLight\"^\n$EnterpriseUserDataKeysByNodeResponse\x12\x36\n\x04keys\x18\x01 \x03(\x0b\x32(.Enterprise.EnterpriseUserDataKeysByNode\"2\n\x15\x45nterpriseDataRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\"0\n\x13SpecialProvisioning\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"\x84\x02\n\x11GeneralDataEntity\x12\x16\n\x0e\x65nterpriseName\x18\x01 \x01(\t\x12\x1a\n\x12restrictVisibility\x18\x02 \x01(\x08\x12<\n\x13specialProvisioning\x18\x04 \x01(\x0b\x32\x1f.Enterprise.SpecialProvisioning\x12\x30\n\ruserPrivilege\x18\x07 \x01(\x0b\x32\x19.Enterprise.UserPrivilege\x12\x13\n\x0b\x64istributor\x18\x08 \x01(\x08\x12\x1d\n\x15\x66orbidAccountTransfer\x18\t \x01(\x08\x12\x17\n\x0fshowUserOnboard\x18\n \x01(\x08\"\xfd\x01\n\x04Node\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x10\n\x08parentId\x18\x02 \x01(\x03\x12\x10\n\x08\x62ridgeId\x18\x03 \x01(\x03\x12\x0e\n\x06scimId\x18\x04 \x01(\x03\x12\x11\n\tlicenseId\x18\x05 \x01(\x03\x12\x15\n\rencryptedData\x18\x06 \x01(\t\x12\x12\n\nduoEnabled\x18\x07 \x01(\x08\x12\x12\n\nrsaEnabled\x18\x08 \x01(\x08\x12 \n\x14ssoServiceProviderId\x18\t \x01(\x03\x42\x02\x18\x01\x12\x1a\n\x12restrictVisibility\x18\n \x01(\x08\x12!\n\x15ssoServiceProviderIds\x18\x0b \x03(\x03\x42\x02\x10\x01\"\x8e\x01\n\x04Role\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\t\x12\x0f\n\x07keyType\x18\x04 \x01(\t\x12\x14\n\x0cvisibleBelow\x18\x05 \x01(\x08\x12\x16\n\x0enewUserInherit\x18\x06 \x01(\x08\x12\x10\n\x08roleType\x18\x07 \x01(\t\"\xb8\x02\n\x04User\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\t\x12\x0f\n\x07keyType\x18\x04 \x01(\t\x12\x10\n\x08username\x18\x05 \x01(\t\x12\x0e\n\x06status\x18\x06 \x01(\t\x12\x0c\n\x04lock\x18\x07 \x01(\x05\x12\x0e\n\x06userId\x18\x08 \x01(\x05\x12\x1e\n\x16\x61\x63\x63ountShareExpiration\x18\t \x01(\x03\x12\x10\n\x08\x66ullName\x18\n \x01(\t\x12\x10\n\x08jobTitle\x18\x0b \x01(\t\x12\x12\n\ntfaEnabled\x18\x0c \x01(\x08\x12\x46\n\x18transferAcceptanceStatus\x18\r \x01(\x0e\x32$.Enterprise.TransferAcceptanceStatus\"7\n\tUserAlias\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08username\x18\x02 \x01(\t\"\xac\x01\n\x18\x43omplianceReportMetaData\x12\x11\n\treportUid\x18\x01 \x01(\x0c\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x12\n\nreportName\x18\x03 \x01(\t\x12\x15\n\rdateGenerated\x18\x04 \x01(\x03\x12\x11\n\trunByName\x18\x05 \x01(\t\x12\x16\n\x0enumberOfOwners\x18\x07 \x01(\x05\x12\x17\n\x0fnumberOfRecords\x18\x08 \x01(\x05\"S\n\x0bManagedNode\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x15\n\rmanagedNodeId\x18\x02 \x01(\x03\x12\x1d\n\x15\x63\x61scadeNodeManagement\x18\x03 \x01(\x08\"T\n\x0fUserManagedNode\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x1d\n\x15\x63\x61scadeNodeManagement\x18\x02 \x01(\x08\x12\x12\n\nprivileges\x18\x03 \x03(\t\"w\n\rUserPrivilege\x12\x35\n\x10userManagedNodes\x18\x01 \x03(\x0b\x32\x1b.Enterprise.UserManagedNode\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\t\"4\n\x08RoleUser\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\"M\n\rRolePrivilege\x12\x15\n\rmanagedNodeId\x18\x01 \x01(\x03\x12\x0e\n\x06roleId\x18\x02 \x01(\x03\x12\x15\n\rprivilegeType\x18\x03 \x01(\t\"T\n\x17PrivilegesByManagedNode\x12\x15\n\rmanagedNodeId\x18\x01 \x01(\x03\x12\x0e\n\x06roleId\x18\x02 \x01(\x03\x12\x12\n\nprivileges\x18\x03 \x03(\t\"I\n\x0fRoleEnforcement\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x17\n\x0f\x65nforcementType\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\"\xa9\x01\n\x04Team\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x03\x12\x14\n\x0crestrictEdit\x18\x04 \x01(\x08\x12\x15\n\rrestrictShare\x18\x05 \x01(\x08\x12\x14\n\x0crestrictView\x18\x06 \x01(\x08\x12\x15\n\rencryptedData\x18\x07 \x01(\t\x12\x18\n\x10\x65ncryptedTeamKey\x18\x08 \x01(\t\"G\n\x08TeamUser\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x10\n\x08userType\x18\x03 \x01(\t\"K\n\x1aGetDistributorInfoResponse\x12-\n\x0c\x64istributors\x18\x01 \x03(\x0b\x32\x17.Enterprise.Distributor\"B\n\x0b\x44istributor\x12\x0c\n\x04name\x18\x01 \x01(\t\x12%\n\x08mspInfos\x18\x02 \x03(\x0b\x32\x13.Enterprise.MspInfo\"\x9d\x02\n\x07MspInfo\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x16\n\x0e\x65nterpriseName\x18\x02 \x01(\t\x12\x19\n\x11\x61llocatedLicenses\x18\x03 \x01(\x05\x12\x19\n\x11\x61llowedMcProducts\x18\x04 \x03(\t\x12\x15\n\rallowedAddOns\x18\x05 \x03(\t\x12\x17\n\x0fmaxFilePlanType\x18\x06 \x01(\t\x12\x34\n\x10managedCompanies\x18\x07 \x03(\x0b\x32\x1a.Enterprise.ManagedCompany\x12\x1e\n\x16\x61llowUnlimitedLicenses\x18\x08 \x01(\x08\x12(\n\x06\x61\x64\x64Ons\x18\t \x03(\x0b\x32\x18.Enterprise.LicenseAddOn\"\xa8\x02\n\x0eManagedCompany\x12\x16\n\x0emcEnterpriseId\x18\x01 \x01(\x05\x12\x18\n\x10mcEnterpriseName\x18\x02 \x01(\t\x12\x11\n\tmspNodeId\x18\x03 \x01(\x03\x12\x15\n\rnumberOfSeats\x18\x04 \x01(\x05\x12\x15\n\rnumberOfUsers\x18\x05 \x01(\x05\x12\x11\n\tproductId\x18\x06 \x01(\t\x12\x11\n\tisExpired\x18\x07 \x01(\x08\x12\x0f\n\x07treeKey\x18\x08 \x01(\t\x12\x15\n\rtree_key_role\x18\t \x01(\x03\x12\x14\n\x0c\x66ilePlanType\x18\n \x01(\t\x12(\n\x06\x61\x64\x64Ons\x18\x0b \x03(\x0b\x32\x18.Enterprise.LicenseAddOn\x12\x15\n\rtreeKeyTypeId\x18\x0c \x01(\x05\"R\n\x07MSPPool\x12\x11\n\tproductId\x18\x01 \x01(\t\x12\r\n\x05seats\x18\x02 \x01(\x05\x12\x16\n\x0e\x61vailableSeats\x18\x03 \x01(\x05\x12\r\n\x05stash\x18\x04 \x01(\x05\":\n\nMSPContact\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x16\n\x0e\x65nterpriseName\x18\x02 \x01(\t\"\x84\x02\n\x0cLicenseAddOn\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x0f\n\x07isTrial\x18\x03 \x01(\x08\x12\x12\n\nexpiration\x18\x04 \x01(\x03\x12\x0f\n\x07\x63reated\x18\x05 \x01(\x03\x12\r\n\x05seats\x18\x06 \x01(\x05\x12\x16\n\x0e\x61\x63tivationTime\x18\x07 \x01(\x03\x12\x19\n\x11includedInProduct\x18\x08 \x01(\x08\x12\x14\n\x0c\x61piCallCount\x18\t \x01(\x05\x12\x17\n\x0ftierDescription\x18\n \x01(\t\x12\x16\n\x0eseatsAllocated\x18\x0b \x01(\x05\x12\x16\n\x0enhiTierAddOnId\x18\x0c \x01(\x05\"s\n\tMCDefault\x12\x11\n\tmcProduct\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x64\x64Ons\x18\x02 \x03(\t\x12\x14\n\x0c\x66ilePlanType\x18\x03 \x01(\t\x12\x13\n\x0bmaxLicenses\x18\x04 \x01(\x05\x12\x18\n\x10\x66ixedMaxLicenses\x18\x05 \x01(\x08\"\xd2\x01\n\nMSPPermits\x12\x12\n\nrestricted\x18\x01 \x01(\x08\x12\x1a\n\x12maxAllowedLicenses\x18\x02 \x01(\x05\x12\x19\n\x11\x61llowedMcProducts\x18\x03 \x03(\t\x12\x15\n\rallowedAddOns\x18\x04 \x03(\t\x12\x17\n\x0fmaxFilePlanType\x18\x05 \x01(\t\x12\x1e\n\x16\x61llowUnlimitedLicenses\x18\x06 \x01(\x08\x12)\n\nmcDefaults\x18\x07 \x03(\x0b\x32\x15.Enterprise.MCDefault\"\xa0\x04\n\x07License\x12\x0c\n\x04paid\x18\x01 \x01(\x08\x12\x15\n\rnumberOfSeats\x18\x02 \x01(\x05\x12\x12\n\nexpiration\x18\x03 \x01(\x03\x12\x14\n\x0clicenseKeyId\x18\x04 \x01(\x05\x12\x15\n\rproductTypeId\x18\x05 \x01(\x05\x12\x0c\n\x04name\x18\x06 \x01(\t\x12\x1b\n\x13\x65nterpriseLicenseId\x18\x07 \x01(\x03\x12\x16\n\x0eseatsAllocated\x18\x08 \x01(\x05\x12\x14\n\x0cseatsPending\x18\t \x01(\x05\x12\x0c\n\x04tier\x18\n \x01(\x05\x12\x16\n\x0e\x66ilePlanTypeId\x18\x0b \x01(\x05\x12\x10\n\x08maxBytes\x18\x0c \x01(\x03\x12\x19\n\x11storageExpiration\x18\r \x01(\x03\x12\x15\n\rlicenseStatus\x18\x0e \x01(\t\x12$\n\x07mspPool\x18\x0f \x03(\x0b\x32\x13.Enterprise.MSPPool\x12)\n\tmanagedBy\x18\x10 \x01(\x0b\x32\x16.Enterprise.MSPContact\x12(\n\x06\x61\x64\x64Ons\x18\x11 \x03(\x0b\x32\x18.Enterprise.LicenseAddOn\x12\x17\n\x0fnextBillingDate\x18\x12 \x01(\x03\x12\x17\n\x0fhasMSPLegacyLog\x18\x13 \x01(\x08\x12*\n\nmspPermits\x18\x14 \x01(\x0b\x32\x16.Enterprise.MSPPermits\x12\x13\n\x0b\x64istributor\x18\x15 \x01(\x08\"n\n\x06\x42ridge\x12\x10\n\x08\x62ridgeId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x18\n\x10wanIpEnforcement\x18\x03 \x01(\t\x12\x18\n\x10lanIpEnforcement\x18\x04 \x01(\t\x12\x0e\n\x06status\x18\x05 \x01(\t\"t\n\x04Scim\x12\x0e\n\x06scimId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x12\n\nlastSynced\x18\x04 \x01(\x03\x12\x12\n\nrolePrefix\x18\x05 \x01(\t\x12\x14\n\x0cuniqueGroups\x18\x06 \x01(\x08\"L\n\x0e\x45mailProvision\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x0e\n\x06\x64omain\x18\x03 \x01(\t\x12\x0e\n\x06method\x18\x04 \x01(\t\"R\n\nQueuedTeam\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x03\x12\x15\n\rencryptedData\x18\x04 \x01(\t\"0\n\x0eQueuedTeamUser\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\r\n\x05users\x18\x02 \x03(\x03\"\xa4\x01\n\x0eTeamsAddResult\x12\x34\n\x11successfulTeamAdd\x18\x01 \x03(\x0b\x32\x19.Enterprise.TeamAddResult\x12\x36\n\x13unsuccessfulTeamAdd\x18\x02 \x03(\x0b\x32\x19.Enterprise.TeamAddResult\x12\x0e\n\x06result\x18\x03 \x01(\t\x12\x14\n\x0c\x65rrorMessage\x18\x04 \x01(\t\"U\n\rTeamAddResult\x12\x1e\n\x04team\x18\x01 \x01(\x0b\x32\x10.Enterprise.Team\x12\x0e\n\x06result\x18\x02 \x01(\t\x12\x14\n\x0c\x65rrorMessage\x18\x03 \x01(\t\"\x91\x01\n\nSsoService\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0e\n\x06sp_url\x18\x04 \x01(\t\x12\x16\n\x0einviteNewUsers\x18\x05 \x01(\x08\x12\x0e\n\x06\x61\x63tive\x18\x06 \x01(\x08\x12\x0f\n\x07isCloud\x18\x07 \x01(\x08\"1\n\x10ReportFilterUser\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\r\n\x05\x65mail\x18\x02 \x01(\t\"\x97\x02\n\x1d\x44\x65viceRequestForAdminApproval\x12\x10\n\x08\x64\x65viceId\x18\x01 \x01(\x03\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x03 \x01(\x0c\x12\x17\n\x0f\x64\x65vicePublicKey\x18\x04 \x01(\x0c\x12\x12\n\ndeviceName\x18\x05 \x01(\t\x12\x15\n\rclientVersion\x18\x06 \x01(\t\x12\x12\n\ndeviceType\x18\x07 \x01(\t\x12\x0c\n\x04\x64\x61te\x18\x08 \x01(\x03\x12\x11\n\tipAddress\x18\t \x01(\t\x12\x10\n\x08location\x18\n \x01(\t\x12\r\n\x05\x65mail\x18\x0b \x01(\t\x12\x12\n\naccountUid\x18\x0c \x01(\x0c\"`\n\x0e\x45nterpriseData\x12\x30\n\x06\x65ntity\x18\x01 \x01(\x0e\x32 .Enterprise.EnterpriseDataEntity\x12\x0e\n\x06\x64\x65lete\x18\x02 \x01(\x08\x12\x0c\n\x04\x64\x61ta\x18\x03 \x03(\x0c\"\xd0\x01\n\x16\x45nterpriseDataResponse\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\x12\x0f\n\x07hasMore\x18\x02 \x01(\x08\x12,\n\x0b\x63\x61\x63heStatus\x18\x03 \x01(\x0e\x32\x17.Enterprise.CacheStatus\x12(\n\x04\x64\x61ta\x18\x04 \x03(\x0b\x32\x1a.Enterprise.EnterpriseData\x12\x32\n\x0bgeneralData\x18\x05 \x01(\x0b\x32\x1d.Enterprise.GeneralDataEntity\"*\n\rBackupRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\"\x98\x01\n\x0c\x42\x61\x63kupRecord\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x0b\n\x03key\x18\x03 \x01(\x0c\x12*\n\x07keyType\x18\x04 \x01(\x0e\x32\x19.Enterprise.BackupKeyType\x12\x0f\n\x07version\x18\x05 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x06 \x01(\x0c\x12\r\n\x05\x65xtra\x18\x07 \x01(\x0c\".\n\tBackupKey\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x11\n\tbackupKey\x18\x02 \x01(\x0c\"\x8d\x02\n\nBackupUser\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x10\n\x08userName\x18\x02 \x01(\t\x12\x0f\n\x07\x64\x61taKey\x18\x03 \x01(\x0c\x12\x36\n\x0b\x64\x61taKeyType\x18\x04 \x01(\x0e\x32!.Enterprise.BackupUserDataKeyType\x12\x12\n\nprivateKey\x18\x05 \x01(\x0c\x12\x0f\n\x07treeKey\x18\x06 \x01(\x0c\x12.\n\x0btreeKeyType\x18\x07 \x01(\x0e\x32\x19.Enterprise.BackupKeyType\x12)\n\nbackupKeys\x18\x08 \x03(\x0b\x32\x15.Enterprise.BackupKey\x12\x14\n\x0cprivateECKey\x18\t \x01(\x0c\"\x9e\x01\n\x0e\x42\x61\x63kupResponse\x12\x1f\n\x17\x65nterpriseEccPrivateKey\x18\x01 \x01(\x0c\x12%\n\x05users\x18\x02 \x03(\x0b\x32\x16.Enterprise.BackupUser\x12)\n\x07records\x18\x03 \x03(\x0b\x32\x18.Enterprise.BackupRecord\x12\x19\n\x11\x63ontinuationToken\x18\x04 \x01(\x0c\"e\n\nBackupFile\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x11\n\tbackupUid\x18\x02 \x01(\x0c\x12\x10\n\x08\x66ileName\x18\x03 \x01(\t\x12\x0f\n\x07\x63reated\x18\x04 \x01(\x03\x12\x13\n\x0b\x64ownloadUrl\x18\x05 \x01(\t\"8\n\x0f\x42\x61\x63kupsResponse\x12%\n\x05\x66iles\x18\x01 \x03(\x0b\x32\x16.Enterprise.BackupFile\".\n\x1cGetEnterpriseDataKeysRequest\x12\x0e\n\x06roleId\x18\x01 \x03(\x03\"\xff\x01\n\x1dGetEnterpriseDataKeysResponse\x12:\n\x12reEncryptedRoleKey\x18\x01 \x03(\x0b\x32\x1e.Enterprise.ReEncryptedRoleKey\x12$\n\x07roleKey\x18\x02 \x03(\x0b\x32\x13.Enterprise.RoleKey\x12\"\n\x06mspKey\x18\x03 \x01(\x0b\x32\x12.Enterprise.MspKey\x12\x32\n\x0e\x65nterpriseKeys\x18\x04 \x01(\x0b\x32\x1a.Enterprise.EnterpriseKeys\x12$\n\x07treeKey\x18\x05 \x01(\x0b\x32\x13.Enterprise.TreeKey\"^\n\x07RoleKey\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x14\n\x0c\x65ncryptedKey\x18\x02 \x01(\t\x12-\n\x07keyType\x18\x03 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"d\n\x06MspKey\x12\x1b\n\x13\x65ncryptedMspTreeKey\x18\x01 \x01(\t\x12=\n\x17\x65ncryptedMspTreeKeyType\x18\x02 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"|\n\x0e\x45nterpriseKeys\x12\x14\n\x0crsaPublicKey\x18\x01 \x01(\x0c\x12\x1e\n\x16rsaEncryptedPrivateKey\x18\x02 \x01(\x0c\x12\x14\n\x0c\x65\x63\x63PublicKey\x18\x03 \x01(\x0c\x12\x1e\n\x16\x65\x63\x63\x45ncryptedPrivateKey\x18\x04 \x01(\x0c\"H\n\x07TreeKey\x12\x0f\n\x07treeKey\x18\x01 \x01(\t\x12,\n\tkeyTypeId\x18\x02 \x01(\x0e\x32\x19.Enterprise.BackupKeyType\"E\n\x14SharedRecordResponse\x12-\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1d.Enterprise.SharedRecordEvent\"p\n\x11SharedRecordEvent\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08userName\x18\x02 \x01(\t\x12\x0f\n\x07\x63\x61nEdit\x18\x03 \x01(\x08\x12\x12\n\ncanReshare\x18\x04 \x01(\x08\x12\x11\n\tshareFrom\x18\x05 \x01(\x05\".\n\x1cSetRestrictVisibilityRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\"\xd0\x01\n\x0eUserAddRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12-\n\x07keyType\x18\x04 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x10\n\x08\x66ullName\x18\x05 \x01(\t\x12\x10\n\x08jobTitle\x18\x06 \x01(\t\x12\r\n\x05\x65mail\x18\x07 \x01(\t\x12\x1b\n\x13suppressEmailInvite\x18\x08 \x01(\x08\":\n\x11UserUpdateRequest\x12%\n\x05users\x18\x01 \x03(\x0b\x32\x16.Enterprise.UserUpdate\"\xaf\x01\n\nUserUpdate\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12-\n\x07keyType\x18\x04 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x10\n\x08\x66ullName\x18\x05 \x01(\t\x12\x10\n\x08jobTitle\x18\x06 \x01(\t\x12\r\n\x05\x65mail\x18\x07 \x01(\t\"A\n\x12UserUpdateResponse\x12+\n\x05users\x18\x01 \x03(\x0b\x32\x1c.Enterprise.UserUpdateResult\"Z\n\x10UserUpdateResult\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12,\n\x06status\x18\x02 \x01(\x0e\x32\x1c.Enterprise.UserUpdateStatus\"J\n\x1d\x43omplianceRecordOwnersRequest\x12\x0f\n\x07nodeIds\x18\x01 \x03(\x03\x12\x18\n\x10includeNonShared\x18\x02 \x01(\x08\"O\n\x1e\x43omplianceRecordOwnersResponse\x12-\n\x0crecordOwners\x18\x01 \x03(\x0b\x32\x17.Enterprise.RecordOwner\"7\n\x0bRecordOwner\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06shared\x18\x02 \x01(\x08\"\xa6\x01\n PreliminaryComplianceDataRequest\x12\x19\n\x11\x65nterpriseUserIds\x18\x01 \x03(\x03\x12\x18\n\x10includeNonShared\x18\x02 \x01(\x08\x12\x19\n\x11\x63ontinuationToken\x18\x03 \x01(\x0c\x12\x32\n*includeTotalMatchingRecordsInFirstResponse\x18\x04 \x01(\x08\"\x9f\x01\n!PreliminaryComplianceDataResponse\x12\x30\n\rauditUserData\x18\x01 \x03(\x0b\x32\x19.Enterprise.AuditUserData\x12\x19\n\x11\x63ontinuationToken\x18\x02 \x01(\x0c\x12\x0f\n\x07hasMore\x18\x03 \x01(\x08\x12\x1c\n\x14totalMatchingRecords\x18\x04 \x01(\x05\"K\n\x0f\x41uditUserRecord\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x15\n\rencryptedData\x18\x02 \x01(\x0c\x12\x0e\n\x06shared\x18\x03 \x01(\x08\"\x8d\x01\n\rAuditUserData\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x35\n\x10\x61uditUserRecords\x18\x02 \x03(\x0b\x32\x1b.Enterprise.AuditUserRecord\x12+\n\x06status\x18\x03 \x01(\x0e\x32\x1b.Enterprise.AuditUserStatus\"\x7f\n\x17\x43omplianceReportFilters\x12\x14\n\x0crecordTitles\x18\x01 \x03(\t\x12\x12\n\nrecordUids\x18\x02 \x03(\x0c\x12\x11\n\tjobTitles\x18\x03 \x03(\x03\x12\x0c\n\x04urls\x18\x04 \x03(\t\x12\x19\n\x11\x65nterpriseUserIds\x18\x05 \x03(\x03\"\x7f\n\x17\x43omplianceReportRequest\x12<\n\x13\x63omplianceReportRun\x18\x01 \x01(\x0b\x32\x1f.Enterprise.ComplianceReportRun\x12\x12\n\nreportName\x18\x02 \x01(\t\x12\x12\n\nsaveReport\x18\x03 \x01(\x08\"\x85\x01\n\x13\x43omplianceReportRun\x12N\n\x17reportCriteriaAndFilter\x18\x01 \x01(\x0b\x32-.Enterprise.ComplianceReportCriteriaAndFilter\x12\r\n\x05users\x18\x02 \x03(\x03\x12\x0f\n\x07records\x18\x03 \x03(\x0c\"\xfc\x01\n!ComplianceReportCriteriaAndFilter\x12\x0e\n\x06nodeId\x18\x01 \x01(\x03\x12\x13\n\x0b\x63riteriaUid\x18\x02 \x01(\x0c\x12\x14\n\x0c\x63riteriaName\x18\x03 \x01(\t\x12\x36\n\x08\x63riteria\x18\x04 \x01(\x0b\x32$.Enterprise.ComplianceReportCriteria\x12\x33\n\x07\x66ilters\x18\x05 \x03(\x0b\x32\".Enterprise.ComplianceReportFilter\x12\x14\n\x0clastModified\x18\x06 \x01(\x03\x12\x19\n\x11nodeEncryptedData\x18\x07 \x01(\x0c\"b\n\x18\x43omplianceReportCriteria\x12\x11\n\tjobTitles\x18\x01 \x03(\t\x12\x19\n\x11\x65nterpriseUserIds\x18\x02 \x03(\x03\x12\x18\n\x10includeNonShared\x18\x03 \x01(\x08\"x\n\x16\x43omplianceReportFilter\x12\x14\n\x0crecordTitles\x18\x01 \x03(\t\x12\x12\n\nrecordUids\x18\x02 \x03(\x0c\x12\x11\n\tjobTitles\x18\x03 \x03(\t\x12\x0c\n\x04urls\x18\x04 \x03(\t\x12\x13\n\x0brecordTypes\x18\x05 \x03(\t\"\xa1\x05\n\x18\x43omplianceReportResponse\x12\x15\n\rdateGenerated\x18\x01 \x01(\x03\x12\x15\n\rrunByUserName\x18\x02 \x01(\t\x12\x12\n\nreportName\x18\x03 \x01(\t\x12\x11\n\treportUid\x18\x04 \x01(\x0c\x12<\n\x13\x63omplianceReportRun\x18\x05 \x01(\x0b\x32\x1f.Enterprise.ComplianceReportRun\x12-\n\x0cuserProfiles\x18\x06 \x03(\x0b\x32\x17.Enterprise.UserProfile\x12)\n\nauditTeams\x18\x07 \x03(\x0b\x32\x15.Enterprise.AuditTeam\x12-\n\x0c\x61uditRecords\x18\x08 \x03(\x0b\x32\x17.Enterprise.AuditRecord\x12+\n\x0buserRecords\x18\t \x03(\x0b\x32\x16.Enterprise.UserRecord\x12;\n\x13sharedFolderRecords\x18\n \x03(\x0b\x32\x1e.Enterprise.SharedFolderRecord\x12\x37\n\x11sharedFolderUsers\x18\x0b \x03(\x0b\x32\x1c.Enterprise.SharedFolderUser\x12\x37\n\x11sharedFolderTeams\x18\x0c \x03(\x0b\x32\x1c.Enterprise.SharedFolderTeam\x12\x31\n\x0e\x61uditTeamUsers\x18\r \x03(\x0b\x32\x19.Enterprise.AuditTeamUser\x12)\n\nauditRoles\x18\x0e \x03(\x0b\x32\x15.Enterprise.AuditRole\x12/\n\rlinkedRecords\x18\x0f \x03(\x0b\x32\x18.Enterprise.LinkedRecord\"\x81\x01\n\x0b\x41uditRecord\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x11\n\tauditData\x18\x02 \x01(\x0c\x12\x16\n\x0ehasAttachments\x18\x03 \x01(\x08\x12\x0f\n\x07inTrash\x18\x04 \x01(\x08\x12\x10\n\x08treeLeft\x18\x05 \x01(\x05\x12\x11\n\ttreeRight\x18\x06 \x01(\x05\"\x80\x02\n\tAuditRole\x12\x0e\n\x06roleId\x18\x01 \x01(\x03\x12\x15\n\rencryptedData\x18\x02 \x01(\x0c\x12&\n\x1erestrictShareOutsideEnterprise\x18\x03 \x01(\x08\x12\x18\n\x10restrictShareAll\x18\x04 \x01(\x08\x12\"\n\x1arestrictShareOfAttachments\x18\x05 \x01(\x08\x12)\n!restrictMaskPasswordsWhileEditing\x18\x06 \x01(\x08\x12;\n\x13roleNodeManagements\x18\x07 \x03(\x0b\x32\x1e.Enterprise.RoleNodeManagement\"^\n\x12RoleNodeManagement\x12\x10\n\x08treeLeft\x18\x01 \x01(\x05\x12\x11\n\ttreeRight\x18\x02 \x01(\x05\x12\x0f\n\x07\x63\x61scade\x18\x03 \x01(\x08\x12\x12\n\nprivileges\x18\x04 \x01(\x05\"k\n\x0bUserProfile\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08\x66ullName\x18\x02 \x01(\t\x12\x10\n\x08jobTitle\x18\x03 \x01(\t\x12\r\n\x05\x65mail\x18\x04 \x01(\t\x12\x0f\n\x07roleIds\x18\x05 \x03(\x03\"=\n\x10RecordPermission\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x16\n\x0epermissionBits\x18\x02 \x01(\x05\"_\n\nUserRecord\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x37\n\x11recordPermissions\x18\x02 \x03(\x0b\x32\x1c.Enterprise.RecordPermission\"[\n\tAuditTeam\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x10\n\x08teamName\x18\x02 \x01(\t\x12\x14\n\x0crestrictEdit\x18\x03 \x01(\x08\x12\x15\n\rrestrictShare\x18\x04 \x01(\x08\";\n\rAuditTeamUser\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x19\n\x11\x65nterpriseUserIds\x18\x02 \x03(\x03\"\x9f\x01\n\x12SharedFolderRecord\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x37\n\x11recordPermissions\x18\x02 \x03(\x0b\x32\x1c.Enterprise.RecordPermission\x12\x37\n\x11shareAdminRecords\x18\x03 \x03(\x0b\x32\x1c.Enterprise.ShareAdminRecord\"M\n\x10ShareAdminRecord\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x1f\n\x17recordPermissionIndexes\x18\x02 \x03(\x05\"F\n\x10SharedFolderUser\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x19\n\x11\x65nterpriseUserIds\x18\x02 \x03(\x03\"=\n\x10SharedFolderTeam\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x10\n\x08teamUids\x18\x02 \x03(\x0c\"/\n\x1aGetComplianceReportRequest\x12\x11\n\treportUid\x18\x01 \x01(\x0c\"2\n\x1bGetComplianceReportResponse\x12\x13\n\x0b\x64ownloadUrl\x18\x01 \x01(\t\"6\n\x1f\x43omplianceReportCriteriaRequest\x12\x13\n\x0b\x63riteriaUid\x18\x01 \x01(\x0c\";\n$SaveComplianceReportCriteriaResponse\x12\x13\n\x0b\x63riteriaUid\x18\x01 \x01(\x0c\"4\n\x0cLinkedRecord\x12\x10\n\x08ownerUid\x18\x01 \x01(\x0c\x12\x12\n\nrecordUids\x18\x02 \x03(\x0c\"W\n\x17GetSharingAdminsRequest\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x10\n\x08username\x18\x03 \x01(\t\"\xe0\x01\n\x0eUserProfileExt\x12\r\n\x05\x65mail\x18\x01 \x01(\t\x12\x10\n\x08\x66ullName\x18\x02 \x01(\t\x12\x10\n\x08jobTitle\x18\x03 \x01(\t\x12\x14\n\x0cisMSPMCAdmin\x18\x04 \x01(\x08\x12\x18\n\x10isInSharedFolder\x18\x05 \x01(\x08\x12&\n\x1eisShareAdminForRequestedObject\x18\x06 \x01(\x08\x12(\n isShareAdminForSharedFolderOwner\x18\x07 \x01(\x08\x12\x19\n\x11hasAccessToObject\x18\x08 \x01(\x08\"O\n\x18GetSharingAdminsResponse\x12\x33\n\x0fuserProfileExts\x18\x01 \x03(\x0b\x32\x1a.Enterprise.UserProfileExt\"_\n\x1eTeamsEnterpriseUsersAddRequest\x12=\n\x05teams\x18\x01 \x03(\x0b\x32..Enterprise.TeamsEnterpriseUsersAddTeamRequest\"t\n\"TeamsEnterpriseUsersAddTeamRequest\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12=\n\x05users\x18\x02 \x03(\x0b\x32..Enterprise.TeamsEnterpriseUsersAddUserRequest\"\xab\x01\n\"TeamsEnterpriseUsersAddUserRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12*\n\x08userType\x18\x02 \x01(\x0e\x32\x18.Enterprise.TeamUserType\x12\x13\n\x07teamKey\x18\x03 \x01(\tB\x02\x18\x01\x12*\n\x0ctypedTeamKey\x18\x04 \x01(\x0b\x32\x14.Enterprise.TypedKey\"F\n\x08TypedKey\x12\x0b\n\x03key\x18\x01 \x01(\x0c\x12-\n\x07keyType\x18\x02 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\"s\n\x1fTeamsEnterpriseUsersAddResponse\x12>\n\x05teams\x18\x01 \x03(\x0b\x32/.Enterprise.TeamsEnterpriseUsersAddTeamResponse\x12\x10\n\x08revision\x18\x02 \x01(\x03\"\xc4\x01\n#TeamsEnterpriseUsersAddTeamResponse\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12>\n\x05users\x18\x02 \x03(\x0b\x32/.Enterprise.TeamsEnterpriseUsersAddUserResponse\x12\x0f\n\x07success\x18\x03 \x01(\x08\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x12\n\nresultCode\x18\x05 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x06 \x01(\t\"\x9f\x01\n#TeamsEnterpriseUsersAddUserResponse\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x0f\n\x07success\x18\x03 \x01(\x08\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x12\n\nresultCode\x18\x05 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x06 \x01(\t\"E\n\x18TeamEnterpriseUserRemove\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x18\n\x10\x65nterpriseUserId\x18\x02 \x01(\x03\"j\n TeamEnterpriseUserRemovesRequest\x12\x46\n\x18teamEnterpriseUserRemove\x18\x01 \x03(\x0b\x32$.Enterprise.TeamEnterpriseUserRemove\"{\n!TeamEnterpriseUserRemovesResponse\x12V\n teamEnterpriseUserRemoveResponse\x18\x01 \x03(\x0b\x32,.Enterprise.TeamEnterpriseUserRemoveResponse\"\xb8\x01\n TeamEnterpriseUserRemoveResponse\x12\x46\n\x18teamEnterpriseUserRemove\x18\x01 \x01(\x0b\x32$.Enterprise.TeamEnterpriseUserRemove\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x12\n\nresultCode\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x05 \x01(\t\"M\n\x0b\x44omainAlias\x12\x0e\n\x06\x64omain\x18\x01 \x01(\t\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\x0e\n\x06status\x18\x03 \x01(\x05\x12\x0f\n\x07message\x18\x04 \x01(\t\"B\n\x12\x44omainAliasRequest\x12,\n\x0b\x64omainAlias\x18\x01 \x03(\x0b\x32\x17.Enterprise.DomainAlias\"C\n\x13\x44omainAliasResponse\x12,\n\x0b\x64omainAlias\x18\x01 \x03(\x0b\x32\x17.Enterprise.DomainAlias\"m\n\x1f\x45nterpriseUsersProvisionRequest\x12\x33\n\x05users\x18\x01 \x03(\x0b\x32$.Enterprise.EnterpriseUsersProvision\x12\x15\n\rclientVersion\x18\x02 \x01(\t\"\xb6\x03\n\x18\x45nterpriseUsersProvision\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x03\x12\x15\n\rencryptedData\x18\x04 \x01(\t\x12-\n\x07keyType\x18\x05 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x10\n\x08\x66ullName\x18\x06 \x01(\t\x12\x10\n\x08jobTitle\x18\x07 \x01(\t\x12\x1e\n\x16\x65nterpriseUsersDataKey\x18\x08 \x01(\x0c\x12\x14\n\x0c\x61uthVerifier\x18\t \x01(\x0c\x12\x18\n\x10\x65ncryptionParams\x18\n \x01(\x0c\x12\x14\n\x0crsaPublicKey\x18\x0b \x01(\x0c\x12\x1e\n\x16rsaEncryptedPrivateKey\x18\x0c \x01(\x0c\x12\x14\n\x0c\x65\x63\x63PublicKey\x18\r \x01(\x0c\x12\x1e\n\x16\x65\x63\x63\x45ncryptedPrivateKey\x18\x0e \x01(\x0c\x12\x1c\n\x14\x65ncryptedDeviceToken\x18\x0f \x01(\x0c\x12\x1a\n\x12\x65ncryptedClientKey\x18\x10 \x01(\x0c\"_\n EnterpriseUsersProvisionResponse\x12;\n\x07results\x18\x01 \x03(\x0b\x32*.Enterprise.EnterpriseUsersProvisionResult\"q\n\x1e\x45nterpriseUsersProvisionResult\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x04 \x01(\t\"a\n\x19\x45nterpriseUsersAddRequest\x12-\n\x05users\x18\x01 \x03(\x0b\x32\x1e.Enterprise.EnterpriseUsersAdd\x12\x15\n\rclientVersion\x18\x02 \x01(\t\"\x8c\x02\n\x12\x45nterpriseUsersAdd\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x03\x12\x15\n\rencryptedData\x18\x04 \x01(\t\x12-\n\x07keyType\x18\x05 \x01(\x0e\x32\x1c.Enterprise.EncryptedKeyType\x12\x10\n\x08\x66ullName\x18\x06 \x01(\t\x12\x10\n\x08jobTitle\x18\x07 \x01(\t\x12\x1b\n\x13suppressEmailInvite\x18\x08 \x01(\x08\x12\x15\n\rinviteeLocale\x18\t \x01(\t\x12\x0c\n\x04move\x18\n \x01(\x08\x12\x0e\n\x06roleId\x18\x0b \x01(\x03\"\x9b\x01\n\x1a\x45nterpriseUsersAddResponse\x12\x35\n\x07results\x18\x01 \x03(\x0b\x32$.Enterprise.EnterpriseUsersAddResult\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x0c\n\x04\x63ode\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x05 \x01(\t\"\x96\x01\n\x18\x45nterpriseUsersAddResult\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x18\n\x10verificationCode\x18\x03 \x01(\t\x12\x0c\n\x04\x63ode\x18\x04 \x01(\t\x12\x0f\n\x07message\x18\x05 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x06 \x01(\t\"\xb9\x01\n\x17UpdateMSPPermitsRequest\x12\x17\n\x0fmspEnterpriseId\x18\x01 \x01(\x05\x12\x1a\n\x12maxAllowedLicenses\x18\x02 \x01(\x05\x12\x19\n\x11\x61llowedMcProducts\x18\x03 \x03(\t\x12\x15\n\rallowedAddOns\x18\x04 \x03(\t\x12\x17\n\x0fmaxFilePlanType\x18\x05 \x01(\t\x12\x1e\n\x16\x61llowUnlimitedLicenses\x18\x06 \x01(\x08\"9\n\x1c\x44\x65leteEnterpriseUsersRequest\x12\x19\n\x11\x65nterpriseUserIds\x18\x01 \x03(\x03\"o\n\x1a\x44\x65leteEnterpriseUserStatus\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.Enterprise.DeleteEnterpriseUsersResult\"]\n\x1d\x44\x65leteEnterpriseUsersResponse\x12<\n\x0c\x64\x65leteStatus\x18\x01 \x03(\x0b\x32&.Enterprise.DeleteEnterpriseUserStatus\"w\n\x18\x43learSecurityDataRequest\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x03(\x03\x12\x10\n\x08\x61llUsers\x18\x02 \x01(\x08\x12/\n\x04type\x18\x03 \x01(\x0e\x32!.Enterprise.ClearSecurityDataType\"%\n\x13ListDomainsResponse\x12\x0e\n\x06\x64omain\x18\x01 \x03(\t\"d\n\x14ReserveDomainRequest\x12<\n\x13reserveDomainAction\x18\x01 \x01(\x0e\x32\x1f.Enterprise.ReserveDomainAction\x12\x0e\n\x06\x64omain\x18\x02 \x01(\t\"&\n\x15ReserveDomainResponse\x12\r\n\x05token\x18\x01 \x01(\t\".\n\x0bRolesByTeam\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x0e\n\x06roleId\x18\x02 \x03(\x03\"\x8d\x01\n\x10LockUsersRequest\x12\x1d\n\x15lockEnterpriseUserIds\x18\x01 \x03(\x03\x12 \n\x18\x64isableEnterpriseUserIds\x18\x02 \x03(\x03\x12\x1f\n\x17unlockEnterpriseUserIds\x18\x03 \x03(\x03\x12\x17\n\x0f\x64\x65leteIfPending\x18\x04 \x01(\x08\"C\n\x11LockUsersResponse\x12.\n\x08response\x18\x01 \x03(\x0b\x32\x1c.Enterprise.LockUserResponse\"n\n\x10LockUserResponse\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.Enterprise.UserLockStatus\x12\x14\n\x0c\x65rrorMessage\x18\x03 \x01(\t*\x1b\n\x07KeyType\x12\x07\n\x03RSA\x10\x00\x12\x07\n\x03\x45\x43\x43\x10\x01*\x9a\x02\n\x14RoleUserModifyStatus\x12\x0f\n\x0bROLE_EXISTS\x10\x00\x12\x14\n\x10MISSING_TREE_KEY\x10\x01\x12\x14\n\x10MISSING_ROLE_KEY\x10\x02\x12\x1e\n\x1aINVALID_ENTERPRISE_USER_ID\x10\x03\x12\x1b\n\x17PENDING_ENTERPRISE_USER\x10\x04\x12\x13\n\x0fINVALID_NODE_ID\x10\x05\x12!\n\x1dMAY_NOT_REMOVE_SELF_FROM_ROLE\x10\x06\x12\x1c\n\x18MUST_HAVE_ONE_USER_ADMIN\x10\x07\x12\x13\n\x0fINVALID_ROLE_ID\x10\x08\x12\x1d\n\x19PAM_LICENSE_SEAT_EXCEEDED\x10\t*=\n\x0e\x45nterpriseType\x12\x17\n\x13\x45NTERPRISE_STANDARD\x10\x00\x12\x12\n\x0e\x45NTERPRISE_MSP\x10\x01*s\n\x18TransferAcceptanceStatus\x12\r\n\tUNDEFINED\x10\x00\x12\x10\n\x0cNOT_REQUIRED\x10\x01\x12\x10\n\x0cNOT_ACCEPTED\x10\x02\x12\x16\n\x12PARTIALLY_ACCEPTED\x10\x03\x12\x0c\n\x08\x41\x43\x43\x45PTED\x10\x04*\xe1\x03\n\x14\x45nterpriseDataEntity\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05NODES\x10\x01\x12\t\n\x05ROLES\x10\x02\x12\t\n\x05USERS\x10\x03\x12\t\n\x05TEAMS\x10\x04\x12\x0e\n\nTEAM_USERS\x10\x05\x12\x0e\n\nROLE_USERS\x10\x06\x12\x13\n\x0fROLE_PRIVILEGES\x10\x07\x12\x15\n\x11ROLE_ENFORCEMENTS\x10\x08\x12\x0e\n\nROLE_TEAMS\x10\t\x12\x0c\n\x08LICENSES\x10\n\x12\x11\n\rMANAGED_NODES\x10\x0b\x12\x15\n\x11MANAGED_COMPANIES\x10\x0c\x12\x0b\n\x07\x42RIDGES\x10\r\x12\t\n\x05SCIMS\x10\x0e\x12\x13\n\x0f\x45MAIL_PROVISION\x10\x0f\x12\x10\n\x0cQUEUED_TEAMS\x10\x10\x12\x15\n\x11QUEUED_TEAM_USERS\x10\x11\x12\x10\n\x0cSSO_SERVICES\x10\x12\x12\x17\n\x13REPORT_FILTER_USERS\x10\x13\x12&\n\"DEVICES_REQUEST_FOR_ADMIN_APPROVAL\x10\x14\x12\x10\n\x0cUSER_ALIASES\x10\x15\x12)\n%COMPLIANCE_REPORT_CRITERIA_AND_FILTER\x10\x16\x12\x16\n\x12\x43OMPLIANCE_REPORTS\x10\x17*\"\n\x0b\x43\x61\x63heStatus\x12\x08\n\x04KEEP\x10\x00\x12\t\n\x05\x43LEAR\x10\x01*\x93\x01\n\rBackupKeyType\x12\n\n\x06NO_KEY\x10\x00\x12\x19\n\x15\x45NCRYPTED_BY_DATA_KEY\x10\x01\x12\x1b\n\x17\x45NCRYPTED_BY_PUBLIC_KEY\x10\x02\x12\x1d\n\x19\x45NCRYPTED_BY_DATA_KEY_GCM\x10\x03\x12\x1f\n\x1b\x45NCRYPTED_BY_PUBLIC_KEY_ECC\x10\x04*:\n\x15\x42\x61\x63kupUserDataKeyType\x12\x07\n\x03OWN\x10\x00\x12\x18\n\x14SHARED_TO_ENTERPRISE\x10\x01*\xa5\x01\n\x10\x45ncryptedKeyType\x12\r\n\tKT_NO_KEY\x10\x00\x12\x1c\n\x18KT_ENCRYPTED_BY_DATA_KEY\x10\x01\x12\x1e\n\x1aKT_ENCRYPTED_BY_PUBLIC_KEY\x10\x02\x12 \n\x1cKT_ENCRYPTED_BY_DATA_KEY_GCM\x10\x03\x12\"\n\x1eKT_ENCRYPTED_BY_PUBLIC_KEY_ECC\x10\x04*\xb7\x02\n\x12\x45nterpriseFlagType\x12\x0b\n\x07INVALID\x10\x00\x12\x1a\n\x16\x41LLOW_PERSONAL_LICENSE\x10\x01\x12\x18\n\x14SPECIAL_PROVISIONING\x10\x02\x12\x10\n\x0cRECORD_TYPES\x10\x03\x12\x13\n\x0fSECRETS_MANAGER\x10\x04\x12\x15\n\x11\x45NTERPRISE_LOCKED\x10\x05\x12\x15\n\x11\x46ORBID_KEY_TYPE_2\x10\x06\x12\x15\n\x11\x43ONSOLE_ONBOARDED\x10\x07\x12\x1b\n\x17\x46ORBID_ACCOUNT_TRANSFER\x10\x08\x12\x15\n\x11NPS_POPUP_OPT_OUT\x10\t\x12\x15\n\x11SHOW_USER_ONBOARD\x10\n\x12\x15\n\x11\x46ORBID_KEY_TYPE_1\x10\x0b\x12\x10\n\x0cKEEPER_DRIVE\x10\x0c*E\n\x10UserUpdateStatus\x12\x12\n\x0eUSER_UPDATE_OK\x10\x00\x12\x1d\n\x19USER_UPDATE_ACCESS_DENIED\x10\x01*I\n\x0f\x41uditUserStatus\x12\x06\n\x02OK\x10\x00\x12\x11\n\rACCESS_DENIED\x10\x01\x12\x1b\n\x17NO_LONGER_IN_ENTERPRISE\x10\x02*3\n\x0cTeamUserType\x12\x08\n\x04USER\x10\x00\x12\t\n\x05\x41\x44MIN\x10\x01\x12\x0e\n\nADMIN_ONLY\x10\x02*x\n\rAppClientType\x12\x0c\n\x08NOT_USED\x10\x00\x12\x0b\n\x07GENERAL\x10\x01\x12%\n!DISCOVERY_AND_ROTATION_CONTROLLER\x10\x02\x12\x12\n\x0eKCM_CONTROLLER\x10\x03\x12\x11\n\rSELF_DESTRUCT\x10\x04*\x8f\x01\n\x1b\x44\x65leteEnterpriseUsersResult\x12\x0b\n\x07SUCCESS\x10\x00\x12\x1a\n\x16NOT_AN_ENTERPRISE_USER\x10\x01\x12\x16\n\x12\x43\x41NNOT_DELETE_SELF\x10\x02\x12$\n BRIDGE_CANNOT_DELETE_ACTIVE_USER\x10\x03\x12\t\n\x05\x45RROR\x10\x04*\x87\x01\n\x15\x43learSecurityDataType\x12\x1e\n\x1aRECALCULATE_SUMMARY_REPORT\x10\x00\x12\'\n#FORCE_CLIENT_CHECK_FOR_MISSING_DATA\x10\x01\x12%\n!FORCE_CLIENT_RESEND_SECURITY_DATA\x10\x02*J\n\x13ReserveDomainAction\x12\x10\n\x0c\x44OMAIN_TOKEN\x10\x00\x12\x0e\n\nDOMAIN_ADD\x10\x01\x12\x11\n\rDOMAIN_DELETE\x10\x02*s\n\x0eUserLockStatus\x12\x17\n\x13UNKNOWN_LOCK_STATUS\x10\x00\x12\n\n\x06LOCKED\x10\x01\x12\x0c\n\x08\x44ISABLED\x10\x02\x12\x0c\n\x08UNLOCKED\x10\x03\x12\x0b\n\x07\x44\x45LETED\x10\x04\x12\x13\n\x0f\x43\x41NT_BE_PENDING\x10\x05*\x80\x01\n\x1d\x45xternalCloudSecretsStoreType\x12\x16\n\x12UNKNOWN_STORE_TYPE\x10\x00\x12\x17\n\x13\x41WS_SECRETS_MANAGER\x10\x01\x12\x13\n\x0f\x41ZURE_KEY_VAULT\x10\x02\x12\x19\n\x15GOOGLE_SECRET_MANAGER\x10\x03\x42&\n\x18\x63om.keepersecurity.protoB\nEnterpriseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -27,42 +38,44 @@ _globals['_NODE'].fields_by_name['ssoServiceProviderIds']._serialized_options = b'\020\001' _globals['_TEAMSENTERPRISEUSERSADDUSERREQUEST'].fields_by_name['teamKey']._loaded_options = None _globals['_TEAMSENTERPRISEUSERSADDUSERREQUEST'].fields_by_name['teamKey']._serialized_options = b'\030\001' - _globals['_KEYTYPE']._serialized_start=20583 - _globals['_KEYTYPE']._serialized_end=20610 - _globals['_ROLEUSERMODIFYSTATUS']._serialized_start=20613 - _globals['_ROLEUSERMODIFYSTATUS']._serialized_end=20895 - _globals['_ENTERPRISETYPE']._serialized_start=20897 - _globals['_ENTERPRISETYPE']._serialized_end=20958 - _globals['_TRANSFERACCEPTANCESTATUS']._serialized_start=20960 - _globals['_TRANSFERACCEPTANCESTATUS']._serialized_end=21075 - _globals['_ENTERPRISEDATAENTITY']._serialized_start=21078 - _globals['_ENTERPRISEDATAENTITY']._serialized_end=21559 - _globals['_CACHESTATUS']._serialized_start=21561 - _globals['_CACHESTATUS']._serialized_end=21595 - _globals['_BACKUPKEYTYPE']._serialized_start=21598 - _globals['_BACKUPKEYTYPE']._serialized_end=21745 - _globals['_BACKUPUSERDATAKEYTYPE']._serialized_start=21747 - _globals['_BACKUPUSERDATAKEYTYPE']._serialized_end=21805 - _globals['_ENCRYPTEDKEYTYPE']._serialized_start=21808 - _globals['_ENCRYPTEDKEYTYPE']._serialized_end=21973 - _globals['_ENTERPRISEFLAGTYPE']._serialized_start=21976 - _globals['_ENTERPRISEFLAGTYPE']._serialized_end=22287 - _globals['_USERUPDATESTATUS']._serialized_start=22289 - _globals['_USERUPDATESTATUS']._serialized_end=22358 - _globals['_AUDITUSERSTATUS']._serialized_start=22360 - _globals['_AUDITUSERSTATUS']._serialized_end=22433 - _globals['_TEAMUSERTYPE']._serialized_start=22435 - _globals['_TEAMUSERTYPE']._serialized_end=22486 - _globals['_APPCLIENTTYPE']._serialized_start=22488 - _globals['_APPCLIENTTYPE']._serialized_end=22608 - _globals['_DELETEENTERPRISEUSERSRESULT']._serialized_start=22611 - _globals['_DELETEENTERPRISEUSERSRESULT']._serialized_end=22754 - _globals['_CLEARSECURITYDATATYPE']._serialized_start=22757 - _globals['_CLEARSECURITYDATATYPE']._serialized_end=22892 - _globals['_RESERVEDOMAINACTION']._serialized_start=22894 - _globals['_RESERVEDOMAINACTION']._serialized_end=22968 - _globals['_USERLOCKSTATUS']._serialized_start=22970 - _globals['_USERLOCKSTATUS']._serialized_end=23085 + _globals['_KEYTYPE']._serialized_start=20649 + _globals['_KEYTYPE']._serialized_end=20676 + _globals['_ROLEUSERMODIFYSTATUS']._serialized_start=20679 + _globals['_ROLEUSERMODIFYSTATUS']._serialized_end=20961 + _globals['_ENTERPRISETYPE']._serialized_start=20963 + _globals['_ENTERPRISETYPE']._serialized_end=21024 + _globals['_TRANSFERACCEPTANCESTATUS']._serialized_start=21026 + _globals['_TRANSFERACCEPTANCESTATUS']._serialized_end=21141 + _globals['_ENTERPRISEDATAENTITY']._serialized_start=21144 + _globals['_ENTERPRISEDATAENTITY']._serialized_end=21625 + _globals['_CACHESTATUS']._serialized_start=21627 + _globals['_CACHESTATUS']._serialized_end=21661 + _globals['_BACKUPKEYTYPE']._serialized_start=21664 + _globals['_BACKUPKEYTYPE']._serialized_end=21811 + _globals['_BACKUPUSERDATAKEYTYPE']._serialized_start=21813 + _globals['_BACKUPUSERDATAKEYTYPE']._serialized_end=21871 + _globals['_ENCRYPTEDKEYTYPE']._serialized_start=21874 + _globals['_ENCRYPTEDKEYTYPE']._serialized_end=22039 + _globals['_ENTERPRISEFLAGTYPE']._serialized_start=22042 + _globals['_ENTERPRISEFLAGTYPE']._serialized_end=22353 + _globals['_USERUPDATESTATUS']._serialized_start=22355 + _globals['_USERUPDATESTATUS']._serialized_end=22424 + _globals['_AUDITUSERSTATUS']._serialized_start=22426 + _globals['_AUDITUSERSTATUS']._serialized_end=22499 + _globals['_TEAMUSERTYPE']._serialized_start=22501 + _globals['_TEAMUSERTYPE']._serialized_end=22552 + _globals['_APPCLIENTTYPE']._serialized_start=22554 + _globals['_APPCLIENTTYPE']._serialized_end=22674 + _globals['_DELETEENTERPRISEUSERSRESULT']._serialized_start=22677 + _globals['_DELETEENTERPRISEUSERSRESULT']._serialized_end=22820 + _globals['_CLEARSECURITYDATATYPE']._serialized_start=22823 + _globals['_CLEARSECURITYDATATYPE']._serialized_end=22958 + _globals['_RESERVEDOMAINACTION']._serialized_start=22960 + _globals['_RESERVEDOMAINACTION']._serialized_end=23034 + _globals['_USERLOCKSTATUS']._serialized_start=23036 + _globals['_USERLOCKSTATUS']._serialized_end=23151 + _globals['_EXTERNALCLOUDSECRETSSTORETYPE']._serialized_start=23154 + _globals['_EXTERNALCLOUDSECRETSSTORETYPE']._serialized_end=23282 _globals['_ENTERPRISEKEYPAIRREQUEST']._serialized_start=33 _globals['_ENTERPRISEKEYPAIRREQUEST']._serialized_end=165 _globals['_GETTEAMMEMBERREQUEST']._serialized_start=167 @@ -120,281 +133,281 @@ _globals['_LOGINTOMCREQUEST']._serialized_start=2873 _globals['_LOGINTOMCREQUEST']._serialized_end=2942 _globals['_LOGINTOMCRESPONSE']._serialized_start=2944 - _globals['_LOGINTOMCRESPONSE']._serialized_end=3044 - _globals['_DOMAINPASSWORDRULESRESPONSE']._serialized_start=3046 - _globals['_DOMAINPASSWORDRULESRESPONSE']._serialized_end=3149 - _globals['_APPROVEUSERDEVICEREQUEST']._serialized_start=3152 - _globals['_APPROVEUSERDEVICEREQUEST']._serialized_end=3288 - _globals['_APPROVEUSERDEVICERESPONSE']._serialized_start=3290 - _globals['_APPROVEUSERDEVICERESPONSE']._serialized_end=3406 - _globals['_APPROVEUSERDEVICESREQUEST']._serialized_start=3408 - _globals['_APPROVEUSERDEVICESREQUEST']._serialized_end=3497 - _globals['_APPROVEUSERDEVICESRESPONSE']._serialized_start=3499 - _globals['_APPROVEUSERDEVICESRESPONSE']._serialized_end=3591 - _globals['_ENTERPRISEUSERDATAKEY']._serialized_start=3594 - _globals['_ENTERPRISEUSERDATAKEY']._serialized_end=3729 - _globals['_ENTERPRISEUSERDATAKEYS']._serialized_start=3731 - _globals['_ENTERPRISEUSERDATAKEYS']._serialized_end=3804 - _globals['_ENTERPRISEUSERDATAKEYLIGHT']._serialized_start=3806 - _globals['_ENTERPRISEUSERDATAKEYLIGHT']._serialized_end=3909 - _globals['_ENTERPRISEUSERDATAKEYSBYNODE']._serialized_start=3911 - _globals['_ENTERPRISEUSERDATAKEYSBYNODE']._serialized_end=4011 - _globals['_ENTERPRISEUSERDATAKEYSBYNODERESPONSE']._serialized_start=4013 - _globals['_ENTERPRISEUSERDATAKEYSBYNODERESPONSE']._serialized_end=4107 - _globals['_ENTERPRISEDATAREQUEST']._serialized_start=4109 - _globals['_ENTERPRISEDATAREQUEST']._serialized_end=4159 - _globals['_SPECIALPROVISIONING']._serialized_start=4161 - _globals['_SPECIALPROVISIONING']._serialized_end=4209 - _globals['_GENERALDATAENTITY']._serialized_start=4212 - _globals['_GENERALDATAENTITY']._serialized_end=4472 - _globals['_NODE']._serialized_start=4475 - _globals['_NODE']._serialized_end=4728 - _globals['_ROLE']._serialized_start=4731 - _globals['_ROLE']._serialized_end=4873 - _globals['_USER']._serialized_start=4876 - _globals['_USER']._serialized_end=5188 - _globals['_USERALIAS']._serialized_start=5190 - _globals['_USERALIAS']._serialized_end=5245 - _globals['_COMPLIANCEREPORTMETADATA']._serialized_start=5248 - _globals['_COMPLIANCEREPORTMETADATA']._serialized_end=5420 - _globals['_MANAGEDNODE']._serialized_start=5422 - _globals['_MANAGEDNODE']._serialized_end=5505 - _globals['_USERMANAGEDNODE']._serialized_start=5507 - _globals['_USERMANAGEDNODE']._serialized_end=5591 - _globals['_USERPRIVILEGE']._serialized_start=5593 - _globals['_USERPRIVILEGE']._serialized_end=5712 - _globals['_ROLEUSER']._serialized_start=5714 - _globals['_ROLEUSER']._serialized_end=5766 - _globals['_ROLEPRIVILEGE']._serialized_start=5768 - _globals['_ROLEPRIVILEGE']._serialized_end=5845 - _globals['_PRIVILEGESBYMANAGEDNODE']._serialized_start=5847 - _globals['_PRIVILEGESBYMANAGEDNODE']._serialized_end=5931 - _globals['_ROLEENFORCEMENT']._serialized_start=5933 - _globals['_ROLEENFORCEMENT']._serialized_end=6006 - _globals['_TEAM']._serialized_start=6009 - _globals['_TEAM']._serialized_end=6178 - _globals['_TEAMUSER']._serialized_start=6180 - _globals['_TEAMUSER']._serialized_end=6251 - _globals['_GETDISTRIBUTORINFORESPONSE']._serialized_start=6253 - _globals['_GETDISTRIBUTORINFORESPONSE']._serialized_end=6328 - _globals['_DISTRIBUTOR']._serialized_start=6330 - _globals['_DISTRIBUTOR']._serialized_end=6396 - _globals['_MSPINFO']._serialized_start=6399 - _globals['_MSPINFO']._serialized_end=6684 - _globals['_MANAGEDCOMPANY']._serialized_start=6687 - _globals['_MANAGEDCOMPANY']._serialized_end=6960 - _globals['_MSPPOOL']._serialized_start=6962 - _globals['_MSPPOOL']._serialized_end=7044 - _globals['_MSPCONTACT']._serialized_start=7046 - _globals['_MSPCONTACT']._serialized_end=7104 - _globals['_LICENSEADDON']._serialized_start=7107 - _globals['_LICENSEADDON']._serialized_end=7343 - _globals['_MCDEFAULT']._serialized_start=7345 - _globals['_MCDEFAULT']._serialized_end=7460 - _globals['_MSPPERMITS']._serialized_start=7463 - _globals['_MSPPERMITS']._serialized_end=7673 - _globals['_LICENSE']._serialized_start=7676 - _globals['_LICENSE']._serialized_end=8220 - _globals['_BRIDGE']._serialized_start=8222 - _globals['_BRIDGE']._serialized_end=8332 - _globals['_SCIM']._serialized_start=8334 - _globals['_SCIM']._serialized_end=8450 - _globals['_EMAILPROVISION']._serialized_start=8452 - _globals['_EMAILPROVISION']._serialized_end=8528 - _globals['_QUEUEDTEAM']._serialized_start=8530 - _globals['_QUEUEDTEAM']._serialized_end=8612 - _globals['_QUEUEDTEAMUSER']._serialized_start=8614 - _globals['_QUEUEDTEAMUSER']._serialized_end=8662 - _globals['_TEAMSADDRESULT']._serialized_start=8665 - _globals['_TEAMSADDRESULT']._serialized_end=8829 - _globals['_TEAMADDRESULT']._serialized_start=8831 - _globals['_TEAMADDRESULT']._serialized_end=8916 - _globals['_SSOSERVICE']._serialized_start=8919 - _globals['_SSOSERVICE']._serialized_end=9064 - _globals['_REPORTFILTERUSER']._serialized_start=9066 - _globals['_REPORTFILTERUSER']._serialized_end=9115 - _globals['_DEVICEREQUESTFORADMINAPPROVAL']._serialized_start=9118 - _globals['_DEVICEREQUESTFORADMINAPPROVAL']._serialized_end=9397 - _globals['_ENTERPRISEDATA']._serialized_start=9399 - _globals['_ENTERPRISEDATA']._serialized_end=9495 - _globals['_ENTERPRISEDATARESPONSE']._serialized_start=9498 - _globals['_ENTERPRISEDATARESPONSE']._serialized_end=9706 - _globals['_BACKUPREQUEST']._serialized_start=9708 - _globals['_BACKUPREQUEST']._serialized_end=9750 - _globals['_BACKUPRECORD']._serialized_start=9753 - _globals['_BACKUPRECORD']._serialized_end=9905 - _globals['_BACKUPKEY']._serialized_start=9907 - _globals['_BACKUPKEY']._serialized_end=9953 - _globals['_BACKUPUSER']._serialized_start=9956 - _globals['_BACKUPUSER']._serialized_end=10225 - _globals['_BACKUPRESPONSE']._serialized_start=10228 - _globals['_BACKUPRESPONSE']._serialized_end=10386 - _globals['_BACKUPFILE']._serialized_start=10388 - _globals['_BACKUPFILE']._serialized_end=10489 - _globals['_BACKUPSRESPONSE']._serialized_start=10491 - _globals['_BACKUPSRESPONSE']._serialized_end=10547 - _globals['_GETENTERPRISEDATAKEYSREQUEST']._serialized_start=10549 - _globals['_GETENTERPRISEDATAKEYSREQUEST']._serialized_end=10595 - _globals['_GETENTERPRISEDATAKEYSRESPONSE']._serialized_start=10598 - _globals['_GETENTERPRISEDATAKEYSRESPONSE']._serialized_end=10853 - _globals['_ROLEKEY']._serialized_start=10855 - _globals['_ROLEKEY']._serialized_end=10949 - _globals['_MSPKEY']._serialized_start=10951 - _globals['_MSPKEY']._serialized_end=11051 - _globals['_ENTERPRISEKEYS']._serialized_start=11053 - _globals['_ENTERPRISEKEYS']._serialized_end=11177 - _globals['_TREEKEY']._serialized_start=11179 - _globals['_TREEKEY']._serialized_end=11251 - _globals['_SHAREDRECORDRESPONSE']._serialized_start=11253 - _globals['_SHAREDRECORDRESPONSE']._serialized_end=11322 - _globals['_SHAREDRECORDEVENT']._serialized_start=11324 - _globals['_SHAREDRECORDEVENT']._serialized_end=11436 - _globals['_SETRESTRICTVISIBILITYREQUEST']._serialized_start=11438 - _globals['_SETRESTRICTVISIBILITYREQUEST']._serialized_end=11484 - _globals['_USERADDREQUEST']._serialized_start=11487 - _globals['_USERADDREQUEST']._serialized_end=11695 - _globals['_USERUPDATEREQUEST']._serialized_start=11697 - _globals['_USERUPDATEREQUEST']._serialized_end=11755 - _globals['_USERUPDATE']._serialized_start=11758 - _globals['_USERUPDATE']._serialized_end=11933 - _globals['_USERUPDATERESPONSE']._serialized_start=11935 - _globals['_USERUPDATERESPONSE']._serialized_end=12000 - _globals['_USERUPDATERESULT']._serialized_start=12002 - _globals['_USERUPDATERESULT']._serialized_end=12092 - _globals['_COMPLIANCERECORDOWNERSREQUEST']._serialized_start=12094 - _globals['_COMPLIANCERECORDOWNERSREQUEST']._serialized_end=12168 - _globals['_COMPLIANCERECORDOWNERSRESPONSE']._serialized_start=12170 - _globals['_COMPLIANCERECORDOWNERSRESPONSE']._serialized_end=12249 - _globals['_RECORDOWNER']._serialized_start=12251 - _globals['_RECORDOWNER']._serialized_end=12306 - _globals['_PRELIMINARYCOMPLIANCEDATAREQUEST']._serialized_start=12309 - _globals['_PRELIMINARYCOMPLIANCEDATAREQUEST']._serialized_end=12475 - _globals['_PRELIMINARYCOMPLIANCEDATARESPONSE']._serialized_start=12478 - _globals['_PRELIMINARYCOMPLIANCEDATARESPONSE']._serialized_end=12637 - _globals['_AUDITUSERRECORD']._serialized_start=12639 - _globals['_AUDITUSERRECORD']._serialized_end=12714 - _globals['_AUDITUSERDATA']._serialized_start=12717 - _globals['_AUDITUSERDATA']._serialized_end=12858 - _globals['_COMPLIANCEREPORTFILTERS']._serialized_start=12860 - _globals['_COMPLIANCEREPORTFILTERS']._serialized_end=12987 - _globals['_COMPLIANCEREPORTREQUEST']._serialized_start=12989 - _globals['_COMPLIANCEREPORTREQUEST']._serialized_end=13116 - _globals['_COMPLIANCEREPORTRUN']._serialized_start=13119 - _globals['_COMPLIANCEREPORTRUN']._serialized_end=13252 - _globals['_COMPLIANCEREPORTCRITERIAANDFILTER']._serialized_start=13255 - _globals['_COMPLIANCEREPORTCRITERIAANDFILTER']._serialized_end=13507 - _globals['_COMPLIANCEREPORTCRITERIA']._serialized_start=13509 - _globals['_COMPLIANCEREPORTCRITERIA']._serialized_end=13607 - _globals['_COMPLIANCEREPORTFILTER']._serialized_start=13609 - _globals['_COMPLIANCEREPORTFILTER']._serialized_end=13729 - _globals['_COMPLIANCEREPORTRESPONSE']._serialized_start=13732 - _globals['_COMPLIANCEREPORTRESPONSE']._serialized_end=14405 - _globals['_AUDITRECORD']._serialized_start=14408 - _globals['_AUDITRECORD']._serialized_end=14537 - _globals['_AUDITROLE']._serialized_start=14540 - _globals['_AUDITROLE']._serialized_end=14796 - _globals['_ROLENODEMANAGEMENT']._serialized_start=14798 - _globals['_ROLENODEMANAGEMENT']._serialized_end=14892 - _globals['_USERPROFILE']._serialized_start=14894 - _globals['_USERPROFILE']._serialized_end=15001 - _globals['_RECORDPERMISSION']._serialized_start=15003 - _globals['_RECORDPERMISSION']._serialized_end=15064 - _globals['_USERRECORD']._serialized_start=15066 - _globals['_USERRECORD']._serialized_end=15161 - _globals['_AUDITTEAM']._serialized_start=15163 - _globals['_AUDITTEAM']._serialized_end=15254 - _globals['_AUDITTEAMUSER']._serialized_start=15256 - _globals['_AUDITTEAMUSER']._serialized_end=15315 - _globals['_SHAREDFOLDERRECORD']._serialized_start=15318 - _globals['_SHAREDFOLDERRECORD']._serialized_end=15477 - _globals['_SHAREADMINRECORD']._serialized_start=15479 - _globals['_SHAREADMINRECORD']._serialized_end=15556 - _globals['_SHAREDFOLDERUSER']._serialized_start=15558 - _globals['_SHAREDFOLDERUSER']._serialized_end=15628 - _globals['_SHAREDFOLDERTEAM']._serialized_start=15630 - _globals['_SHAREDFOLDERTEAM']._serialized_end=15691 - _globals['_GETCOMPLIANCEREPORTREQUEST']._serialized_start=15693 - _globals['_GETCOMPLIANCEREPORTREQUEST']._serialized_end=15740 - _globals['_GETCOMPLIANCEREPORTRESPONSE']._serialized_start=15742 - _globals['_GETCOMPLIANCEREPORTRESPONSE']._serialized_end=15792 - _globals['_COMPLIANCEREPORTCRITERIAREQUEST']._serialized_start=15794 - _globals['_COMPLIANCEREPORTCRITERIAREQUEST']._serialized_end=15848 - _globals['_SAVECOMPLIANCEREPORTCRITERIARESPONSE']._serialized_start=15850 - _globals['_SAVECOMPLIANCEREPORTCRITERIARESPONSE']._serialized_end=15909 - _globals['_LINKEDRECORD']._serialized_start=15911 - _globals['_LINKEDRECORD']._serialized_end=15963 - _globals['_GETSHARINGADMINSREQUEST']._serialized_start=15965 - _globals['_GETSHARINGADMINSREQUEST']._serialized_end=16052 - _globals['_USERPROFILEEXT']._serialized_start=16055 - _globals['_USERPROFILEEXT']._serialized_end=16279 - _globals['_GETSHARINGADMINSRESPONSE']._serialized_start=16281 - _globals['_GETSHARINGADMINSRESPONSE']._serialized_end=16360 - _globals['_TEAMSENTERPRISEUSERSADDREQUEST']._serialized_start=16362 - _globals['_TEAMSENTERPRISEUSERSADDREQUEST']._serialized_end=16457 - _globals['_TEAMSENTERPRISEUSERSADDTEAMREQUEST']._serialized_start=16459 - _globals['_TEAMSENTERPRISEUSERSADDTEAMREQUEST']._serialized_end=16575 - _globals['_TEAMSENTERPRISEUSERSADDUSERREQUEST']._serialized_start=16578 - _globals['_TEAMSENTERPRISEUSERSADDUSERREQUEST']._serialized_end=16749 - _globals['_TYPEDKEY']._serialized_start=16751 - _globals['_TYPEDKEY']._serialized_end=16821 - _globals['_TEAMSENTERPRISEUSERSADDRESPONSE']._serialized_start=16823 - _globals['_TEAMSENTERPRISEUSERSADDRESPONSE']._serialized_end=16938 - _globals['_TEAMSENTERPRISEUSERSADDTEAMRESPONSE']._serialized_start=16941 - _globals['_TEAMSENTERPRISEUSERSADDTEAMRESPONSE']._serialized_end=17137 - _globals['_TEAMSENTERPRISEUSERSADDUSERRESPONSE']._serialized_start=17140 - _globals['_TEAMSENTERPRISEUSERSADDUSERRESPONSE']._serialized_end=17299 - _globals['_TEAMENTERPRISEUSERREMOVE']._serialized_start=17301 - _globals['_TEAMENTERPRISEUSERREMOVE']._serialized_end=17370 - _globals['_TEAMENTERPRISEUSERREMOVESREQUEST']._serialized_start=17372 - _globals['_TEAMENTERPRISEUSERREMOVESREQUEST']._serialized_end=17478 - _globals['_TEAMENTERPRISEUSERREMOVESRESPONSE']._serialized_start=17480 - _globals['_TEAMENTERPRISEUSERREMOVESRESPONSE']._serialized_end=17603 - _globals['_TEAMENTERPRISEUSERREMOVERESPONSE']._serialized_start=17606 - _globals['_TEAMENTERPRISEUSERREMOVERESPONSE']._serialized_end=17790 - _globals['_DOMAINALIAS']._serialized_start=17792 - _globals['_DOMAINALIAS']._serialized_end=17869 - _globals['_DOMAINALIASREQUEST']._serialized_start=17871 - _globals['_DOMAINALIASREQUEST']._serialized_end=17937 - _globals['_DOMAINALIASRESPONSE']._serialized_start=17939 - _globals['_DOMAINALIASRESPONSE']._serialized_end=18006 - _globals['_ENTERPRISEUSERSPROVISIONREQUEST']._serialized_start=18008 - _globals['_ENTERPRISEUSERSPROVISIONREQUEST']._serialized_end=18117 - _globals['_ENTERPRISEUSERSPROVISION']._serialized_start=18120 - _globals['_ENTERPRISEUSERSPROVISION']._serialized_end=18558 - _globals['_ENTERPRISEUSERSPROVISIONRESPONSE']._serialized_start=18560 - _globals['_ENTERPRISEUSERSPROVISIONRESPONSE']._serialized_end=18655 - _globals['_ENTERPRISEUSERSPROVISIONRESULT']._serialized_start=18657 - _globals['_ENTERPRISEUSERSPROVISIONRESULT']._serialized_end=18770 - _globals['_ENTERPRISEUSERSADDREQUEST']._serialized_start=18772 - _globals['_ENTERPRISEUSERSADDREQUEST']._serialized_end=18869 - _globals['_ENTERPRISEUSERSADD']._serialized_start=18872 - _globals['_ENTERPRISEUSERSADD']._serialized_end=19140 - _globals['_ENTERPRISEUSERSADDRESPONSE']._serialized_start=19143 - _globals['_ENTERPRISEUSERSADDRESPONSE']._serialized_end=19298 - _globals['_ENTERPRISEUSERSADDRESULT']._serialized_start=19301 - _globals['_ENTERPRISEUSERSADDRESULT']._serialized_end=19451 - _globals['_UPDATEMSPPERMITSREQUEST']._serialized_start=19454 - _globals['_UPDATEMSPPERMITSREQUEST']._serialized_end=19639 - _globals['_DELETEENTERPRISEUSERSREQUEST']._serialized_start=19641 - _globals['_DELETEENTERPRISEUSERSREQUEST']._serialized_end=19698 - _globals['_DELETEENTERPRISEUSERSTATUS']._serialized_start=19700 - _globals['_DELETEENTERPRISEUSERSTATUS']._serialized_end=19811 - _globals['_DELETEENTERPRISEUSERSRESPONSE']._serialized_start=19813 - _globals['_DELETEENTERPRISEUSERSRESPONSE']._serialized_end=19906 - _globals['_CLEARSECURITYDATAREQUEST']._serialized_start=19908 - _globals['_CLEARSECURITYDATAREQUEST']._serialized_end=20027 - _globals['_LISTDOMAINSRESPONSE']._serialized_start=20029 - _globals['_LISTDOMAINSRESPONSE']._serialized_end=20066 - _globals['_RESERVEDOMAINREQUEST']._serialized_start=20068 - _globals['_RESERVEDOMAINREQUEST']._serialized_end=20168 - _globals['_RESERVEDOMAINRESPONSE']._serialized_start=20170 - _globals['_RESERVEDOMAINRESPONSE']._serialized_end=20208 - _globals['_ROLESBYTEAM']._serialized_start=20210 - _globals['_ROLESBYTEAM']._serialized_end=20256 - _globals['_LOCKUSERSREQUEST']._serialized_start=20259 - _globals['_LOCKUSERSREQUEST']._serialized_end=20400 - _globals['_LOCKUSERSRESPONSE']._serialized_start=20402 - _globals['_LOCKUSERSRESPONSE']._serialized_end=20469 - _globals['_LOCKUSERRESPONSE']._serialized_start=20471 - _globals['_LOCKUSERRESPONSE']._serialized_end=20581 + _globals['_LOGINTOMCRESPONSE']._serialized_end=3063 + _globals['_DOMAINPASSWORDRULESRESPONSE']._serialized_start=3065 + _globals['_DOMAINPASSWORDRULESRESPONSE']._serialized_end=3168 + _globals['_APPROVEUSERDEVICEREQUEST']._serialized_start=3171 + _globals['_APPROVEUSERDEVICEREQUEST']._serialized_end=3307 + _globals['_APPROVEUSERDEVICERESPONSE']._serialized_start=3309 + _globals['_APPROVEUSERDEVICERESPONSE']._serialized_end=3425 + _globals['_APPROVEUSERDEVICESREQUEST']._serialized_start=3427 + _globals['_APPROVEUSERDEVICESREQUEST']._serialized_end=3516 + _globals['_APPROVEUSERDEVICESRESPONSE']._serialized_start=3518 + _globals['_APPROVEUSERDEVICESRESPONSE']._serialized_end=3610 + _globals['_ENTERPRISEUSERDATAKEY']._serialized_start=3613 + _globals['_ENTERPRISEUSERDATAKEY']._serialized_end=3748 + _globals['_ENTERPRISEUSERDATAKEYS']._serialized_start=3750 + _globals['_ENTERPRISEUSERDATAKEYS']._serialized_end=3823 + _globals['_ENTERPRISEUSERDATAKEYLIGHT']._serialized_start=3825 + _globals['_ENTERPRISEUSERDATAKEYLIGHT']._serialized_end=3928 + _globals['_ENTERPRISEUSERDATAKEYSBYNODE']._serialized_start=3930 + _globals['_ENTERPRISEUSERDATAKEYSBYNODE']._serialized_end=4030 + _globals['_ENTERPRISEUSERDATAKEYSBYNODERESPONSE']._serialized_start=4032 + _globals['_ENTERPRISEUSERDATAKEYSBYNODERESPONSE']._serialized_end=4126 + _globals['_ENTERPRISEDATAREQUEST']._serialized_start=4128 + _globals['_ENTERPRISEDATAREQUEST']._serialized_end=4178 + _globals['_SPECIALPROVISIONING']._serialized_start=4180 + _globals['_SPECIALPROVISIONING']._serialized_end=4228 + _globals['_GENERALDATAENTITY']._serialized_start=4231 + _globals['_GENERALDATAENTITY']._serialized_end=4491 + _globals['_NODE']._serialized_start=4494 + _globals['_NODE']._serialized_end=4747 + _globals['_ROLE']._serialized_start=4750 + _globals['_ROLE']._serialized_end=4892 + _globals['_USER']._serialized_start=4895 + _globals['_USER']._serialized_end=5207 + _globals['_USERALIAS']._serialized_start=5209 + _globals['_USERALIAS']._serialized_end=5264 + _globals['_COMPLIANCEREPORTMETADATA']._serialized_start=5267 + _globals['_COMPLIANCEREPORTMETADATA']._serialized_end=5439 + _globals['_MANAGEDNODE']._serialized_start=5441 + _globals['_MANAGEDNODE']._serialized_end=5524 + _globals['_USERMANAGEDNODE']._serialized_start=5526 + _globals['_USERMANAGEDNODE']._serialized_end=5610 + _globals['_USERPRIVILEGE']._serialized_start=5612 + _globals['_USERPRIVILEGE']._serialized_end=5731 + _globals['_ROLEUSER']._serialized_start=5733 + _globals['_ROLEUSER']._serialized_end=5785 + _globals['_ROLEPRIVILEGE']._serialized_start=5787 + _globals['_ROLEPRIVILEGE']._serialized_end=5864 + _globals['_PRIVILEGESBYMANAGEDNODE']._serialized_start=5866 + _globals['_PRIVILEGESBYMANAGEDNODE']._serialized_end=5950 + _globals['_ROLEENFORCEMENT']._serialized_start=5952 + _globals['_ROLEENFORCEMENT']._serialized_end=6025 + _globals['_TEAM']._serialized_start=6028 + _globals['_TEAM']._serialized_end=6197 + _globals['_TEAMUSER']._serialized_start=6199 + _globals['_TEAMUSER']._serialized_end=6270 + _globals['_GETDISTRIBUTORINFORESPONSE']._serialized_start=6272 + _globals['_GETDISTRIBUTORINFORESPONSE']._serialized_end=6347 + _globals['_DISTRIBUTOR']._serialized_start=6349 + _globals['_DISTRIBUTOR']._serialized_end=6415 + _globals['_MSPINFO']._serialized_start=6418 + _globals['_MSPINFO']._serialized_end=6703 + _globals['_MANAGEDCOMPANY']._serialized_start=6706 + _globals['_MANAGEDCOMPANY']._serialized_end=7002 + _globals['_MSPPOOL']._serialized_start=7004 + _globals['_MSPPOOL']._serialized_end=7086 + _globals['_MSPCONTACT']._serialized_start=7088 + _globals['_MSPCONTACT']._serialized_end=7146 + _globals['_LICENSEADDON']._serialized_start=7149 + _globals['_LICENSEADDON']._serialized_end=7409 + _globals['_MCDEFAULT']._serialized_start=7411 + _globals['_MCDEFAULT']._serialized_end=7526 + _globals['_MSPPERMITS']._serialized_start=7529 + _globals['_MSPPERMITS']._serialized_end=7739 + _globals['_LICENSE']._serialized_start=7742 + _globals['_LICENSE']._serialized_end=8286 + _globals['_BRIDGE']._serialized_start=8288 + _globals['_BRIDGE']._serialized_end=8398 + _globals['_SCIM']._serialized_start=8400 + _globals['_SCIM']._serialized_end=8516 + _globals['_EMAILPROVISION']._serialized_start=8518 + _globals['_EMAILPROVISION']._serialized_end=8594 + _globals['_QUEUEDTEAM']._serialized_start=8596 + _globals['_QUEUEDTEAM']._serialized_end=8678 + _globals['_QUEUEDTEAMUSER']._serialized_start=8680 + _globals['_QUEUEDTEAMUSER']._serialized_end=8728 + _globals['_TEAMSADDRESULT']._serialized_start=8731 + _globals['_TEAMSADDRESULT']._serialized_end=8895 + _globals['_TEAMADDRESULT']._serialized_start=8897 + _globals['_TEAMADDRESULT']._serialized_end=8982 + _globals['_SSOSERVICE']._serialized_start=8985 + _globals['_SSOSERVICE']._serialized_end=9130 + _globals['_REPORTFILTERUSER']._serialized_start=9132 + _globals['_REPORTFILTERUSER']._serialized_end=9181 + _globals['_DEVICEREQUESTFORADMINAPPROVAL']._serialized_start=9184 + _globals['_DEVICEREQUESTFORADMINAPPROVAL']._serialized_end=9463 + _globals['_ENTERPRISEDATA']._serialized_start=9465 + _globals['_ENTERPRISEDATA']._serialized_end=9561 + _globals['_ENTERPRISEDATARESPONSE']._serialized_start=9564 + _globals['_ENTERPRISEDATARESPONSE']._serialized_end=9772 + _globals['_BACKUPREQUEST']._serialized_start=9774 + _globals['_BACKUPREQUEST']._serialized_end=9816 + _globals['_BACKUPRECORD']._serialized_start=9819 + _globals['_BACKUPRECORD']._serialized_end=9971 + _globals['_BACKUPKEY']._serialized_start=9973 + _globals['_BACKUPKEY']._serialized_end=10019 + _globals['_BACKUPUSER']._serialized_start=10022 + _globals['_BACKUPUSER']._serialized_end=10291 + _globals['_BACKUPRESPONSE']._serialized_start=10294 + _globals['_BACKUPRESPONSE']._serialized_end=10452 + _globals['_BACKUPFILE']._serialized_start=10454 + _globals['_BACKUPFILE']._serialized_end=10555 + _globals['_BACKUPSRESPONSE']._serialized_start=10557 + _globals['_BACKUPSRESPONSE']._serialized_end=10613 + _globals['_GETENTERPRISEDATAKEYSREQUEST']._serialized_start=10615 + _globals['_GETENTERPRISEDATAKEYSREQUEST']._serialized_end=10661 + _globals['_GETENTERPRISEDATAKEYSRESPONSE']._serialized_start=10664 + _globals['_GETENTERPRISEDATAKEYSRESPONSE']._serialized_end=10919 + _globals['_ROLEKEY']._serialized_start=10921 + _globals['_ROLEKEY']._serialized_end=11015 + _globals['_MSPKEY']._serialized_start=11017 + _globals['_MSPKEY']._serialized_end=11117 + _globals['_ENTERPRISEKEYS']._serialized_start=11119 + _globals['_ENTERPRISEKEYS']._serialized_end=11243 + _globals['_TREEKEY']._serialized_start=11245 + _globals['_TREEKEY']._serialized_end=11317 + _globals['_SHAREDRECORDRESPONSE']._serialized_start=11319 + _globals['_SHAREDRECORDRESPONSE']._serialized_end=11388 + _globals['_SHAREDRECORDEVENT']._serialized_start=11390 + _globals['_SHAREDRECORDEVENT']._serialized_end=11502 + _globals['_SETRESTRICTVISIBILITYREQUEST']._serialized_start=11504 + _globals['_SETRESTRICTVISIBILITYREQUEST']._serialized_end=11550 + _globals['_USERADDREQUEST']._serialized_start=11553 + _globals['_USERADDREQUEST']._serialized_end=11761 + _globals['_USERUPDATEREQUEST']._serialized_start=11763 + _globals['_USERUPDATEREQUEST']._serialized_end=11821 + _globals['_USERUPDATE']._serialized_start=11824 + _globals['_USERUPDATE']._serialized_end=11999 + _globals['_USERUPDATERESPONSE']._serialized_start=12001 + _globals['_USERUPDATERESPONSE']._serialized_end=12066 + _globals['_USERUPDATERESULT']._serialized_start=12068 + _globals['_USERUPDATERESULT']._serialized_end=12158 + _globals['_COMPLIANCERECORDOWNERSREQUEST']._serialized_start=12160 + _globals['_COMPLIANCERECORDOWNERSREQUEST']._serialized_end=12234 + _globals['_COMPLIANCERECORDOWNERSRESPONSE']._serialized_start=12236 + _globals['_COMPLIANCERECORDOWNERSRESPONSE']._serialized_end=12315 + _globals['_RECORDOWNER']._serialized_start=12317 + _globals['_RECORDOWNER']._serialized_end=12372 + _globals['_PRELIMINARYCOMPLIANCEDATAREQUEST']._serialized_start=12375 + _globals['_PRELIMINARYCOMPLIANCEDATAREQUEST']._serialized_end=12541 + _globals['_PRELIMINARYCOMPLIANCEDATARESPONSE']._serialized_start=12544 + _globals['_PRELIMINARYCOMPLIANCEDATARESPONSE']._serialized_end=12703 + _globals['_AUDITUSERRECORD']._serialized_start=12705 + _globals['_AUDITUSERRECORD']._serialized_end=12780 + _globals['_AUDITUSERDATA']._serialized_start=12783 + _globals['_AUDITUSERDATA']._serialized_end=12924 + _globals['_COMPLIANCEREPORTFILTERS']._serialized_start=12926 + _globals['_COMPLIANCEREPORTFILTERS']._serialized_end=13053 + _globals['_COMPLIANCEREPORTREQUEST']._serialized_start=13055 + _globals['_COMPLIANCEREPORTREQUEST']._serialized_end=13182 + _globals['_COMPLIANCEREPORTRUN']._serialized_start=13185 + _globals['_COMPLIANCEREPORTRUN']._serialized_end=13318 + _globals['_COMPLIANCEREPORTCRITERIAANDFILTER']._serialized_start=13321 + _globals['_COMPLIANCEREPORTCRITERIAANDFILTER']._serialized_end=13573 + _globals['_COMPLIANCEREPORTCRITERIA']._serialized_start=13575 + _globals['_COMPLIANCEREPORTCRITERIA']._serialized_end=13673 + _globals['_COMPLIANCEREPORTFILTER']._serialized_start=13675 + _globals['_COMPLIANCEREPORTFILTER']._serialized_end=13795 + _globals['_COMPLIANCEREPORTRESPONSE']._serialized_start=13798 + _globals['_COMPLIANCEREPORTRESPONSE']._serialized_end=14471 + _globals['_AUDITRECORD']._serialized_start=14474 + _globals['_AUDITRECORD']._serialized_end=14603 + _globals['_AUDITROLE']._serialized_start=14606 + _globals['_AUDITROLE']._serialized_end=14862 + _globals['_ROLENODEMANAGEMENT']._serialized_start=14864 + _globals['_ROLENODEMANAGEMENT']._serialized_end=14958 + _globals['_USERPROFILE']._serialized_start=14960 + _globals['_USERPROFILE']._serialized_end=15067 + _globals['_RECORDPERMISSION']._serialized_start=15069 + _globals['_RECORDPERMISSION']._serialized_end=15130 + _globals['_USERRECORD']._serialized_start=15132 + _globals['_USERRECORD']._serialized_end=15227 + _globals['_AUDITTEAM']._serialized_start=15229 + _globals['_AUDITTEAM']._serialized_end=15320 + _globals['_AUDITTEAMUSER']._serialized_start=15322 + _globals['_AUDITTEAMUSER']._serialized_end=15381 + _globals['_SHAREDFOLDERRECORD']._serialized_start=15384 + _globals['_SHAREDFOLDERRECORD']._serialized_end=15543 + _globals['_SHAREADMINRECORD']._serialized_start=15545 + _globals['_SHAREADMINRECORD']._serialized_end=15622 + _globals['_SHAREDFOLDERUSER']._serialized_start=15624 + _globals['_SHAREDFOLDERUSER']._serialized_end=15694 + _globals['_SHAREDFOLDERTEAM']._serialized_start=15696 + _globals['_SHAREDFOLDERTEAM']._serialized_end=15757 + _globals['_GETCOMPLIANCEREPORTREQUEST']._serialized_start=15759 + _globals['_GETCOMPLIANCEREPORTREQUEST']._serialized_end=15806 + _globals['_GETCOMPLIANCEREPORTRESPONSE']._serialized_start=15808 + _globals['_GETCOMPLIANCEREPORTRESPONSE']._serialized_end=15858 + _globals['_COMPLIANCEREPORTCRITERIAREQUEST']._serialized_start=15860 + _globals['_COMPLIANCEREPORTCRITERIAREQUEST']._serialized_end=15914 + _globals['_SAVECOMPLIANCEREPORTCRITERIARESPONSE']._serialized_start=15916 + _globals['_SAVECOMPLIANCEREPORTCRITERIARESPONSE']._serialized_end=15975 + _globals['_LINKEDRECORD']._serialized_start=15977 + _globals['_LINKEDRECORD']._serialized_end=16029 + _globals['_GETSHARINGADMINSREQUEST']._serialized_start=16031 + _globals['_GETSHARINGADMINSREQUEST']._serialized_end=16118 + _globals['_USERPROFILEEXT']._serialized_start=16121 + _globals['_USERPROFILEEXT']._serialized_end=16345 + _globals['_GETSHARINGADMINSRESPONSE']._serialized_start=16347 + _globals['_GETSHARINGADMINSRESPONSE']._serialized_end=16426 + _globals['_TEAMSENTERPRISEUSERSADDREQUEST']._serialized_start=16428 + _globals['_TEAMSENTERPRISEUSERSADDREQUEST']._serialized_end=16523 + _globals['_TEAMSENTERPRISEUSERSADDTEAMREQUEST']._serialized_start=16525 + _globals['_TEAMSENTERPRISEUSERSADDTEAMREQUEST']._serialized_end=16641 + _globals['_TEAMSENTERPRISEUSERSADDUSERREQUEST']._serialized_start=16644 + _globals['_TEAMSENTERPRISEUSERSADDUSERREQUEST']._serialized_end=16815 + _globals['_TYPEDKEY']._serialized_start=16817 + _globals['_TYPEDKEY']._serialized_end=16887 + _globals['_TEAMSENTERPRISEUSERSADDRESPONSE']._serialized_start=16889 + _globals['_TEAMSENTERPRISEUSERSADDRESPONSE']._serialized_end=17004 + _globals['_TEAMSENTERPRISEUSERSADDTEAMRESPONSE']._serialized_start=17007 + _globals['_TEAMSENTERPRISEUSERSADDTEAMRESPONSE']._serialized_end=17203 + _globals['_TEAMSENTERPRISEUSERSADDUSERRESPONSE']._serialized_start=17206 + _globals['_TEAMSENTERPRISEUSERSADDUSERRESPONSE']._serialized_end=17365 + _globals['_TEAMENTERPRISEUSERREMOVE']._serialized_start=17367 + _globals['_TEAMENTERPRISEUSERREMOVE']._serialized_end=17436 + _globals['_TEAMENTERPRISEUSERREMOVESREQUEST']._serialized_start=17438 + _globals['_TEAMENTERPRISEUSERREMOVESREQUEST']._serialized_end=17544 + _globals['_TEAMENTERPRISEUSERREMOVESRESPONSE']._serialized_start=17546 + _globals['_TEAMENTERPRISEUSERREMOVESRESPONSE']._serialized_end=17669 + _globals['_TEAMENTERPRISEUSERREMOVERESPONSE']._serialized_start=17672 + _globals['_TEAMENTERPRISEUSERREMOVERESPONSE']._serialized_end=17856 + _globals['_DOMAINALIAS']._serialized_start=17858 + _globals['_DOMAINALIAS']._serialized_end=17935 + _globals['_DOMAINALIASREQUEST']._serialized_start=17937 + _globals['_DOMAINALIASREQUEST']._serialized_end=18003 + _globals['_DOMAINALIASRESPONSE']._serialized_start=18005 + _globals['_DOMAINALIASRESPONSE']._serialized_end=18072 + _globals['_ENTERPRISEUSERSPROVISIONREQUEST']._serialized_start=18074 + _globals['_ENTERPRISEUSERSPROVISIONREQUEST']._serialized_end=18183 + _globals['_ENTERPRISEUSERSPROVISION']._serialized_start=18186 + _globals['_ENTERPRISEUSERSPROVISION']._serialized_end=18624 + _globals['_ENTERPRISEUSERSPROVISIONRESPONSE']._serialized_start=18626 + _globals['_ENTERPRISEUSERSPROVISIONRESPONSE']._serialized_end=18721 + _globals['_ENTERPRISEUSERSPROVISIONRESULT']._serialized_start=18723 + _globals['_ENTERPRISEUSERSPROVISIONRESULT']._serialized_end=18836 + _globals['_ENTERPRISEUSERSADDREQUEST']._serialized_start=18838 + _globals['_ENTERPRISEUSERSADDREQUEST']._serialized_end=18935 + _globals['_ENTERPRISEUSERSADD']._serialized_start=18938 + _globals['_ENTERPRISEUSERSADD']._serialized_end=19206 + _globals['_ENTERPRISEUSERSADDRESPONSE']._serialized_start=19209 + _globals['_ENTERPRISEUSERSADDRESPONSE']._serialized_end=19364 + _globals['_ENTERPRISEUSERSADDRESULT']._serialized_start=19367 + _globals['_ENTERPRISEUSERSADDRESULT']._serialized_end=19517 + _globals['_UPDATEMSPPERMITSREQUEST']._serialized_start=19520 + _globals['_UPDATEMSPPERMITSREQUEST']._serialized_end=19705 + _globals['_DELETEENTERPRISEUSERSREQUEST']._serialized_start=19707 + _globals['_DELETEENTERPRISEUSERSREQUEST']._serialized_end=19764 + _globals['_DELETEENTERPRISEUSERSTATUS']._serialized_start=19766 + _globals['_DELETEENTERPRISEUSERSTATUS']._serialized_end=19877 + _globals['_DELETEENTERPRISEUSERSRESPONSE']._serialized_start=19879 + _globals['_DELETEENTERPRISEUSERSRESPONSE']._serialized_end=19972 + _globals['_CLEARSECURITYDATAREQUEST']._serialized_start=19974 + _globals['_CLEARSECURITYDATAREQUEST']._serialized_end=20093 + _globals['_LISTDOMAINSRESPONSE']._serialized_start=20095 + _globals['_LISTDOMAINSRESPONSE']._serialized_end=20132 + _globals['_RESERVEDOMAINREQUEST']._serialized_start=20134 + _globals['_RESERVEDOMAINREQUEST']._serialized_end=20234 + _globals['_RESERVEDOMAINRESPONSE']._serialized_start=20236 + _globals['_RESERVEDOMAINRESPONSE']._serialized_end=20274 + _globals['_ROLESBYTEAM']._serialized_start=20276 + _globals['_ROLESBYTEAM']._serialized_end=20322 + _globals['_LOCKUSERSREQUEST']._serialized_start=20325 + _globals['_LOCKUSERSREQUEST']._serialized_end=20466 + _globals['_LOCKUSERSRESPONSE']._serialized_start=20468 + _globals['_LOCKUSERSRESPONSE']._serialized_end=20535 + _globals['_LOCKUSERRESPONSE']._serialized_start=20537 + _globals['_LOCKUSERRESPONSE']._serialized_end=20647 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/enterprise_pb2.pyi b/keepercommander/proto/enterprise_pb2.pyi index c09888285..2d6931822 100644 --- a/keepercommander/proto/enterprise_pb2.pyi +++ b/keepercommander/proto/enterprise_pb2.pyi @@ -2,8 +2,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -160,6 +159,13 @@ class UserLockStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): UNLOCKED: _ClassVar[UserLockStatus] DELETED: _ClassVar[UserLockStatus] CANT_BE_PENDING: _ClassVar[UserLockStatus] + +class ExternalCloudSecretsStoreType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + UNKNOWN_STORE_TYPE: _ClassVar[ExternalCloudSecretsStoreType] + AWS_SECRETS_MANAGER: _ClassVar[ExternalCloudSecretsStoreType] + AZURE_KEY_VAULT: _ClassVar[ExternalCloudSecretsStoreType] + GOOGLE_SECRET_MANAGER: _ClassVar[ExternalCloudSecretsStoreType] RSA: KeyType ECC: KeyType ROLE_EXISTS: RoleUserModifyStatus @@ -260,6 +266,10 @@ DISABLED: UserLockStatus UNLOCKED: UserLockStatus DELETED: UserLockStatus CANT_BE_PENDING: UserLockStatus +UNKNOWN_STORE_TYPE: ExternalCloudSecretsStoreType +AWS_SECRETS_MANAGER: ExternalCloudSecretsStoreType +AZURE_KEY_VAULT: ExternalCloudSecretsStoreType +GOOGLE_SECRET_MANAGER: ExternalCloudSecretsStoreType class EnterpriseKeyPairRequest(_message.Message): __slots__ = ("enterprisePublicKey", "encryptedEnterprisePrivateKey", "keyType") @@ -289,7 +299,7 @@ class EnterpriseUser(_message.Message): enterpriseUsername: str isShareAdmin: bool username: str - def __init__(self, enterpriseUserId: _Optional[int] = ..., email: _Optional[str] = ..., enterpriseUsername: _Optional[str] = ..., isShareAdmin: _Optional[bool] = ..., username: _Optional[str] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., email: _Optional[str] = ..., enterpriseUsername: _Optional[str] = ..., isShareAdmin: bool = ..., username: _Optional[str] = ...) -> None: ... class GetTeamMemberResponse(_message.Message): __slots__ = ("enterpriseUser",) @@ -319,7 +329,7 @@ class EncryptedTeamKeyRequest(_message.Message): teamUid: bytes encryptedTeamKey: bytes force: bool - def __init__(self, teamUid: _Optional[bytes] = ..., encryptedTeamKey: _Optional[bytes] = ..., force: _Optional[bool] = ...) -> None: ... + def __init__(self, teamUid: _Optional[bytes] = ..., encryptedTeamKey: _Optional[bytes] = ..., force: bool = ...) -> None: ... class ReEncryptedData(_message.Message): __slots__ = ("id", "data") @@ -521,7 +531,7 @@ class DomainPasswordRulesFields(_message.Message): minimum: int maximum: int allowed: bool - def __init__(self, type: _Optional[str] = ..., minimum: _Optional[int] = ..., maximum: _Optional[int] = ..., allowed: _Optional[bool] = ...) -> None: ... + def __init__(self, type: _Optional[str] = ..., minimum: _Optional[int] = ..., maximum: _Optional[int] = ..., allowed: bool = ...) -> None: ... class LoginToMcRequest(_message.Message): __slots__ = ("mcEnterpriseId", "messageSessionUid") @@ -532,14 +542,16 @@ class LoginToMcRequest(_message.Message): def __init__(self, mcEnterpriseId: _Optional[int] = ..., messageSessionUid: _Optional[bytes] = ...) -> None: ... class LoginToMcResponse(_message.Message): - __slots__ = ("encryptedSessionToken", "encryptedTreeKey", "forbidKeyType2") + __slots__ = ("encryptedSessionToken", "encryptedTreeKey", "keyTypeId", "forbidKeyType2") ENCRYPTEDSESSIONTOKEN_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDTREEKEY_FIELD_NUMBER: _ClassVar[int] + KEYTYPEID_FIELD_NUMBER: _ClassVar[int] FORBIDKEYTYPE2_FIELD_NUMBER: _ClassVar[int] encryptedSessionToken: bytes encryptedTreeKey: str + keyTypeId: int forbidKeyType2: bool - def __init__(self, encryptedSessionToken: _Optional[bytes] = ..., encryptedTreeKey: _Optional[str] = ..., forbidKeyType2: _Optional[bool] = ...) -> None: ... + def __init__(self, encryptedSessionToken: _Optional[bytes] = ..., encryptedTreeKey: _Optional[str] = ..., keyTypeId: _Optional[int] = ..., forbidKeyType2: bool = ...) -> None: ... class DomainPasswordRulesResponse(_message.Message): __slots__ = ("domainPasswordRulesFields",) @@ -557,7 +569,7 @@ class ApproveUserDeviceRequest(_message.Message): encryptedDeviceToken: bytes encryptedDeviceDataKey: bytes denyApproval: bool - def __init__(self, enterpriseUserId: _Optional[int] = ..., encryptedDeviceToken: _Optional[bytes] = ..., encryptedDeviceDataKey: _Optional[bytes] = ..., denyApproval: _Optional[bool] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., encryptedDeviceToken: _Optional[bytes] = ..., encryptedDeviceDataKey: _Optional[bytes] = ..., denyApproval: bool = ...) -> None: ... class ApproveUserDeviceResponse(_message.Message): __slots__ = ("enterpriseUserId", "encryptedDeviceToken", "failed", "message") @@ -569,7 +581,7 @@ class ApproveUserDeviceResponse(_message.Message): encryptedDeviceToken: bytes failed: bool message: str - def __init__(self, enterpriseUserId: _Optional[int] = ..., encryptedDeviceToken: _Optional[bytes] = ..., failed: _Optional[bool] = ..., message: _Optional[str] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., encryptedDeviceToken: _Optional[bytes] = ..., failed: bool = ..., message: _Optional[str] = ...) -> None: ... class ApproveUserDevicesRequest(_message.Message): __slots__ = ("deviceRequests",) @@ -657,7 +669,7 @@ class GeneralDataEntity(_message.Message): distributor: bool forbidAccountTransfer: bool showUserOnboard: bool - def __init__(self, enterpriseName: _Optional[str] = ..., restrictVisibility: _Optional[bool] = ..., specialProvisioning: _Optional[_Union[SpecialProvisioning, _Mapping]] = ..., userPrivilege: _Optional[_Union[UserPrivilege, _Mapping]] = ..., distributor: _Optional[bool] = ..., forbidAccountTransfer: _Optional[bool] = ..., showUserOnboard: _Optional[bool] = ...) -> None: ... + def __init__(self, enterpriseName: _Optional[str] = ..., restrictVisibility: bool = ..., specialProvisioning: _Optional[_Union[SpecialProvisioning, _Mapping]] = ..., userPrivilege: _Optional[_Union[UserPrivilege, _Mapping]] = ..., distributor: bool = ..., forbidAccountTransfer: bool = ..., showUserOnboard: bool = ...) -> None: ... class Node(_message.Message): __slots__ = ("nodeId", "parentId", "bridgeId", "scimId", "licenseId", "encryptedData", "duoEnabled", "rsaEnabled", "ssoServiceProviderId", "restrictVisibility", "ssoServiceProviderIds") @@ -683,7 +695,7 @@ class Node(_message.Message): ssoServiceProviderId: int restrictVisibility: bool ssoServiceProviderIds: _containers.RepeatedScalarFieldContainer[int] - def __init__(self, nodeId: _Optional[int] = ..., parentId: _Optional[int] = ..., bridgeId: _Optional[int] = ..., scimId: _Optional[int] = ..., licenseId: _Optional[int] = ..., encryptedData: _Optional[str] = ..., duoEnabled: _Optional[bool] = ..., rsaEnabled: _Optional[bool] = ..., ssoServiceProviderId: _Optional[int] = ..., restrictVisibility: _Optional[bool] = ..., ssoServiceProviderIds: _Optional[_Iterable[int]] = ...) -> None: ... + def __init__(self, nodeId: _Optional[int] = ..., parentId: _Optional[int] = ..., bridgeId: _Optional[int] = ..., scimId: _Optional[int] = ..., licenseId: _Optional[int] = ..., encryptedData: _Optional[str] = ..., duoEnabled: bool = ..., rsaEnabled: bool = ..., ssoServiceProviderId: _Optional[int] = ..., restrictVisibility: bool = ..., ssoServiceProviderIds: _Optional[_Iterable[int]] = ...) -> None: ... class Role(_message.Message): __slots__ = ("roleId", "nodeId", "encryptedData", "keyType", "visibleBelow", "newUserInherit", "roleType") @@ -701,7 +713,7 @@ class Role(_message.Message): visibleBelow: bool newUserInherit: bool roleType: str - def __init__(self, roleId: _Optional[int] = ..., nodeId: _Optional[int] = ..., encryptedData: _Optional[str] = ..., keyType: _Optional[str] = ..., visibleBelow: _Optional[bool] = ..., newUserInherit: _Optional[bool] = ..., roleType: _Optional[str] = ...) -> None: ... + def __init__(self, roleId: _Optional[int] = ..., nodeId: _Optional[int] = ..., encryptedData: _Optional[str] = ..., keyType: _Optional[str] = ..., visibleBelow: bool = ..., newUserInherit: bool = ..., roleType: _Optional[str] = ...) -> None: ... class User(_message.Message): __slots__ = ("enterpriseUserId", "nodeId", "encryptedData", "keyType", "username", "status", "lock", "userId", "accountShareExpiration", "fullName", "jobTitle", "tfaEnabled", "transferAcceptanceStatus") @@ -731,7 +743,7 @@ class User(_message.Message): jobTitle: str tfaEnabled: bool transferAcceptanceStatus: TransferAcceptanceStatus - def __init__(self, enterpriseUserId: _Optional[int] = ..., nodeId: _Optional[int] = ..., encryptedData: _Optional[str] = ..., keyType: _Optional[str] = ..., username: _Optional[str] = ..., status: _Optional[str] = ..., lock: _Optional[int] = ..., userId: _Optional[int] = ..., accountShareExpiration: _Optional[int] = ..., fullName: _Optional[str] = ..., jobTitle: _Optional[str] = ..., tfaEnabled: _Optional[bool] = ..., transferAcceptanceStatus: _Optional[_Union[TransferAcceptanceStatus, str]] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., nodeId: _Optional[int] = ..., encryptedData: _Optional[str] = ..., keyType: _Optional[str] = ..., username: _Optional[str] = ..., status: _Optional[str] = ..., lock: _Optional[int] = ..., userId: _Optional[int] = ..., accountShareExpiration: _Optional[int] = ..., fullName: _Optional[str] = ..., jobTitle: _Optional[str] = ..., tfaEnabled: bool = ..., transferAcceptanceStatus: _Optional[_Union[TransferAcceptanceStatus, str]] = ...) -> None: ... class UserAlias(_message.Message): __slots__ = ("enterpriseUserId", "username") @@ -767,7 +779,7 @@ class ManagedNode(_message.Message): roleId: int managedNodeId: int cascadeNodeManagement: bool - def __init__(self, roleId: _Optional[int] = ..., managedNodeId: _Optional[int] = ..., cascadeNodeManagement: _Optional[bool] = ...) -> None: ... + def __init__(self, roleId: _Optional[int] = ..., managedNodeId: _Optional[int] = ..., cascadeNodeManagement: bool = ...) -> None: ... class UserManagedNode(_message.Message): __slots__ = ("nodeId", "cascadeNodeManagement", "privileges") @@ -777,7 +789,7 @@ class UserManagedNode(_message.Message): nodeId: int cascadeNodeManagement: bool privileges: _containers.RepeatedScalarFieldContainer[str] - def __init__(self, nodeId: _Optional[int] = ..., cascadeNodeManagement: _Optional[bool] = ..., privileges: _Optional[_Iterable[str]] = ...) -> None: ... + def __init__(self, nodeId: _Optional[int] = ..., cascadeNodeManagement: bool = ..., privileges: _Optional[_Iterable[str]] = ...) -> None: ... class UserPrivilege(_message.Message): __slots__ = ("userManagedNodes", "enterpriseUserId", "encryptedData") @@ -845,7 +857,7 @@ class Team(_message.Message): restrictView: bool encryptedData: str encryptedTeamKey: str - def __init__(self, teamUid: _Optional[bytes] = ..., name: _Optional[str] = ..., nodeId: _Optional[int] = ..., restrictEdit: _Optional[bool] = ..., restrictShare: _Optional[bool] = ..., restrictView: _Optional[bool] = ..., encryptedData: _Optional[str] = ..., encryptedTeamKey: _Optional[str] = ...) -> None: ... + def __init__(self, teamUid: _Optional[bytes] = ..., name: _Optional[str] = ..., nodeId: _Optional[int] = ..., restrictEdit: bool = ..., restrictShare: bool = ..., restrictView: bool = ..., encryptedData: _Optional[str] = ..., encryptedTeamKey: _Optional[str] = ...) -> None: ... class TeamUser(_message.Message): __slots__ = ("teamUid", "enterpriseUserId", "userType") @@ -891,10 +903,10 @@ class MspInfo(_message.Message): managedCompanies: _containers.RepeatedCompositeFieldContainer[ManagedCompany] allowUnlimitedLicenses: bool addOns: _containers.RepeatedCompositeFieldContainer[LicenseAddOn] - def __init__(self, enterpriseId: _Optional[int] = ..., enterpriseName: _Optional[str] = ..., allocatedLicenses: _Optional[int] = ..., allowedMcProducts: _Optional[_Iterable[str]] = ..., allowedAddOns: _Optional[_Iterable[str]] = ..., maxFilePlanType: _Optional[str] = ..., managedCompanies: _Optional[_Iterable[_Union[ManagedCompany, _Mapping]]] = ..., allowUnlimitedLicenses: _Optional[bool] = ..., addOns: _Optional[_Iterable[_Union[LicenseAddOn, _Mapping]]] = ...) -> None: ... + def __init__(self, enterpriseId: _Optional[int] = ..., enterpriseName: _Optional[str] = ..., allocatedLicenses: _Optional[int] = ..., allowedMcProducts: _Optional[_Iterable[str]] = ..., allowedAddOns: _Optional[_Iterable[str]] = ..., maxFilePlanType: _Optional[str] = ..., managedCompanies: _Optional[_Iterable[_Union[ManagedCompany, _Mapping]]] = ..., allowUnlimitedLicenses: bool = ..., addOns: _Optional[_Iterable[_Union[LicenseAddOn, _Mapping]]] = ...) -> None: ... class ManagedCompany(_message.Message): - __slots__ = ("mcEnterpriseId", "mcEnterpriseName", "mspNodeId", "numberOfSeats", "numberOfUsers", "productId", "isExpired", "treeKey", "tree_key_role", "filePlanType", "addOns") + __slots__ = ("mcEnterpriseId", "mcEnterpriseName", "mspNodeId", "numberOfSeats", "numberOfUsers", "productId", "isExpired", "treeKey", "tree_key_role", "filePlanType", "addOns", "treeKeyTypeId") MCENTERPRISEID_FIELD_NUMBER: _ClassVar[int] MCENTERPRISENAME_FIELD_NUMBER: _ClassVar[int] MSPNODEID_FIELD_NUMBER: _ClassVar[int] @@ -906,6 +918,7 @@ class ManagedCompany(_message.Message): TREE_KEY_ROLE_FIELD_NUMBER: _ClassVar[int] FILEPLANTYPE_FIELD_NUMBER: _ClassVar[int] ADDONS_FIELD_NUMBER: _ClassVar[int] + TREEKEYTYPEID_FIELD_NUMBER: _ClassVar[int] mcEnterpriseId: int mcEnterpriseName: str mspNodeId: int @@ -917,7 +930,8 @@ class ManagedCompany(_message.Message): tree_key_role: int filePlanType: str addOns: _containers.RepeatedCompositeFieldContainer[LicenseAddOn] - def __init__(self, mcEnterpriseId: _Optional[int] = ..., mcEnterpriseName: _Optional[str] = ..., mspNodeId: _Optional[int] = ..., numberOfSeats: _Optional[int] = ..., numberOfUsers: _Optional[int] = ..., productId: _Optional[str] = ..., isExpired: _Optional[bool] = ..., treeKey: _Optional[str] = ..., tree_key_role: _Optional[int] = ..., filePlanType: _Optional[str] = ..., addOns: _Optional[_Iterable[_Union[LicenseAddOn, _Mapping]]] = ...) -> None: ... + treeKeyTypeId: int + def __init__(self, mcEnterpriseId: _Optional[int] = ..., mcEnterpriseName: _Optional[str] = ..., mspNodeId: _Optional[int] = ..., numberOfSeats: _Optional[int] = ..., numberOfUsers: _Optional[int] = ..., productId: _Optional[str] = ..., isExpired: bool = ..., treeKey: _Optional[str] = ..., tree_key_role: _Optional[int] = ..., filePlanType: _Optional[str] = ..., addOns: _Optional[_Iterable[_Union[LicenseAddOn, _Mapping]]] = ..., treeKeyTypeId: _Optional[int] = ...) -> None: ... class MSPPool(_message.Message): __slots__ = ("productId", "seats", "availableSeats", "stash") @@ -940,7 +954,7 @@ class MSPContact(_message.Message): def __init__(self, enterpriseId: _Optional[int] = ..., enterpriseName: _Optional[str] = ...) -> None: ... class LicenseAddOn(_message.Message): - __slots__ = ("name", "enabled", "isTrial", "expiration", "created", "seats", "activationTime", "includedInProduct", "apiCallCount", "tierDescription", "seatsAllocated") + __slots__ = ("name", "enabled", "isTrial", "expiration", "created", "seats", "activationTime", "includedInProduct", "apiCallCount", "tierDescription", "seatsAllocated", "nhiTierAddOnId") NAME_FIELD_NUMBER: _ClassVar[int] ENABLED_FIELD_NUMBER: _ClassVar[int] ISTRIAL_FIELD_NUMBER: _ClassVar[int] @@ -952,6 +966,7 @@ class LicenseAddOn(_message.Message): APICALLCOUNT_FIELD_NUMBER: _ClassVar[int] TIERDESCRIPTION_FIELD_NUMBER: _ClassVar[int] SEATSALLOCATED_FIELD_NUMBER: _ClassVar[int] + NHITIERADDONID_FIELD_NUMBER: _ClassVar[int] name: str enabled: bool isTrial: bool @@ -963,7 +978,8 @@ class LicenseAddOn(_message.Message): apiCallCount: int tierDescription: str seatsAllocated: int - def __init__(self, name: _Optional[str] = ..., enabled: _Optional[bool] = ..., isTrial: _Optional[bool] = ..., expiration: _Optional[int] = ..., created: _Optional[int] = ..., seats: _Optional[int] = ..., activationTime: _Optional[int] = ..., includedInProduct: _Optional[bool] = ..., apiCallCount: _Optional[int] = ..., tierDescription: _Optional[str] = ..., seatsAllocated: _Optional[int] = ...) -> None: ... + nhiTierAddOnId: int + def __init__(self, name: _Optional[str] = ..., enabled: bool = ..., isTrial: bool = ..., expiration: _Optional[int] = ..., created: _Optional[int] = ..., seats: _Optional[int] = ..., activationTime: _Optional[int] = ..., includedInProduct: bool = ..., apiCallCount: _Optional[int] = ..., tierDescription: _Optional[str] = ..., seatsAllocated: _Optional[int] = ..., nhiTierAddOnId: _Optional[int] = ...) -> None: ... class MCDefault(_message.Message): __slots__ = ("mcProduct", "addOns", "filePlanType", "maxLicenses", "fixedMaxLicenses") @@ -977,7 +993,7 @@ class MCDefault(_message.Message): filePlanType: str maxLicenses: int fixedMaxLicenses: bool - def __init__(self, mcProduct: _Optional[str] = ..., addOns: _Optional[_Iterable[str]] = ..., filePlanType: _Optional[str] = ..., maxLicenses: _Optional[int] = ..., fixedMaxLicenses: _Optional[bool] = ...) -> None: ... + def __init__(self, mcProduct: _Optional[str] = ..., addOns: _Optional[_Iterable[str]] = ..., filePlanType: _Optional[str] = ..., maxLicenses: _Optional[int] = ..., fixedMaxLicenses: bool = ...) -> None: ... class MSPPermits(_message.Message): __slots__ = ("restricted", "maxAllowedLicenses", "allowedMcProducts", "allowedAddOns", "maxFilePlanType", "allowUnlimitedLicenses", "mcDefaults") @@ -995,7 +1011,7 @@ class MSPPermits(_message.Message): maxFilePlanType: str allowUnlimitedLicenses: bool mcDefaults: _containers.RepeatedCompositeFieldContainer[MCDefault] - def __init__(self, restricted: _Optional[bool] = ..., maxAllowedLicenses: _Optional[int] = ..., allowedMcProducts: _Optional[_Iterable[str]] = ..., allowedAddOns: _Optional[_Iterable[str]] = ..., maxFilePlanType: _Optional[str] = ..., allowUnlimitedLicenses: _Optional[bool] = ..., mcDefaults: _Optional[_Iterable[_Union[MCDefault, _Mapping]]] = ...) -> None: ... + def __init__(self, restricted: bool = ..., maxAllowedLicenses: _Optional[int] = ..., allowedMcProducts: _Optional[_Iterable[str]] = ..., allowedAddOns: _Optional[_Iterable[str]] = ..., maxFilePlanType: _Optional[str] = ..., allowUnlimitedLicenses: bool = ..., mcDefaults: _Optional[_Iterable[_Union[MCDefault, _Mapping]]] = ...) -> None: ... class License(_message.Message): __slots__ = ("paid", "numberOfSeats", "expiration", "licenseKeyId", "productTypeId", "name", "enterpriseLicenseId", "seatsAllocated", "seatsPending", "tier", "filePlanTypeId", "maxBytes", "storageExpiration", "licenseStatus", "mspPool", "managedBy", "addOns", "nextBillingDate", "hasMSPLegacyLog", "mspPermits", "distributor") @@ -1041,7 +1057,7 @@ class License(_message.Message): hasMSPLegacyLog: bool mspPermits: MSPPermits distributor: bool - def __init__(self, paid: _Optional[bool] = ..., numberOfSeats: _Optional[int] = ..., expiration: _Optional[int] = ..., licenseKeyId: _Optional[int] = ..., productTypeId: _Optional[int] = ..., name: _Optional[str] = ..., enterpriseLicenseId: _Optional[int] = ..., seatsAllocated: _Optional[int] = ..., seatsPending: _Optional[int] = ..., tier: _Optional[int] = ..., filePlanTypeId: _Optional[int] = ..., maxBytes: _Optional[int] = ..., storageExpiration: _Optional[int] = ..., licenseStatus: _Optional[str] = ..., mspPool: _Optional[_Iterable[_Union[MSPPool, _Mapping]]] = ..., managedBy: _Optional[_Union[MSPContact, _Mapping]] = ..., addOns: _Optional[_Iterable[_Union[LicenseAddOn, _Mapping]]] = ..., nextBillingDate: _Optional[int] = ..., hasMSPLegacyLog: _Optional[bool] = ..., mspPermits: _Optional[_Union[MSPPermits, _Mapping]] = ..., distributor: _Optional[bool] = ...) -> None: ... + def __init__(self, paid: bool = ..., numberOfSeats: _Optional[int] = ..., expiration: _Optional[int] = ..., licenseKeyId: _Optional[int] = ..., productTypeId: _Optional[int] = ..., name: _Optional[str] = ..., enterpriseLicenseId: _Optional[int] = ..., seatsAllocated: _Optional[int] = ..., seatsPending: _Optional[int] = ..., tier: _Optional[int] = ..., filePlanTypeId: _Optional[int] = ..., maxBytes: _Optional[int] = ..., storageExpiration: _Optional[int] = ..., licenseStatus: _Optional[str] = ..., mspPool: _Optional[_Iterable[_Union[MSPPool, _Mapping]]] = ..., managedBy: _Optional[_Union[MSPContact, _Mapping]] = ..., addOns: _Optional[_Iterable[_Union[LicenseAddOn, _Mapping]]] = ..., nextBillingDate: _Optional[int] = ..., hasMSPLegacyLog: bool = ..., mspPermits: _Optional[_Union[MSPPermits, _Mapping]] = ..., distributor: bool = ...) -> None: ... class Bridge(_message.Message): __slots__ = ("bridgeId", "nodeId", "wanIpEnforcement", "lanIpEnforcement", "status") @@ -1071,7 +1087,7 @@ class Scim(_message.Message): lastSynced: int rolePrefix: str uniqueGroups: bool - def __init__(self, scimId: _Optional[int] = ..., nodeId: _Optional[int] = ..., status: _Optional[str] = ..., lastSynced: _Optional[int] = ..., rolePrefix: _Optional[str] = ..., uniqueGroups: _Optional[bool] = ...) -> None: ... + def __init__(self, scimId: _Optional[int] = ..., nodeId: _Optional[int] = ..., status: _Optional[str] = ..., lastSynced: _Optional[int] = ..., rolePrefix: _Optional[str] = ..., uniqueGroups: bool = ...) -> None: ... class EmailProvision(_message.Message): __slots__ = ("id", "nodeId", "domain", "method") @@ -1143,7 +1159,7 @@ class SsoService(_message.Message): inviteNewUsers: bool active: bool isCloud: bool - def __init__(self, ssoServiceProviderId: _Optional[int] = ..., nodeId: _Optional[int] = ..., name: _Optional[str] = ..., sp_url: _Optional[str] = ..., inviteNewUsers: _Optional[bool] = ..., active: _Optional[bool] = ..., isCloud: _Optional[bool] = ...) -> None: ... + def __init__(self, ssoServiceProviderId: _Optional[int] = ..., nodeId: _Optional[int] = ..., name: _Optional[str] = ..., sp_url: _Optional[str] = ..., inviteNewUsers: bool = ..., active: bool = ..., isCloud: bool = ...) -> None: ... class ReportFilterUser(_message.Message): __slots__ = ("userId", "email") @@ -1189,7 +1205,7 @@ class EnterpriseData(_message.Message): entity: EnterpriseDataEntity delete: bool data: _containers.RepeatedScalarFieldContainer[bytes] - def __init__(self, entity: _Optional[_Union[EnterpriseDataEntity, str]] = ..., delete: _Optional[bool] = ..., data: _Optional[_Iterable[bytes]] = ...) -> None: ... + def __init__(self, entity: _Optional[_Union[EnterpriseDataEntity, str]] = ..., delete: bool = ..., data: _Optional[_Iterable[bytes]] = ...) -> None: ... class EnterpriseDataResponse(_message.Message): __slots__ = ("continuationToken", "hasMore", "cacheStatus", "data", "generalData") @@ -1203,7 +1219,7 @@ class EnterpriseDataResponse(_message.Message): cacheStatus: CacheStatus data: _containers.RepeatedCompositeFieldContainer[EnterpriseData] generalData: GeneralDataEntity - def __init__(self, continuationToken: _Optional[bytes] = ..., hasMore: _Optional[bool] = ..., cacheStatus: _Optional[_Union[CacheStatus, str]] = ..., data: _Optional[_Iterable[_Union[EnterpriseData, _Mapping]]] = ..., generalData: _Optional[_Union[GeneralDataEntity, _Mapping]] = ...) -> None: ... + def __init__(self, continuationToken: _Optional[bytes] = ..., hasMore: bool = ..., cacheStatus: _Optional[_Union[CacheStatus, str]] = ..., data: _Optional[_Iterable[_Union[EnterpriseData, _Mapping]]] = ..., generalData: _Optional[_Union[GeneralDataEntity, _Mapping]] = ...) -> None: ... class BackupRequest(_message.Message): __slots__ = ("continuationToken",) @@ -1367,7 +1383,7 @@ class SharedRecordEvent(_message.Message): canEdit: bool canReshare: bool shareFrom: int - def __init__(self, recordUid: _Optional[bytes] = ..., userName: _Optional[str] = ..., canEdit: _Optional[bool] = ..., canReshare: _Optional[bool] = ..., shareFrom: _Optional[int] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., userName: _Optional[str] = ..., canEdit: bool = ..., canReshare: bool = ..., shareFrom: _Optional[int] = ...) -> None: ... class SetRestrictVisibilityRequest(_message.Message): __slots__ = ("nodeId",) @@ -1393,7 +1409,7 @@ class UserAddRequest(_message.Message): jobTitle: str email: str suppressEmailInvite: bool - def __init__(self, enterpriseUserId: _Optional[int] = ..., nodeId: _Optional[int] = ..., encryptedData: _Optional[bytes] = ..., keyType: _Optional[_Union[EncryptedKeyType, str]] = ..., fullName: _Optional[str] = ..., jobTitle: _Optional[str] = ..., email: _Optional[str] = ..., suppressEmailInvite: _Optional[bool] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., nodeId: _Optional[int] = ..., encryptedData: _Optional[bytes] = ..., keyType: _Optional[_Union[EncryptedKeyType, str]] = ..., fullName: _Optional[str] = ..., jobTitle: _Optional[str] = ..., email: _Optional[str] = ..., suppressEmailInvite: bool = ...) -> None: ... class UserUpdateRequest(_message.Message): __slots__ = ("users",) @@ -1439,7 +1455,7 @@ class ComplianceRecordOwnersRequest(_message.Message): INCLUDENONSHARED_FIELD_NUMBER: _ClassVar[int] nodeIds: _containers.RepeatedScalarFieldContainer[int] includeNonShared: bool - def __init__(self, nodeIds: _Optional[_Iterable[int]] = ..., includeNonShared: _Optional[bool] = ...) -> None: ... + def __init__(self, nodeIds: _Optional[_Iterable[int]] = ..., includeNonShared: bool = ...) -> None: ... class ComplianceRecordOwnersResponse(_message.Message): __slots__ = ("recordOwners",) @@ -1453,7 +1469,7 @@ class RecordOwner(_message.Message): SHARED_FIELD_NUMBER: _ClassVar[int] enterpriseUserId: int shared: bool - def __init__(self, enterpriseUserId: _Optional[int] = ..., shared: _Optional[bool] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., shared: bool = ...) -> None: ... class PreliminaryComplianceDataRequest(_message.Message): __slots__ = ("enterpriseUserIds", "includeNonShared", "continuationToken", "includeTotalMatchingRecordsInFirstResponse") @@ -1465,7 +1481,7 @@ class PreliminaryComplianceDataRequest(_message.Message): includeNonShared: bool continuationToken: bytes includeTotalMatchingRecordsInFirstResponse: bool - def __init__(self, enterpriseUserIds: _Optional[_Iterable[int]] = ..., includeNonShared: _Optional[bool] = ..., continuationToken: _Optional[bytes] = ..., includeTotalMatchingRecordsInFirstResponse: _Optional[bool] = ...) -> None: ... + def __init__(self, enterpriseUserIds: _Optional[_Iterable[int]] = ..., includeNonShared: bool = ..., continuationToken: _Optional[bytes] = ..., includeTotalMatchingRecordsInFirstResponse: bool = ...) -> None: ... class PreliminaryComplianceDataResponse(_message.Message): __slots__ = ("auditUserData", "continuationToken", "hasMore", "totalMatchingRecords") @@ -1477,7 +1493,7 @@ class PreliminaryComplianceDataResponse(_message.Message): continuationToken: bytes hasMore: bool totalMatchingRecords: int - def __init__(self, auditUserData: _Optional[_Iterable[_Union[AuditUserData, _Mapping]]] = ..., continuationToken: _Optional[bytes] = ..., hasMore: _Optional[bool] = ..., totalMatchingRecords: _Optional[int] = ...) -> None: ... + def __init__(self, auditUserData: _Optional[_Iterable[_Union[AuditUserData, _Mapping]]] = ..., continuationToken: _Optional[bytes] = ..., hasMore: bool = ..., totalMatchingRecords: _Optional[int] = ...) -> None: ... class AuditUserRecord(_message.Message): __slots__ = ("recordUid", "encryptedData", "shared") @@ -1487,7 +1503,7 @@ class AuditUserRecord(_message.Message): recordUid: bytes encryptedData: bytes shared: bool - def __init__(self, recordUid: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., shared: _Optional[bool] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., shared: bool = ...) -> None: ... class AuditUserData(_message.Message): __slots__ = ("enterpriseUserId", "auditUserRecords", "status") @@ -1521,7 +1537,7 @@ class ComplianceReportRequest(_message.Message): complianceReportRun: ComplianceReportRun reportName: str saveReport: bool - def __init__(self, complianceReportRun: _Optional[_Union[ComplianceReportRun, _Mapping]] = ..., reportName: _Optional[str] = ..., saveReport: _Optional[bool] = ...) -> None: ... + def __init__(self, complianceReportRun: _Optional[_Union[ComplianceReportRun, _Mapping]] = ..., reportName: _Optional[str] = ..., saveReport: bool = ...) -> None: ... class ComplianceReportRun(_message.Message): __slots__ = ("reportCriteriaAndFilter", "users", "records") @@ -1559,7 +1575,7 @@ class ComplianceReportCriteria(_message.Message): jobTitles: _containers.RepeatedScalarFieldContainer[str] enterpriseUserIds: _containers.RepeatedScalarFieldContainer[int] includeNonShared: bool - def __init__(self, jobTitles: _Optional[_Iterable[str]] = ..., enterpriseUserIds: _Optional[_Iterable[int]] = ..., includeNonShared: _Optional[bool] = ...) -> None: ... + def __init__(self, jobTitles: _Optional[_Iterable[str]] = ..., enterpriseUserIds: _Optional[_Iterable[int]] = ..., includeNonShared: bool = ...) -> None: ... class ComplianceReportFilter(_message.Message): __slots__ = ("recordTitles", "recordUids", "jobTitles", "urls", "recordTypes") @@ -1623,7 +1639,7 @@ class AuditRecord(_message.Message): inTrash: bool treeLeft: int treeRight: int - def __init__(self, recordUid: _Optional[bytes] = ..., auditData: _Optional[bytes] = ..., hasAttachments: _Optional[bool] = ..., inTrash: _Optional[bool] = ..., treeLeft: _Optional[int] = ..., treeRight: _Optional[int] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., auditData: _Optional[bytes] = ..., hasAttachments: bool = ..., inTrash: bool = ..., treeLeft: _Optional[int] = ..., treeRight: _Optional[int] = ...) -> None: ... class AuditRole(_message.Message): __slots__ = ("roleId", "encryptedData", "restrictShareOutsideEnterprise", "restrictShareAll", "restrictShareOfAttachments", "restrictMaskPasswordsWhileEditing", "roleNodeManagements") @@ -1641,7 +1657,7 @@ class AuditRole(_message.Message): restrictShareOfAttachments: bool restrictMaskPasswordsWhileEditing: bool roleNodeManagements: _containers.RepeatedCompositeFieldContainer[RoleNodeManagement] - def __init__(self, roleId: _Optional[int] = ..., encryptedData: _Optional[bytes] = ..., restrictShareOutsideEnterprise: _Optional[bool] = ..., restrictShareAll: _Optional[bool] = ..., restrictShareOfAttachments: _Optional[bool] = ..., restrictMaskPasswordsWhileEditing: _Optional[bool] = ..., roleNodeManagements: _Optional[_Iterable[_Union[RoleNodeManagement, _Mapping]]] = ...) -> None: ... + def __init__(self, roleId: _Optional[int] = ..., encryptedData: _Optional[bytes] = ..., restrictShareOutsideEnterprise: bool = ..., restrictShareAll: bool = ..., restrictShareOfAttachments: bool = ..., restrictMaskPasswordsWhileEditing: bool = ..., roleNodeManagements: _Optional[_Iterable[_Union[RoleNodeManagement, _Mapping]]] = ...) -> None: ... class RoleNodeManagement(_message.Message): __slots__ = ("treeLeft", "treeRight", "cascade", "privileges") @@ -1653,7 +1669,7 @@ class RoleNodeManagement(_message.Message): treeRight: int cascade: bool privileges: int - def __init__(self, treeLeft: _Optional[int] = ..., treeRight: _Optional[int] = ..., cascade: _Optional[bool] = ..., privileges: _Optional[int] = ...) -> None: ... + def __init__(self, treeLeft: _Optional[int] = ..., treeRight: _Optional[int] = ..., cascade: bool = ..., privileges: _Optional[int] = ...) -> None: ... class UserProfile(_message.Message): __slots__ = ("enterpriseUserId", "fullName", "jobTitle", "email", "roleIds") @@ -1695,7 +1711,7 @@ class AuditTeam(_message.Message): teamName: str restrictEdit: bool restrictShare: bool - def __init__(self, teamUid: _Optional[bytes] = ..., teamName: _Optional[str] = ..., restrictEdit: _Optional[bool] = ..., restrictShare: _Optional[bool] = ...) -> None: ... + def __init__(self, teamUid: _Optional[bytes] = ..., teamName: _Optional[str] = ..., restrictEdit: bool = ..., restrictShare: bool = ...) -> None: ... class AuditTeamUser(_message.Message): __slots__ = ("teamUid", "enterpriseUserIds") @@ -1799,7 +1815,7 @@ class UserProfileExt(_message.Message): isShareAdminForRequestedObject: bool isShareAdminForSharedFolderOwner: bool hasAccessToObject: bool - def __init__(self, email: _Optional[str] = ..., fullName: _Optional[str] = ..., jobTitle: _Optional[str] = ..., isMSPMCAdmin: _Optional[bool] = ..., isInSharedFolder: _Optional[bool] = ..., isShareAdminForRequestedObject: _Optional[bool] = ..., isShareAdminForSharedFolderOwner: _Optional[bool] = ..., hasAccessToObject: _Optional[bool] = ...) -> None: ... + def __init__(self, email: _Optional[str] = ..., fullName: _Optional[str] = ..., jobTitle: _Optional[str] = ..., isMSPMCAdmin: bool = ..., isInSharedFolder: bool = ..., isShareAdminForRequestedObject: bool = ..., isShareAdminForSharedFolderOwner: bool = ..., hasAccessToObject: bool = ...) -> None: ... class GetSharingAdminsResponse(_message.Message): __slots__ = ("userProfileExts",) @@ -1863,7 +1879,7 @@ class TeamsEnterpriseUsersAddTeamResponse(_message.Message): message: str resultCode: str additionalInfo: str - def __init__(self, teamUid: _Optional[bytes] = ..., users: _Optional[_Iterable[_Union[TeamsEnterpriseUsersAddUserResponse, _Mapping]]] = ..., success: _Optional[bool] = ..., message: _Optional[str] = ..., resultCode: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... + def __init__(self, teamUid: _Optional[bytes] = ..., users: _Optional[_Iterable[_Union[TeamsEnterpriseUsersAddUserResponse, _Mapping]]] = ..., success: bool = ..., message: _Optional[str] = ..., resultCode: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... class TeamsEnterpriseUsersAddUserResponse(_message.Message): __slots__ = ("enterpriseUserId", "revision", "success", "message", "resultCode", "additionalInfo") @@ -1879,7 +1895,7 @@ class TeamsEnterpriseUsersAddUserResponse(_message.Message): message: str resultCode: str additionalInfo: str - def __init__(self, enterpriseUserId: _Optional[int] = ..., revision: _Optional[int] = ..., success: _Optional[bool] = ..., message: _Optional[str] = ..., resultCode: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., revision: _Optional[int] = ..., success: bool = ..., message: _Optional[str] = ..., resultCode: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... class TeamEnterpriseUserRemove(_message.Message): __slots__ = ("teamUid", "enterpriseUserId") @@ -1913,7 +1929,7 @@ class TeamEnterpriseUserRemoveResponse(_message.Message): resultCode: str message: str additionalInfo: str - def __init__(self, teamEnterpriseUserRemove: _Optional[_Union[TeamEnterpriseUserRemove, _Mapping]] = ..., success: _Optional[bool] = ..., resultCode: _Optional[str] = ..., message: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... + def __init__(self, teamEnterpriseUserRemove: _Optional[_Union[TeamEnterpriseUserRemove, _Mapping]] = ..., success: bool = ..., resultCode: _Optional[str] = ..., message: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... class DomainAlias(_message.Message): __slots__ = ("domain", "alias", "status", "message") @@ -2033,7 +2049,7 @@ class EnterpriseUsersAdd(_message.Message): inviteeLocale: str move: bool roleId: int - def __init__(self, enterpriseUserId: _Optional[int] = ..., username: _Optional[str] = ..., nodeId: _Optional[int] = ..., encryptedData: _Optional[str] = ..., keyType: _Optional[_Union[EncryptedKeyType, str]] = ..., fullName: _Optional[str] = ..., jobTitle: _Optional[str] = ..., suppressEmailInvite: _Optional[bool] = ..., inviteeLocale: _Optional[str] = ..., move: _Optional[bool] = ..., roleId: _Optional[int] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., username: _Optional[str] = ..., nodeId: _Optional[int] = ..., encryptedData: _Optional[str] = ..., keyType: _Optional[_Union[EncryptedKeyType, str]] = ..., fullName: _Optional[str] = ..., jobTitle: _Optional[str] = ..., suppressEmailInvite: bool = ..., inviteeLocale: _Optional[str] = ..., move: bool = ..., roleId: _Optional[int] = ...) -> None: ... class EnterpriseUsersAddResponse(_message.Message): __slots__ = ("results", "success", "code", "message", "additionalInfo") @@ -2047,7 +2063,7 @@ class EnterpriseUsersAddResponse(_message.Message): code: str message: str additionalInfo: str - def __init__(self, results: _Optional[_Iterable[_Union[EnterpriseUsersAddResult, _Mapping]]] = ..., success: _Optional[bool] = ..., code: _Optional[str] = ..., message: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... + def __init__(self, results: _Optional[_Iterable[_Union[EnterpriseUsersAddResult, _Mapping]]] = ..., success: bool = ..., code: _Optional[str] = ..., message: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... class EnterpriseUsersAddResult(_message.Message): __slots__ = ("enterpriseUserId", "success", "verificationCode", "code", "message", "additionalInfo") @@ -2063,7 +2079,7 @@ class EnterpriseUsersAddResult(_message.Message): code: str message: str additionalInfo: str - def __init__(self, enterpriseUserId: _Optional[int] = ..., success: _Optional[bool] = ..., verificationCode: _Optional[str] = ..., code: _Optional[str] = ..., message: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[int] = ..., success: bool = ..., verificationCode: _Optional[str] = ..., code: _Optional[str] = ..., message: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... class UpdateMSPPermitsRequest(_message.Message): __slots__ = ("mspEnterpriseId", "maxAllowedLicenses", "allowedMcProducts", "allowedAddOns", "maxFilePlanType", "allowUnlimitedLicenses") @@ -2079,7 +2095,7 @@ class UpdateMSPPermitsRequest(_message.Message): allowedAddOns: _containers.RepeatedScalarFieldContainer[str] maxFilePlanType: str allowUnlimitedLicenses: bool - def __init__(self, mspEnterpriseId: _Optional[int] = ..., maxAllowedLicenses: _Optional[int] = ..., allowedMcProducts: _Optional[_Iterable[str]] = ..., allowedAddOns: _Optional[_Iterable[str]] = ..., maxFilePlanType: _Optional[str] = ..., allowUnlimitedLicenses: _Optional[bool] = ...) -> None: ... + def __init__(self, mspEnterpriseId: _Optional[int] = ..., maxAllowedLicenses: _Optional[int] = ..., allowedMcProducts: _Optional[_Iterable[str]] = ..., allowedAddOns: _Optional[_Iterable[str]] = ..., maxFilePlanType: _Optional[str] = ..., allowUnlimitedLicenses: bool = ...) -> None: ... class DeleteEnterpriseUsersRequest(_message.Message): __slots__ = ("enterpriseUserIds",) @@ -2109,7 +2125,7 @@ class ClearSecurityDataRequest(_message.Message): enterpriseUserId: _containers.RepeatedScalarFieldContainer[int] allUsers: bool type: ClearSecurityDataType - def __init__(self, enterpriseUserId: _Optional[_Iterable[int]] = ..., allUsers: _Optional[bool] = ..., type: _Optional[_Union[ClearSecurityDataType, str]] = ...) -> None: ... + def __init__(self, enterpriseUserId: _Optional[_Iterable[int]] = ..., allUsers: bool = ..., type: _Optional[_Union[ClearSecurityDataType, str]] = ...) -> None: ... class ListDomainsResponse(_message.Message): __slots__ = ("domain",) @@ -2149,7 +2165,7 @@ class LockUsersRequest(_message.Message): disableEnterpriseUserIds: _containers.RepeatedScalarFieldContainer[int] unlockEnterpriseUserIds: _containers.RepeatedScalarFieldContainer[int] deleteIfPending: bool - def __init__(self, lockEnterpriseUserIds: _Optional[_Iterable[int]] = ..., disableEnterpriseUserIds: _Optional[_Iterable[int]] = ..., unlockEnterpriseUserIds: _Optional[_Iterable[int]] = ..., deleteIfPending: _Optional[bool] = ...) -> None: ... + def __init__(self, lockEnterpriseUserIds: _Optional[_Iterable[int]] = ..., disableEnterpriseUserIds: _Optional[_Iterable[int]] = ..., unlockEnterpriseUserIds: _Optional[_Iterable[int]] = ..., deleteIfPending: bool = ...) -> None: ... class LockUsersResponse(_message.Message): __slots__ = ("response",) diff --git a/keepercommander/proto/folder_access_pb2.py b/keepercommander/proto/folder_access_pb2.py index 4fff617fb..d5e7d1968 100644 --- a/keepercommander/proto/folder_access_pb2.py +++ b/keepercommander/proto/folder_access_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: folder_access.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'folder_access.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/folder_access_pb2.pyi b/keepercommander/proto/folder_access_pb2.pyi index f555cb6e8..9e2e82bbf 100644 --- a/keepercommander/proto/folder_access_pb2.pyi +++ b/keepercommander/proto/folder_access_pb2.pyi @@ -3,8 +3,7 @@ from google.api import annotations_pb2 as _annotations_pb2 from google.protobuf.internal import containers as _containers 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -26,7 +25,7 @@ class GetFolderAccessResponse(_message.Message): folderAccessResults: _containers.RepeatedCompositeFieldContainer[GetFolderAccessResult] continuationToken: ContinuationToken hasMore: bool - def __init__(self, folderAccessResults: _Optional[_Iterable[_Union[GetFolderAccessResult, _Mapping]]] = ..., continuationToken: _Optional[_Union[ContinuationToken, _Mapping]] = ..., hasMore: _Optional[bool] = ...) -> None: ... + def __init__(self, folderAccessResults: _Optional[_Iterable[_Union[GetFolderAccessResult, _Mapping]]] = ..., continuationToken: _Optional[_Union[ContinuationToken, _Mapping]] = ..., hasMore: bool = ...) -> None: ... class ContinuationToken(_message.Message): __slots__ = ("lastModified",) diff --git a/keepercommander/proto/folder_pb2.py b/keepercommander/proto/folder_pb2.py index 52c30223b..d934a4354 100644 --- a/keepercommander/proto/folder_pb2.py +++ b/keepercommander/proto/folder_pb2.py @@ -1,17 +1,28 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: folder.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'folder.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -from .import record_pb2 as record__pb2 +from . import record_pb2 as record__pb2 from . import tla_pb2 as tla__pb2 diff --git a/keepercommander/proto/folder_pb2.pyi b/keepercommander/proto/folder_pb2.pyi index f9a2fb97a..57a857974 100644 --- a/keepercommander/proto/folder_pb2.pyi +++ b/keepercommander/proto/folder_pb2.pyi @@ -4,8 +4,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -244,7 +243,7 @@ class SharedFolderFields(_message.Message): manageRecords: bool canEdit: bool canShare: bool - def __init__(self, encryptedFolderName: _Optional[bytes] = ..., manageUsers: _Optional[bool] = ..., manageRecords: _Optional[bool] = ..., canEdit: _Optional[bool] = ..., canShare: _Optional[bool] = ...) -> None: ... + def __init__(self, encryptedFolderName: _Optional[bytes] = ..., manageUsers: bool = ..., manageRecords: bool = ..., canEdit: bool = ..., canShare: bool = ...) -> None: ... class SharedFolderFolderFields(_message.Message): __slots__ = ("sharedFolderUid",) @@ -318,7 +317,7 @@ class SharedFolderUpdateRecord(_message.Message): expiration: int timerNotificationType: _record_pb2.TimerNotificationType rotateOnExpiration: bool - def __init__(self, recordUid: _Optional[bytes] = ..., sharedFolderUid: _Optional[bytes] = ..., teamUid: _Optional[bytes] = ..., canEdit: _Optional[_Union[SetBooleanValue, str]] = ..., canShare: _Optional[_Union[SetBooleanValue, str]] = ..., encryptedRecordKey: _Optional[bytes] = ..., revision: _Optional[int] = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., sharedFolderUid: _Optional[bytes] = ..., teamUid: _Optional[bytes] = ..., canEdit: _Optional[_Union[SetBooleanValue, str]] = ..., canShare: _Optional[_Union[SetBooleanValue, str]] = ..., encryptedRecordKey: _Optional[bytes] = ..., revision: _Optional[int] = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., rotateOnExpiration: bool = ...) -> None: ... class SharedFolderUpdateUser(_message.Message): __slots__ = ("username", "manageUsers", "manageRecords", "sharedFolderKey", "expiration", "timerNotificationType", "typedSharedFolderKey", "rotateOnExpiration") @@ -338,7 +337,7 @@ class SharedFolderUpdateUser(_message.Message): timerNotificationType: _record_pb2.TimerNotificationType typedSharedFolderKey: EncryptedDataKey rotateOnExpiration: bool - def __init__(self, username: _Optional[str] = ..., manageUsers: _Optional[_Union[SetBooleanValue, str]] = ..., manageRecords: _Optional[_Union[SetBooleanValue, str]] = ..., sharedFolderKey: _Optional[bytes] = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., typedSharedFolderKey: _Optional[_Union[EncryptedDataKey, _Mapping]] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, username: _Optional[str] = ..., manageUsers: _Optional[_Union[SetBooleanValue, str]] = ..., manageRecords: _Optional[_Union[SetBooleanValue, str]] = ..., sharedFolderKey: _Optional[bytes] = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., typedSharedFolderKey: _Optional[_Union[EncryptedDataKey, _Mapping]] = ..., rotateOnExpiration: bool = ...) -> None: ... class SharedFolderUpdateTeam(_message.Message): __slots__ = ("teamUid", "manageUsers", "manageRecords", "sharedFolderKey", "expiration", "timerNotificationType", "typedSharedFolderKey", "rotateOnExpiration") @@ -358,7 +357,7 @@ class SharedFolderUpdateTeam(_message.Message): timerNotificationType: _record_pb2.TimerNotificationType typedSharedFolderKey: EncryptedDataKey rotateOnExpiration: bool - def __init__(self, teamUid: _Optional[bytes] = ..., manageUsers: _Optional[bool] = ..., manageRecords: _Optional[bool] = ..., sharedFolderKey: _Optional[bytes] = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., typedSharedFolderKey: _Optional[_Union[EncryptedDataKey, _Mapping]] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, teamUid: _Optional[bytes] = ..., manageUsers: bool = ..., manageRecords: bool = ..., sharedFolderKey: _Optional[bytes] = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[_record_pb2.TimerNotificationType, str]] = ..., typedSharedFolderKey: _Optional[_Union[EncryptedDataKey, _Mapping]] = ..., rotateOnExpiration: bool = ...) -> None: ... class SharedFolderUpdateV3Request(_message.Message): __slots__ = ("sharedFolderUpdateOperation_dont_use", "sharedFolderUid", "encryptedSharedFolderName", "revision", "forceUpdate", "fromTeamUid", "defaultManageUsers", "defaultManageRecords", "defaultCanEdit", "defaultCanShare", "sharedFolderAddRecord", "sharedFolderAddUser", "sharedFolderAddTeam", "sharedFolderUpdateRecord", "sharedFolderUpdateUser", "sharedFolderUpdateTeam", "sharedFolderRemoveRecord", "sharedFolderRemoveUser", "sharedFolderRemoveTeam", "sharedFolderOwner") @@ -402,7 +401,7 @@ class SharedFolderUpdateV3Request(_message.Message): sharedFolderRemoveUser: _containers.RepeatedScalarFieldContainer[str] sharedFolderRemoveTeam: _containers.RepeatedScalarFieldContainer[bytes] sharedFolderOwner: str - def __init__(self, sharedFolderUpdateOperation_dont_use: _Optional[int] = ..., sharedFolderUid: _Optional[bytes] = ..., encryptedSharedFolderName: _Optional[bytes] = ..., revision: _Optional[int] = ..., forceUpdate: _Optional[bool] = ..., fromTeamUid: _Optional[bytes] = ..., defaultManageUsers: _Optional[_Union[SetBooleanValue, str]] = ..., defaultManageRecords: _Optional[_Union[SetBooleanValue, str]] = ..., defaultCanEdit: _Optional[_Union[SetBooleanValue, str]] = ..., defaultCanShare: _Optional[_Union[SetBooleanValue, str]] = ..., sharedFolderAddRecord: _Optional[_Iterable[_Union[SharedFolderUpdateRecord, _Mapping]]] = ..., sharedFolderAddUser: _Optional[_Iterable[_Union[SharedFolderUpdateUser, _Mapping]]] = ..., sharedFolderAddTeam: _Optional[_Iterable[_Union[SharedFolderUpdateTeam, _Mapping]]] = ..., sharedFolderUpdateRecord: _Optional[_Iterable[_Union[SharedFolderUpdateRecord, _Mapping]]] = ..., sharedFolderUpdateUser: _Optional[_Iterable[_Union[SharedFolderUpdateUser, _Mapping]]] = ..., sharedFolderUpdateTeam: _Optional[_Iterable[_Union[SharedFolderUpdateTeam, _Mapping]]] = ..., sharedFolderRemoveRecord: _Optional[_Iterable[bytes]] = ..., sharedFolderRemoveUser: _Optional[_Iterable[str]] = ..., sharedFolderRemoveTeam: _Optional[_Iterable[bytes]] = ..., sharedFolderOwner: _Optional[str] = ...) -> None: ... + def __init__(self, sharedFolderUpdateOperation_dont_use: _Optional[int] = ..., sharedFolderUid: _Optional[bytes] = ..., encryptedSharedFolderName: _Optional[bytes] = ..., revision: _Optional[int] = ..., forceUpdate: bool = ..., fromTeamUid: _Optional[bytes] = ..., defaultManageUsers: _Optional[_Union[SetBooleanValue, str]] = ..., defaultManageRecords: _Optional[_Union[SetBooleanValue, str]] = ..., defaultCanEdit: _Optional[_Union[SetBooleanValue, str]] = ..., defaultCanShare: _Optional[_Union[SetBooleanValue, str]] = ..., sharedFolderAddRecord: _Optional[_Iterable[_Union[SharedFolderUpdateRecord, _Mapping]]] = ..., sharedFolderAddUser: _Optional[_Iterable[_Union[SharedFolderUpdateUser, _Mapping]]] = ..., sharedFolderAddTeam: _Optional[_Iterable[_Union[SharedFolderUpdateTeam, _Mapping]]] = ..., sharedFolderUpdateRecord: _Optional[_Iterable[_Union[SharedFolderUpdateRecord, _Mapping]]] = ..., sharedFolderUpdateUser: _Optional[_Iterable[_Union[SharedFolderUpdateUser, _Mapping]]] = ..., sharedFolderUpdateTeam: _Optional[_Iterable[_Union[SharedFolderUpdateTeam, _Mapping]]] = ..., sharedFolderRemoveRecord: _Optional[_Iterable[bytes]] = ..., sharedFolderRemoveUser: _Optional[_Iterable[str]] = ..., sharedFolderRemoveTeam: _Optional[_Iterable[bytes]] = ..., sharedFolderOwner: _Optional[str] = ...) -> None: ... class SharedFolderUpdateV3RequestV2(_message.Message): __slots__ = ("sharedFoldersUpdateV3",) @@ -650,7 +649,7 @@ class FolderPermissions(_message.Message): canUpdateSetting: bool canListRecords: bool canListFolders: bool - def __init__(self, canAdd: _Optional[bool] = ..., canRemove: _Optional[bool] = ..., canDelete: _Optional[bool] = ..., canListAccess: _Optional[bool] = ..., canUpdateAccess: _Optional[bool] = ..., canChangeOwnership: _Optional[bool] = ..., canEditRecords: _Optional[bool] = ..., canViewRecords: _Optional[bool] = ..., canApproveAccess: _Optional[bool] = ..., canRequestAccess: _Optional[bool] = ..., canUpdateSetting: _Optional[bool] = ..., canListRecords: _Optional[bool] = ..., canListFolders: _Optional[bool] = ...) -> None: ... + def __init__(self, canAdd: bool = ..., canRemove: bool = ..., canDelete: bool = ..., canListAccess: bool = ..., canUpdateAccess: bool = ..., canChangeOwnership: bool = ..., canEditRecords: bool = ..., canViewRecords: bool = ..., canApproveAccess: bool = ..., canRequestAccess: bool = ..., canUpdateSetting: bool = ..., canListRecords: bool = ..., canListFolders: bool = ...) -> None: ... class Capabilities(_message.Message): __slots__ = ("canAdd", "canRemove", "canDelete", "canListAccess", "canUpdateAccess", "canChangeOwnership", "canEditRecords", "canViewRecords", "canApproveAccess", "canRequestAccess", "canUpdateSetting", "canListRecords", "canListFolders") @@ -760,7 +759,7 @@ class FolderAccessData(_message.Message): dateCreated: int lastModified: int deniedAccess: bool - def __init__(self, folderUid: _Optional[bytes] = ..., accessTypeUid: _Optional[bytes] = ..., accessType: _Optional[_Union[AccessType, str]] = ..., accessRoleType: _Optional[_Union[AccessRoleType, str]] = ..., folderKey: _Optional[_Union[EncryptedDataKey, _Mapping]] = ..., inherited: _Optional[bool] = ..., hidden: _Optional[bool] = ..., permissions: _Optional[_Union[FolderPermissions, _Mapping]] = ..., tlaProperties: _Optional[_Union[_tla_pb2.TLAProperties, _Mapping]] = ..., dateCreated: _Optional[int] = ..., lastModified: _Optional[int] = ..., deniedAccess: _Optional[bool] = ...) -> None: ... + def __init__(self, folderUid: _Optional[bytes] = ..., accessTypeUid: _Optional[bytes] = ..., accessType: _Optional[_Union[AccessType, str]] = ..., accessRoleType: _Optional[_Union[AccessRoleType, str]] = ..., folderKey: _Optional[_Union[EncryptedDataKey, _Mapping]] = ..., inherited: bool = ..., hidden: bool = ..., permissions: _Optional[_Union[FolderPermissions, _Mapping]] = ..., tlaProperties: _Optional[_Union[_tla_pb2.TLAProperties, _Mapping]] = ..., dateCreated: _Optional[int] = ..., lastModified: _Optional[int] = ..., deniedAccess: bool = ...) -> None: ... class RevokedAccess(_message.Message): __slots__ = ("folderUid", "actorUid", "accessType") @@ -820,7 +819,7 @@ class RecordAccessData(_message.Message): dateCreated: int lastModified: int tlaProperties: _tla_pb2.TLAProperties - def __init__(self, accessTypeUid: _Optional[bytes] = ..., accessType: _Optional[_Union[AccessType, str]] = ..., recordUid: _Optional[bytes] = ..., accessRoleType: _Optional[_Union[AccessRoleType, str]] = ..., owner: _Optional[bool] = ..., inherited: _Optional[bool] = ..., hidden: _Optional[bool] = ..., deniedAccess: _Optional[bool] = ..., can_view_title: _Optional[bool] = ..., can_edit: _Optional[bool] = ..., can_view: _Optional[bool] = ..., can_list_access: _Optional[bool] = ..., can_update_access: _Optional[bool] = ..., can_delete: _Optional[bool] = ..., can_change_ownership: _Optional[bool] = ..., can_request_access: _Optional[bool] = ..., can_approve_access: _Optional[bool] = ..., dateCreated: _Optional[int] = ..., lastModified: _Optional[int] = ..., tlaProperties: _Optional[_Union[_tla_pb2.TLAProperties, _Mapping]] = ...) -> None: ... + def __init__(self, accessTypeUid: _Optional[bytes] = ..., accessType: _Optional[_Union[AccessType, str]] = ..., recordUid: _Optional[bytes] = ..., accessRoleType: _Optional[_Union[AccessRoleType, str]] = ..., owner: bool = ..., inherited: bool = ..., hidden: bool = ..., deniedAccess: bool = ..., can_view_title: bool = ..., can_edit: bool = ..., can_view: bool = ..., can_list_access: bool = ..., can_update_access: bool = ..., can_delete: bool = ..., can_change_ownership: bool = ..., can_request_access: bool = ..., can_approve_access: bool = ..., dateCreated: _Optional[int] = ..., lastModified: _Optional[int] = ..., tlaProperties: _Optional[_Union[_tla_pb2.TLAProperties, _Mapping]] = ...) -> None: ... class AccessData(_message.Message): __slots__ = ("accessTypeUid", "accessRoleType", "deniedAccess", "inherited", "hidden", "capabilities") @@ -836,7 +835,7 @@ class AccessData(_message.Message): inherited: bool hidden: bool capabilities: Capabilities - def __init__(self, accessTypeUid: _Optional[bytes] = ..., accessRoleType: _Optional[_Union[AccessRoleType, str]] = ..., deniedAccess: _Optional[bool] = ..., inherited: _Optional[bool] = ..., hidden: _Optional[bool] = ..., capabilities: _Optional[_Union[Capabilities, _Mapping]] = ...) -> None: ... + def __init__(self, accessTypeUid: _Optional[bytes] = ..., accessRoleType: _Optional[_Union[AccessRoleType, str]] = ..., deniedAccess: bool = ..., inherited: bool = ..., hidden: bool = ..., capabilities: _Optional[_Union[Capabilities, _Mapping]] = ...) -> None: ... class FolderAccessRequest(_message.Message): __slots__ = ("folderAccessAdds", "folderAccessUpdates", "folderAccessRemoves") diff --git a/keepercommander/proto/pagination_pb2.py b/keepercommander/proto/pagination_pb2.py index e74c67e39..85b834b66 100644 --- a/keepercommander/proto/pagination_pb2.py +++ b/keepercommander/proto/pagination_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: pagination.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'pagination.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/pagination_pb2.pyi b/keepercommander/proto/pagination_pb2.pyi index 08c2e73a5..bb7a09bfb 100644 --- a/keepercommander/proto/pagination_pb2.pyi +++ b/keepercommander/proto/pagination_pb2.pyi @@ -26,4 +26,4 @@ class PageInfo(_message.Message): totalCount: int hasMore: bool cursorToken: str - def __init__(self, pageNumber: _Optional[int] = ..., pageSize: _Optional[int] = ..., totalCount: _Optional[int] = ..., hasMore: _Optional[bool] = ..., cursorToken: _Optional[str] = ...) -> None: ... + def __init__(self, pageNumber: _Optional[int] = ..., pageSize: _Optional[int] = ..., totalCount: _Optional[int] = ..., hasMore: bool = ..., cursorToken: _Optional[str] = ...) -> None: ... diff --git a/keepercommander/proto/pam_pb2.py b/keepercommander/proto/pam_pb2.py index dbeced705..9f8a54d8a 100644 --- a/keepercommander/proto/pam_pb2.py +++ b/keepercommander/proto/pam_pb2.py @@ -1,13 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: pam.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'pam.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,662 +26,118 @@ from . import record_pb2 as record__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tpam.proto\x12\x03PAM\x1a\x10\x65nterprise.proto\x1a\x0crecord.proto\"\x83\x01\n\x13PAMRotationSchedule\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\x14\n\x0cscheduleData\x18\x04 \x01(\t\x12\x12\n\nnoSchedule\x18\x05 \x01(\x08\"K\n\x1cPAMRotationSchedulesResponse\x12+\n\tschedules\x18\x01 \x03(\x0b\x32\x18.PAM.PAMRotationSchedule\"\x94\x01\n\x13PAMOnlineController\x12\x15\n\rcontrollerUid\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63onnectedOn\x18\x02 \x01(\x03\x12\x11\n\tipAddress\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12-\n\x0b\x63onnections\x18\x05 \x03(\x0b\x32\x18.PAM.PAMWebRtcConnection\"\xa7\x01\n\x13PAMWebRtcConnection\x12\x15\n\rconnectionUid\x18\x01 \x01(\x0c\x12\'\n\x04type\x18\x02 \x01(\x0e\x32\x19.PAM.WebRtcConnectionType\x12\x11\n\trecordUid\x18\x03 \x01(\x0c\x12\x10\n\x08userName\x18\x04 \x01(\t\x12\x11\n\tstartedOn\x18\x05 \x01(\x03\x12\x18\n\x10\x63onfigurationUid\x18\x06 \x01(\x0c\"Y\n\x14PAMOnlineControllers\x12\x12\n\ndeprecated\x18\x01 \x03(\x0c\x12-\n\x0b\x63ontrollers\x18\x02 \x03(\x0b\x32\x18.PAM.PAMOnlineController\"9\n\x10PAMRotateRequest\x12\x12\n\nrequestUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\"A\n\x16PAMControllersResponse\x12\'\n\x0b\x63ontrollers\x18\x01 \x03(\x0b\x32\x12.PAM.PAMController\"=\n\x13PAMRemoveController\x12\x15\n\rcontrollerUid\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\t\"L\n\x1bPAMRemoveControllerResponse\x12-\n\x0b\x63ontrollers\x18\x01 \x03(\x0b\x32\x18.PAM.PAMRemoveController\"=\n\x10PAMModifyRequest\x12)\n\noperations\x18\x01 \x03(\x0b\x32\x15.PAM.PAMDataOperation\"\x98\x01\n\x10PAMDataOperation\x12,\n\roperationType\x18\x01 \x01(\x0e\x32\x15.PAM.PAMOperationType\x12\x30\n\rconfiguration\x18\x02 \x01(\x0b\x32\x19.PAM.PAMConfigurationData\x12$\n\x07\x65lement\x18\x03 \x01(\x0b\x32\x13.PAM.PAMElementData\"e\n\x14PAMConfigurationData\x12\x18\n\x10\x63onfigurationUid\x18\x01 \x01(\x0c\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\"E\n\x0ePAMElementData\x12\x12\n\nelementUid\x18\x01 \x01(\x0c\x12\x11\n\tparentUid\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\"m\n\x19PAMElementOperationResult\x12\x12\n\nelementUid\x18\x01 \x01(\x0c\x12+\n\x06result\x18\x02 \x01(\x0e\x32\x1b.PAM.PAMOperationResultType\x12\x0f\n\x07message\x18\x03 \x01(\t\"B\n\x0fPAMModifyResult\x12/\n\x07results\x18\x01 \x03(\x0b\x32\x1e.PAM.PAMElementOperationResult\"x\n\nPAMElement\x12\x12\n\nelementUid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x03 \x01(\x03\x12\x14\n\x0clastModified\x18\x04 \x01(\x03\x12!\n\x08\x63hildren\x18\x05 \x03(\x0b\x32\x0f.PAM.PAMElement\"#\n\x14PAMGenericUidRequest\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\"%\n\x15PAMGenericUidsRequest\x12\x0c\n\x04uids\x18\x01 \x03(\x0c\"\xab\x01\n\x10PAMConfiguration\x12\x18\n\x10\x63onfigurationUid\x18\x01 \x01(\x0c\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x05 \x01(\x03\x12\x14\n\x0clastModified\x18\x06 \x01(\x03\x12!\n\x08\x63hildren\x18\x07 \x03(\x0b\x32\x0f.PAM.PAMElement\"B\n\x11PAMConfigurations\x12-\n\x0e\x63onfigurations\x18\x01 \x03(\x0b\x32\x15.PAM.PAMConfiguration\"\xff\x01\n\rPAMController\x12\x15\n\rcontrollerUid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x63ontrollerName\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65viceToken\x18\x03 \x01(\t\x12\x12\n\ndeviceName\x18\x04 \x01(\t\x12\x0e\n\x06nodeId\x18\x05 \x01(\x03\x12\x0f\n\x07\x63reated\x18\x06 \x01(\x03\x12\x14\n\x0clastModified\x18\x07 \x01(\x03\x12\x16\n\x0e\x61pplicationUid\x18\x08 \x01(\x0c\x12\x30\n\rappClientType\x18\t \x01(\x0e\x32\x19.Enterprise.AppClientType\x12\x15\n\risInitialized\x18\n \x01(\x08\"P\n\x1dPAMSetMaxInstanceCountRequest\x12\x15\n\rcontrollerUid\x18\x01 \x01(\x0c\x12\x18\n\x10maxInstanceCount\x18\x02 \x01(\x05\"%\n\x12\x43ontrollerResponse\x12\x0f\n\x07payload\x18\x01 \x01(\t\"M\n\x1aPAMConfigurationController\x12\x18\n\x10\x63onfigurationUid\x18\x01 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\x02 \x01(\x0c\"\xa3\x01\n\x17\x43onfigurationAddRequest\x12\x18\n\x10\x63onfigurationUid\x18\x01 \x01(\x0c\x12\x11\n\trecordKey\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12(\n\x0brecordLinks\x18\x04 \x03(\x0b\x32\x13.Records.RecordLink\x12#\n\x05\x61udit\x18\x05 \x01(\x0b\x32\x14.Records.RecordAudit\"J\n\x10RelayAccessCreds\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\x12\x12\n\nserverTime\x18\x03 \x01(\x03\"\x81\x02\n\x14PAMRecordingsRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08maxCount\x18\x02 \x01(\x05\x12\x17\n\nrangeStart\x18\x03 \x01(\x03H\x00\x88\x01\x01\x12\x15\n\x08rangeEnd\x18\x04 \x01(\x03H\x01\x88\x01\x01\x12$\n\x05types\x18\x05 \x03(\x0e\x32\x15.PAM.PAMRecordingType\x12)\n\x05risks\x18\x06 \x03(\x0e\x32\x1a.PAM.PAMRecordingRiskLevel\x12\x11\n\tprotocols\x18\x07 \x03(\t\x12\x14\n\x0c\x63loseReasons\x18\x08 \x03(\x05\x42\r\n\x0b_rangeStartB\x0b\n\t_rangeEnd\"\xd4\x02\n\x0cPAMRecording\x12\x15\n\rconnectionUid\x18\x01 \x01(\x0c\x12,\n\rrecordingType\x18\x02 \x01(\x0e\x32\x15.PAM.PAMRecordingType\x12\x11\n\trecordUid\x18\x03 \x01(\x0c\x12\x10\n\x08userName\x18\x04 \x01(\t\x12\x11\n\tstartedOn\x18\x05 \x01(\x03\x12\x0e\n\x06length\x18\x06 \x01(\x05\x12\x10\n\x08\x66ileSize\x18\x07 \x01(\x03\x12\x11\n\tcreatedOn\x18\x08 \x01(\x03\x12\x10\n\x08protocol\x18\t \x01(\t\x12\x13\n\x0b\x63loseReason\x18\n \x01(\x05\x12\x19\n\x11recordingDuration\x18\x0b \x01(\x05\x12\x36\n\x12\x61iOverallRiskLevel\x18\x0c \x01(\x0e\x32\x1a.PAM.PAMRecordingRiskLevel\x12\x18\n\x10\x61iOverallSummary\x18\r \x01(\x0c\"O\n\x15PAMRecordingsResponse\x12%\n\nrecordings\x18\x01 \x03(\x0b\x32\x11.PAM.PAMRecording\x12\x0f\n\x07hasMore\x18\x02 \x01(\x08\"*\n\x07PAMData\x12\x0e\n\x06vertex\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\x0c\"\x17\n\x07UidList\x12\x0c\n\x04uids\x18\x01 \x03(\x0c\"\x84\x03\n\x11PAMResourceConfig\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x17\n\nnetworkUid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08\x61\x64minUid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x11\n\x04meta\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x1f\n\x12\x63onnectionSettings\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\'\n\x0c\x63onnectUsers\x18\x06 \x01(\x0b\x32\x0c.PAM.UidListH\x04\x88\x01\x01\x12\x16\n\tdomainUid\x18\x07 \x01(\x0cH\x05\x88\x01\x01\x12\x18\n\x0bjitSettings\x18\x08 \x01(\x0cH\x06\x88\x01\x01\x12\x1d\n\x10keeperAiSettings\x18\t \x01(\x0cH\x07\x88\x01\x01\x42\r\n\x0b_networkUidB\x0b\n\t_adminUidB\x07\n\x05_metaB\x15\n\x13_connectionSettingsB\x0f\n\r_connectUsersB\x0c\n\n_domainUidB\x0e\n\x0c_jitSettingsB\x13\n\x11_keeperAiSettings\"%\n\x16PAMUniversalSyncFolder\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\"\xfc\x01\n\x16PAMUniversalSyncConfig\x12\x12\n\nnetworkUid\x18\x01 \x01(\x0c\x12\x14\n\x07\x65nabled\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rdryRunEnabled\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12,\n\x07\x66olders\x18\x04 \x03(\x0b\x32\x1b.PAM.PAMUniversalSyncFolder\x12\x19\n\x0csyncIdentity\x18\x05 \x01(\x0cH\x02\x88\x01\x01\x12\x16\n\tvaultName\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x42\n\n\x08_enabledB\x10\n\x0e_dryRunEnabledB\x0f\n\r_syncIdentityB\x0c\n\n_vaultName\"\xd5\x01\n\x13\x43nappWebhookRequest\x12\x12\n\nnetworkUid\x18\x01 \x01(\x0c\x12\x10\n\x08provider\x18\x02 \x01(\t\x12\x10\n\x08\x63lientId\x18\x03 \x01(\t\x12\x14\n\x0c\x63lientSecret\x18\x04 \x01(\t\x12\x16\n\x0e\x61piEndpointUrl\x18\x05 \x01(\t\x12\x0f\n\x07\x61uthUrl\x18\x06 \x01(\t\x12\x1d\n\x15\x65ncryptionRecordKeyId\x18\x07 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\x08 \x01(\x0c\x12\x11\n\twebhookId\x18\t \x01(\t\"S\n\x14\x43nappWebhookResponse\x12\x11\n\twebhookId\x18\x01 \x01(\t\x12\x12\n\nwebhookUrl\x18\x02 \x01(\t\x12\x14\n\x0cwebhookToken\x18\x03 \x01(\t\"/\n\x19\x43nappDeleteWebhookRequest\x12\x12\n\nnetworkUid\x18\x01 \x01(\x0c\"0\n\x1a\x43nappGetIntegrationRequest\x12\x12\n\nnetworkUid\x18\x01 \x01(\x0c\"\x8a\x01\n\x17\x43nappGetIntegrationItem\x12\x10\n\x08provider\x18\x01 \x01(\t\x12\x11\n\twebhookId\x18\x02 \x01(\t\x12\x0e\n\x06\x61piUrl\x18\x03 \x01(\t\x12\x0f\n\x07\x61uthUrl\x18\x04 \x01(\t\x12\x15\n\rcontrollerUid\x18\x05 \x01(\t\x12\x12\n\nwebhookUrl\x18\x06 \x01(\t\"Q\n\x1b\x43nappGetIntegrationResponse\x12\x32\n\x0cintegrations\x18\x01 \x03(\x0b\x32\x1c.PAM.CnappGetIntegrationItem\"\x80\x01\n\x1b\x43nappTestCredentialsRequest\x12\x10\n\x08provider\x18\x01 \x01(\t\x12\x10\n\x08\x63lientId\x18\x02 \x01(\t\x12\x14\n\x0c\x63lientSecret\x18\x03 \x01(\t\x12\x16\n\x0e\x61piEndpointUrl\x18\x04 \x01(\t\x12\x0f\n\x07\x61uthUrl\x18\x05 \x01(\t\"M\n\x1c\x43nappTestCredentialsResponse\x12\r\n\x05valid\x18\x01 \x01(\x08\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\"`\n\x15\x43nappQueueListRequest\x12\x12\n\nnetworkUid\x18\x01 \x01(\x0c\x12\x14\n\x0cstatusFilter\x18\x02 \x01(\x05\x12\r\n\x05limit\x18\x03 \x01(\x05\x12\x0e\n\x06offset\x18\x04 \x01(\x05\"\xbb\x01\n\x0e\x43nappQueueItem\x12\x14\n\x0c\x63nappQueueId\x18\x01 \x01(\t\x12\x12\n\ncontrolKey\x18\x02 \x01(\t\x12\x17\n\x0f\x63nappProviderId\x18\x03 \x01(\x05\x12\x1a\n\x12\x63nappQueueStatusId\x18\x04 \x01(\x05\x12\x12\n\nreceivedAt\x18\x05 \x01(\x03\x12\x12\n\nresolvedAt\x18\x06 \x01(\x03\x12\x11\n\trecordUid\x18\x07 \x01(\x0c\x12\x0f\n\x07payload\x18\x08 \x01(\x0c\"j\n\x16\x43nappQueueListResponse\x12\"\n\x05items\x18\x01 \x03(\x0b\x32\x13.PAM.CnappQueueItem\x12\r\n\x05total\x18\x02 \x01(\x05\x12\x1d\n\x15\x65ncryptionRecordKeyId\x18\x03 \x01(\x0c\"\x8c\x02\n\x16\x43nappQueueItemResponse\x12\x14\n\x0c\x63nappQueueId\x18\x01 \x01(\t\x12\x12\n\ncontrolKey\x18\x02 \x01(\t\x12\x17\n\x0f\x63nappProviderId\x18\x03 \x01(\x05\x12\x1a\n\x12\x63nappQueueStatusId\x18\x04 \x01(\x05\x12\x12\n\nreceivedAt\x18\x05 \x01(\x03\x12\x12\n\nresolvedAt\x18\x06 \x01(\x03\x12\x11\n\trecordUid\x18\x07 \x01(\x0c\x12\x0f\n\x07payload\x18\x08 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\t \x01(\x0c\x12\x11\n\tnetworkId\x18\n \x01(\x0c\x12\x1d\n\x15\x65ncryptionRecordKeyId\x18\x0b \x01(\x0c\"E\n\x15\x43nappAssociateRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x19\n\x11\x65xecuteAfterSetup\x18\x02 \x01(\x08\"F\n\x16\x43nappAssociateResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\x12\x1c\n\x14remediationTriggered\x18\x02 \x01(\x08\".\n\x13\x43nappResolveRequest\x12\x17\n\x0fresolutionNotes\x18\x01 \x01(\t\"+\n\x15\x43nappRemediateRequest\x12\x12\n\nactionType\x18\x01 \x01(\t\"e\n\x16\x43nappRemediateResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\x12\x12\n\nactionType\x18\x02 \x01(\t\x12\x0e\n\x06result\x18\x03 \x01(\t\x12\x17\n\x0f\x65xecutionTimeMs\x18\x04 \x01(\x03\"$\n\x12\x43nappIgnoreRequest\x12\x0e\n\x06reason\x18\x01 \x01(\t\"\x8d\x01\n\x1b\x43nappDefaultBehaviorRequest\x12\x11\n\tnetworkId\x18\x01 \x01(\x0c\x12\x17\n\x0f\x63nappProviderId\x18\x02 \x01(\x05\x12\x12\n\ncontrolKey\x18\x03 \x01(\t\x12\x19\n\x11\x63nappActionTypeId\x18\x04 \x01(\x05\x12\x13\n\x0b\x61utoExecute\x18\x05 \x01(\x08\">\n\x1c\x43nappDefaultBehaviorResponse\x12\x1e\n\x16\x63nappDefaultBehaviorId\x18\x01 \x01(\x05\".\n\x18\x43nappBehaviorListRequest\x12\x12\n\nnetworkUid\x18\x01 \x01(\x0c\"I\n\x19\x43nappBehaviorListResponse\x12,\n\x05items\x18\x01 \x03(\x0b\x32\x1d.PAM.CnappDefaultBehaviorItem\"\xa7\x01\n\x18\x43nappDefaultBehaviorItem\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x11\n\tnetworkId\x18\x02 \x01(\x0c\x12\x17\n\x0f\x63nappProviderId\x18\x03 \x01(\x05\x12\x12\n\ncontrolKey\x18\x04 \x01(\t\x12\x19\n\x11\x63nappActionTypeId\x18\x05 \x01(\x05\x12\x13\n\x0b\x61utoExecute\x18\x06 \x01(\x08\x12\x0f\n\x07\x65nabled\x18\x07 \x01(\x08\"\x91\x01\n\x1a\x43nappBehaviorUpdateRequest\x12\x1e\n\x16\x63nappDefaultBehaviorId\x18\x01 \x01(\x05\x12\x12\n\ncontrolKey\x18\x02 \x01(\t\x12\x19\n\x11\x63nappActionTypeId\x18\x03 \x01(\x05\x12\x13\n\x0b\x61utoExecute\x18\x04 \x01(\x08\x12\x0f\n\x07\x65nabled\x18\x05 \x01(\x08\"<\n\x1a\x43nappBehaviorDeleteRequest\x12\x1e\n\x16\x63nappDefaultBehaviorId\x18\x01 \x01(\x05*\x9e\x01\n\x14WebRtcConnectionType\x12\x0e\n\nCONNECTION\x10\x00\x12\n\n\x06TUNNEL\x10\x01\x12\x07\n\x03SSH\x10\x02\x12\x07\n\x03RDP\x10\x03\x12\x08\n\x04HTTP\x10\x04\x12\x07\n\x03VNC\x10\x05\x12\n\n\x06TELNET\x10\x06\x12\t\n\x05MYSQL\x10\x07\x12\x0e\n\nSQL_SERVER\x10\x08\x12\x0e\n\nPOSTGRESQL\x10\t\x12\x0e\n\nKUBERNETES\x10\n*@\n\x10PAMOperationType\x12\x07\n\x03\x41\x44\x44\x10\x00\x12\n\n\x06UPDATE\x10\x01\x12\x0b\n\x07REPLACE\x10\x02\x12\n\n\x06\x44\x45LETE\x10\x03*p\n\x16PAMOperationResultType\x12\x0f\n\x0bPOT_SUCCESS\x10\x00\x12\x15\n\x11POT_UNKNOWN_ERROR\x10\x01\x12\x16\n\x12POT_ALREADY_EXISTS\x10\x02\x12\x16\n\x12POT_DOES_NOT_EXIST\x10\x03*\xb5\x01\n\x15\x43ontrollerMessageType\x12\x0f\n\x0b\x43MT_GENERAL\x10\x00\x12\x0e\n\nCMT_ROTATE\x10\x01\x12\x11\n\rCMT_DISCOVERY\x10\x02\x12\x0f\n\x0b\x43MT_CONNECT\x10\x03\x12\x19\n\x15\x43MT_ANALYZE_RECORDING\x10\x04\x12!\n\x1d\x43MT_WORKFLOW_ACCESS_ELEVATION\x10\x05\x12\x0b\n\x07\x43MT_USS\x10\x06\x12\x0c\n\x08\x43MT_INFO\x10\x07*V\n\x10PAMRecordingType\x12\x0f\n\x0bPRT_SESSION\x10\x00\x12\x12\n\x0ePRT_TYPESCRIPT\x10\x01\x12\x0c\n\x08PRT_TIME\x10\x02\x12\x0f\n\x0bPRT_SUMMARY\x10\x03*i\n\x15PAMRecordingRiskLevel\x12\x13\n\x0fPRR_UNSPECIFIED\x10\x00\x12\x0b\n\x07PRR_LOW\x10\x01\x12\x0e\n\nPRR_MEDIUM\x10\x02\x12\x0c\n\x08PRR_HIGH\x10\x03\x12\x10\n\x0cPRR_CRITICAL\x10\x04\x42\x1f\n\x18\x63om.keepersecurity.protoB\x03PAMb\x06proto3') - -_WEBRTCCONNECTIONTYPE = DESCRIPTOR.enum_types_by_name['WebRtcConnectionType'] -WebRtcConnectionType = enum_type_wrapper.EnumTypeWrapper(_WEBRTCCONNECTIONTYPE) -_PAMOPERATIONTYPE = DESCRIPTOR.enum_types_by_name['PAMOperationType'] -PAMOperationType = enum_type_wrapper.EnumTypeWrapper(_PAMOPERATIONTYPE) -_PAMOPERATIONRESULTTYPE = DESCRIPTOR.enum_types_by_name['PAMOperationResultType'] -PAMOperationResultType = enum_type_wrapper.EnumTypeWrapper(_PAMOPERATIONRESULTTYPE) -_CONTROLLERMESSAGETYPE = DESCRIPTOR.enum_types_by_name['ControllerMessageType'] -ControllerMessageType = enum_type_wrapper.EnumTypeWrapper(_CONTROLLERMESSAGETYPE) -_PAMRECORDINGTYPE = DESCRIPTOR.enum_types_by_name['PAMRecordingType'] -PAMRecordingType = enum_type_wrapper.EnumTypeWrapper(_PAMRECORDINGTYPE) -_PAMRECORDINGRISKLEVEL = DESCRIPTOR.enum_types_by_name['PAMRecordingRiskLevel'] -PAMRecordingRiskLevel = enum_type_wrapper.EnumTypeWrapper(_PAMRECORDINGRISKLEVEL) -CONNECTION = 0 -TUNNEL = 1 -SSH = 2 -RDP = 3 -HTTP = 4 -VNC = 5 -TELNET = 6 -MYSQL = 7 -SQL_SERVER = 8 -POSTGRESQL = 9 -KUBERNETES = 10 -ADD = 0 -UPDATE = 1 -REPLACE = 2 -DELETE = 3 -POT_SUCCESS = 0 -POT_UNKNOWN_ERROR = 1 -POT_ALREADY_EXISTS = 2 -POT_DOES_NOT_EXIST = 3 -CMT_GENERAL = 0 -CMT_ROTATE = 1 -CMT_DISCOVERY = 2 -CMT_CONNECT = 3 -CMT_ANALYZE_RECORDING = 4 -CMT_WORKFLOW_ACCESS_ELEVATION = 5 -CMT_USS = 6 -CMT_INFO = 7 -PRT_SESSION = 0 -PRT_TYPESCRIPT = 1 -PRT_TIME = 2 -PRT_SUMMARY = 3 -PRR_UNSPECIFIED = 0 -PRR_LOW = 1 -PRR_MEDIUM = 2 -PRR_HIGH = 3 -PRR_CRITICAL = 4 - - -_PAMROTATIONSCHEDULE = DESCRIPTOR.message_types_by_name['PAMRotationSchedule'] -_PAMROTATIONSCHEDULESRESPONSE = DESCRIPTOR.message_types_by_name['PAMRotationSchedulesResponse'] -_PAMONLINECONTROLLER = DESCRIPTOR.message_types_by_name['PAMOnlineController'] -_PAMWEBRTCCONNECTION = DESCRIPTOR.message_types_by_name['PAMWebRtcConnection'] -_PAMONLINECONTROLLERS = DESCRIPTOR.message_types_by_name['PAMOnlineControllers'] -_PAMROTATEREQUEST = DESCRIPTOR.message_types_by_name['PAMRotateRequest'] -_PAMCONTROLLERSRESPONSE = DESCRIPTOR.message_types_by_name['PAMControllersResponse'] -_PAMREMOVECONTROLLER = DESCRIPTOR.message_types_by_name['PAMRemoveController'] -_PAMREMOVECONTROLLERRESPONSE = DESCRIPTOR.message_types_by_name['PAMRemoveControllerResponse'] -_PAMMODIFYREQUEST = DESCRIPTOR.message_types_by_name['PAMModifyRequest'] -_PAMDATAOPERATION = DESCRIPTOR.message_types_by_name['PAMDataOperation'] -_PAMCONFIGURATIONDATA = DESCRIPTOR.message_types_by_name['PAMConfigurationData'] -_PAMELEMENTDATA = DESCRIPTOR.message_types_by_name['PAMElementData'] -_PAMELEMENTOPERATIONRESULT = DESCRIPTOR.message_types_by_name['PAMElementOperationResult'] -_PAMMODIFYRESULT = DESCRIPTOR.message_types_by_name['PAMModifyResult'] -_PAMELEMENT = DESCRIPTOR.message_types_by_name['PAMElement'] -_PAMGENERICUIDREQUEST = DESCRIPTOR.message_types_by_name['PAMGenericUidRequest'] -_PAMGENERICUIDSREQUEST = DESCRIPTOR.message_types_by_name['PAMGenericUidsRequest'] -_PAMCONFIGURATION = DESCRIPTOR.message_types_by_name['PAMConfiguration'] -_PAMCONFIGURATIONS = DESCRIPTOR.message_types_by_name['PAMConfigurations'] -_PAMCONTROLLER = DESCRIPTOR.message_types_by_name['PAMController'] -_PAMSETMAXINSTANCECOUNTREQUEST = DESCRIPTOR.message_types_by_name['PAMSetMaxInstanceCountRequest'] -_CONTROLLERRESPONSE = DESCRIPTOR.message_types_by_name['ControllerResponse'] -_PAMCONFIGURATIONCONTROLLER = DESCRIPTOR.message_types_by_name['PAMConfigurationController'] -_CONFIGURATIONADDREQUEST = DESCRIPTOR.message_types_by_name['ConfigurationAddRequest'] -_RELAYACCESSCREDS = DESCRIPTOR.message_types_by_name['RelayAccessCreds'] -_PAMRECORDINGSREQUEST = DESCRIPTOR.message_types_by_name['PAMRecordingsRequest'] -_PAMRECORDING = DESCRIPTOR.message_types_by_name['PAMRecording'] -_PAMRECORDINGSRESPONSE = DESCRIPTOR.message_types_by_name['PAMRecordingsResponse'] -_PAMDATA = DESCRIPTOR.message_types_by_name['PAMData'] -_UIDLIST = DESCRIPTOR.message_types_by_name['UidList'] -_PAMRESOURCECONFIG = DESCRIPTOR.message_types_by_name['PAMResourceConfig'] -_PAMUNIVERSALSYNCFOLDER = DESCRIPTOR.message_types_by_name['PAMUniversalSyncFolder'] -_PAMUNIVERSALSYNCCONFIG = DESCRIPTOR.message_types_by_name['PAMUniversalSyncConfig'] -_CNAPPWEBHOOKREQUEST = DESCRIPTOR.message_types_by_name['CnappWebhookRequest'] -_CNAPPWEBHOOKRESPONSE = DESCRIPTOR.message_types_by_name['CnappWebhookResponse'] -_CNAPPDELETEWEBHOOKREQUEST = DESCRIPTOR.message_types_by_name['CnappDeleteWebhookRequest'] -_CNAPPGETINTEGRATIONREQUEST = DESCRIPTOR.message_types_by_name['CnappGetIntegrationRequest'] -_CNAPPGETINTEGRATIONITEM = DESCRIPTOR.message_types_by_name['CnappGetIntegrationItem'] -_CNAPPGETINTEGRATIONRESPONSE = DESCRIPTOR.message_types_by_name['CnappGetIntegrationResponse'] -_CNAPPTESTCREDENTIALSREQUEST = DESCRIPTOR.message_types_by_name['CnappTestCredentialsRequest'] -_CNAPPTESTCREDENTIALSRESPONSE = DESCRIPTOR.message_types_by_name['CnappTestCredentialsResponse'] -_CNAPPQUEUELISTREQUEST = DESCRIPTOR.message_types_by_name['CnappQueueListRequest'] -_CNAPPQUEUEITEM = DESCRIPTOR.message_types_by_name['CnappQueueItem'] -_CNAPPQUEUELISTRESPONSE = DESCRIPTOR.message_types_by_name['CnappQueueListResponse'] -_CNAPPQUEUEITEMRESPONSE = DESCRIPTOR.message_types_by_name['CnappQueueItemResponse'] -_CNAPPASSOCIATEREQUEST = DESCRIPTOR.message_types_by_name['CnappAssociateRequest'] -_CNAPPASSOCIATERESPONSE = DESCRIPTOR.message_types_by_name['CnappAssociateResponse'] -_CNAPPRESOLVEREQUEST = DESCRIPTOR.message_types_by_name['CnappResolveRequest'] -_CNAPPREMEDIATEREQUEST = DESCRIPTOR.message_types_by_name['CnappRemediateRequest'] -_CNAPPREMEDIATERESPONSE = DESCRIPTOR.message_types_by_name['CnappRemediateResponse'] -_CNAPPIGNOREREQUEST = DESCRIPTOR.message_types_by_name['CnappIgnoreRequest'] -_CNAPPDEFAULTBEHAVIORREQUEST = DESCRIPTOR.message_types_by_name['CnappDefaultBehaviorRequest'] -_CNAPPDEFAULTBEHAVIORRESPONSE = DESCRIPTOR.message_types_by_name['CnappDefaultBehaviorResponse'] -_CNAPPBEHAVIORLISTREQUEST = DESCRIPTOR.message_types_by_name['CnappBehaviorListRequest'] -_CNAPPBEHAVIORLISTRESPONSE = DESCRIPTOR.message_types_by_name['CnappBehaviorListResponse'] -_CNAPPDEFAULTBEHAVIORITEM = DESCRIPTOR.message_types_by_name['CnappDefaultBehaviorItem'] -_CNAPPBEHAVIORUPDATEREQUEST = DESCRIPTOR.message_types_by_name['CnappBehaviorUpdateRequest'] -_CNAPPBEHAVIORDELETEREQUEST = DESCRIPTOR.message_types_by_name['CnappBehaviorDeleteRequest'] -PAMRotationSchedule = _reflection.GeneratedProtocolMessageType('PAMRotationSchedule', (_message.Message,), { - 'DESCRIPTOR' : _PAMROTATIONSCHEDULE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMRotationSchedule) - }) -_sym_db.RegisterMessage(PAMRotationSchedule) - -PAMRotationSchedulesResponse = _reflection.GeneratedProtocolMessageType('PAMRotationSchedulesResponse', (_message.Message,), { - 'DESCRIPTOR' : _PAMROTATIONSCHEDULESRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMRotationSchedulesResponse) - }) -_sym_db.RegisterMessage(PAMRotationSchedulesResponse) - -PAMOnlineController = _reflection.GeneratedProtocolMessageType('PAMOnlineController', (_message.Message,), { - 'DESCRIPTOR' : _PAMONLINECONTROLLER, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMOnlineController) - }) -_sym_db.RegisterMessage(PAMOnlineController) - -PAMWebRtcConnection = _reflection.GeneratedProtocolMessageType('PAMWebRtcConnection', (_message.Message,), { - 'DESCRIPTOR' : _PAMWEBRTCCONNECTION, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMWebRtcConnection) - }) -_sym_db.RegisterMessage(PAMWebRtcConnection) - -PAMOnlineControllers = _reflection.GeneratedProtocolMessageType('PAMOnlineControllers', (_message.Message,), { - 'DESCRIPTOR' : _PAMONLINECONTROLLERS, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMOnlineControllers) - }) -_sym_db.RegisterMessage(PAMOnlineControllers) - -PAMRotateRequest = _reflection.GeneratedProtocolMessageType('PAMRotateRequest', (_message.Message,), { - 'DESCRIPTOR' : _PAMROTATEREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMRotateRequest) - }) -_sym_db.RegisterMessage(PAMRotateRequest) - -PAMControllersResponse = _reflection.GeneratedProtocolMessageType('PAMControllersResponse', (_message.Message,), { - 'DESCRIPTOR' : _PAMCONTROLLERSRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMControllersResponse) - }) -_sym_db.RegisterMessage(PAMControllersResponse) - -PAMRemoveController = _reflection.GeneratedProtocolMessageType('PAMRemoveController', (_message.Message,), { - 'DESCRIPTOR' : _PAMREMOVECONTROLLER, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMRemoveController) - }) -_sym_db.RegisterMessage(PAMRemoveController) - -PAMRemoveControllerResponse = _reflection.GeneratedProtocolMessageType('PAMRemoveControllerResponse', (_message.Message,), { - 'DESCRIPTOR' : _PAMREMOVECONTROLLERRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMRemoveControllerResponse) - }) -_sym_db.RegisterMessage(PAMRemoveControllerResponse) - -PAMModifyRequest = _reflection.GeneratedProtocolMessageType('PAMModifyRequest', (_message.Message,), { - 'DESCRIPTOR' : _PAMMODIFYREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMModifyRequest) - }) -_sym_db.RegisterMessage(PAMModifyRequest) - -PAMDataOperation = _reflection.GeneratedProtocolMessageType('PAMDataOperation', (_message.Message,), { - 'DESCRIPTOR' : _PAMDATAOPERATION, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMDataOperation) - }) -_sym_db.RegisterMessage(PAMDataOperation) - -PAMConfigurationData = _reflection.GeneratedProtocolMessageType('PAMConfigurationData', (_message.Message,), { - 'DESCRIPTOR' : _PAMCONFIGURATIONDATA, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMConfigurationData) - }) -_sym_db.RegisterMessage(PAMConfigurationData) - -PAMElementData = _reflection.GeneratedProtocolMessageType('PAMElementData', (_message.Message,), { - 'DESCRIPTOR' : _PAMELEMENTDATA, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMElementData) - }) -_sym_db.RegisterMessage(PAMElementData) - -PAMElementOperationResult = _reflection.GeneratedProtocolMessageType('PAMElementOperationResult', (_message.Message,), { - 'DESCRIPTOR' : _PAMELEMENTOPERATIONRESULT, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMElementOperationResult) - }) -_sym_db.RegisterMessage(PAMElementOperationResult) - -PAMModifyResult = _reflection.GeneratedProtocolMessageType('PAMModifyResult', (_message.Message,), { - 'DESCRIPTOR' : _PAMMODIFYRESULT, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMModifyResult) - }) -_sym_db.RegisterMessage(PAMModifyResult) - -PAMElement = _reflection.GeneratedProtocolMessageType('PAMElement', (_message.Message,), { - 'DESCRIPTOR' : _PAMELEMENT, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMElement) - }) -_sym_db.RegisterMessage(PAMElement) - -PAMGenericUidRequest = _reflection.GeneratedProtocolMessageType('PAMGenericUidRequest', (_message.Message,), { - 'DESCRIPTOR' : _PAMGENERICUIDREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMGenericUidRequest) - }) -_sym_db.RegisterMessage(PAMGenericUidRequest) - -PAMGenericUidsRequest = _reflection.GeneratedProtocolMessageType('PAMGenericUidsRequest', (_message.Message,), { - 'DESCRIPTOR' : _PAMGENERICUIDSREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMGenericUidsRequest) - }) -_sym_db.RegisterMessage(PAMGenericUidsRequest) - -PAMConfiguration = _reflection.GeneratedProtocolMessageType('PAMConfiguration', (_message.Message,), { - 'DESCRIPTOR' : _PAMCONFIGURATION, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMConfiguration) - }) -_sym_db.RegisterMessage(PAMConfiguration) - -PAMConfigurations = _reflection.GeneratedProtocolMessageType('PAMConfigurations', (_message.Message,), { - 'DESCRIPTOR' : _PAMCONFIGURATIONS, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMConfigurations) - }) -_sym_db.RegisterMessage(PAMConfigurations) - -PAMController = _reflection.GeneratedProtocolMessageType('PAMController', (_message.Message,), { - 'DESCRIPTOR' : _PAMCONTROLLER, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMController) - }) -_sym_db.RegisterMessage(PAMController) - -PAMSetMaxInstanceCountRequest = _reflection.GeneratedProtocolMessageType('PAMSetMaxInstanceCountRequest', (_message.Message,), { - 'DESCRIPTOR' : _PAMSETMAXINSTANCECOUNTREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMSetMaxInstanceCountRequest) - }) -_sym_db.RegisterMessage(PAMSetMaxInstanceCountRequest) - -ControllerResponse = _reflection.GeneratedProtocolMessageType('ControllerResponse', (_message.Message,), { - 'DESCRIPTOR' : _CONTROLLERRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.ControllerResponse) - }) -_sym_db.RegisterMessage(ControllerResponse) - -PAMConfigurationController = _reflection.GeneratedProtocolMessageType('PAMConfigurationController', (_message.Message,), { - 'DESCRIPTOR' : _PAMCONFIGURATIONCONTROLLER, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMConfigurationController) - }) -_sym_db.RegisterMessage(PAMConfigurationController) - -ConfigurationAddRequest = _reflection.GeneratedProtocolMessageType('ConfigurationAddRequest', (_message.Message,), { - 'DESCRIPTOR' : _CONFIGURATIONADDREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.ConfigurationAddRequest) - }) -_sym_db.RegisterMessage(ConfigurationAddRequest) - -RelayAccessCreds = _reflection.GeneratedProtocolMessageType('RelayAccessCreds', (_message.Message,), { - 'DESCRIPTOR' : _RELAYACCESSCREDS, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.RelayAccessCreds) - }) -_sym_db.RegisterMessage(RelayAccessCreds) - -PAMRecordingsRequest = _reflection.GeneratedProtocolMessageType('PAMRecordingsRequest', (_message.Message,), { - 'DESCRIPTOR' : _PAMRECORDINGSREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMRecordingsRequest) - }) -_sym_db.RegisterMessage(PAMRecordingsRequest) - -PAMRecording = _reflection.GeneratedProtocolMessageType('PAMRecording', (_message.Message,), { - 'DESCRIPTOR' : _PAMRECORDING, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMRecording) - }) -_sym_db.RegisterMessage(PAMRecording) - -PAMRecordingsResponse = _reflection.GeneratedProtocolMessageType('PAMRecordingsResponse', (_message.Message,), { - 'DESCRIPTOR' : _PAMRECORDINGSRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMRecordingsResponse) - }) -_sym_db.RegisterMessage(PAMRecordingsResponse) - -PAMData = _reflection.GeneratedProtocolMessageType('PAMData', (_message.Message,), { - 'DESCRIPTOR' : _PAMDATA, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMData) - }) -_sym_db.RegisterMessage(PAMData) - -UidList = _reflection.GeneratedProtocolMessageType('UidList', (_message.Message,), { - 'DESCRIPTOR' : _UIDLIST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.UidList) - }) -_sym_db.RegisterMessage(UidList) - -PAMResourceConfig = _reflection.GeneratedProtocolMessageType('PAMResourceConfig', (_message.Message,), { - 'DESCRIPTOR' : _PAMRESOURCECONFIG, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMResourceConfig) - }) -_sym_db.RegisterMessage(PAMResourceConfig) - -PAMUniversalSyncFolder = _reflection.GeneratedProtocolMessageType('PAMUniversalSyncFolder', (_message.Message,), { - 'DESCRIPTOR' : _PAMUNIVERSALSYNCFOLDER, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMUniversalSyncFolder) - }) -_sym_db.RegisterMessage(PAMUniversalSyncFolder) - -PAMUniversalSyncConfig = _reflection.GeneratedProtocolMessageType('PAMUniversalSyncConfig', (_message.Message,), { - 'DESCRIPTOR' : _PAMUNIVERSALSYNCCONFIG, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.PAMUniversalSyncConfig) - }) -_sym_db.RegisterMessage(PAMUniversalSyncConfig) - -CnappWebhookRequest = _reflection.GeneratedProtocolMessageType('CnappWebhookRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPWEBHOOKREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappWebhookRequest) - }) -_sym_db.RegisterMessage(CnappWebhookRequest) - -CnappWebhookResponse = _reflection.GeneratedProtocolMessageType('CnappWebhookResponse', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPWEBHOOKRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappWebhookResponse) - }) -_sym_db.RegisterMessage(CnappWebhookResponse) - -CnappDeleteWebhookRequest = _reflection.GeneratedProtocolMessageType('CnappDeleteWebhookRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPDELETEWEBHOOKREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappDeleteWebhookRequest) - }) -_sym_db.RegisterMessage(CnappDeleteWebhookRequest) - -CnappGetIntegrationRequest = _reflection.GeneratedProtocolMessageType('CnappGetIntegrationRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPGETINTEGRATIONREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappGetIntegrationRequest) - }) -_sym_db.RegisterMessage(CnappGetIntegrationRequest) - -CnappGetIntegrationItem = _reflection.GeneratedProtocolMessageType('CnappGetIntegrationItem', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPGETINTEGRATIONITEM, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappGetIntegrationItem) - }) -_sym_db.RegisterMessage(CnappGetIntegrationItem) - -CnappGetIntegrationResponse = _reflection.GeneratedProtocolMessageType('CnappGetIntegrationResponse', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPGETINTEGRATIONRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappGetIntegrationResponse) - }) -_sym_db.RegisterMessage(CnappGetIntegrationResponse) - -CnappTestCredentialsRequest = _reflection.GeneratedProtocolMessageType('CnappTestCredentialsRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPTESTCREDENTIALSREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappTestCredentialsRequest) - }) -_sym_db.RegisterMessage(CnappTestCredentialsRequest) - -CnappTestCredentialsResponse = _reflection.GeneratedProtocolMessageType('CnappTestCredentialsResponse', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPTESTCREDENTIALSRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappTestCredentialsResponse) - }) -_sym_db.RegisterMessage(CnappTestCredentialsResponse) - -CnappQueueListRequest = _reflection.GeneratedProtocolMessageType('CnappQueueListRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPQUEUELISTREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappQueueListRequest) - }) -_sym_db.RegisterMessage(CnappQueueListRequest) - -CnappQueueItem = _reflection.GeneratedProtocolMessageType('CnappQueueItem', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPQUEUEITEM, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappQueueItem) - }) -_sym_db.RegisterMessage(CnappQueueItem) - -CnappQueueListResponse = _reflection.GeneratedProtocolMessageType('CnappQueueListResponse', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPQUEUELISTRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappQueueListResponse) - }) -_sym_db.RegisterMessage(CnappQueueListResponse) - -CnappQueueItemResponse = _reflection.GeneratedProtocolMessageType('CnappQueueItemResponse', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPQUEUEITEMRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappQueueItemResponse) - }) -_sym_db.RegisterMessage(CnappQueueItemResponse) - -CnappAssociateRequest = _reflection.GeneratedProtocolMessageType('CnappAssociateRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPASSOCIATEREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappAssociateRequest) - }) -_sym_db.RegisterMessage(CnappAssociateRequest) - -CnappAssociateResponse = _reflection.GeneratedProtocolMessageType('CnappAssociateResponse', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPASSOCIATERESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappAssociateResponse) - }) -_sym_db.RegisterMessage(CnappAssociateResponse) - -CnappResolveRequest = _reflection.GeneratedProtocolMessageType('CnappResolveRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPRESOLVEREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappResolveRequest) - }) -_sym_db.RegisterMessage(CnappResolveRequest) - -CnappRemediateRequest = _reflection.GeneratedProtocolMessageType('CnappRemediateRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPREMEDIATEREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappRemediateRequest) - }) -_sym_db.RegisterMessage(CnappRemediateRequest) - -CnappRemediateResponse = _reflection.GeneratedProtocolMessageType('CnappRemediateResponse', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPREMEDIATERESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappRemediateResponse) - }) -_sym_db.RegisterMessage(CnappRemediateResponse) - -CnappIgnoreRequest = _reflection.GeneratedProtocolMessageType('CnappIgnoreRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPIGNOREREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappIgnoreRequest) - }) -_sym_db.RegisterMessage(CnappIgnoreRequest) - -CnappDefaultBehaviorRequest = _reflection.GeneratedProtocolMessageType('CnappDefaultBehaviorRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPDEFAULTBEHAVIORREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappDefaultBehaviorRequest) - }) -_sym_db.RegisterMessage(CnappDefaultBehaviorRequest) - -CnappDefaultBehaviorResponse = _reflection.GeneratedProtocolMessageType('CnappDefaultBehaviorResponse', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPDEFAULTBEHAVIORRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappDefaultBehaviorResponse) - }) -_sym_db.RegisterMessage(CnappDefaultBehaviorResponse) - -CnappBehaviorListRequest = _reflection.GeneratedProtocolMessageType('CnappBehaviorListRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPBEHAVIORLISTREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappBehaviorListRequest) - }) -_sym_db.RegisterMessage(CnappBehaviorListRequest) - -CnappBehaviorListResponse = _reflection.GeneratedProtocolMessageType('CnappBehaviorListResponse', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPBEHAVIORLISTRESPONSE, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappBehaviorListResponse) - }) -_sym_db.RegisterMessage(CnappBehaviorListResponse) - -CnappDefaultBehaviorItem = _reflection.GeneratedProtocolMessageType('CnappDefaultBehaviorItem', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPDEFAULTBEHAVIORITEM, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappDefaultBehaviorItem) - }) -_sym_db.RegisterMessage(CnappDefaultBehaviorItem) - -CnappBehaviorUpdateRequest = _reflection.GeneratedProtocolMessageType('CnappBehaviorUpdateRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPBEHAVIORUPDATEREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappBehaviorUpdateRequest) - }) -_sym_db.RegisterMessage(CnappBehaviorUpdateRequest) - -CnappBehaviorDeleteRequest = _reflection.GeneratedProtocolMessageType('CnappBehaviorDeleteRequest', (_message.Message,), { - 'DESCRIPTOR' : _CNAPPBEHAVIORDELETEREQUEST, - '__module__' : 'pam_pb2' - # @@protoc_insertion_point(class_scope:PAM.CnappBehaviorDeleteRequest) - }) -_sym_db.RegisterMessage(CnappBehaviorDeleteRequest) - -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\030com.keepersecurity.protoB\003PAM' - _WEBRTCCONNECTIONTYPE._serialized_start=6679 - _WEBRTCCONNECTIONTYPE._serialized_end=6837 - _PAMOPERATIONTYPE._serialized_start=6839 - _PAMOPERATIONTYPE._serialized_end=6903 - _PAMOPERATIONRESULTTYPE._serialized_start=6905 - _PAMOPERATIONRESULTTYPE._serialized_end=7017 - _CONTROLLERMESSAGETYPE._serialized_start=7020 - _CONTROLLERMESSAGETYPE._serialized_end=7201 - _PAMRECORDINGTYPE._serialized_start=7203 - _PAMRECORDINGTYPE._serialized_end=7289 - _PAMRECORDINGRISKLEVEL._serialized_start=7291 - _PAMRECORDINGRISKLEVEL._serialized_end=7396 - _PAMROTATIONSCHEDULE._serialized_start=51 - _PAMROTATIONSCHEDULE._serialized_end=182 - _PAMROTATIONSCHEDULESRESPONSE._serialized_start=184 - _PAMROTATIONSCHEDULESRESPONSE._serialized_end=259 - _PAMONLINECONTROLLER._serialized_start=262 - _PAMONLINECONTROLLER._serialized_end=410 - _PAMWEBRTCCONNECTION._serialized_start=413 - _PAMWEBRTCCONNECTION._serialized_end=580 - _PAMONLINECONTROLLERS._serialized_start=582 - _PAMONLINECONTROLLERS._serialized_end=671 - _PAMROTATEREQUEST._serialized_start=673 - _PAMROTATEREQUEST._serialized_end=730 - _PAMCONTROLLERSRESPONSE._serialized_start=732 - _PAMCONTROLLERSRESPONSE._serialized_end=797 - _PAMREMOVECONTROLLER._serialized_start=799 - _PAMREMOVECONTROLLER._serialized_end=860 - _PAMREMOVECONTROLLERRESPONSE._serialized_start=862 - _PAMREMOVECONTROLLERRESPONSE._serialized_end=938 - _PAMMODIFYREQUEST._serialized_start=940 - _PAMMODIFYREQUEST._serialized_end=1001 - _PAMDATAOPERATION._serialized_start=1004 - _PAMDATAOPERATION._serialized_end=1156 - _PAMCONFIGURATIONDATA._serialized_start=1158 - _PAMCONFIGURATIONDATA._serialized_end=1259 - _PAMELEMENTDATA._serialized_start=1261 - _PAMELEMENTDATA._serialized_end=1330 - _PAMELEMENTOPERATIONRESULT._serialized_start=1332 - _PAMELEMENTOPERATIONRESULT._serialized_end=1441 - _PAMMODIFYRESULT._serialized_start=1443 - _PAMMODIFYRESULT._serialized_end=1509 - _PAMELEMENT._serialized_start=1511 - _PAMELEMENT._serialized_end=1631 - _PAMGENERICUIDREQUEST._serialized_start=1633 - _PAMGENERICUIDREQUEST._serialized_end=1668 - _PAMGENERICUIDSREQUEST._serialized_start=1670 - _PAMGENERICUIDSREQUEST._serialized_end=1707 - _PAMCONFIGURATION._serialized_start=1710 - _PAMCONFIGURATION._serialized_end=1881 - _PAMCONFIGURATIONS._serialized_start=1883 - _PAMCONFIGURATIONS._serialized_end=1949 - _PAMCONTROLLER._serialized_start=1952 - _PAMCONTROLLER._serialized_end=2207 - _PAMSETMAXINSTANCECOUNTREQUEST._serialized_start=2209 - _PAMSETMAXINSTANCECOUNTREQUEST._serialized_end=2289 - _CONTROLLERRESPONSE._serialized_start=2291 - _CONTROLLERRESPONSE._serialized_end=2328 - _PAMCONFIGURATIONCONTROLLER._serialized_start=2330 - _PAMCONFIGURATIONCONTROLLER._serialized_end=2407 - _CONFIGURATIONADDREQUEST._serialized_start=2410 - _CONFIGURATIONADDREQUEST._serialized_end=2573 - _RELAYACCESSCREDS._serialized_start=2575 - _RELAYACCESSCREDS._serialized_end=2649 - _PAMRECORDINGSREQUEST._serialized_start=2652 - _PAMRECORDINGSREQUEST._serialized_end=2909 - _PAMRECORDING._serialized_start=2912 - _PAMRECORDING._serialized_end=3252 - _PAMRECORDINGSRESPONSE._serialized_start=3254 - _PAMRECORDINGSRESPONSE._serialized_end=3333 - _PAMDATA._serialized_start=3335 - _PAMDATA._serialized_end=3377 - _UIDLIST._serialized_start=3379 - _UIDLIST._serialized_end=3402 - _PAMRESOURCECONFIG._serialized_start=3405 - _PAMRESOURCECONFIG._serialized_end=3793 - _PAMUNIVERSALSYNCFOLDER._serialized_start=3795 - _PAMUNIVERSALSYNCFOLDER._serialized_end=3832 - _PAMUNIVERSALSYNCCONFIG._serialized_start=3835 - _PAMUNIVERSALSYNCCONFIG._serialized_end=4087 - _CNAPPWEBHOOKREQUEST._serialized_start=4090 - _CNAPPWEBHOOKREQUEST._serialized_end=4303 - _CNAPPWEBHOOKRESPONSE._serialized_start=4305 - _CNAPPWEBHOOKRESPONSE._serialized_end=4388 - _CNAPPDELETEWEBHOOKREQUEST._serialized_start=4390 - _CNAPPDELETEWEBHOOKREQUEST._serialized_end=4437 - _CNAPPGETINTEGRATIONREQUEST._serialized_start=4439 - _CNAPPGETINTEGRATIONREQUEST._serialized_end=4487 - _CNAPPGETINTEGRATIONITEM._serialized_start=4490 - _CNAPPGETINTEGRATIONITEM._serialized_end=4628 - _CNAPPGETINTEGRATIONRESPONSE._serialized_start=4630 - _CNAPPGETINTEGRATIONRESPONSE._serialized_end=4711 - _CNAPPTESTCREDENTIALSREQUEST._serialized_start=4714 - _CNAPPTESTCREDENTIALSREQUEST._serialized_end=4842 - _CNAPPTESTCREDENTIALSRESPONSE._serialized_start=4844 - _CNAPPTESTCREDENTIALSRESPONSE._serialized_end=4921 - _CNAPPQUEUELISTREQUEST._serialized_start=4923 - _CNAPPQUEUELISTREQUEST._serialized_end=5019 - _CNAPPQUEUEITEM._serialized_start=5022 - _CNAPPQUEUEITEM._serialized_end=5209 - _CNAPPQUEUELISTRESPONSE._serialized_start=5211 - _CNAPPQUEUELISTRESPONSE._serialized_end=5317 - _CNAPPQUEUEITEMRESPONSE._serialized_start=5320 - _CNAPPQUEUEITEMRESPONSE._serialized_end=5588 - _CNAPPASSOCIATEREQUEST._serialized_start=5590 - _CNAPPASSOCIATEREQUEST._serialized_end=5659 - _CNAPPASSOCIATERESPONSE._serialized_start=5661 - _CNAPPASSOCIATERESPONSE._serialized_end=5731 - _CNAPPRESOLVEREQUEST._serialized_start=5733 - _CNAPPRESOLVEREQUEST._serialized_end=5779 - _CNAPPREMEDIATEREQUEST._serialized_start=5781 - _CNAPPREMEDIATEREQUEST._serialized_end=5824 - _CNAPPREMEDIATERESPONSE._serialized_start=5826 - _CNAPPREMEDIATERESPONSE._serialized_end=5927 - _CNAPPIGNOREREQUEST._serialized_start=5929 - _CNAPPIGNOREREQUEST._serialized_end=5965 - _CNAPPDEFAULTBEHAVIORREQUEST._serialized_start=5968 - _CNAPPDEFAULTBEHAVIORREQUEST._serialized_end=6109 - _CNAPPDEFAULTBEHAVIORRESPONSE._serialized_start=6111 - _CNAPPDEFAULTBEHAVIORRESPONSE._serialized_end=6173 - _CNAPPBEHAVIORLISTREQUEST._serialized_start=6175 - _CNAPPBEHAVIORLISTREQUEST._serialized_end=6221 - _CNAPPBEHAVIORLISTRESPONSE._serialized_start=6223 - _CNAPPBEHAVIORLISTRESPONSE._serialized_end=6296 - _CNAPPDEFAULTBEHAVIORITEM._serialized_start=6299 - _CNAPPDEFAULTBEHAVIORITEM._serialized_end=6466 - _CNAPPBEHAVIORUPDATEREQUEST._serialized_start=6469 - _CNAPPBEHAVIORUPDATEREQUEST._serialized_end=6614 - _CNAPPBEHAVIORDELETEREQUEST._serialized_start=6616 - _CNAPPBEHAVIORDELETEREQUEST._serialized_end=6676 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tpam.proto\x12\x03PAM\x1a\x10\x65nterprise.proto\x1a\x0crecord.proto\"\x83\x01\n\x13PAMRotationSchedule\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\x14\n\x0cscheduleData\x18\x04 \x01(\t\x12\x12\n\nnoSchedule\x18\x05 \x01(\x08\"K\n\x1cPAMRotationSchedulesResponse\x12+\n\tschedules\x18\x01 \x03(\x0b\x32\x18.PAM.PAMRotationSchedule\"\x94\x01\n\x13PAMOnlineController\x12\x15\n\rcontrollerUid\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63onnectedOn\x18\x02 \x01(\x03\x12\x11\n\tipAddress\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12-\n\x0b\x63onnections\x18\x05 \x03(\x0b\x32\x18.PAM.PAMWebRtcConnection\"\xa7\x01\n\x13PAMWebRtcConnection\x12\x15\n\rconnectionUid\x18\x01 \x01(\x0c\x12\'\n\x04type\x18\x02 \x01(\x0e\x32\x19.PAM.WebRtcConnectionType\x12\x11\n\trecordUid\x18\x03 \x01(\x0c\x12\x10\n\x08userName\x18\x04 \x01(\t\x12\x11\n\tstartedOn\x18\x05 \x01(\x03\x12\x18\n\x10\x63onfigurationUid\x18\x06 \x01(\x0c\"Y\n\x14PAMOnlineControllers\x12\x12\n\ndeprecated\x18\x01 \x03(\x0c\x12-\n\x0b\x63ontrollers\x18\x02 \x03(\x0b\x32\x18.PAM.PAMOnlineController\"9\n\x10PAMRotateRequest\x12\x12\n\nrequestUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\"A\n\x16PAMControllersResponse\x12\'\n\x0b\x63ontrollers\x18\x01 \x03(\x0b\x32\x12.PAM.PAMController\"=\n\x13PAMRemoveController\x12\x15\n\rcontrollerUid\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\t\"L\n\x1bPAMRemoveControllerResponse\x12-\n\x0b\x63ontrollers\x18\x01 \x03(\x0b\x32\x18.PAM.PAMRemoveController\"=\n\x10PAMModifyRequest\x12)\n\noperations\x18\x01 \x03(\x0b\x32\x15.PAM.PAMDataOperation\"\x98\x01\n\x10PAMDataOperation\x12,\n\roperationType\x18\x01 \x01(\x0e\x32\x15.PAM.PAMOperationType\x12\x30\n\rconfiguration\x18\x02 \x01(\x0b\x32\x19.PAM.PAMConfigurationData\x12$\n\x07\x65lement\x18\x03 \x01(\x0b\x32\x13.PAM.PAMElementData\"e\n\x14PAMConfigurationData\x12\x18\n\x10\x63onfigurationUid\x18\x01 \x01(\x0c\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\"E\n\x0ePAMElementData\x12\x12\n\nelementUid\x18\x01 \x01(\x0c\x12\x11\n\tparentUid\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\"m\n\x19PAMElementOperationResult\x12\x12\n\nelementUid\x18\x01 \x01(\x0c\x12+\n\x06result\x18\x02 \x01(\x0e\x32\x1b.PAM.PAMOperationResultType\x12\x0f\n\x07message\x18\x03 \x01(\t\"B\n\x0fPAMModifyResult\x12/\n\x07results\x18\x01 \x03(\x0b\x32\x1e.PAM.PAMElementOperationResult\"x\n\nPAMElement\x12\x12\n\nelementUid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x03 \x01(\x03\x12\x14\n\x0clastModified\x18\x04 \x01(\x03\x12!\n\x08\x63hildren\x18\x05 \x03(\x0b\x32\x0f.PAM.PAMElement\"#\n\x14PAMGenericUidRequest\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\"%\n\x15PAMGenericUidsRequest\x12\x0c\n\x04uids\x18\x01 \x03(\x0c\"\xab\x01\n\x10PAMConfiguration\x12\x18\n\x10\x63onfigurationUid\x18\x01 \x01(\x0c\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x15\n\rcontrollerUid\x18\x03 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x05 \x01(\x03\x12\x14\n\x0clastModified\x18\x06 \x01(\x03\x12!\n\x08\x63hildren\x18\x07 \x03(\x0b\x32\x0f.PAM.PAMElement\"B\n\x11PAMConfigurations\x12-\n\x0e\x63onfigurations\x18\x01 \x03(\x0b\x32\x15.PAM.PAMConfiguration\"\xff\x01\n\rPAMController\x12\x15\n\rcontrollerUid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x63ontrollerName\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65viceToken\x18\x03 \x01(\t\x12\x12\n\ndeviceName\x18\x04 \x01(\t\x12\x0e\n\x06nodeId\x18\x05 \x01(\x03\x12\x0f\n\x07\x63reated\x18\x06 \x01(\x03\x12\x14\n\x0clastModified\x18\x07 \x01(\x03\x12\x16\n\x0e\x61pplicationUid\x18\x08 \x01(\x0c\x12\x30\n\rappClientType\x18\t \x01(\x0e\x32\x19.Enterprise.AppClientType\x12\x15\n\risInitialized\x18\n \x01(\x08\"P\n\x1dPAMSetMaxInstanceCountRequest\x12\x15\n\rcontrollerUid\x18\x01 \x01(\x0c\x12\x18\n\x10maxInstanceCount\x18\x02 \x01(\x05\"%\n\x12\x43ontrollerResponse\x12\x0f\n\x07payload\x18\x01 \x01(\t\"M\n\x1aPAMConfigurationController\x12\x18\n\x10\x63onfigurationUid\x18\x01 \x01(\x0c\x12\x15\n\rcontrollerUid\x18\x02 \x01(\x0c\"\xa3\x01\n\x17\x43onfigurationAddRequest\x12\x18\n\x10\x63onfigurationUid\x18\x01 \x01(\x0c\x12\x11\n\trecordKey\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12(\n\x0brecordLinks\x18\x04 \x03(\x0b\x32\x13.Records.RecordLink\x12#\n\x05\x61udit\x18\x05 \x01(\x0b\x32\x14.Records.RecordAudit\"J\n\x10RelayAccessCreds\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\x12\x12\n\nserverTime\x18\x03 \x01(\x03\"\x81\x02\n\x14PAMRecordingsRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08maxCount\x18\x02 \x01(\x05\x12\x17\n\nrangeStart\x18\x03 \x01(\x03H\x00\x88\x01\x01\x12\x15\n\x08rangeEnd\x18\x04 \x01(\x03H\x01\x88\x01\x01\x12$\n\x05types\x18\x05 \x03(\x0e\x32\x15.PAM.PAMRecordingType\x12)\n\x05risks\x18\x06 \x03(\x0e\x32\x1a.PAM.PAMRecordingRiskLevel\x12\x11\n\tprotocols\x18\x07 \x03(\t\x12\x14\n\x0c\x63loseReasons\x18\x08 \x03(\x05\x42\r\n\x0b_rangeStartB\x0b\n\t_rangeEnd\"\xd4\x02\n\x0cPAMRecording\x12\x15\n\rconnectionUid\x18\x01 \x01(\x0c\x12,\n\rrecordingType\x18\x02 \x01(\x0e\x32\x15.PAM.PAMRecordingType\x12\x11\n\trecordUid\x18\x03 \x01(\x0c\x12\x10\n\x08userName\x18\x04 \x01(\t\x12\x11\n\tstartedOn\x18\x05 \x01(\x03\x12\x0e\n\x06length\x18\x06 \x01(\x05\x12\x10\n\x08\x66ileSize\x18\x07 \x01(\x03\x12\x11\n\tcreatedOn\x18\x08 \x01(\x03\x12\x10\n\x08protocol\x18\t \x01(\t\x12\x13\n\x0b\x63loseReason\x18\n \x01(\x05\x12\x19\n\x11recordingDuration\x18\x0b \x01(\x05\x12\x36\n\x12\x61iOverallRiskLevel\x18\x0c \x01(\x0e\x32\x1a.PAM.PAMRecordingRiskLevel\x12\x18\n\x10\x61iOverallSummary\x18\r \x01(\x0c\"O\n\x15PAMRecordingsResponse\x12%\n\nrecordings\x18\x01 \x03(\x0b\x32\x11.PAM.PAMRecording\x12\x0f\n\x07hasMore\x18\x02 \x01(\x08\"*\n\x07PAMData\x12\x0e\n\x06vertex\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\x0c\"\x17\n\x07UidList\x12\x0c\n\x04uids\x18\x01 \x03(\x0c\"\xb4\x03\n\x11PAMResourceConfig\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x17\n\nnetworkUid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08\x61\x64minUid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x11\n\x04meta\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x1f\n\x12\x63onnectionSettings\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\'\n\x0c\x63onnectUsers\x18\x06 \x01(\x0b\x32\x0c.PAM.UidListH\x04\x88\x01\x01\x12\x16\n\tdomainUid\x18\x07 \x01(\x0cH\x05\x88\x01\x01\x12\x18\n\x0bjitSettings\x18\x08 \x01(\x0cH\x06\x88\x01\x01\x12\x1d\n\x10keeperAiSettings\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x1b\n\x0eupdateServices\x18\n \x01(\x08H\x08\x88\x01\x01\x42\r\n\x0b_networkUidB\x0b\n\t_adminUidB\x07\n\x05_metaB\x15\n\x13_connectionSettingsB\x0f\n\r_connectUsersB\x0c\n\n_domainUidB\x0e\n\x0c_jitSettingsB\x13\n\x11_keeperAiSettingsB\x11\n\x0f_updateServices\"%\n\x16PAMUniversalSyncFolder\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\"\xfc\x01\n\x16PAMUniversalSyncConfig\x12\x12\n\nnetworkUid\x18\x01 \x01(\x0c\x12\x14\n\x07\x65nabled\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rdryRunEnabled\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12,\n\x07\x66olders\x18\x04 \x03(\x0b\x32\x1b.PAM.PAMUniversalSyncFolder\x12\x19\n\x0csyncIdentity\x18\x05 \x01(\x0cH\x02\x88\x01\x01\x12\x16\n\tvaultName\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x42\n\n\x08_enabledB\x10\n\x0e_dryRunEnabledB\x0f\n\r_syncIdentityB\x0c\n\n_vaultName\"7\n\x11NhiMetricsRequest\x12\x11\n\tstartTime\x18\x01 \x01(\x03\x12\x0f\n\x07\x65ndTime\x18\x02 \x01(\x03\"\x9c\x02\n\x0ePamUsageByUser\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12!\n\x19recordRotationScheduledOk\x18\x02 \x01(\x05\x12\x1c\n\x14pamConnectionStarted\x18\x03 \x01(\x05\x12\x18\n\x10pamTunnelStarted\x18\x04 \x01(\x05\x12\x1b\n\x13\x64iscoveryJobStarted\x18\x05 \x01(\x05\x12 \n\x18recordRotationOnDemandOk\x18\x06 \x01(\x05\x12\"\n\x1apamSessionRecordingStarted\x18\x07 \x01(\x05\x12\x15\n\rpamRbiStarted\x18\x08 \x01(\x05\x12%\n\x1dpamSessionRbiRecordingStarted\x18\t \x01(\x05\"p\n\x0eNhiUsageByUser\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x11\n\trotations\x18\x02 \x01(\x05\x12\x0f\n\x07tunnels\x18\x03 \x01(\x05\x12\x13\n\x0b\x63onnections\x18\x04 \x01(\x05\x12\x15\n\rdiscoveryJobs\x18\x05 \x01(\x05\"\x84\x02\n\x12NhiMetricsResponse\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x03\x12\x11\n\tstartTime\x18\x02 \x01(\x03\x12\x0f\n\x07\x65ndTime\x18\x03 \x01(\x03\x12\x18\n\x10uniqueKsmDevices\x18\x04 \x01(\x05\x12\x18\n\x10pamGatewayOnline\x18\x05 \x01(\x05\x12/\n\x0epamUsageByUser\x18\x06 \x03(\x0b\x32\x13.PAM.PamUsageByUserB\x02\x18\x01\x12\x10\n\x08nhiCount\x18\x07 \x01(\x05\x12\x13\n\x0bksmNhiCount\x18\x08 \x01(\x05\x12(\n\x0busageByUser\x18\t \x03(\x0b\x32\x13.PAM.NhiUsageByUser\"D\n\x16NhiBulkMetricsResponse\x12*\n\tresponses\x18\x01 \x03(\x0b\x32\x17.PAM.NhiMetricsResponse\"N\n\x0bNhiUidEntry\x12\x0b\n\x03uid\x18\x01 \x01(\t\x12\"\n\x08\x63\x61tegory\x18\x02 \x01(\x0e\x32\x10.PAM.NhiCategory\x12\x0e\n\x06ksmNhi\x18\x03 \x01(\x08\"7\n\x11GetNhiUidsRequest\x12\x11\n\tstartTime\x18\x01 \x01(\x03\x12\x0f\n\x07\x65ndTime\x18\x02 \x01(\x03\"4\n\x12GetNhiUidsResponse\x12\x1e\n\x04uids\x18\x01 \x03(\x0b\x32\x10.PAM.NhiUidEntry\"I\n\x1fPAMUniversalSyncPreCheckRequest\x12\x12\n\nnetworkUid\x18\x01 \x01(\x0c\x12\x12\n\nfolderUids\x18\x02 \x03(\x0c\"C\n\x1ePAMUniversalSyncPreCheckResult\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x0e\n\x06isUsed\x18\x02 \x01(\x08\"X\n PAMUniversalSyncPreCheckResponse\x12\x34\n\x07results\x18\x01 \x03(\x0b\x32#.PAM.PAMUniversalSyncPreCheckResult*\x9e\x01\n\x14WebRtcConnectionType\x12\x0e\n\nCONNECTION\x10\x00\x12\n\n\x06TUNNEL\x10\x01\x12\x07\n\x03SSH\x10\x02\x12\x07\n\x03RDP\x10\x03\x12\x08\n\x04HTTP\x10\x04\x12\x07\n\x03VNC\x10\x05\x12\n\n\x06TELNET\x10\x06\x12\t\n\x05MYSQL\x10\x07\x12\x0e\n\nSQL_SERVER\x10\x08\x12\x0e\n\nPOSTGRESQL\x10\t\x12\x0e\n\nKUBERNETES\x10\n*@\n\x10PAMOperationType\x12\x07\n\x03\x41\x44\x44\x10\x00\x12\n\n\x06UPDATE\x10\x01\x12\x0b\n\x07REPLACE\x10\x02\x12\n\n\x06\x44\x45LETE\x10\x03*p\n\x16PAMOperationResultType\x12\x0f\n\x0bPOT_SUCCESS\x10\x00\x12\x15\n\x11POT_UNKNOWN_ERROR\x10\x01\x12\x16\n\x12POT_ALREADY_EXISTS\x10\x02\x12\x16\n\x12POT_DOES_NOT_EXIST\x10\x03*\xc9\x01\n\x15\x43ontrollerMessageType\x12\x0f\n\x0b\x43MT_GENERAL\x10\x00\x12\x0e\n\nCMT_ROTATE\x10\x01\x12\x11\n\rCMT_DISCOVERY\x10\x02\x12\x0f\n\x0b\x43MT_CONNECT\x10\x03\x12\x19\n\x15\x43MT_ANALYZE_RECORDING\x10\x04\x12!\n\x1d\x43MT_WORKFLOW_ACCESS_ELEVATION\x10\x05\x12\x0b\n\x07\x43MT_USS\x10\x06\x12\x0c\n\x08\x43MT_INFO\x10\x07\x12\x12\n\x0e\x43MT_AUTOMATION\x10\x08*V\n\x10PAMRecordingType\x12\x0f\n\x0bPRT_SESSION\x10\x00\x12\x12\n\x0ePRT_TYPESCRIPT\x10\x01\x12\x0c\n\x08PRT_TIME\x10\x02\x12\x0f\n\x0bPRT_SUMMARY\x10\x03*i\n\x15PAMRecordingRiskLevel\x12\x13\n\x0fPRR_UNSPECIFIED\x10\x00\x12\x0b\n\x07PRR_LOW\x10\x01\x12\x0e\n\nPRR_MEDIUM\x10\x02\x12\x0c\n\x08PRR_HIGH\x10\x03\x12\x10\n\x0cPRR_CRITICAL\x10\x04*`\n\x0bNhiCategory\x12\x18\n\x14NHI_CATEGORY_UNKNOWN\x10\x00\x12\x0c\n\x08PAM_USER\x10\x01\x12\x10\n\x0cPAM_RESOURCE\x10\x02\x12\x0b\n\x07GATEWAY\x10\x03\x12\n\n\x06\x44\x45VICE\x10\x04\x42\x1f\n\x18\x63om.keepersecurity.protoB\x03PAMb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pam_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\030com.keepersecurity.protoB\003PAM' + _globals['_NHIMETRICSRESPONSE'].fields_by_name['pamUsageByUser']._loaded_options = None + _globals['_NHIMETRICSRESPONSE'].fields_by_name['pamUsageByUser']._serialized_options = b'\030\001' + _globals['_WEBRTCCONNECTIONTYPE']._serialized_start=5354 + _globals['_WEBRTCCONNECTIONTYPE']._serialized_end=5512 + _globals['_PAMOPERATIONTYPE']._serialized_start=5514 + _globals['_PAMOPERATIONTYPE']._serialized_end=5578 + _globals['_PAMOPERATIONRESULTTYPE']._serialized_start=5580 + _globals['_PAMOPERATIONRESULTTYPE']._serialized_end=5692 + _globals['_CONTROLLERMESSAGETYPE']._serialized_start=5695 + _globals['_CONTROLLERMESSAGETYPE']._serialized_end=5896 + _globals['_PAMRECORDINGTYPE']._serialized_start=5898 + _globals['_PAMRECORDINGTYPE']._serialized_end=5984 + _globals['_PAMRECORDINGRISKLEVEL']._serialized_start=5986 + _globals['_PAMRECORDINGRISKLEVEL']._serialized_end=6091 + _globals['_NHICATEGORY']._serialized_start=6093 + _globals['_NHICATEGORY']._serialized_end=6189 + _globals['_PAMROTATIONSCHEDULE']._serialized_start=51 + _globals['_PAMROTATIONSCHEDULE']._serialized_end=182 + _globals['_PAMROTATIONSCHEDULESRESPONSE']._serialized_start=184 + _globals['_PAMROTATIONSCHEDULESRESPONSE']._serialized_end=259 + _globals['_PAMONLINECONTROLLER']._serialized_start=262 + _globals['_PAMONLINECONTROLLER']._serialized_end=410 + _globals['_PAMWEBRTCCONNECTION']._serialized_start=413 + _globals['_PAMWEBRTCCONNECTION']._serialized_end=580 + _globals['_PAMONLINECONTROLLERS']._serialized_start=582 + _globals['_PAMONLINECONTROLLERS']._serialized_end=671 + _globals['_PAMROTATEREQUEST']._serialized_start=673 + _globals['_PAMROTATEREQUEST']._serialized_end=730 + _globals['_PAMCONTROLLERSRESPONSE']._serialized_start=732 + _globals['_PAMCONTROLLERSRESPONSE']._serialized_end=797 + _globals['_PAMREMOVECONTROLLER']._serialized_start=799 + _globals['_PAMREMOVECONTROLLER']._serialized_end=860 + _globals['_PAMREMOVECONTROLLERRESPONSE']._serialized_start=862 + _globals['_PAMREMOVECONTROLLERRESPONSE']._serialized_end=938 + _globals['_PAMMODIFYREQUEST']._serialized_start=940 + _globals['_PAMMODIFYREQUEST']._serialized_end=1001 + _globals['_PAMDATAOPERATION']._serialized_start=1004 + _globals['_PAMDATAOPERATION']._serialized_end=1156 + _globals['_PAMCONFIGURATIONDATA']._serialized_start=1158 + _globals['_PAMCONFIGURATIONDATA']._serialized_end=1259 + _globals['_PAMELEMENTDATA']._serialized_start=1261 + _globals['_PAMELEMENTDATA']._serialized_end=1330 + _globals['_PAMELEMENTOPERATIONRESULT']._serialized_start=1332 + _globals['_PAMELEMENTOPERATIONRESULT']._serialized_end=1441 + _globals['_PAMMODIFYRESULT']._serialized_start=1443 + _globals['_PAMMODIFYRESULT']._serialized_end=1509 + _globals['_PAMELEMENT']._serialized_start=1511 + _globals['_PAMELEMENT']._serialized_end=1631 + _globals['_PAMGENERICUIDREQUEST']._serialized_start=1633 + _globals['_PAMGENERICUIDREQUEST']._serialized_end=1668 + _globals['_PAMGENERICUIDSREQUEST']._serialized_start=1670 + _globals['_PAMGENERICUIDSREQUEST']._serialized_end=1707 + _globals['_PAMCONFIGURATION']._serialized_start=1710 + _globals['_PAMCONFIGURATION']._serialized_end=1881 + _globals['_PAMCONFIGURATIONS']._serialized_start=1883 + _globals['_PAMCONFIGURATIONS']._serialized_end=1949 + _globals['_PAMCONTROLLER']._serialized_start=1952 + _globals['_PAMCONTROLLER']._serialized_end=2207 + _globals['_PAMSETMAXINSTANCECOUNTREQUEST']._serialized_start=2209 + _globals['_PAMSETMAXINSTANCECOUNTREQUEST']._serialized_end=2289 + _globals['_CONTROLLERRESPONSE']._serialized_start=2291 + _globals['_CONTROLLERRESPONSE']._serialized_end=2328 + _globals['_PAMCONFIGURATIONCONTROLLER']._serialized_start=2330 + _globals['_PAMCONFIGURATIONCONTROLLER']._serialized_end=2407 + _globals['_CONFIGURATIONADDREQUEST']._serialized_start=2410 + _globals['_CONFIGURATIONADDREQUEST']._serialized_end=2573 + _globals['_RELAYACCESSCREDS']._serialized_start=2575 + _globals['_RELAYACCESSCREDS']._serialized_end=2649 + _globals['_PAMRECORDINGSREQUEST']._serialized_start=2652 + _globals['_PAMRECORDINGSREQUEST']._serialized_end=2909 + _globals['_PAMRECORDING']._serialized_start=2912 + _globals['_PAMRECORDING']._serialized_end=3252 + _globals['_PAMRECORDINGSRESPONSE']._serialized_start=3254 + _globals['_PAMRECORDINGSRESPONSE']._serialized_end=3333 + _globals['_PAMDATA']._serialized_start=3335 + _globals['_PAMDATA']._serialized_end=3377 + _globals['_UIDLIST']._serialized_start=3379 + _globals['_UIDLIST']._serialized_end=3402 + _globals['_PAMRESOURCECONFIG']._serialized_start=3405 + _globals['_PAMRESOURCECONFIG']._serialized_end=3841 + _globals['_PAMUNIVERSALSYNCFOLDER']._serialized_start=3843 + _globals['_PAMUNIVERSALSYNCFOLDER']._serialized_end=3880 + _globals['_PAMUNIVERSALSYNCCONFIG']._serialized_start=3883 + _globals['_PAMUNIVERSALSYNCCONFIG']._serialized_end=4135 + _globals['_NHIMETRICSREQUEST']._serialized_start=4137 + _globals['_NHIMETRICSREQUEST']._serialized_end=4192 + _globals['_PAMUSAGEBYUSER']._serialized_start=4195 + _globals['_PAMUSAGEBYUSER']._serialized_end=4479 + _globals['_NHIUSAGEBYUSER']._serialized_start=4481 + _globals['_NHIUSAGEBYUSER']._serialized_end=4593 + _globals['_NHIMETRICSRESPONSE']._serialized_start=4596 + _globals['_NHIMETRICSRESPONSE']._serialized_end=4856 + _globals['_NHIBULKMETRICSRESPONSE']._serialized_start=4858 + _globals['_NHIBULKMETRICSRESPONSE']._serialized_end=4926 + _globals['_NHIUIDENTRY']._serialized_start=4928 + _globals['_NHIUIDENTRY']._serialized_end=5006 + _globals['_GETNHIUIDSREQUEST']._serialized_start=5008 + _globals['_GETNHIUIDSREQUEST']._serialized_end=5063 + _globals['_GETNHIUIDSRESPONSE']._serialized_start=5065 + _globals['_GETNHIUIDSRESPONSE']._serialized_end=5117 + _globals['_PAMUNIVERSALSYNCPRECHECKREQUEST']._serialized_start=5119 + _globals['_PAMUNIVERSALSYNCPRECHECKREQUEST']._serialized_end=5192 + _globals['_PAMUNIVERSALSYNCPRECHECKRESULT']._serialized_start=5194 + _globals['_PAMUNIVERSALSYNCPRECHECKRESULT']._serialized_end=5261 + _globals['_PAMUNIVERSALSYNCPRECHECKRESPONSE']._serialized_start=5263 + _globals['_PAMUNIVERSALSYNCPRECHECKRESPONSE']._serialized_end=5351 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/pam_pb2.pyi b/keepercommander/proto/pam_pb2.pyi index 4853706aa..b5a56975a 100644 --- a/keepercommander/proto/pam_pb2.pyi +++ b/keepercommander/proto/pam_pb2.pyi @@ -9,7 +9,7 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class WebRtcConnectionType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () CONNECTION: _ClassVar[WebRtcConnectionType] TUNNEL: _ClassVar[WebRtcConnectionType] SSH: _ClassVar[WebRtcConnectionType] @@ -23,40 +23,53 @@ class WebRtcConnectionType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): KUBERNETES: _ClassVar[WebRtcConnectionType] class PAMOperationType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () ADD: _ClassVar[PAMOperationType] UPDATE: _ClassVar[PAMOperationType] REPLACE: _ClassVar[PAMOperationType] DELETE: _ClassVar[PAMOperationType] class PAMOperationResultType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () POT_SUCCESS: _ClassVar[PAMOperationResultType] POT_UNKNOWN_ERROR: _ClassVar[PAMOperationResultType] POT_ALREADY_EXISTS: _ClassVar[PAMOperationResultType] POT_DOES_NOT_EXIST: _ClassVar[PAMOperationResultType] class ControllerMessageType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () CMT_GENERAL: _ClassVar[ControllerMessageType] CMT_ROTATE: _ClassVar[ControllerMessageType] CMT_DISCOVERY: _ClassVar[ControllerMessageType] CMT_CONNECT: _ClassVar[ControllerMessageType] + CMT_ANALYZE_RECORDING: _ClassVar[ControllerMessageType] + CMT_WORKFLOW_ACCESS_ELEVATION: _ClassVar[ControllerMessageType] + CMT_USS: _ClassVar[ControllerMessageType] + CMT_INFO: _ClassVar[ControllerMessageType] + CMT_AUTOMATION: _ClassVar[ControllerMessageType] class PAMRecordingType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () PRT_SESSION: _ClassVar[PAMRecordingType] PRT_TYPESCRIPT: _ClassVar[PAMRecordingType] PRT_TIME: _ClassVar[PAMRecordingType] PRT_SUMMARY: _ClassVar[PAMRecordingType] class PAMRecordingRiskLevel(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () PRR_UNSPECIFIED: _ClassVar[PAMRecordingRiskLevel] PRR_LOW: _ClassVar[PAMRecordingRiskLevel] PRR_MEDIUM: _ClassVar[PAMRecordingRiskLevel] PRR_HIGH: _ClassVar[PAMRecordingRiskLevel] PRR_CRITICAL: _ClassVar[PAMRecordingRiskLevel] + +class NhiCategory(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + NHI_CATEGORY_UNKNOWN: _ClassVar[NhiCategory] + PAM_USER: _ClassVar[NhiCategory] + PAM_RESOURCE: _ClassVar[NhiCategory] + GATEWAY: _ClassVar[NhiCategory] + DEVICE: _ClassVar[NhiCategory] CONNECTION: WebRtcConnectionType TUNNEL: WebRtcConnectionType SSH: WebRtcConnectionType @@ -80,6 +93,11 @@ CMT_GENERAL: ControllerMessageType CMT_ROTATE: ControllerMessageType CMT_DISCOVERY: ControllerMessageType CMT_CONNECT: ControllerMessageType +CMT_ANALYZE_RECORDING: ControllerMessageType +CMT_WORKFLOW_ACCESS_ELEVATION: ControllerMessageType +CMT_USS: ControllerMessageType +CMT_INFO: ControllerMessageType +CMT_AUTOMATION: ControllerMessageType PRT_SESSION: PAMRecordingType PRT_TYPESCRIPT: PAMRecordingType PRT_TIME: PAMRecordingType @@ -89,9 +107,14 @@ PRR_LOW: PAMRecordingRiskLevel PRR_MEDIUM: PAMRecordingRiskLevel PRR_HIGH: PAMRecordingRiskLevel PRR_CRITICAL: PAMRecordingRiskLevel +NHI_CATEGORY_UNKNOWN: NhiCategory +PAM_USER: NhiCategory +PAM_RESOURCE: NhiCategory +GATEWAY: NhiCategory +DEVICE: NhiCategory class PAMRotationSchedule(_message.Message): - __slots__ = ["recordUid", "configurationUid", "controllerUid", "scheduleData", "noSchedule"] + __slots__ = ("recordUid", "configurationUid", "controllerUid", "scheduleData", "noSchedule") RECORDUID_FIELD_NUMBER: _ClassVar[int] CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] @@ -105,13 +128,13 @@ class PAMRotationSchedule(_message.Message): def __init__(self, recordUid: _Optional[bytes] = ..., configurationUid: _Optional[bytes] = ..., controllerUid: _Optional[bytes] = ..., scheduleData: _Optional[str] = ..., noSchedule: bool = ...) -> None: ... class PAMRotationSchedulesResponse(_message.Message): - __slots__ = ["schedules"] + __slots__ = ("schedules",) SCHEDULES_FIELD_NUMBER: _ClassVar[int] schedules: _containers.RepeatedCompositeFieldContainer[PAMRotationSchedule] def __init__(self, schedules: _Optional[_Iterable[_Union[PAMRotationSchedule, _Mapping]]] = ...) -> None: ... class PAMOnlineController(_message.Message): - __slots__ = ["controllerUid", "connectedOn", "ipAddress", "version", "connections"] + __slots__ = ("controllerUid", "connectedOn", "ipAddress", "version", "connections") CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] CONNECTEDON_FIELD_NUMBER: _ClassVar[int] IPADDRESS_FIELD_NUMBER: _ClassVar[int] @@ -125,7 +148,7 @@ class PAMOnlineController(_message.Message): def __init__(self, controllerUid: _Optional[bytes] = ..., connectedOn: _Optional[int] = ..., ipAddress: _Optional[str] = ..., version: _Optional[str] = ..., connections: _Optional[_Iterable[_Union[PAMWebRtcConnection, _Mapping]]] = ...) -> None: ... class PAMWebRtcConnection(_message.Message): - __slots__ = ["connectionUid", "type", "recordUid", "userName", "startedOn", "configurationUid"] + __slots__ = ("connectionUid", "type", "recordUid", "userName", "startedOn", "configurationUid") CONNECTIONUID_FIELD_NUMBER: _ClassVar[int] TYPE_FIELD_NUMBER: _ClassVar[int] RECORDUID_FIELD_NUMBER: _ClassVar[int] @@ -141,7 +164,7 @@ class PAMWebRtcConnection(_message.Message): def __init__(self, connectionUid: _Optional[bytes] = ..., type: _Optional[_Union[WebRtcConnectionType, str]] = ..., recordUid: _Optional[bytes] = ..., userName: _Optional[str] = ..., startedOn: _Optional[int] = ..., configurationUid: _Optional[bytes] = ...) -> None: ... class PAMOnlineControllers(_message.Message): - __slots__ = ["deprecated", "controllers"] + __slots__ = ("deprecated", "controllers") DEPRECATED_FIELD_NUMBER: _ClassVar[int] CONTROLLERS_FIELD_NUMBER: _ClassVar[int] deprecated: _containers.RepeatedScalarFieldContainer[bytes] @@ -149,7 +172,7 @@ class PAMOnlineControllers(_message.Message): def __init__(self, deprecated: _Optional[_Iterable[bytes]] = ..., controllers: _Optional[_Iterable[_Union[PAMOnlineController, _Mapping]]] = ...) -> None: ... class PAMRotateRequest(_message.Message): - __slots__ = ["requestUid", "recordUid"] + __slots__ = ("requestUid", "recordUid") REQUESTUID_FIELD_NUMBER: _ClassVar[int] RECORDUID_FIELD_NUMBER: _ClassVar[int] requestUid: bytes @@ -157,13 +180,13 @@ class PAMRotateRequest(_message.Message): def __init__(self, requestUid: _Optional[bytes] = ..., recordUid: _Optional[bytes] = ...) -> None: ... class PAMControllersResponse(_message.Message): - __slots__ = ["controllers"] + __slots__ = ("controllers",) CONTROLLERS_FIELD_NUMBER: _ClassVar[int] controllers: _containers.RepeatedCompositeFieldContainer[PAMController] def __init__(self, controllers: _Optional[_Iterable[_Union[PAMController, _Mapping]]] = ...) -> None: ... class PAMRemoveController(_message.Message): - __slots__ = ["controllerUid", "message"] + __slots__ = ("controllerUid", "message") CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] controllerUid: bytes @@ -171,19 +194,19 @@ class PAMRemoveController(_message.Message): def __init__(self, controllerUid: _Optional[bytes] = ..., message: _Optional[str] = ...) -> None: ... class PAMRemoveControllerResponse(_message.Message): - __slots__ = ["controllers"] + __slots__ = ("controllers",) CONTROLLERS_FIELD_NUMBER: _ClassVar[int] controllers: _containers.RepeatedCompositeFieldContainer[PAMRemoveController] def __init__(self, controllers: _Optional[_Iterable[_Union[PAMRemoveController, _Mapping]]] = ...) -> None: ... class PAMModifyRequest(_message.Message): - __slots__ = ["operations"] + __slots__ = ("operations",) OPERATIONS_FIELD_NUMBER: _ClassVar[int] operations: _containers.RepeatedCompositeFieldContainer[PAMDataOperation] def __init__(self, operations: _Optional[_Iterable[_Union[PAMDataOperation, _Mapping]]] = ...) -> None: ... class PAMDataOperation(_message.Message): - __slots__ = ["operationType", "configuration", "element"] + __slots__ = ("operationType", "configuration", "element") OPERATIONTYPE_FIELD_NUMBER: _ClassVar[int] CONFIGURATION_FIELD_NUMBER: _ClassVar[int] ELEMENT_FIELD_NUMBER: _ClassVar[int] @@ -193,7 +216,7 @@ class PAMDataOperation(_message.Message): def __init__(self, operationType: _Optional[_Union[PAMOperationType, str]] = ..., configuration: _Optional[_Union[PAMConfigurationData, _Mapping]] = ..., element: _Optional[_Union[PAMElementData, _Mapping]] = ...) -> None: ... class PAMConfigurationData(_message.Message): - __slots__ = ["configurationUid", "nodeId", "controllerUid", "data"] + __slots__ = ("configurationUid", "nodeId", "controllerUid", "data") CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] NODEID_FIELD_NUMBER: _ClassVar[int] CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] @@ -205,7 +228,7 @@ class PAMConfigurationData(_message.Message): def __init__(self, configurationUid: _Optional[bytes] = ..., nodeId: _Optional[int] = ..., controllerUid: _Optional[bytes] = ..., data: _Optional[bytes] = ...) -> None: ... class PAMElementData(_message.Message): - __slots__ = ["elementUid", "parentUid", "data"] + __slots__ = ("elementUid", "parentUid", "data") ELEMENTUID_FIELD_NUMBER: _ClassVar[int] PARENTUID_FIELD_NUMBER: _ClassVar[int] DATA_FIELD_NUMBER: _ClassVar[int] @@ -215,7 +238,7 @@ class PAMElementData(_message.Message): def __init__(self, elementUid: _Optional[bytes] = ..., parentUid: _Optional[bytes] = ..., data: _Optional[bytes] = ...) -> None: ... class PAMElementOperationResult(_message.Message): - __slots__ = ["elementUid", "result", "message"] + __slots__ = ("elementUid", "result", "message") ELEMENTUID_FIELD_NUMBER: _ClassVar[int] RESULT_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -225,13 +248,13 @@ class PAMElementOperationResult(_message.Message): def __init__(self, elementUid: _Optional[bytes] = ..., result: _Optional[_Union[PAMOperationResultType, str]] = ..., message: _Optional[str] = ...) -> None: ... class PAMModifyResult(_message.Message): - __slots__ = ["results"] + __slots__ = ("results",) RESULTS_FIELD_NUMBER: _ClassVar[int] results: _containers.RepeatedCompositeFieldContainer[PAMElementOperationResult] def __init__(self, results: _Optional[_Iterable[_Union[PAMElementOperationResult, _Mapping]]] = ...) -> None: ... class PAMElement(_message.Message): - __slots__ = ["elementUid", "data", "created", "lastModified", "children"] + __slots__ = ("elementUid", "data", "created", "lastModified", "children") ELEMENTUID_FIELD_NUMBER: _ClassVar[int] DATA_FIELD_NUMBER: _ClassVar[int] CREATED_FIELD_NUMBER: _ClassVar[int] @@ -245,19 +268,19 @@ class PAMElement(_message.Message): def __init__(self, elementUid: _Optional[bytes] = ..., data: _Optional[bytes] = ..., created: _Optional[int] = ..., lastModified: _Optional[int] = ..., children: _Optional[_Iterable[_Union[PAMElement, _Mapping]]] = ...) -> None: ... class PAMGenericUidRequest(_message.Message): - __slots__ = ["uid"] + __slots__ = ("uid",) UID_FIELD_NUMBER: _ClassVar[int] uid: bytes def __init__(self, uid: _Optional[bytes] = ...) -> None: ... class PAMGenericUidsRequest(_message.Message): - __slots__ = ["uids"] + __slots__ = ("uids",) UIDS_FIELD_NUMBER: _ClassVar[int] uids: _containers.RepeatedScalarFieldContainer[bytes] def __init__(self, uids: _Optional[_Iterable[bytes]] = ...) -> None: ... class PAMConfiguration(_message.Message): - __slots__ = ["configurationUid", "nodeId", "controllerUid", "data", "created", "lastModified", "children"] + __slots__ = ("configurationUid", "nodeId", "controllerUid", "data", "created", "lastModified", "children") CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] NODEID_FIELD_NUMBER: _ClassVar[int] CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] @@ -275,13 +298,13 @@ class PAMConfiguration(_message.Message): def __init__(self, configurationUid: _Optional[bytes] = ..., nodeId: _Optional[int] = ..., controllerUid: _Optional[bytes] = ..., data: _Optional[bytes] = ..., created: _Optional[int] = ..., lastModified: _Optional[int] = ..., children: _Optional[_Iterable[_Union[PAMElement, _Mapping]]] = ...) -> None: ... class PAMConfigurations(_message.Message): - __slots__ = ["configurations"] + __slots__ = ("configurations",) CONFIGURATIONS_FIELD_NUMBER: _ClassVar[int] configurations: _containers.RepeatedCompositeFieldContainer[PAMConfiguration] def __init__(self, configurations: _Optional[_Iterable[_Union[PAMConfiguration, _Mapping]]] = ...) -> None: ... class PAMController(_message.Message): - __slots__ = ["controllerUid", "controllerName", "deviceToken", "deviceName", "nodeId", "created", "lastModified", "applicationUid", "appClientType", "isInitialized"] + __slots__ = ("controllerUid", "controllerName", "deviceToken", "deviceName", "nodeId", "created", "lastModified", "applicationUid", "appClientType", "isInitialized") CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] CONTROLLERNAME_FIELD_NUMBER: _ClassVar[int] DEVICETOKEN_FIELD_NUMBER: _ClassVar[int] @@ -305,7 +328,7 @@ class PAMController(_message.Message): def __init__(self, controllerUid: _Optional[bytes] = ..., controllerName: _Optional[str] = ..., deviceToken: _Optional[str] = ..., deviceName: _Optional[str] = ..., nodeId: _Optional[int] = ..., created: _Optional[int] = ..., lastModified: _Optional[int] = ..., applicationUid: _Optional[bytes] = ..., appClientType: _Optional[_Union[_enterprise_pb2.AppClientType, str]] = ..., isInitialized: bool = ...) -> None: ... class PAMSetMaxInstanceCountRequest(_message.Message): - __slots__ = ["controllerUid", "maxInstanceCount"] + __slots__ = ("controllerUid", "maxInstanceCount") CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] MAXINSTANCECOUNT_FIELD_NUMBER: _ClassVar[int] controllerUid: bytes @@ -313,13 +336,13 @@ class PAMSetMaxInstanceCountRequest(_message.Message): def __init__(self, controllerUid: _Optional[bytes] = ..., maxInstanceCount: _Optional[int] = ...) -> None: ... class ControllerResponse(_message.Message): - __slots__ = ["payload"] + __slots__ = ("payload",) PAYLOAD_FIELD_NUMBER: _ClassVar[int] payload: str def __init__(self, payload: _Optional[str] = ...) -> None: ... class PAMConfigurationController(_message.Message): - __slots__ = ["configurationUid", "controllerUid"] + __slots__ = ("configurationUid", "controllerUid") CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] configurationUid: bytes @@ -327,7 +350,7 @@ class PAMConfigurationController(_message.Message): def __init__(self, configurationUid: _Optional[bytes] = ..., controllerUid: _Optional[bytes] = ...) -> None: ... class ConfigurationAddRequest(_message.Message): - __slots__ = ["configurationUid", "recordKey", "data", "recordLinks", "audit"] + __slots__ = ("configurationUid", "recordKey", "data", "recordLinks", "audit") CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] RECORDKEY_FIELD_NUMBER: _ClassVar[int] DATA_FIELD_NUMBER: _ClassVar[int] @@ -341,7 +364,7 @@ class ConfigurationAddRequest(_message.Message): def __init__(self, configurationUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., data: _Optional[bytes] = ..., recordLinks: _Optional[_Iterable[_Union[_record_pb2.RecordLink, _Mapping]]] = ..., audit: _Optional[_Union[_record_pb2.RecordAudit, _Mapping]] = ...) -> None: ... class RelayAccessCreds(_message.Message): - __slots__ = ["username", "password", "serverTime"] + __slots__ = ("username", "password", "serverTime") USERNAME_FIELD_NUMBER: _ClassVar[int] PASSWORD_FIELD_NUMBER: _ClassVar[int] SERVERTIME_FIELD_NUMBER: _ClassVar[int] @@ -351,7 +374,7 @@ class RelayAccessCreds(_message.Message): def __init__(self, username: _Optional[str] = ..., password: _Optional[str] = ..., serverTime: _Optional[int] = ...) -> None: ... class PAMRecordingsRequest(_message.Message): - __slots__ = ["recordUid", "maxCount", "rangeStart", "rangeEnd", "types", "risks", "protocols", "closeReasons"] + __slots__ = ("recordUid", "maxCount", "rangeStart", "rangeEnd", "types", "risks", "protocols", "closeReasons") RECORDUID_FIELD_NUMBER: _ClassVar[int] MAXCOUNT_FIELD_NUMBER: _ClassVar[int] RANGESTART_FIELD_NUMBER: _ClassVar[int] @@ -371,7 +394,7 @@ class PAMRecordingsRequest(_message.Message): def __init__(self, recordUid: _Optional[bytes] = ..., maxCount: _Optional[int] = ..., rangeStart: _Optional[int] = ..., rangeEnd: _Optional[int] = ..., types: _Optional[_Iterable[_Union[PAMRecordingType, str]]] = ..., risks: _Optional[_Iterable[_Union[PAMRecordingRiskLevel, str]]] = ..., protocols: _Optional[_Iterable[str]] = ..., closeReasons: _Optional[_Iterable[int]] = ...) -> None: ... class PAMRecording(_message.Message): - __slots__ = ["connectionUid", "recordingType", "recordUid", "userName", "startedOn", "length", "fileSize", "createdOn", "protocol", "closeReason", "recordingDuration", "aiOverallRiskLevel", "aiOverallSummary"] + __slots__ = ("connectionUid", "recordingType", "recordUid", "userName", "startedOn", "length", "fileSize", "createdOn", "protocol", "closeReason", "recordingDuration", "aiOverallRiskLevel", "aiOverallSummary") CONNECTIONUID_FIELD_NUMBER: _ClassVar[int] RECORDINGTYPE_FIELD_NUMBER: _ClassVar[int] RECORDUID_FIELD_NUMBER: _ClassVar[int] @@ -401,7 +424,7 @@ class PAMRecording(_message.Message): def __init__(self, connectionUid: _Optional[bytes] = ..., recordingType: _Optional[_Union[PAMRecordingType, str]] = ..., recordUid: _Optional[bytes] = ..., userName: _Optional[str] = ..., startedOn: _Optional[int] = ..., length: _Optional[int] = ..., fileSize: _Optional[int] = ..., createdOn: _Optional[int] = ..., protocol: _Optional[str] = ..., closeReason: _Optional[int] = ..., recordingDuration: _Optional[int] = ..., aiOverallRiskLevel: _Optional[_Union[PAMRecordingRiskLevel, str]] = ..., aiOverallSummary: _Optional[bytes] = ...) -> None: ... class PAMRecordingsResponse(_message.Message): - __slots__ = ["recordings", "hasMore"] + __slots__ = ("recordings", "hasMore") RECORDINGS_FIELD_NUMBER: _ClassVar[int] HASMORE_FIELD_NUMBER: _ClassVar[int] recordings: _containers.RepeatedCompositeFieldContainer[PAMRecording] @@ -409,7 +432,7 @@ class PAMRecordingsResponse(_message.Message): def __init__(self, recordings: _Optional[_Iterable[_Union[PAMRecording, _Mapping]]] = ..., hasMore: bool = ...) -> None: ... class PAMData(_message.Message): - __slots__ = ["vertex", "content"] + __slots__ = ("vertex", "content") VERTEX_FIELD_NUMBER: _ClassVar[int] CONTENT_FIELD_NUMBER: _ClassVar[int] vertex: bytes @@ -417,13 +440,13 @@ class PAMData(_message.Message): def __init__(self, vertex: _Optional[bytes] = ..., content: _Optional[bytes] = ...) -> None: ... class UidList(_message.Message): - __slots__ = ["uids"] + __slots__ = ("uids",) UIDS_FIELD_NUMBER: _ClassVar[int] uids: _containers.RepeatedScalarFieldContainer[bytes] def __init__(self, uids: _Optional[_Iterable[bytes]] = ...) -> None: ... class PAMResourceConfig(_message.Message): - __slots__ = ["recordUid", "networkUid", "adminUid", "meta", "connectionSettings", "connectUsers", "domainUid", "jitSettings", "keeperAiSettings"] + __slots__ = ("recordUid", "networkUid", "adminUid", "meta", "connectionSettings", "connectUsers", "domainUid", "jitSettings", "keeperAiSettings", "updateServices") RECORDUID_FIELD_NUMBER: _ClassVar[int] NETWORKUID_FIELD_NUMBER: _ClassVar[int] ADMINUID_FIELD_NUMBER: _ClassVar[int] @@ -433,6 +456,7 @@ class PAMResourceConfig(_message.Message): DOMAINUID_FIELD_NUMBER: _ClassVar[int] JITSETTINGS_FIELD_NUMBER: _ClassVar[int] KEEPERAISETTINGS_FIELD_NUMBER: _ClassVar[int] + UPDATESERVICES_FIELD_NUMBER: _ClassVar[int] recordUid: bytes networkUid: bytes adminUid: bytes @@ -442,4 +466,145 @@ class PAMResourceConfig(_message.Message): domainUid: bytes jitSettings: bytes keeperAiSettings: bytes - def __init__(self, recordUid: _Optional[bytes] = ..., networkUid: _Optional[bytes] = ..., adminUid: _Optional[bytes] = ..., meta: _Optional[bytes] = ..., connectionSettings: _Optional[bytes] = ..., connectUsers: _Optional[_Union[UidList, _Mapping]] = ..., domainUid: _Optional[bytes] = ..., jitSettings: _Optional[bytes] = ..., keeperAiSettings: _Optional[bytes] = ...) -> None: ... + updateServices: bool + def __init__(self, recordUid: _Optional[bytes] = ..., networkUid: _Optional[bytes] = ..., adminUid: _Optional[bytes] = ..., meta: _Optional[bytes] = ..., connectionSettings: _Optional[bytes] = ..., connectUsers: _Optional[_Union[UidList, _Mapping]] = ..., domainUid: _Optional[bytes] = ..., jitSettings: _Optional[bytes] = ..., keeperAiSettings: _Optional[bytes] = ..., updateServices: bool = ...) -> None: ... + +class PAMUniversalSyncFolder(_message.Message): + __slots__ = ("uid",) + UID_FIELD_NUMBER: _ClassVar[int] + uid: bytes + def __init__(self, uid: _Optional[bytes] = ...) -> None: ... + +class PAMUniversalSyncConfig(_message.Message): + __slots__ = ("networkUid", "enabled", "dryRunEnabled", "folders", "syncIdentity", "vaultName") + NETWORKUID_FIELD_NUMBER: _ClassVar[int] + ENABLED_FIELD_NUMBER: _ClassVar[int] + DRYRUNENABLED_FIELD_NUMBER: _ClassVar[int] + FOLDERS_FIELD_NUMBER: _ClassVar[int] + SYNCIDENTITY_FIELD_NUMBER: _ClassVar[int] + VAULTNAME_FIELD_NUMBER: _ClassVar[int] + networkUid: bytes + enabled: bool + dryRunEnabled: bool + folders: _containers.RepeatedCompositeFieldContainer[PAMUniversalSyncFolder] + syncIdentity: bytes + vaultName: bytes + def __init__(self, networkUid: _Optional[bytes] = ..., enabled: bool = ..., dryRunEnabled: bool = ..., folders: _Optional[_Iterable[_Union[PAMUniversalSyncFolder, _Mapping]]] = ..., syncIdentity: _Optional[bytes] = ..., vaultName: _Optional[bytes] = ...) -> None: ... + +class NhiMetricsRequest(_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 PamUsageByUser(_message.Message): + __slots__ = ("userId", "recordRotationScheduledOk", "pamConnectionStarted", "pamTunnelStarted", "discoveryJobStarted", "recordRotationOnDemandOk", "pamSessionRecordingStarted", "pamRbiStarted", "pamSessionRbiRecordingStarted") + USERID_FIELD_NUMBER: _ClassVar[int] + RECORDROTATIONSCHEDULEDOK_FIELD_NUMBER: _ClassVar[int] + PAMCONNECTIONSTARTED_FIELD_NUMBER: _ClassVar[int] + PAMTUNNELSTARTED_FIELD_NUMBER: _ClassVar[int] + DISCOVERYJOBSTARTED_FIELD_NUMBER: _ClassVar[int] + RECORDROTATIONONDEMANDOK_FIELD_NUMBER: _ClassVar[int] + PAMSESSIONRECORDINGSTARTED_FIELD_NUMBER: _ClassVar[int] + PAMRBISTARTED_FIELD_NUMBER: _ClassVar[int] + PAMSESSIONRBIRECORDINGSTARTED_FIELD_NUMBER: _ClassVar[int] + userId: int + recordRotationScheduledOk: int + pamConnectionStarted: int + pamTunnelStarted: int + discoveryJobStarted: int + recordRotationOnDemandOk: int + pamSessionRecordingStarted: int + pamRbiStarted: int + pamSessionRbiRecordingStarted: int + def __init__(self, userId: _Optional[int] = ..., recordRotationScheduledOk: _Optional[int] = ..., pamConnectionStarted: _Optional[int] = ..., pamTunnelStarted: _Optional[int] = ..., discoveryJobStarted: _Optional[int] = ..., recordRotationOnDemandOk: _Optional[int] = ..., pamSessionRecordingStarted: _Optional[int] = ..., pamRbiStarted: _Optional[int] = ..., pamSessionRbiRecordingStarted: _Optional[int] = ...) -> None: ... + +class NhiUsageByUser(_message.Message): + __slots__ = ("userId", "rotations", "tunnels", "connections", "discoveryJobs") + USERID_FIELD_NUMBER: _ClassVar[int] + ROTATIONS_FIELD_NUMBER: _ClassVar[int] + TUNNELS_FIELD_NUMBER: _ClassVar[int] + CONNECTIONS_FIELD_NUMBER: _ClassVar[int] + DISCOVERYJOBS_FIELD_NUMBER: _ClassVar[int] + userId: int + rotations: int + tunnels: int + connections: int + discoveryJobs: int + def __init__(self, userId: _Optional[int] = ..., rotations: _Optional[int] = ..., tunnels: _Optional[int] = ..., connections: _Optional[int] = ..., discoveryJobs: _Optional[int] = ...) -> None: ... + +class NhiMetricsResponse(_message.Message): + __slots__ = ("enterpriseId", "startTime", "endTime", "uniqueKsmDevices", "pamGatewayOnline", "pamUsageByUser", "nhiCount", "ksmNhiCount", "usageByUser") + ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] + STARTTIME_FIELD_NUMBER: _ClassVar[int] + ENDTIME_FIELD_NUMBER: _ClassVar[int] + UNIQUEKSMDEVICES_FIELD_NUMBER: _ClassVar[int] + PAMGATEWAYONLINE_FIELD_NUMBER: _ClassVar[int] + PAMUSAGEBYUSER_FIELD_NUMBER: _ClassVar[int] + NHICOUNT_FIELD_NUMBER: _ClassVar[int] + KSMNHICOUNT_FIELD_NUMBER: _ClassVar[int] + USAGEBYUSER_FIELD_NUMBER: _ClassVar[int] + enterpriseId: int + startTime: int + endTime: int + uniqueKsmDevices: int + pamGatewayOnline: int + pamUsageByUser: _containers.RepeatedCompositeFieldContainer[PamUsageByUser] + nhiCount: int + ksmNhiCount: int + usageByUser: _containers.RepeatedCompositeFieldContainer[NhiUsageByUser] + def __init__(self, enterpriseId: _Optional[int] = ..., startTime: _Optional[int] = ..., endTime: _Optional[int] = ..., uniqueKsmDevices: _Optional[int] = ..., pamGatewayOnline: _Optional[int] = ..., pamUsageByUser: _Optional[_Iterable[_Union[PamUsageByUser, _Mapping]]] = ..., nhiCount: _Optional[int] = ..., ksmNhiCount: _Optional[int] = ..., usageByUser: _Optional[_Iterable[_Union[NhiUsageByUser, _Mapping]]] = ...) -> None: ... + +class NhiBulkMetricsResponse(_message.Message): + __slots__ = ("responses",) + RESPONSES_FIELD_NUMBER: _ClassVar[int] + responses: _containers.RepeatedCompositeFieldContainer[NhiMetricsResponse] + def __init__(self, responses: _Optional[_Iterable[_Union[NhiMetricsResponse, _Mapping]]] = ...) -> None: ... + +class NhiUidEntry(_message.Message): + __slots__ = ("uid", "category", "ksmNhi") + UID_FIELD_NUMBER: _ClassVar[int] + CATEGORY_FIELD_NUMBER: _ClassVar[int] + KSMNHI_FIELD_NUMBER: _ClassVar[int] + uid: str + category: NhiCategory + ksmNhi: bool + def __init__(self, uid: _Optional[str] = ..., category: _Optional[_Union[NhiCategory, str]] = ..., ksmNhi: bool = ...) -> None: ... + +class GetNhiUidsRequest(_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 GetNhiUidsResponse(_message.Message): + __slots__ = ("uids",) + UIDS_FIELD_NUMBER: _ClassVar[int] + uids: _containers.RepeatedCompositeFieldContainer[NhiUidEntry] + def __init__(self, uids: _Optional[_Iterable[_Union[NhiUidEntry, _Mapping]]] = ...) -> None: ... + +class PAMUniversalSyncPreCheckRequest(_message.Message): + __slots__ = ("networkUid", "folderUids") + NETWORKUID_FIELD_NUMBER: _ClassVar[int] + FOLDERUIDS_FIELD_NUMBER: _ClassVar[int] + networkUid: bytes + folderUids: _containers.RepeatedScalarFieldContainer[bytes] + def __init__(self, networkUid: _Optional[bytes] = ..., folderUids: _Optional[_Iterable[bytes]] = ...) -> None: ... + +class PAMUniversalSyncPreCheckResult(_message.Message): + __slots__ = ("folderUid", "isUsed") + FOLDERUID_FIELD_NUMBER: _ClassVar[int] + ISUSED_FIELD_NUMBER: _ClassVar[int] + folderUid: bytes + isUsed: bool + def __init__(self, folderUid: _Optional[bytes] = ..., isUsed: bool = ...) -> None: ... + +class PAMUniversalSyncPreCheckResponse(_message.Message): + __slots__ = ("results",) + RESULTS_FIELD_NUMBER: _ClassVar[int] + results: _containers.RepeatedCompositeFieldContainer[PAMUniversalSyncPreCheckResult] + def __init__(self, results: _Optional[_Iterable[_Union[PAMUniversalSyncPreCheckResult, _Mapping]]] = ...) -> None: ... diff --git a/keepercommander/proto/pedm_pb2.py b/keepercommander/proto/pedm_pb2.py index a6b4adc8d..39a869ae7 100644 --- a/keepercommander/proto/pedm_pb2.py +++ b/keepercommander/proto/pedm_pb2.py @@ -1,142 +1,154 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: pedm.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'pedm.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from . import folder_pb2 as folder__pb2 -from . import NotificationCenter_pb2 as NotificationCenter__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\npedm.proto\x12\x04PEDM\x1a\x0c\x66older.proto\x1a\x18NotificationCenter.proto\"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\";\n\nPedmStatus\x12\x0b\n\x03key\x18\x01 \x03(\x0c\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x0f\n\x07message\x18\x03 \x01(\t\"\x89\x01\n\x12PedmStatusResponse\x12#\n\taddStatus\x18\x01 \x03(\x0b\x32\x10.PEDM.PedmStatus\x12&\n\x0cupdateStatus\x18\x02 \x03(\x0b\x32\x10.PEDM.PedmStatus\x12&\n\x0cremoveStatus\x18\x03 \x03(\x0b\x32\x10.PEDM.PedmStatus\"4\n\x0e\x44\x65ploymentData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0c\x65\x63PrivateKey\x18\x02 \x01(\x0c\"\x9a\x01\n\x17\x44\x65ploymentCreateRequest\x12\x15\n\rdeploymentUid\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x65sKey\x18\x02 \x01(\x0c\x12\x13\n\x0b\x65\x63PublicKey\x18\x03 \x01(\x0c\x12\x19\n\x11spiffeCertificate\x18\x04 \x01(\x0c\x12\x15\n\rencryptedData\x18\x05 \x01(\x0c\x12\x11\n\tagentData\x18\x06 \x01(\x0c\"\x8d\x01\n\x17\x44\x65ploymentUpdateRequest\x12\x15\n\rdeploymentUid\x18\x01 \x01(\x0c\x12\x15\n\rencryptedData\x18\x02 \x01(\x0c\x12)\n\x08\x64isabled\x18\x03 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x19\n\x11spiffeCertificate\x18\x04 \x01(\x0c\"\xa2\x01\n\x17ModifyDeploymentRequest\x12\x34\n\raddDeployment\x18\x01 \x03(\x0b\x32\x1d.PEDM.DeploymentCreateRequest\x12\x37\n\x10updateDeployment\x18\x02 \x03(\x0b\x32\x1d.PEDM.DeploymentUpdateRequest\x12\x18\n\x10removeDeployment\x18\x03 \x03(\x0c\"a\n\x0b\x41gentUpdate\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\x12)\n\x08\x64isabled\x18\x02 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x15\n\rdeploymentUid\x18\x03 \x01(\x0c\"Q\n\x12ModifyAgentRequest\x12&\n\x0bupdateAgent\x18\x02 \x03(\x0b\x32\x11.PEDM.AgentUpdate\x12\x13\n\x0bremoveAgent\x18\x03 \x03(\x0c\"p\n\tPolicyAdd\x12\x11\n\tpolicyUid\x18\x01 \x01(\x0c\x12\x11\n\tplainData\x18\x02 \x01(\x0c\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12\x14\n\x0c\x65ncryptedKey\x18\x04 \x01(\x0c\x12\x10\n\x08\x64isabled\x18\x05 \x01(\x08\"v\n\x0cPolicyUpdate\x12\x11\n\tpolicyUid\x18\x01 \x01(\x0c\x12\x11\n\tplainData\x18\x02 \x01(\x0c\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12)\n\x08\x64isabled\x18\x04 \x01(\x0e\x32\x17.Folder.SetBooleanValue\"s\n\rPolicyRequest\x12\"\n\taddPolicy\x18\x01 \x03(\x0b\x32\x0f.PEDM.PolicyAdd\x12(\n\x0cupdatePolicy\x18\x02 \x03(\x0b\x32\x12.PEDM.PolicyUpdate\x12\x14\n\x0cremovePolicy\x18\x03 \x03(\x0c\"6\n\nPolicyLink\x12\x11\n\tpolicyUid\x18\x01 \x01(\x0c\x12\x15\n\rcollectionUid\x18\x02 \x03(\x0c\"E\n\x1aSetPolicyCollectionRequest\x12\'\n\rsetCollection\x18\x01 \x03(\x0b\x32\x10.PEDM.PolicyLink\"W\n\x0f\x43ollectionValue\x12\x15\n\rcollectionUid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x63ollectionType\x18\x02 \x01(\x05\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\"z\n\x12\x43ollectionLinkData\x12\x15\n\rcollectionUid\x18\x01 \x01(\x0c\x12\x0f\n\x07linkUid\x18\x02 \x01(\x0c\x12*\n\x08linkType\x18\x03 \x01(\x0e\x32\x18.PEDM.CollectionLinkType\x12\x10\n\x08linkData\x18\x04 \x01(\x0c\"\x8c\x01\n\x11\x43ollectionRequest\x12,\n\raddCollection\x18\x01 \x03(\x0b\x32\x15.PEDM.CollectionValue\x12/\n\x10updateCollection\x18\x02 \x03(\x0b\x32\x15.PEDM.CollectionValue\x12\x18\n\x10removeCollection\x18\x03 \x03(\x0c\"{\n\x18SetCollectionLinkRequest\x12/\n\raddCollection\x18\x01 \x03(\x0b\x32\x18.PEDM.CollectionLinkData\x12.\n\x10removeCollection\x18\x02 \x03(\x0b\x32\x14.PEDM.CollectionLink\";\n\x12\x41pprovalExtendData\x12\x13\n\x0b\x61pprovalUid\x18\x01 \x01(\x0c\x12\x10\n\x08\x65xpireIn\x18\x02 \x01(\x05\"I\n\x15ModifyApprovalRequest\x12\x30\n\x0e\x65xtendApproval\x18\x01 \x03(\x0b\x32\x18.PEDM.ApprovalExtendData\"F\n\x15\x41pprovalActionRequest\x12\x0f\n\x07\x61pprove\x18\x01 \x03(\x0c\x12\x0c\n\x04\x64\x65ny\x18\x02 \x03(\x0c\x12\x0e\n\x06remove\x18\x03 \x03(\x0c\"\xab\x01\n\x0e\x44\x65ploymentNode\x12\x15\n\rdeploymentUid\x18\x01 \x01(\x0c\x12\x10\n\x08\x64isabled\x18\x02 \x01(\x08\x12\x0e\n\x06\x61\x65sKey\x18\x03 \x01(\x0c\x12\x13\n\x0b\x65\x63PublicKey\x18\x04 \x01(\x0c\x12\x15\n\rencryptedData\x18\x05 \x01(\x0c\x12\x11\n\tagentData\x18\x06 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x07 \x01(\x03\x12\x10\n\x08modified\x18\x08 \x01(\x03\"\xa8\x01\n\tAgentNode\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\x12\x11\n\tmachineId\x18\x02 \x01(\t\x12\x15\n\rdeploymentUid\x18\x03 \x01(\x0c\x12\x13\n\x0b\x65\x63PublicKey\x18\x04 \x01(\x0c\x12\x10\n\x08\x64isabled\x18\x05 \x01(\x08\x12\x15\n\rencryptedData\x18\x06 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x07 \x01(\x03\x12\x10\n\x08modified\x18\x08 \x01(\x03\"\x94\x01\n\nPolicyNode\x12\x11\n\tpolicyUid\x18\x01 \x01(\x0c\x12\x11\n\tplainData\x18\x02 \x01(\x0c\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12\x14\n\x0c\x65ncryptedKey\x18\x04 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x05 \x01(\x03\x12\x10\n\x08modified\x18\x06 \x01(\x03\x12\x10\n\x08\x64isabled\x18\x07 \x01(\x08\"g\n\x0e\x43ollectionNode\x12\x15\n\rcollectionUid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x63ollectionType\x18\x02 \x01(\x05\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x04 \x01(\x03\"d\n\x0e\x43ollectionLink\x12\x15\n\rcollectionUid\x18\x01 \x01(\x0c\x12\x0f\n\x07linkUid\x18\x02 \x01(\x0c\x12*\n\x08linkType\x18\x03 \x01(\x0e\x32\x18.PEDM.CollectionLinkType\"\x9d\x01\n\x12\x41pprovalStatusNode\x12\x13\n\x0b\x61pprovalUid\x18\x01 \x01(\x0c\x12\x46\n\x0e\x61pprovalStatus\x18\x02 \x01(\x0e\x32..NotificationCenter.NotificationApprovalStatus\x12\x18\n\x10\x65nterpriseUserId\x18\x03 \x01(\x03\x12\x10\n\x08modified\x18\n \x01(\x03\"\xb3\x01\n\x0c\x41pprovalNode\x12\x13\n\x0b\x61pprovalUid\x18\x01 \x01(\x0c\x12\x14\n\x0c\x61pprovalType\x18\x02 \x01(\x05\x12\x10\n\x08\x61gentUid\x18\x03 \x01(\x0c\x12\x13\n\x0b\x61\x63\x63ountInfo\x18\x04 \x01(\x0c\x12\x17\n\x0f\x61pplicationInfo\x18\x05 \x01(\x0c\x12\x15\n\rjustification\x18\x06 \x01(\x0c\x12\x10\n\x08\x65xpireIn\x18\x07 \x01(\x05\x12\x0f\n\x07\x63reated\x18\n \x01(\x03\"C\n\rFullSyncToken\x12\x15\n\rstartRevision\x18\x01 \x01(\x03\x12\x0e\n\x06\x65ntity\x18\x02 \x01(\x05\x12\x0b\n\x03key\x18\x03 \x03(\x0c\"$\n\x0cIncSyncToken\x12\x14\n\x0clastRevision\x18\x02 \x01(\x03\"h\n\rPedmSyncToken\x12\'\n\x08\x66ullSync\x18\x02 \x01(\x0b\x32\x13.PEDM.FullSyncTokenH\x00\x12%\n\x07incSync\x18\x03 \x01(\x0b\x32\x12.PEDM.IncSyncTokenH\x00\x42\x07\n\x05token\"/\n\x12GetPedmDataRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\"\xad\x04\n\x13GetPedmDataResponse\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\x12\x12\n\nresetCache\x18\x02 \x01(\x08\x12\x0f\n\x07hasMore\x18\x03 \x01(\x08\x12\x1a\n\x12removedDeployments\x18\n \x03(\x0c\x12\x15\n\rremovedAgents\x18\x0b \x03(\x0c\x12\x17\n\x0fremovedPolicies\x18\x0c \x03(\x0c\x12\x19\n\x11removedCollection\x18\r \x03(\x0c\x12\x33\n\x15removedCollectionLink\x18\x0e \x03(\x0b\x32\x14.PEDM.CollectionLink\x12\x18\n\x10removedApprovals\x18\x0f \x03(\x0c\x12)\n\x0b\x64\x65ployments\x18\x14 \x03(\x0b\x32\x14.PEDM.DeploymentNode\x12\x1f\n\x06\x61gents\x18\x15 \x03(\x0b\x32\x0f.PEDM.AgentNode\x12\"\n\x08policies\x18\x16 \x03(\x0b\x32\x10.PEDM.PolicyNode\x12)\n\x0b\x63ollections\x18\x17 \x03(\x0b\x32\x14.PEDM.CollectionNode\x12,\n\x0e\x63ollectionLink\x18\x18 \x03(\x0b\x32\x14.PEDM.CollectionLink\x12%\n\tapprovals\x18\x19 \x03(\x0b\x32\x12.PEDM.ApprovalNode\x12\x30\n\x0e\x61pprovalStatus\x18\x1a \x03(\x0b\x32\x18.PEDM.ApprovalStatusNode\"<\n\x12PolicyAgentRequest\x12\x11\n\tpolicyUid\x18\x01 \x03(\x0c\x12\x13\n\x0bsummaryOnly\x18\x02 \x01(\x08\";\n\x13PolicyAgentResponse\x12\x12\n\nagentCount\x18\x01 \x01(\x05\x12\x10\n\x08\x61gentUid\x18\x02 \x03(\x0c\"]\n\x16\x41uditCollectionRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\x12\x10\n\x08valueUid\x18\x02 \x03(\x0c\x12\x16\n\x0e\x63ollectionName\x18\x03 \x03(\t\"h\n\x14\x41uditCollectionValue\x12\x16\n\x0e\x63ollectionName\x18\x01 \x01(\t\x12\x10\n\x08valueUid\x18\x02 \x01(\x0c\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x04 \x01(\x03\"q\n\x17\x41uditCollectionResponse\x12*\n\x06values\x18\x01 \x03(\x0b\x32\x1a.PEDM.AuditCollectionValue\x12\x0f\n\x07hasMore\x18\x02 \x01(\x08\x12\x19\n\x11\x63ontinuationToken\x18\x03 \x01(\x0c\"H\n\x18GetCollectionLinkRequest\x12,\n\x0e\x63ollectionLink\x18\x01 \x03(\x0b\x32\x14.PEDM.CollectionLink\"Q\n\x19GetCollectionLinkResponse\x12\x34\n\x12\x63ollectionLinkData\x18\x01 \x03(\x0b\x32\x18.PEDM.CollectionLinkData\"\xaa\x01\n\x1bOfflineAgentRegisterRequest\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\x12\x15\n\rdeploymentUid\x18\x02 \x01(\x0c\x12\x11\n\tpublicKey\x18\x03 \x01(\x0c\x12\x11\n\tmachineId\x18\x04 \x01(\t\x12)\n\ncollection\x18\x05 \x03(\x0b\x32\x15.PEDM.CollectionValue\x12\x11\n\tagentData\x18\x07 \x01(\x0c\"0\n\x1cOfflineAgentRegisterResponse\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\"/\n\x1bOfflineAgentSyncDownRequest\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\"9\n\x1cOfflineAgentSyncDownResponse\x12\x19\n\x11\x65ncryptedSyncData\x18\x01 \x01(\x0c\"?\n\x17GetAgentLastSeenRequest\x12\x12\n\nactiveOnly\x18\x01 \x01(\x08\x12\x10\n\x08\x61gentUid\x18\x02 \x03(\x0c\"3\n\rAgentLastSeen\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\x12\x10\n\x08lastSeen\x18\x02 \x01(\x03\"A\n\x18GetAgentLastSeenResponse\x12%\n\x08lastSeen\x18\x01 \x03(\x0b\x32\x13.PEDM.AgentLastSeen\"2\n\x1aGetActiveAgentCountRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x03(\x05\">\n\x10\x41\x63tiveAgentCount\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x14\n\x0c\x61\x63tiveAgents\x18\x02 \x01(\x05\";\n\x12\x41\x63tiveAgentFailure\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"x\n\x1bGetActiveAgentCountResponse\x12*\n\nagentCount\x18\x01 \x03(\x0b\x32\x16.PEDM.ActiveAgentCount\x12-\n\x0b\x66\x61iledCount\x18\x02 \x03(\x0b\x32\x18.PEDM.ActiveAgentFailure\"\x87\x01\n\x19GetAgentDailyCountRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x03(\x05\x12$\n\tmonthYear\x18\x02 \x01(\x0b\x32\x0f.PEDM.MonthYearH\x00\x12$\n\tdateRange\x18\x03 \x01(\x0b\x32\x0f.PEDM.DateRangeH\x00\x42\x08\n\x06period\"(\n\tMonthYear\x12\r\n\x05month\x18\x01 \x01(\x05\x12\x0c\n\x04year\x18\x02 \x01(\x05\"\'\n\tDateRange\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03\"3\n\x0f\x41gentDailyCount\x12\x0c\n\x04\x64\x61te\x18\x01 \x01(\x03\x12\x12\n\nagentCount\x18\x02 \x01(\x05\"V\n\x17\x41gentCountForEnterprise\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12%\n\x06\x63ounts\x18\x02 \x03(\x0b\x32\x15.PEDM.AgentDailyCount\"U\n\x1aGetAgentDailyCountResponse\x12\x37\n\x10\x65nterpriseCounts\x18\x01 \x03(\x0b\x32\x1d.PEDM.AgentCountForEnterprise*j\n\x12\x43ollectionLinkType\x12\r\n\tCLT_OTHER\x10\x00\x12\r\n\tCLT_AGENT\x10\x01\x12\x0e\n\nCLT_POLICY\x10\x02\x12\x12\n\x0e\x43LT_COLLECTION\x10\x03\x12\x12\n\x0e\x43LT_DEPLOYMENT\x10\x04\x42 \n\x18\x63om.keepersecurity.protoB\x04PEDMb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\npedm.proto\x12\x04PEDM\x1a\x0c\x66older.proto\"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\";\n\nPedmStatus\x12\x0b\n\x03key\x18\x01 \x03(\x0c\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x0f\n\x07message\x18\x03 \x01(\t\"\x89\x01\n\x12PedmStatusResponse\x12#\n\taddStatus\x18\x01 \x03(\x0b\x32\x10.PEDM.PedmStatus\x12&\n\x0cupdateStatus\x18\x02 \x03(\x0b\x32\x10.PEDM.PedmStatus\x12&\n\x0cremoveStatus\x18\x03 \x03(\x0b\x32\x10.PEDM.PedmStatus\"4\n\x0e\x44\x65ploymentData\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0c\x65\x63PrivateKey\x18\x02 \x01(\x0c\"\x9a\x01\n\x17\x44\x65ploymentCreateRequest\x12\x15\n\rdeploymentUid\x18\x01 \x01(\x0c\x12\x0e\n\x06\x61\x65sKey\x18\x02 \x01(\x0c\x12\x13\n\x0b\x65\x63PublicKey\x18\x03 \x01(\x0c\x12\x19\n\x11spiffeCertificate\x18\x04 \x01(\x0c\x12\x15\n\rencryptedData\x18\x05 \x01(\x0c\x12\x11\n\tagentData\x18\x06 \x01(\x0c\"\x8d\x01\n\x17\x44\x65ploymentUpdateRequest\x12\x15\n\rdeploymentUid\x18\x01 \x01(\x0c\x12\x15\n\rencryptedData\x18\x02 \x01(\x0c\x12)\n\x08\x64isabled\x18\x03 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x19\n\x11spiffeCertificate\x18\x04 \x01(\x0c\"\xa2\x01\n\x17ModifyDeploymentRequest\x12\x34\n\raddDeployment\x18\x01 \x03(\x0b\x32\x1d.PEDM.DeploymentCreateRequest\x12\x37\n\x10updateDeployment\x18\x02 \x03(\x0b\x32\x1d.PEDM.DeploymentUpdateRequest\x12\x18\n\x10removeDeployment\x18\x03 \x03(\x0c\"a\n\x0b\x41gentUpdate\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\x12)\n\x08\x64isabled\x18\x02 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x15\n\rdeploymentUid\x18\x03 \x01(\x0c\"Q\n\x12ModifyAgentRequest\x12&\n\x0bupdateAgent\x18\x02 \x03(\x0b\x32\x11.PEDM.AgentUpdate\x12\x13\n\x0bremoveAgent\x18\x03 \x03(\x0c\"p\n\tPolicyAdd\x12\x11\n\tpolicyUid\x18\x01 \x01(\x0c\x12\x11\n\tplainData\x18\x02 \x01(\x0c\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12\x14\n\x0c\x65ncryptedKey\x18\x04 \x01(\x0c\x12\x10\n\x08\x64isabled\x18\x05 \x01(\x08\"v\n\x0cPolicyUpdate\x12\x11\n\tpolicyUid\x18\x01 \x01(\x0c\x12\x11\n\tplainData\x18\x02 \x01(\x0c\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12)\n\x08\x64isabled\x18\x04 \x01(\x0e\x32\x17.Folder.SetBooleanValue\"s\n\rPolicyRequest\x12\"\n\taddPolicy\x18\x01 \x03(\x0b\x32\x0f.PEDM.PolicyAdd\x12(\n\x0cupdatePolicy\x18\x02 \x03(\x0b\x32\x12.PEDM.PolicyUpdate\x12\x14\n\x0cremovePolicy\x18\x03 \x03(\x0c\"6\n\nPolicyLink\x12\x11\n\tpolicyUid\x18\x01 \x01(\x0c\x12\x15\n\rcollectionUid\x18\x02 \x03(\x0c\"E\n\x1aSetPolicyCollectionRequest\x12\'\n\rsetCollection\x18\x01 \x03(\x0b\x32\x10.PEDM.PolicyLink\"W\n\x0f\x43ollectionValue\x12\x15\n\rcollectionUid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x63ollectionType\x18\x02 \x01(\x05\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\"z\n\x12\x43ollectionLinkData\x12\x15\n\rcollectionUid\x18\x01 \x01(\x0c\x12\x0f\n\x07linkUid\x18\x02 \x01(\x0c\x12*\n\x08linkType\x18\x03 \x01(\x0e\x32\x18.PEDM.CollectionLinkType\x12\x10\n\x08linkData\x18\x04 \x01(\x0c\"\x8c\x01\n\x11\x43ollectionRequest\x12,\n\raddCollection\x18\x01 \x03(\x0b\x32\x15.PEDM.CollectionValue\x12/\n\x10updateCollection\x18\x02 \x03(\x0b\x32\x15.PEDM.CollectionValue\x12\x18\n\x10removeCollection\x18\x03 \x03(\x0c\"{\n\x18SetCollectionLinkRequest\x12/\n\raddCollection\x18\x01 \x03(\x0b\x32\x18.PEDM.CollectionLinkData\x12.\n\x10removeCollection\x18\x02 \x03(\x0b\x32\x14.PEDM.CollectionLink\";\n\x12\x41pprovalExtendData\x12\x13\n\x0b\x61pprovalUid\x18\x01 \x01(\x0c\x12\x10\n\x08\x65xpireIn\x18\x02 \x01(\x05\"I\n\x15ModifyApprovalRequest\x12\x30\n\x0e\x65xtendApproval\x18\x01 \x03(\x0b\x32\x18.PEDM.ApprovalExtendData\"F\n\x15\x41pprovalActionRequest\x12\x0f\n\x07\x61pprove\x18\x01 \x03(\x0c\x12\x0c\n\x04\x64\x65ny\x18\x02 \x03(\x0c\x12\x0e\n\x06remove\x18\x03 \x03(\x0c\"\xab\x01\n\x0e\x44\x65ploymentNode\x12\x15\n\rdeploymentUid\x18\x01 \x01(\x0c\x12\x10\n\x08\x64isabled\x18\x02 \x01(\x08\x12\x0e\n\x06\x61\x65sKey\x18\x03 \x01(\x0c\x12\x13\n\x0b\x65\x63PublicKey\x18\x04 \x01(\x0c\x12\x15\n\rencryptedData\x18\x05 \x01(\x0c\x12\x11\n\tagentData\x18\x06 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x07 \x01(\x03\x12\x10\n\x08modified\x18\x08 \x01(\x03\"\xa8\x01\n\tAgentNode\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\x12\x11\n\tmachineId\x18\x02 \x01(\t\x12\x15\n\rdeploymentUid\x18\x03 \x01(\x0c\x12\x13\n\x0b\x65\x63PublicKey\x18\x04 \x01(\x0c\x12\x10\n\x08\x64isabled\x18\x05 \x01(\x08\x12\x15\n\rencryptedData\x18\x06 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x07 \x01(\x03\x12\x10\n\x08modified\x18\x08 \x01(\x03\"\x94\x01\n\nPolicyNode\x12\x11\n\tpolicyUid\x18\x01 \x01(\x0c\x12\x11\n\tplainData\x18\x02 \x01(\x0c\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12\x14\n\x0c\x65ncryptedKey\x18\x04 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x05 \x01(\x03\x12\x10\n\x08modified\x18\x06 \x01(\x03\x12\x10\n\x08\x64isabled\x18\x07 \x01(\x08\"g\n\x0e\x43ollectionNode\x12\x15\n\rcollectionUid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x63ollectionType\x18\x02 \x01(\x05\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x04 \x01(\x03\"d\n\x0e\x43ollectionLink\x12\x15\n\rcollectionUid\x18\x01 \x01(\x0c\x12\x0f\n\x07linkUid\x18\x02 \x01(\x0c\x12*\n\x08linkType\x18\x03 \x01(\x0e\x32\x18.PEDM.CollectionLinkType\"\x87\x01\n\x12\x41pprovalStatusNode\x12\x13\n\x0b\x61pprovalUid\x18\x01 \x01(\x0c\x12\x30\n\x0e\x61pprovalStatus\x18\x02 \x01(\x0e\x32\x18.PEDM.ApprovalStatusType\x12\x18\n\x10\x65nterpriseUserId\x18\x03 \x01(\x03\x12\x10\n\x08modified\x18\n \x01(\x03\"\xb3\x01\n\x0c\x41pprovalNode\x12\x13\n\x0b\x61pprovalUid\x18\x01 \x01(\x0c\x12\x14\n\x0c\x61pprovalType\x18\x02 \x01(\x05\x12\x10\n\x08\x61gentUid\x18\x03 \x01(\x0c\x12\x13\n\x0b\x61\x63\x63ountInfo\x18\x04 \x01(\x0c\x12\x17\n\x0f\x61pplicationInfo\x18\x05 \x01(\x0c\x12\x15\n\rjustification\x18\x06 \x01(\x0c\x12\x10\n\x08\x65xpireIn\x18\x07 \x01(\x05\x12\x0f\n\x07\x63reated\x18\n \x01(\x03\"C\n\rFullSyncToken\x12\x15\n\rstartRevision\x18\x01 \x01(\x03\x12\x0e\n\x06\x65ntity\x18\x02 \x01(\x05\x12\x0b\n\x03key\x18\x03 \x03(\x0c\"$\n\x0cIncSyncToken\x12\x14\n\x0clastRevision\x18\x02 \x01(\x03\"h\n\rPedmSyncToken\x12\'\n\x08\x66ullSync\x18\x02 \x01(\x0b\x32\x13.PEDM.FullSyncTokenH\x00\x12%\n\x07incSync\x18\x03 \x01(\x0b\x32\x12.PEDM.IncSyncTokenH\x00\x42\x07\n\x05token\"/\n\x12GetPedmDataRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\"\xad\x04\n\x13GetPedmDataResponse\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\x12\x12\n\nresetCache\x18\x02 \x01(\x08\x12\x0f\n\x07hasMore\x18\x03 \x01(\x08\x12\x1a\n\x12removedDeployments\x18\n \x03(\x0c\x12\x15\n\rremovedAgents\x18\x0b \x03(\x0c\x12\x17\n\x0fremovedPolicies\x18\x0c \x03(\x0c\x12\x19\n\x11removedCollection\x18\r \x03(\x0c\x12\x33\n\x15removedCollectionLink\x18\x0e \x03(\x0b\x32\x14.PEDM.CollectionLink\x12\x18\n\x10removedApprovals\x18\x0f \x03(\x0c\x12)\n\x0b\x64\x65ployments\x18\x14 \x03(\x0b\x32\x14.PEDM.DeploymentNode\x12\x1f\n\x06\x61gents\x18\x15 \x03(\x0b\x32\x0f.PEDM.AgentNode\x12\"\n\x08policies\x18\x16 \x03(\x0b\x32\x10.PEDM.PolicyNode\x12)\n\x0b\x63ollections\x18\x17 \x03(\x0b\x32\x14.PEDM.CollectionNode\x12,\n\x0e\x63ollectionLink\x18\x18 \x03(\x0b\x32\x14.PEDM.CollectionLink\x12%\n\tapprovals\x18\x19 \x03(\x0b\x32\x12.PEDM.ApprovalNode\x12\x30\n\x0e\x61pprovalStatus\x18\x1a \x03(\x0b\x32\x18.PEDM.ApprovalStatusNode\"<\n\x12PolicyAgentRequest\x12\x11\n\tpolicyUid\x18\x01 \x03(\x0c\x12\x13\n\x0bsummaryOnly\x18\x02 \x01(\x08\";\n\x13PolicyAgentResponse\x12\x12\n\nagentCount\x18\x01 \x01(\x05\x12\x10\n\x08\x61gentUid\x18\x02 \x03(\x0c\"]\n\x16\x41uditCollectionRequest\x12\x19\n\x11\x63ontinuationToken\x18\x01 \x01(\x0c\x12\x10\n\x08valueUid\x18\x02 \x03(\x0c\x12\x16\n\x0e\x63ollectionName\x18\x03 \x03(\t\"h\n\x14\x41uditCollectionValue\x12\x16\n\x0e\x63ollectionName\x18\x01 \x01(\t\x12\x10\n\x08valueUid\x18\x02 \x01(\x0c\x12\x15\n\rencryptedData\x18\x03 \x01(\x0c\x12\x0f\n\x07\x63reated\x18\x04 \x01(\x03\"q\n\x17\x41uditCollectionResponse\x12*\n\x06values\x18\x01 \x03(\x0b\x32\x1a.PEDM.AuditCollectionValue\x12\x0f\n\x07hasMore\x18\x02 \x01(\x08\x12\x19\n\x11\x63ontinuationToken\x18\x03 \x01(\x0c\"H\n\x18GetCollectionLinkRequest\x12,\n\x0e\x63ollectionLink\x18\x01 \x03(\x0b\x32\x14.PEDM.CollectionLink\"Q\n\x19GetCollectionLinkResponse\x12\x34\n\x12\x63ollectionLinkData\x18\x01 \x03(\x0b\x32\x18.PEDM.CollectionLinkData\"\xaa\x01\n\x1bOfflineAgentRegisterRequest\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\x12\x15\n\rdeploymentUid\x18\x02 \x01(\x0c\x12\x11\n\tpublicKey\x18\x03 \x01(\x0c\x12\x11\n\tmachineId\x18\x04 \x01(\t\x12)\n\ncollection\x18\x05 \x03(\x0b\x32\x15.PEDM.CollectionValue\x12\x11\n\tagentData\x18\x07 \x01(\x0c\"0\n\x1cOfflineAgentRegisterResponse\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\"/\n\x1bOfflineAgentSyncDownRequest\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\"9\n\x1cOfflineAgentSyncDownResponse\x12\x19\n\x11\x65ncryptedSyncData\x18\x01 \x01(\x0c\"?\n\x17GetAgentLastSeenRequest\x12\x12\n\nactiveOnly\x18\x01 \x01(\x08\x12\x10\n\x08\x61gentUid\x18\x02 \x03(\x0c\"3\n\rAgentLastSeen\x12\x10\n\x08\x61gentUid\x18\x01 \x01(\x0c\x12\x10\n\x08lastSeen\x18\x02 \x01(\x03\"A\n\x18GetAgentLastSeenResponse\x12%\n\x08lastSeen\x18\x01 \x03(\x0b\x32\x13.PEDM.AgentLastSeen\"2\n\x1aGetActiveAgentCountRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x03(\x05\">\n\x10\x41\x63tiveAgentCount\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x14\n\x0c\x61\x63tiveAgents\x18\x02 \x01(\x05\";\n\x12\x41\x63tiveAgentFailure\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"x\n\x1bGetActiveAgentCountResponse\x12*\n\nagentCount\x18\x01 \x03(\x0b\x32\x16.PEDM.ActiveAgentCount\x12-\n\x0b\x66\x61iledCount\x18\x02 \x03(\x0b\x32\x18.PEDM.ActiveAgentFailure\"\x87\x01\n\x19GetAgentDailyCountRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x03(\x05\x12$\n\tmonthYear\x18\x02 \x01(\x0b\x32\x0f.PEDM.MonthYearH\x00\x12$\n\tdateRange\x18\x03 \x01(\x0b\x32\x0f.PEDM.DateRangeH\x00\x42\x08\n\x06period\"(\n\tMonthYear\x12\r\n\x05month\x18\x01 \x01(\x05\x12\x0c\n\x04year\x18\x02 \x01(\x05\"\'\n\tDateRange\x12\r\n\x05start\x18\x01 \x01(\x03\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x03\"3\n\x0f\x41gentDailyCount\x12\x0c\n\x04\x64\x61te\x18\x01 \x01(\x03\x12\x12\n\nagentCount\x18\x02 \x01(\x05\"V\n\x17\x41gentCountForEnterprise\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12%\n\x06\x63ounts\x18\x02 \x03(\x0b\x32\x15.PEDM.AgentDailyCount\"U\n\x1aGetAgentDailyCountResponse\x12\x37\n\x10\x65nterpriseCounts\x18\x01 \x03(\x0b\x32\x1d.PEDM.AgentCountForEnterprise*j\n\x12\x43ollectionLinkType\x12\r\n\tCLT_OTHER\x10\x00\x12\r\n\tCLT_AGENT\x10\x01\x12\x0e\n\nCLT_POLICY\x10\x02\x12\x12\n\x0e\x43LT_COLLECTION\x10\x03\x12\x12\n\x0e\x43LT_DEPLOYMENT\x10\x04*o\n\x12\x41pprovalStatusType\x12\x13\n\x0f\x41ST_UNSPECIFIED\x10\x00\x12\x10\n\x0c\x41ST_APPROVED\x10\x01\x12\x0e\n\nAST_DENIED\x10\x02\x12\x0f\n\x0b\x41ST_EXPIRED\x10\x03\x12\x11\n\rAST_ESCALATED\x10\x05\x42 \n\x18\x63om.keepersecurity.protoB\x04PEDMb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pedm_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\004PEDM' - _globals['_COLLECTIONLINKTYPE']._serialized_start=5938 - _globals['_COLLECTIONLINKTYPE']._serialized_end=6044 - _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_start=60 - _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_end=139 - _globals['_PEDMSTATUS']._serialized_start=141 - _globals['_PEDMSTATUS']._serialized_end=200 - _globals['_PEDMSTATUSRESPONSE']._serialized_start=203 - _globals['_PEDMSTATUSRESPONSE']._serialized_end=340 - _globals['_DEPLOYMENTDATA']._serialized_start=342 - _globals['_DEPLOYMENTDATA']._serialized_end=394 - _globals['_DEPLOYMENTCREATEREQUEST']._serialized_start=397 - _globals['_DEPLOYMENTCREATEREQUEST']._serialized_end=551 - _globals['_DEPLOYMENTUPDATEREQUEST']._serialized_start=554 - _globals['_DEPLOYMENTUPDATEREQUEST']._serialized_end=695 - _globals['_MODIFYDEPLOYMENTREQUEST']._serialized_start=698 - _globals['_MODIFYDEPLOYMENTREQUEST']._serialized_end=860 - _globals['_AGENTUPDATE']._serialized_start=862 - _globals['_AGENTUPDATE']._serialized_end=959 - _globals['_MODIFYAGENTREQUEST']._serialized_start=961 - _globals['_MODIFYAGENTREQUEST']._serialized_end=1042 - _globals['_POLICYADD']._serialized_start=1044 - _globals['_POLICYADD']._serialized_end=1156 - _globals['_POLICYUPDATE']._serialized_start=1158 - _globals['_POLICYUPDATE']._serialized_end=1276 - _globals['_POLICYREQUEST']._serialized_start=1278 - _globals['_POLICYREQUEST']._serialized_end=1393 - _globals['_POLICYLINK']._serialized_start=1395 - _globals['_POLICYLINK']._serialized_end=1449 - _globals['_SETPOLICYCOLLECTIONREQUEST']._serialized_start=1451 - _globals['_SETPOLICYCOLLECTIONREQUEST']._serialized_end=1520 - _globals['_COLLECTIONVALUE']._serialized_start=1522 - _globals['_COLLECTIONVALUE']._serialized_end=1609 - _globals['_COLLECTIONLINKDATA']._serialized_start=1611 - _globals['_COLLECTIONLINKDATA']._serialized_end=1733 - _globals['_COLLECTIONREQUEST']._serialized_start=1736 - _globals['_COLLECTIONREQUEST']._serialized_end=1876 - _globals['_SETCOLLECTIONLINKREQUEST']._serialized_start=1878 - _globals['_SETCOLLECTIONLINKREQUEST']._serialized_end=2001 - _globals['_APPROVALEXTENDDATA']._serialized_start=2003 - _globals['_APPROVALEXTENDDATA']._serialized_end=2062 - _globals['_MODIFYAPPROVALREQUEST']._serialized_start=2064 - _globals['_MODIFYAPPROVALREQUEST']._serialized_end=2137 - _globals['_APPROVALACTIONREQUEST']._serialized_start=2139 - _globals['_APPROVALACTIONREQUEST']._serialized_end=2209 - _globals['_DEPLOYMENTNODE']._serialized_start=2212 - _globals['_DEPLOYMENTNODE']._serialized_end=2383 - _globals['_AGENTNODE']._serialized_start=2386 - _globals['_AGENTNODE']._serialized_end=2554 - _globals['_POLICYNODE']._serialized_start=2557 - _globals['_POLICYNODE']._serialized_end=2705 - _globals['_COLLECTIONNODE']._serialized_start=2707 - _globals['_COLLECTIONNODE']._serialized_end=2810 - _globals['_COLLECTIONLINK']._serialized_start=2812 - _globals['_COLLECTIONLINK']._serialized_end=2912 - _globals['_APPROVALSTATUSNODE']._serialized_start=2915 - _globals['_APPROVALSTATUSNODE']._serialized_end=3072 - _globals['_APPROVALNODE']._serialized_start=3075 - _globals['_APPROVALNODE']._serialized_end=3254 - _globals['_FULLSYNCTOKEN']._serialized_start=3256 - _globals['_FULLSYNCTOKEN']._serialized_end=3323 - _globals['_INCSYNCTOKEN']._serialized_start=3325 - _globals['_INCSYNCTOKEN']._serialized_end=3361 - _globals['_PEDMSYNCTOKEN']._serialized_start=3363 - _globals['_PEDMSYNCTOKEN']._serialized_end=3467 - _globals['_GETPEDMDATAREQUEST']._serialized_start=3469 - _globals['_GETPEDMDATAREQUEST']._serialized_end=3516 - _globals['_GETPEDMDATARESPONSE']._serialized_start=3519 - _globals['_GETPEDMDATARESPONSE']._serialized_end=4076 - _globals['_POLICYAGENTREQUEST']._serialized_start=4078 - _globals['_POLICYAGENTREQUEST']._serialized_end=4138 - _globals['_POLICYAGENTRESPONSE']._serialized_start=4140 - _globals['_POLICYAGENTRESPONSE']._serialized_end=4199 - _globals['_AUDITCOLLECTIONREQUEST']._serialized_start=4201 - _globals['_AUDITCOLLECTIONREQUEST']._serialized_end=4294 - _globals['_AUDITCOLLECTIONVALUE']._serialized_start=4296 - _globals['_AUDITCOLLECTIONVALUE']._serialized_end=4400 - _globals['_AUDITCOLLECTIONRESPONSE']._serialized_start=4402 - _globals['_AUDITCOLLECTIONRESPONSE']._serialized_end=4515 - _globals['_GETCOLLECTIONLINKREQUEST']._serialized_start=4517 - _globals['_GETCOLLECTIONLINKREQUEST']._serialized_end=4589 - _globals['_GETCOLLECTIONLINKRESPONSE']._serialized_start=4591 - _globals['_GETCOLLECTIONLINKRESPONSE']._serialized_end=4672 - _globals['_OFFLINEAGENTREGISTERREQUEST']._serialized_start=4675 - _globals['_OFFLINEAGENTREGISTERREQUEST']._serialized_end=4845 - _globals['_OFFLINEAGENTREGISTERRESPONSE']._serialized_start=4847 - _globals['_OFFLINEAGENTREGISTERRESPONSE']._serialized_end=4895 - _globals['_OFFLINEAGENTSYNCDOWNREQUEST']._serialized_start=4897 - _globals['_OFFLINEAGENTSYNCDOWNREQUEST']._serialized_end=4944 - _globals['_OFFLINEAGENTSYNCDOWNRESPONSE']._serialized_start=4946 - _globals['_OFFLINEAGENTSYNCDOWNRESPONSE']._serialized_end=5003 - _globals['_GETAGENTLASTSEENREQUEST']._serialized_start=5005 - _globals['_GETAGENTLASTSEENREQUEST']._serialized_end=5068 - _globals['_AGENTLASTSEEN']._serialized_start=5070 - _globals['_AGENTLASTSEEN']._serialized_end=5121 - _globals['_GETAGENTLASTSEENRESPONSE']._serialized_start=5123 - _globals['_GETAGENTLASTSEENRESPONSE']._serialized_end=5188 - _globals['_GETACTIVEAGENTCOUNTREQUEST']._serialized_start=5190 - _globals['_GETACTIVEAGENTCOUNTREQUEST']._serialized_end=5240 - _globals['_ACTIVEAGENTCOUNT']._serialized_start=5242 - _globals['_ACTIVEAGENTCOUNT']._serialized_end=5304 - _globals['_ACTIVEAGENTFAILURE']._serialized_start=5306 - _globals['_ACTIVEAGENTFAILURE']._serialized_end=5365 - _globals['_GETACTIVEAGENTCOUNTRESPONSE']._serialized_start=5367 - _globals['_GETACTIVEAGENTCOUNTRESPONSE']._serialized_end=5487 - _globals['_GETAGENTDAILYCOUNTREQUEST']._serialized_start=5490 - _globals['_GETAGENTDAILYCOUNTREQUEST']._serialized_end=5625 - _globals['_MONTHYEAR']._serialized_start=5627 - _globals['_MONTHYEAR']._serialized_end=5667 - _globals['_DATERANGE']._serialized_start=5669 - _globals['_DATERANGE']._serialized_end=5708 - _globals['_AGENTDAILYCOUNT']._serialized_start=5710 - _globals['_AGENTDAILYCOUNT']._serialized_end=5761 - _globals['_AGENTCOUNTFORENTERPRISE']._serialized_start=5763 - _globals['_AGENTCOUNTFORENTERPRISE']._serialized_end=5849 - _globals['_GETAGENTDAILYCOUNTRESPONSE']._serialized_start=5851 - _globals['_GETAGENTDAILYCOUNTRESPONSE']._serialized_end=5936 + _globals['_COLLECTIONLINKTYPE']._serialized_start=5890 + _globals['_COLLECTIONLINKTYPE']._serialized_end=5996 + _globals['_APPROVALSTATUSTYPE']._serialized_start=5998 + _globals['_APPROVALSTATUSTYPE']._serialized_end=6109 + _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_start=34 + _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_end=113 + _globals['_PEDMSTATUS']._serialized_start=115 + _globals['_PEDMSTATUS']._serialized_end=174 + _globals['_PEDMSTATUSRESPONSE']._serialized_start=177 + _globals['_PEDMSTATUSRESPONSE']._serialized_end=314 + _globals['_DEPLOYMENTDATA']._serialized_start=316 + _globals['_DEPLOYMENTDATA']._serialized_end=368 + _globals['_DEPLOYMENTCREATEREQUEST']._serialized_start=371 + _globals['_DEPLOYMENTCREATEREQUEST']._serialized_end=525 + _globals['_DEPLOYMENTUPDATEREQUEST']._serialized_start=528 + _globals['_DEPLOYMENTUPDATEREQUEST']._serialized_end=669 + _globals['_MODIFYDEPLOYMENTREQUEST']._serialized_start=672 + _globals['_MODIFYDEPLOYMENTREQUEST']._serialized_end=834 + _globals['_AGENTUPDATE']._serialized_start=836 + _globals['_AGENTUPDATE']._serialized_end=933 + _globals['_MODIFYAGENTREQUEST']._serialized_start=935 + _globals['_MODIFYAGENTREQUEST']._serialized_end=1016 + _globals['_POLICYADD']._serialized_start=1018 + _globals['_POLICYADD']._serialized_end=1130 + _globals['_POLICYUPDATE']._serialized_start=1132 + _globals['_POLICYUPDATE']._serialized_end=1250 + _globals['_POLICYREQUEST']._serialized_start=1252 + _globals['_POLICYREQUEST']._serialized_end=1367 + _globals['_POLICYLINK']._serialized_start=1369 + _globals['_POLICYLINK']._serialized_end=1423 + _globals['_SETPOLICYCOLLECTIONREQUEST']._serialized_start=1425 + _globals['_SETPOLICYCOLLECTIONREQUEST']._serialized_end=1494 + _globals['_COLLECTIONVALUE']._serialized_start=1496 + _globals['_COLLECTIONVALUE']._serialized_end=1583 + _globals['_COLLECTIONLINKDATA']._serialized_start=1585 + _globals['_COLLECTIONLINKDATA']._serialized_end=1707 + _globals['_COLLECTIONREQUEST']._serialized_start=1710 + _globals['_COLLECTIONREQUEST']._serialized_end=1850 + _globals['_SETCOLLECTIONLINKREQUEST']._serialized_start=1852 + _globals['_SETCOLLECTIONLINKREQUEST']._serialized_end=1975 + _globals['_APPROVALEXTENDDATA']._serialized_start=1977 + _globals['_APPROVALEXTENDDATA']._serialized_end=2036 + _globals['_MODIFYAPPROVALREQUEST']._serialized_start=2038 + _globals['_MODIFYAPPROVALREQUEST']._serialized_end=2111 + _globals['_APPROVALACTIONREQUEST']._serialized_start=2113 + _globals['_APPROVALACTIONREQUEST']._serialized_end=2183 + _globals['_DEPLOYMENTNODE']._serialized_start=2186 + _globals['_DEPLOYMENTNODE']._serialized_end=2357 + _globals['_AGENTNODE']._serialized_start=2360 + _globals['_AGENTNODE']._serialized_end=2528 + _globals['_POLICYNODE']._serialized_start=2531 + _globals['_POLICYNODE']._serialized_end=2679 + _globals['_COLLECTIONNODE']._serialized_start=2681 + _globals['_COLLECTIONNODE']._serialized_end=2784 + _globals['_COLLECTIONLINK']._serialized_start=2786 + _globals['_COLLECTIONLINK']._serialized_end=2886 + _globals['_APPROVALSTATUSNODE']._serialized_start=2889 + _globals['_APPROVALSTATUSNODE']._serialized_end=3024 + _globals['_APPROVALNODE']._serialized_start=3027 + _globals['_APPROVALNODE']._serialized_end=3206 + _globals['_FULLSYNCTOKEN']._serialized_start=3208 + _globals['_FULLSYNCTOKEN']._serialized_end=3275 + _globals['_INCSYNCTOKEN']._serialized_start=3277 + _globals['_INCSYNCTOKEN']._serialized_end=3313 + _globals['_PEDMSYNCTOKEN']._serialized_start=3315 + _globals['_PEDMSYNCTOKEN']._serialized_end=3419 + _globals['_GETPEDMDATAREQUEST']._serialized_start=3421 + _globals['_GETPEDMDATAREQUEST']._serialized_end=3468 + _globals['_GETPEDMDATARESPONSE']._serialized_start=3471 + _globals['_GETPEDMDATARESPONSE']._serialized_end=4028 + _globals['_POLICYAGENTREQUEST']._serialized_start=4030 + _globals['_POLICYAGENTREQUEST']._serialized_end=4090 + _globals['_POLICYAGENTRESPONSE']._serialized_start=4092 + _globals['_POLICYAGENTRESPONSE']._serialized_end=4151 + _globals['_AUDITCOLLECTIONREQUEST']._serialized_start=4153 + _globals['_AUDITCOLLECTIONREQUEST']._serialized_end=4246 + _globals['_AUDITCOLLECTIONVALUE']._serialized_start=4248 + _globals['_AUDITCOLLECTIONVALUE']._serialized_end=4352 + _globals['_AUDITCOLLECTIONRESPONSE']._serialized_start=4354 + _globals['_AUDITCOLLECTIONRESPONSE']._serialized_end=4467 + _globals['_GETCOLLECTIONLINKREQUEST']._serialized_start=4469 + _globals['_GETCOLLECTIONLINKREQUEST']._serialized_end=4541 + _globals['_GETCOLLECTIONLINKRESPONSE']._serialized_start=4543 + _globals['_GETCOLLECTIONLINKRESPONSE']._serialized_end=4624 + _globals['_OFFLINEAGENTREGISTERREQUEST']._serialized_start=4627 + _globals['_OFFLINEAGENTREGISTERREQUEST']._serialized_end=4797 + _globals['_OFFLINEAGENTREGISTERRESPONSE']._serialized_start=4799 + _globals['_OFFLINEAGENTREGISTERRESPONSE']._serialized_end=4847 + _globals['_OFFLINEAGENTSYNCDOWNREQUEST']._serialized_start=4849 + _globals['_OFFLINEAGENTSYNCDOWNREQUEST']._serialized_end=4896 + _globals['_OFFLINEAGENTSYNCDOWNRESPONSE']._serialized_start=4898 + _globals['_OFFLINEAGENTSYNCDOWNRESPONSE']._serialized_end=4955 + _globals['_GETAGENTLASTSEENREQUEST']._serialized_start=4957 + _globals['_GETAGENTLASTSEENREQUEST']._serialized_end=5020 + _globals['_AGENTLASTSEEN']._serialized_start=5022 + _globals['_AGENTLASTSEEN']._serialized_end=5073 + _globals['_GETAGENTLASTSEENRESPONSE']._serialized_start=5075 + _globals['_GETAGENTLASTSEENRESPONSE']._serialized_end=5140 + _globals['_GETACTIVEAGENTCOUNTREQUEST']._serialized_start=5142 + _globals['_GETACTIVEAGENTCOUNTREQUEST']._serialized_end=5192 + _globals['_ACTIVEAGENTCOUNT']._serialized_start=5194 + _globals['_ACTIVEAGENTCOUNT']._serialized_end=5256 + _globals['_ACTIVEAGENTFAILURE']._serialized_start=5258 + _globals['_ACTIVEAGENTFAILURE']._serialized_end=5317 + _globals['_GETACTIVEAGENTCOUNTRESPONSE']._serialized_start=5319 + _globals['_GETACTIVEAGENTCOUNTRESPONSE']._serialized_end=5439 + _globals['_GETAGENTDAILYCOUNTREQUEST']._serialized_start=5442 + _globals['_GETAGENTDAILYCOUNTREQUEST']._serialized_end=5577 + _globals['_MONTHYEAR']._serialized_start=5579 + _globals['_MONTHYEAR']._serialized_end=5619 + _globals['_DATERANGE']._serialized_start=5621 + _globals['_DATERANGE']._serialized_end=5660 + _globals['_AGENTDAILYCOUNT']._serialized_start=5662 + _globals['_AGENTDAILYCOUNT']._serialized_end=5713 + _globals['_AGENTCOUNTFORENTERPRISE']._serialized_start=5715 + _globals['_AGENTCOUNTFORENTERPRISE']._serialized_end=5801 + _globals['_GETAGENTDAILYCOUNTRESPONSE']._serialized_start=5803 + _globals['_GETAGENTDAILYCOUNTRESPONSE']._serialized_end=5888 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/pedm_pb2.pyi b/keepercommander/proto/pedm_pb2.pyi index 88decca11..52f82d9b3 100644 --- a/keepercommander/proto/pedm_pb2.pyi +++ b/keepercommander/proto/pedm_pb2.pyi @@ -1,5 +1,4 @@ import folder_pb2 as _folder_pb2 -import NotificationCenter_pb2 as _NotificationCenter_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 @@ -9,20 +8,33 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class CollectionLinkType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () CLT_OTHER: _ClassVar[CollectionLinkType] CLT_AGENT: _ClassVar[CollectionLinkType] CLT_POLICY: _ClassVar[CollectionLinkType] CLT_COLLECTION: _ClassVar[CollectionLinkType] CLT_DEPLOYMENT: _ClassVar[CollectionLinkType] + +class ApprovalStatusType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + AST_UNSPECIFIED: _ClassVar[ApprovalStatusType] + AST_APPROVED: _ClassVar[ApprovalStatusType] + AST_DENIED: _ClassVar[ApprovalStatusType] + AST_EXPIRED: _ClassVar[ApprovalStatusType] + AST_ESCALATED: _ClassVar[ApprovalStatusType] CLT_OTHER: CollectionLinkType CLT_AGENT: CollectionLinkType CLT_POLICY: CollectionLinkType CLT_COLLECTION: CollectionLinkType CLT_DEPLOYMENT: CollectionLinkType +AST_UNSPECIFIED: ApprovalStatusType +AST_APPROVED: ApprovalStatusType +AST_DENIED: ApprovalStatusType +AST_EXPIRED: ApprovalStatusType +AST_ESCALATED: ApprovalStatusType 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] @@ -32,7 +44,7 @@ class PEDMTOTPValidateRequest(_message.Message): def __init__(self, username: _Optional[str] = ..., enterpriseId: _Optional[int] = ..., code: _Optional[int] = ...) -> None: ... class PedmStatus(_message.Message): - __slots__ = ["key", "success", "message"] + __slots__ = ("key", "success", "message") KEY_FIELD_NUMBER: _ClassVar[int] SUCCESS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -42,7 +54,7 @@ class PedmStatus(_message.Message): def __init__(self, key: _Optional[_Iterable[bytes]] = ..., success: bool = ..., message: _Optional[str] = ...) -> None: ... class PedmStatusResponse(_message.Message): - __slots__ = ["addStatus", "updateStatus", "removeStatus"] + __slots__ = ("addStatus", "updateStatus", "removeStatus") ADDSTATUS_FIELD_NUMBER: _ClassVar[int] UPDATESTATUS_FIELD_NUMBER: _ClassVar[int] REMOVESTATUS_FIELD_NUMBER: _ClassVar[int] @@ -52,7 +64,7 @@ class PedmStatusResponse(_message.Message): def __init__(self, addStatus: _Optional[_Iterable[_Union[PedmStatus, _Mapping]]] = ..., updateStatus: _Optional[_Iterable[_Union[PedmStatus, _Mapping]]] = ..., removeStatus: _Optional[_Iterable[_Union[PedmStatus, _Mapping]]] = ...) -> None: ... class DeploymentData(_message.Message): - __slots__ = ["name", "ecPrivateKey"] + __slots__ = ("name", "ecPrivateKey") NAME_FIELD_NUMBER: _ClassVar[int] ECPRIVATEKEY_FIELD_NUMBER: _ClassVar[int] name: str @@ -60,7 +72,7 @@ class DeploymentData(_message.Message): def __init__(self, name: _Optional[str] = ..., ecPrivateKey: _Optional[bytes] = ...) -> None: ... class DeploymentCreateRequest(_message.Message): - __slots__ = ["deploymentUid", "aesKey", "ecPublicKey", "spiffeCertificate", "encryptedData", "agentData"] + __slots__ = ("deploymentUid", "aesKey", "ecPublicKey", "spiffeCertificate", "encryptedData", "agentData") DEPLOYMENTUID_FIELD_NUMBER: _ClassVar[int] AESKEY_FIELD_NUMBER: _ClassVar[int] ECPUBLICKEY_FIELD_NUMBER: _ClassVar[int] @@ -76,7 +88,7 @@ class DeploymentCreateRequest(_message.Message): def __init__(self, deploymentUid: _Optional[bytes] = ..., aesKey: _Optional[bytes] = ..., ecPublicKey: _Optional[bytes] = ..., spiffeCertificate: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., agentData: _Optional[bytes] = ...) -> None: ... class DeploymentUpdateRequest(_message.Message): - __slots__ = ["deploymentUid", "encryptedData", "disabled", "spiffeCertificate"] + __slots__ = ("deploymentUid", "encryptedData", "disabled", "spiffeCertificate") DEPLOYMENTUID_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] DISABLED_FIELD_NUMBER: _ClassVar[int] @@ -88,7 +100,7 @@ class DeploymentUpdateRequest(_message.Message): def __init__(self, deploymentUid: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., disabled: _Optional[_Union[_folder_pb2.SetBooleanValue, str]] = ..., spiffeCertificate: _Optional[bytes] = ...) -> None: ... class ModifyDeploymentRequest(_message.Message): - __slots__ = ["addDeployment", "updateDeployment", "removeDeployment"] + __slots__ = ("addDeployment", "updateDeployment", "removeDeployment") ADDDEPLOYMENT_FIELD_NUMBER: _ClassVar[int] UPDATEDEPLOYMENT_FIELD_NUMBER: _ClassVar[int] REMOVEDEPLOYMENT_FIELD_NUMBER: _ClassVar[int] @@ -98,7 +110,7 @@ class ModifyDeploymentRequest(_message.Message): def __init__(self, addDeployment: _Optional[_Iterable[_Union[DeploymentCreateRequest, _Mapping]]] = ..., updateDeployment: _Optional[_Iterable[_Union[DeploymentUpdateRequest, _Mapping]]] = ..., removeDeployment: _Optional[_Iterable[bytes]] = ...) -> None: ... class AgentUpdate(_message.Message): - __slots__ = ["agentUid", "disabled", "deploymentUid"] + __slots__ = ("agentUid", "disabled", "deploymentUid") AGENTUID_FIELD_NUMBER: _ClassVar[int] DISABLED_FIELD_NUMBER: _ClassVar[int] DEPLOYMENTUID_FIELD_NUMBER: _ClassVar[int] @@ -108,7 +120,7 @@ class AgentUpdate(_message.Message): def __init__(self, agentUid: _Optional[bytes] = ..., disabled: _Optional[_Union[_folder_pb2.SetBooleanValue, str]] = ..., deploymentUid: _Optional[bytes] = ...) -> None: ... class ModifyAgentRequest(_message.Message): - __slots__ = ["updateAgent", "removeAgent"] + __slots__ = ("updateAgent", "removeAgent") UPDATEAGENT_FIELD_NUMBER: _ClassVar[int] REMOVEAGENT_FIELD_NUMBER: _ClassVar[int] updateAgent: _containers.RepeatedCompositeFieldContainer[AgentUpdate] @@ -116,7 +128,7 @@ class ModifyAgentRequest(_message.Message): def __init__(self, updateAgent: _Optional[_Iterable[_Union[AgentUpdate, _Mapping]]] = ..., removeAgent: _Optional[_Iterable[bytes]] = ...) -> None: ... class PolicyAdd(_message.Message): - __slots__ = ["policyUid", "plainData", "encryptedData", "encryptedKey", "disabled"] + __slots__ = ("policyUid", "plainData", "encryptedData", "encryptedKey", "disabled") POLICYUID_FIELD_NUMBER: _ClassVar[int] PLAINDATA_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] @@ -130,7 +142,7 @@ class PolicyAdd(_message.Message): def __init__(self, policyUid: _Optional[bytes] = ..., plainData: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., encryptedKey: _Optional[bytes] = ..., disabled: bool = ...) -> None: ... class PolicyUpdate(_message.Message): - __slots__ = ["policyUid", "plainData", "encryptedData", "disabled"] + __slots__ = ("policyUid", "plainData", "encryptedData", "disabled") POLICYUID_FIELD_NUMBER: _ClassVar[int] PLAINDATA_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] @@ -142,7 +154,7 @@ class PolicyUpdate(_message.Message): def __init__(self, policyUid: _Optional[bytes] = ..., plainData: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., disabled: _Optional[_Union[_folder_pb2.SetBooleanValue, str]] = ...) -> None: ... class PolicyRequest(_message.Message): - __slots__ = ["addPolicy", "updatePolicy", "removePolicy"] + __slots__ = ("addPolicy", "updatePolicy", "removePolicy") ADDPOLICY_FIELD_NUMBER: _ClassVar[int] UPDATEPOLICY_FIELD_NUMBER: _ClassVar[int] REMOVEPOLICY_FIELD_NUMBER: _ClassVar[int] @@ -152,7 +164,7 @@ class PolicyRequest(_message.Message): def __init__(self, addPolicy: _Optional[_Iterable[_Union[PolicyAdd, _Mapping]]] = ..., updatePolicy: _Optional[_Iterable[_Union[PolicyUpdate, _Mapping]]] = ..., removePolicy: _Optional[_Iterable[bytes]] = ...) -> None: ... class PolicyLink(_message.Message): - __slots__ = ["policyUid", "collectionUid"] + __slots__ = ("policyUid", "collectionUid") POLICYUID_FIELD_NUMBER: _ClassVar[int] COLLECTIONUID_FIELD_NUMBER: _ClassVar[int] policyUid: bytes @@ -160,13 +172,13 @@ class PolicyLink(_message.Message): def __init__(self, policyUid: _Optional[bytes] = ..., collectionUid: _Optional[_Iterable[bytes]] = ...) -> None: ... class SetPolicyCollectionRequest(_message.Message): - __slots__ = ["setCollection"] + __slots__ = ("setCollection",) SETCOLLECTION_FIELD_NUMBER: _ClassVar[int] setCollection: _containers.RepeatedCompositeFieldContainer[PolicyLink] def __init__(self, setCollection: _Optional[_Iterable[_Union[PolicyLink, _Mapping]]] = ...) -> None: ... class CollectionValue(_message.Message): - __slots__ = ["collectionUid", "collectionType", "encryptedData"] + __slots__ = ("collectionUid", "collectionType", "encryptedData") COLLECTIONUID_FIELD_NUMBER: _ClassVar[int] COLLECTIONTYPE_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] @@ -176,7 +188,7 @@ class CollectionValue(_message.Message): def __init__(self, collectionUid: _Optional[bytes] = ..., collectionType: _Optional[int] = ..., encryptedData: _Optional[bytes] = ...) -> None: ... class CollectionLinkData(_message.Message): - __slots__ = ["collectionUid", "linkUid", "linkType", "linkData"] + __slots__ = ("collectionUid", "linkUid", "linkType", "linkData") COLLECTIONUID_FIELD_NUMBER: _ClassVar[int] LINKUID_FIELD_NUMBER: _ClassVar[int] LINKTYPE_FIELD_NUMBER: _ClassVar[int] @@ -188,7 +200,7 @@ class CollectionLinkData(_message.Message): def __init__(self, collectionUid: _Optional[bytes] = ..., linkUid: _Optional[bytes] = ..., linkType: _Optional[_Union[CollectionLinkType, str]] = ..., linkData: _Optional[bytes] = ...) -> None: ... class CollectionRequest(_message.Message): - __slots__ = ["addCollection", "updateCollection", "removeCollection"] + __slots__ = ("addCollection", "updateCollection", "removeCollection") ADDCOLLECTION_FIELD_NUMBER: _ClassVar[int] UPDATECOLLECTION_FIELD_NUMBER: _ClassVar[int] REMOVECOLLECTION_FIELD_NUMBER: _ClassVar[int] @@ -198,7 +210,7 @@ class CollectionRequest(_message.Message): def __init__(self, addCollection: _Optional[_Iterable[_Union[CollectionValue, _Mapping]]] = ..., updateCollection: _Optional[_Iterable[_Union[CollectionValue, _Mapping]]] = ..., removeCollection: _Optional[_Iterable[bytes]] = ...) -> None: ... class SetCollectionLinkRequest(_message.Message): - __slots__ = ["addCollection", "removeCollection"] + __slots__ = ("addCollection", "removeCollection") ADDCOLLECTION_FIELD_NUMBER: _ClassVar[int] REMOVECOLLECTION_FIELD_NUMBER: _ClassVar[int] addCollection: _containers.RepeatedCompositeFieldContainer[CollectionLinkData] @@ -206,7 +218,7 @@ class SetCollectionLinkRequest(_message.Message): def __init__(self, addCollection: _Optional[_Iterable[_Union[CollectionLinkData, _Mapping]]] = ..., removeCollection: _Optional[_Iterable[_Union[CollectionLink, _Mapping]]] = ...) -> None: ... class ApprovalExtendData(_message.Message): - __slots__ = ["approvalUid", "expireIn"] + __slots__ = ("approvalUid", "expireIn") APPROVALUID_FIELD_NUMBER: _ClassVar[int] EXPIREIN_FIELD_NUMBER: _ClassVar[int] approvalUid: bytes @@ -214,13 +226,13 @@ class ApprovalExtendData(_message.Message): def __init__(self, approvalUid: _Optional[bytes] = ..., expireIn: _Optional[int] = ...) -> None: ... class ModifyApprovalRequest(_message.Message): - __slots__ = ["extendApproval"] + __slots__ = ("extendApproval",) EXTENDAPPROVAL_FIELD_NUMBER: _ClassVar[int] extendApproval: _containers.RepeatedCompositeFieldContainer[ApprovalExtendData] def __init__(self, extendApproval: _Optional[_Iterable[_Union[ApprovalExtendData, _Mapping]]] = ...) -> None: ... class ApprovalActionRequest(_message.Message): - __slots__ = ["approve", "deny", "remove"] + __slots__ = ("approve", "deny", "remove") APPROVE_FIELD_NUMBER: _ClassVar[int] DENY_FIELD_NUMBER: _ClassVar[int] REMOVE_FIELD_NUMBER: _ClassVar[int] @@ -230,7 +242,7 @@ class ApprovalActionRequest(_message.Message): def __init__(self, approve: _Optional[_Iterable[bytes]] = ..., deny: _Optional[_Iterable[bytes]] = ..., remove: _Optional[_Iterable[bytes]] = ...) -> None: ... class DeploymentNode(_message.Message): - __slots__ = ["deploymentUid", "disabled", "aesKey", "ecPublicKey", "encryptedData", "agentData", "created", "modified"] + __slots__ = ("deploymentUid", "disabled", "aesKey", "ecPublicKey", "encryptedData", "agentData", "created", "modified") DEPLOYMENTUID_FIELD_NUMBER: _ClassVar[int] DISABLED_FIELD_NUMBER: _ClassVar[int] AESKEY_FIELD_NUMBER: _ClassVar[int] @@ -250,7 +262,7 @@ class DeploymentNode(_message.Message): def __init__(self, deploymentUid: _Optional[bytes] = ..., disabled: bool = ..., aesKey: _Optional[bytes] = ..., ecPublicKey: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., agentData: _Optional[bytes] = ..., created: _Optional[int] = ..., modified: _Optional[int] = ...) -> None: ... class AgentNode(_message.Message): - __slots__ = ["agentUid", "machineId", "deploymentUid", "ecPublicKey", "disabled", "encryptedData", "created", "modified"] + __slots__ = ("agentUid", "machineId", "deploymentUid", "ecPublicKey", "disabled", "encryptedData", "created", "modified") AGENTUID_FIELD_NUMBER: _ClassVar[int] MACHINEID_FIELD_NUMBER: _ClassVar[int] DEPLOYMENTUID_FIELD_NUMBER: _ClassVar[int] @@ -270,7 +282,7 @@ class AgentNode(_message.Message): def __init__(self, agentUid: _Optional[bytes] = ..., machineId: _Optional[str] = ..., deploymentUid: _Optional[bytes] = ..., ecPublicKey: _Optional[bytes] = ..., disabled: bool = ..., encryptedData: _Optional[bytes] = ..., created: _Optional[int] = ..., modified: _Optional[int] = ...) -> None: ... class PolicyNode(_message.Message): - __slots__ = ["policyUid", "plainData", "encryptedData", "encryptedKey", "created", "modified", "disabled"] + __slots__ = ("policyUid", "plainData", "encryptedData", "encryptedKey", "created", "modified", "disabled") POLICYUID_FIELD_NUMBER: _ClassVar[int] PLAINDATA_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] @@ -288,7 +300,7 @@ class PolicyNode(_message.Message): def __init__(self, policyUid: _Optional[bytes] = ..., plainData: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., encryptedKey: _Optional[bytes] = ..., created: _Optional[int] = ..., modified: _Optional[int] = ..., disabled: bool = ...) -> None: ... class CollectionNode(_message.Message): - __slots__ = ["collectionUid", "collectionType", "encryptedData", "created"] + __slots__ = ("collectionUid", "collectionType", "encryptedData", "created") COLLECTIONUID_FIELD_NUMBER: _ClassVar[int] COLLECTIONTYPE_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] @@ -300,7 +312,7 @@ class CollectionNode(_message.Message): def __init__(self, collectionUid: _Optional[bytes] = ..., collectionType: _Optional[int] = ..., encryptedData: _Optional[bytes] = ..., created: _Optional[int] = ...) -> None: ... class CollectionLink(_message.Message): - __slots__ = ["collectionUid", "linkUid", "linkType"] + __slots__ = ("collectionUid", "linkUid", "linkType") COLLECTIONUID_FIELD_NUMBER: _ClassVar[int] LINKUID_FIELD_NUMBER: _ClassVar[int] LINKTYPE_FIELD_NUMBER: _ClassVar[int] @@ -310,19 +322,19 @@ class CollectionLink(_message.Message): def __init__(self, collectionUid: _Optional[bytes] = ..., linkUid: _Optional[bytes] = ..., linkType: _Optional[_Union[CollectionLinkType, str]] = ...) -> None: ... class ApprovalStatusNode(_message.Message): - __slots__ = ["approvalUid", "approvalStatus", "enterpriseUserId", "modified"] + __slots__ = ("approvalUid", "approvalStatus", "enterpriseUserId", "modified") APPROVALUID_FIELD_NUMBER: _ClassVar[int] APPROVALSTATUS_FIELD_NUMBER: _ClassVar[int] ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] MODIFIED_FIELD_NUMBER: _ClassVar[int] approvalUid: bytes - approvalStatus: _NotificationCenter_pb2.NotificationApprovalStatus + approvalStatus: ApprovalStatusType enterpriseUserId: int modified: int - def __init__(self, approvalUid: _Optional[bytes] = ..., approvalStatus: _Optional[_Union[_NotificationCenter_pb2.NotificationApprovalStatus, str]] = ..., enterpriseUserId: _Optional[int] = ..., modified: _Optional[int] = ...) -> None: ... + def __init__(self, approvalUid: _Optional[bytes] = ..., approvalStatus: _Optional[_Union[ApprovalStatusType, str]] = ..., enterpriseUserId: _Optional[int] = ..., modified: _Optional[int] = ...) -> None: ... class ApprovalNode(_message.Message): - __slots__ = ["approvalUid", "approvalType", "agentUid", "accountInfo", "applicationInfo", "justification", "expireIn", "created"] + __slots__ = ("approvalUid", "approvalType", "agentUid", "accountInfo", "applicationInfo", "justification", "expireIn", "created") APPROVALUID_FIELD_NUMBER: _ClassVar[int] APPROVALTYPE_FIELD_NUMBER: _ClassVar[int] AGENTUID_FIELD_NUMBER: _ClassVar[int] @@ -342,7 +354,7 @@ class ApprovalNode(_message.Message): def __init__(self, approvalUid: _Optional[bytes] = ..., approvalType: _Optional[int] = ..., agentUid: _Optional[bytes] = ..., accountInfo: _Optional[bytes] = ..., applicationInfo: _Optional[bytes] = ..., justification: _Optional[bytes] = ..., expireIn: _Optional[int] = ..., created: _Optional[int] = ...) -> None: ... class FullSyncToken(_message.Message): - __slots__ = ["startRevision", "entity", "key"] + __slots__ = ("startRevision", "entity", "key") STARTREVISION_FIELD_NUMBER: _ClassVar[int] ENTITY_FIELD_NUMBER: _ClassVar[int] KEY_FIELD_NUMBER: _ClassVar[int] @@ -352,13 +364,13 @@ class FullSyncToken(_message.Message): def __init__(self, startRevision: _Optional[int] = ..., entity: _Optional[int] = ..., key: _Optional[_Iterable[bytes]] = ...) -> None: ... class IncSyncToken(_message.Message): - __slots__ = ["lastRevision"] + __slots__ = ("lastRevision",) LASTREVISION_FIELD_NUMBER: _ClassVar[int] lastRevision: int def __init__(self, lastRevision: _Optional[int] = ...) -> None: ... class PedmSyncToken(_message.Message): - __slots__ = ["fullSync", "incSync"] + __slots__ = ("fullSync", "incSync") FULLSYNC_FIELD_NUMBER: _ClassVar[int] INCSYNC_FIELD_NUMBER: _ClassVar[int] fullSync: FullSyncToken @@ -366,13 +378,13 @@ class PedmSyncToken(_message.Message): def __init__(self, fullSync: _Optional[_Union[FullSyncToken, _Mapping]] = ..., incSync: _Optional[_Union[IncSyncToken, _Mapping]] = ...) -> None: ... class GetPedmDataRequest(_message.Message): - __slots__ = ["continuationToken"] + __slots__ = ("continuationToken",) CONTINUATIONTOKEN_FIELD_NUMBER: _ClassVar[int] continuationToken: bytes def __init__(self, continuationToken: _Optional[bytes] = ...) -> None: ... class GetPedmDataResponse(_message.Message): - __slots__ = ["continuationToken", "resetCache", "hasMore", "removedDeployments", "removedAgents", "removedPolicies", "removedCollection", "removedCollectionLink", "removedApprovals", "deployments", "agents", "policies", "collections", "collectionLink", "approvals", "approvalStatus"] + __slots__ = ("continuationToken", "resetCache", "hasMore", "removedDeployments", "removedAgents", "removedPolicies", "removedCollection", "removedCollectionLink", "removedApprovals", "deployments", "agents", "policies", "collections", "collectionLink", "approvals", "approvalStatus") CONTINUATIONTOKEN_FIELD_NUMBER: _ClassVar[int] RESETCACHE_FIELD_NUMBER: _ClassVar[int] HASMORE_FIELD_NUMBER: _ClassVar[int] @@ -408,7 +420,7 @@ class GetPedmDataResponse(_message.Message): def __init__(self, continuationToken: _Optional[bytes] = ..., resetCache: bool = ..., hasMore: bool = ..., removedDeployments: _Optional[_Iterable[bytes]] = ..., removedAgents: _Optional[_Iterable[bytes]] = ..., removedPolicies: _Optional[_Iterable[bytes]] = ..., removedCollection: _Optional[_Iterable[bytes]] = ..., removedCollectionLink: _Optional[_Iterable[_Union[CollectionLink, _Mapping]]] = ..., removedApprovals: _Optional[_Iterable[bytes]] = ..., deployments: _Optional[_Iterable[_Union[DeploymentNode, _Mapping]]] = ..., agents: _Optional[_Iterable[_Union[AgentNode, _Mapping]]] = ..., policies: _Optional[_Iterable[_Union[PolicyNode, _Mapping]]] = ..., collections: _Optional[_Iterable[_Union[CollectionNode, _Mapping]]] = ..., collectionLink: _Optional[_Iterable[_Union[CollectionLink, _Mapping]]] = ..., approvals: _Optional[_Iterable[_Union[ApprovalNode, _Mapping]]] = ..., approvalStatus: _Optional[_Iterable[_Union[ApprovalStatusNode, _Mapping]]] = ...) -> None: ... class PolicyAgentRequest(_message.Message): - __slots__ = ["policyUid", "summaryOnly"] + __slots__ = ("policyUid", "summaryOnly") POLICYUID_FIELD_NUMBER: _ClassVar[int] SUMMARYONLY_FIELD_NUMBER: _ClassVar[int] policyUid: _containers.RepeatedScalarFieldContainer[bytes] @@ -416,7 +428,7 @@ class PolicyAgentRequest(_message.Message): def __init__(self, policyUid: _Optional[_Iterable[bytes]] = ..., summaryOnly: bool = ...) -> None: ... class PolicyAgentResponse(_message.Message): - __slots__ = ["agentCount", "agentUid"] + __slots__ = ("agentCount", "agentUid") AGENTCOUNT_FIELD_NUMBER: _ClassVar[int] AGENTUID_FIELD_NUMBER: _ClassVar[int] agentCount: int @@ -424,7 +436,7 @@ class PolicyAgentResponse(_message.Message): def __init__(self, agentCount: _Optional[int] = ..., agentUid: _Optional[_Iterable[bytes]] = ...) -> None: ... class AuditCollectionRequest(_message.Message): - __slots__ = ["continuationToken", "valueUid", "collectionName"] + __slots__ = ("continuationToken", "valueUid", "collectionName") CONTINUATIONTOKEN_FIELD_NUMBER: _ClassVar[int] VALUEUID_FIELD_NUMBER: _ClassVar[int] COLLECTIONNAME_FIELD_NUMBER: _ClassVar[int] @@ -434,7 +446,7 @@ class AuditCollectionRequest(_message.Message): def __init__(self, continuationToken: _Optional[bytes] = ..., valueUid: _Optional[_Iterable[bytes]] = ..., collectionName: _Optional[_Iterable[str]] = ...) -> None: ... class AuditCollectionValue(_message.Message): - __slots__ = ["collectionName", "valueUid", "encryptedData", "created"] + __slots__ = ("collectionName", "valueUid", "encryptedData", "created") COLLECTIONNAME_FIELD_NUMBER: _ClassVar[int] VALUEUID_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] @@ -446,7 +458,7 @@ class AuditCollectionValue(_message.Message): def __init__(self, collectionName: _Optional[str] = ..., valueUid: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., created: _Optional[int] = ...) -> None: ... class AuditCollectionResponse(_message.Message): - __slots__ = ["values", "hasMore", "continuationToken"] + __slots__ = ("values", "hasMore", "continuationToken") VALUES_FIELD_NUMBER: _ClassVar[int] HASMORE_FIELD_NUMBER: _ClassVar[int] CONTINUATIONTOKEN_FIELD_NUMBER: _ClassVar[int] @@ -456,19 +468,19 @@ class AuditCollectionResponse(_message.Message): def __init__(self, values: _Optional[_Iterable[_Union[AuditCollectionValue, _Mapping]]] = ..., hasMore: bool = ..., continuationToken: _Optional[bytes] = ...) -> None: ... class GetCollectionLinkRequest(_message.Message): - __slots__ = ["collectionLink"] + __slots__ = ("collectionLink",) COLLECTIONLINK_FIELD_NUMBER: _ClassVar[int] collectionLink: _containers.RepeatedCompositeFieldContainer[CollectionLink] def __init__(self, collectionLink: _Optional[_Iterable[_Union[CollectionLink, _Mapping]]] = ...) -> None: ... class GetCollectionLinkResponse(_message.Message): - __slots__ = ["collectionLinkData"] + __slots__ = ("collectionLinkData",) COLLECTIONLINKDATA_FIELD_NUMBER: _ClassVar[int] collectionLinkData: _containers.RepeatedCompositeFieldContainer[CollectionLinkData] def __init__(self, collectionLinkData: _Optional[_Iterable[_Union[CollectionLinkData, _Mapping]]] = ...) -> None: ... class OfflineAgentRegisterRequest(_message.Message): - __slots__ = ["agentUid", "deploymentUid", "publicKey", "machineId", "collection", "agentData"] + __slots__ = ("agentUid", "deploymentUid", "publicKey", "machineId", "collection", "agentData") AGENTUID_FIELD_NUMBER: _ClassVar[int] DEPLOYMENTUID_FIELD_NUMBER: _ClassVar[int] PUBLICKEY_FIELD_NUMBER: _ClassVar[int] @@ -484,25 +496,25 @@ class OfflineAgentRegisterRequest(_message.Message): def __init__(self, agentUid: _Optional[bytes] = ..., deploymentUid: _Optional[bytes] = ..., publicKey: _Optional[bytes] = ..., machineId: _Optional[str] = ..., collection: _Optional[_Iterable[_Union[CollectionValue, _Mapping]]] = ..., agentData: _Optional[bytes] = ...) -> None: ... class OfflineAgentRegisterResponse(_message.Message): - __slots__ = ["agentUid"] + __slots__ = ("agentUid",) AGENTUID_FIELD_NUMBER: _ClassVar[int] agentUid: bytes def __init__(self, agentUid: _Optional[bytes] = ...) -> None: ... class OfflineAgentSyncDownRequest(_message.Message): - __slots__ = ["agentUid"] + __slots__ = ("agentUid",) AGENTUID_FIELD_NUMBER: _ClassVar[int] agentUid: bytes def __init__(self, agentUid: _Optional[bytes] = ...) -> None: ... class OfflineAgentSyncDownResponse(_message.Message): - __slots__ = ["encryptedSyncData"] + __slots__ = ("encryptedSyncData",) ENCRYPTEDSYNCDATA_FIELD_NUMBER: _ClassVar[int] encryptedSyncData: bytes def __init__(self, encryptedSyncData: _Optional[bytes] = ...) -> None: ... class GetAgentLastSeenRequest(_message.Message): - __slots__ = ["activeOnly", "agentUid"] + __slots__ = ("activeOnly", "agentUid") ACTIVEONLY_FIELD_NUMBER: _ClassVar[int] AGENTUID_FIELD_NUMBER: _ClassVar[int] activeOnly: bool @@ -510,7 +522,7 @@ class GetAgentLastSeenRequest(_message.Message): def __init__(self, activeOnly: bool = ..., agentUid: _Optional[_Iterable[bytes]] = ...) -> None: ... class AgentLastSeen(_message.Message): - __slots__ = ["agentUid", "lastSeen"] + __slots__ = ("agentUid", "lastSeen") AGENTUID_FIELD_NUMBER: _ClassVar[int] LASTSEEN_FIELD_NUMBER: _ClassVar[int] agentUid: bytes @@ -518,19 +530,19 @@ class AgentLastSeen(_message.Message): def __init__(self, agentUid: _Optional[bytes] = ..., lastSeen: _Optional[int] = ...) -> None: ... class GetAgentLastSeenResponse(_message.Message): - __slots__ = ["lastSeen"] + __slots__ = ("lastSeen",) LASTSEEN_FIELD_NUMBER: _ClassVar[int] lastSeen: _containers.RepeatedCompositeFieldContainer[AgentLastSeen] def __init__(self, lastSeen: _Optional[_Iterable[_Union[AgentLastSeen, _Mapping]]] = ...) -> None: ... class GetActiveAgentCountRequest(_message.Message): - __slots__ = ["enterpriseId"] + __slots__ = ("enterpriseId",) ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] enterpriseId: _containers.RepeatedScalarFieldContainer[int] def __init__(self, enterpriseId: _Optional[_Iterable[int]] = ...) -> None: ... class ActiveAgentCount(_message.Message): - __slots__ = ["enterpriseId", "activeAgents"] + __slots__ = ("enterpriseId", "activeAgents") ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] ACTIVEAGENTS_FIELD_NUMBER: _ClassVar[int] enterpriseId: int @@ -538,7 +550,7 @@ class ActiveAgentCount(_message.Message): def __init__(self, enterpriseId: _Optional[int] = ..., activeAgents: _Optional[int] = ...) -> None: ... class ActiveAgentFailure(_message.Message): - __slots__ = ["enterpriseId", "message"] + __slots__ = ("enterpriseId", "message") ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] enterpriseId: int @@ -546,7 +558,7 @@ class ActiveAgentFailure(_message.Message): def __init__(self, enterpriseId: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ... class GetActiveAgentCountResponse(_message.Message): - __slots__ = ["agentCount", "failedCount"] + __slots__ = ("agentCount", "failedCount") AGENTCOUNT_FIELD_NUMBER: _ClassVar[int] FAILEDCOUNT_FIELD_NUMBER: _ClassVar[int] agentCount: _containers.RepeatedCompositeFieldContainer[ActiveAgentCount] @@ -554,7 +566,7 @@ class GetActiveAgentCountResponse(_message.Message): def __init__(self, agentCount: _Optional[_Iterable[_Union[ActiveAgentCount, _Mapping]]] = ..., failedCount: _Optional[_Iterable[_Union[ActiveAgentFailure, _Mapping]]] = ...) -> None: ... class GetAgentDailyCountRequest(_message.Message): - __slots__ = ["enterpriseId", "monthYear", "dateRange"] + __slots__ = ("enterpriseId", "monthYear", "dateRange") ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] MONTHYEAR_FIELD_NUMBER: _ClassVar[int] DATERANGE_FIELD_NUMBER: _ClassVar[int] @@ -564,7 +576,7 @@ class GetAgentDailyCountRequest(_message.Message): def __init__(self, enterpriseId: _Optional[_Iterable[int]] = ..., monthYear: _Optional[_Union[MonthYear, _Mapping]] = ..., dateRange: _Optional[_Union[DateRange, _Mapping]] = ...) -> None: ... class MonthYear(_message.Message): - __slots__ = ["month", "year"] + __slots__ = ("month", "year") MONTH_FIELD_NUMBER: _ClassVar[int] YEAR_FIELD_NUMBER: _ClassVar[int] month: int @@ -572,7 +584,7 @@ class MonthYear(_message.Message): def __init__(self, month: _Optional[int] = ..., year: _Optional[int] = ...) -> None: ... class DateRange(_message.Message): - __slots__ = ["start", "end"] + __slots__ = ("start", "end") START_FIELD_NUMBER: _ClassVar[int] END_FIELD_NUMBER: _ClassVar[int] start: int @@ -580,7 +592,7 @@ class DateRange(_message.Message): def __init__(self, start: _Optional[int] = ..., end: _Optional[int] = ...) -> None: ... class AgentDailyCount(_message.Message): - __slots__ = ["date", "agentCount"] + __slots__ = ("date", "agentCount") DATE_FIELD_NUMBER: _ClassVar[int] AGENTCOUNT_FIELD_NUMBER: _ClassVar[int] date: int @@ -588,7 +600,7 @@ class AgentDailyCount(_message.Message): def __init__(self, date: _Optional[int] = ..., agentCount: _Optional[int] = ...) -> None: ... class AgentCountForEnterprise(_message.Message): - __slots__ = ["enterpriseId", "counts"] + __slots__ = ("enterpriseId", "counts") ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] COUNTS_FIELD_NUMBER: _ClassVar[int] enterpriseId: int @@ -596,7 +608,7 @@ class AgentCountForEnterprise(_message.Message): def __init__(self, enterpriseId: _Optional[int] = ..., counts: _Optional[_Iterable[_Union[AgentDailyCount, _Mapping]]] = ...) -> None: ... class GetAgentDailyCountResponse(_message.Message): - __slots__ = ["enterpriseCounts"] + __slots__ = ("enterpriseCounts",) ENTERPRISECOUNTS_FIELD_NUMBER: _ClassVar[int] enterpriseCounts: _containers.RepeatedCompositeFieldContainer[AgentCountForEnterprise] def __init__(self, enterpriseCounts: _Optional[_Iterable[_Union[AgentCountForEnterprise, _Mapping]]] = ...) -> None: ... diff --git a/keepercommander/proto/publicapi_pb2.py b/keepercommander/proto/publicapi_pb2.py index 0aebfba33..505425aa1 100644 --- a/keepercommander/proto/publicapi_pb2.py +++ b/keepercommander/proto/publicapi_pb2.py @@ -1,13 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: publicapi.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'publicapi.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,164 +26,42 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fpublicapi.proto\x12\tPublicApi\"A\n\x04Role\x12\x0e\n\x06roleId\x18\x01 \x01(\x05\x12)\n\nactionType\x18\x02 \x01(\x0e\x32\x15.PublicApi.ActionType\"]\n\x12IntegrationRequest\x12\x1c\n\x14\x61piIntegrationTypeId\x18\x01 \x01(\x05\x12)\n\nactionType\x18\x02 \x01(\x0e\x32\x15.PublicApi.ActionType\"\xb1\x01\n\x14GenerateTokenRequest\x12\x1e\n\x05roles\x18\x01 \x03(\x0b\x32\x0f.PublicApi.Role\x12\x11\n\ttokenName\x18\x02 \x01(\t\x12\x12\n\nissuedDate\x18\x03 \x01(\x03\x12\x16\n\x0e\x65xpirationDate\x18\x04 \x01(\x03\x12:\n\x13integrationRequests\x18\x05 \x03(\x0b\x32\x1d.PublicApi.IntegrationRequest\"\x89\x01\n\x0cIntegrations\x12\x10\n\x08roleName\x18\x01 \x01(\t\x12\x1c\n\x14\x61piIntegrationTypeId\x18\x02 \x01(\x05\x12)\n\nactionType\x18\x03 \x01(\x0e\x32\x15.PublicApi.ActionType\x12\x1e\n\x16\x61piIntegrationTypeName\x18\x04 \x01(\t\"\xe3\x01\n\x0ePublicApiToken\x12\"\n\x1a\x65nterprisePublicApiTokenId\x18\x01 \x01(\x05\x12\x15\n\renterprise_id\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05token\x18\x04 \x01(\t\x12\x13\n\x06\x61\x63tive\x18\x05 \x01(\x08H\x00\x88\x01\x01\x12\x12\n\nissuedDate\x18\x06 \x01(\x03\x12\x16\n\x0e\x65xpirationDate\x18\x07 \x01(\x03\x12-\n\x0cintegrations\x18\x08 \x03(\x0b\x32\x17.PublicApi.IntegrationsB\t\n\x07_active\"\xc7\x01\n\x16PublicApiTokenResponse\x12\x15\n\renterprise_id\x18\x01 \x01(\x05\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\r\n\x05token\x18\x03 \x01(\t\x12\x13\n\x06\x61\x63tive\x18\x04 \x01(\x08H\x00\x88\x01\x01\x12\x12\n\nissuedDate\x18\x05 \x01(\x03\x12\x16\n\x0e\x65xpirationDate\x18\x06 \x01(\x03\x12-\n\x0cintegrations\x18\x07 \x03(\x0b\x32\x17.PublicApi.IntegrationsB\t\n\x07_active\"O\n\x1aPublicApiTokenResponseList\x12\x31\n\x06tokens\x18\x01 \x03(\x0b\x32!.PublicApi.PublicApiTokenResponse\"<\n\x0fPublicApiTokens\x12)\n\x06tokens\x18\x01 \x03(\x0b\x32\x19.PublicApi.PublicApiToken\"1\n\x12RevokeTokenRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05token\x18\x02 \x01(\t\"&\n\x13RevokeTokenResponse\x12\x0f\n\x07message\x18\x01 \x01(\t\"D\n\x12\x41piIntegrationType\x12\x1c\n\x14\x61piIntegrationTypeId\x18\x01 \x01(\x05\x12\x10\n\x08roleName\x18\x02 \x01(\t\"Q\n\x13\x41piIntegrationTypes\x12:\n\x13\x61piIntegrationTypes\x18\x01 \x03(\x0b\x32\x1d.PublicApi.ApiIntegrationType\"+\n\x05Token\x12\"\n\x1a\x65nterprisePublicApiTokenId\x18\x01 \x01(\x05\"\xb0\x01\n\x19ListPublicApiTokenRequest\x12\x32\n\x0cstatusFilter\x18\x01 \x01(\x0e\x32\x17.PublicApi.StatusFilterH\x00\x88\x01\x01\x12\x17\n\nsortByName\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nnameFilter\x18\x03 \x01(\tH\x02\x88\x01\x01\x42\x0f\n\r_statusFilterB\r\n\x0b_sortByNameB\r\n\x0b_nameFilter*0\n\nActionType\x12\x08\n\x04NONE\x10\x00\x12\x08\n\x04READ\x10\x01\x12\x0e\n\nREAD_WRITE\x10\x02*1\n\x0cStatusFilter\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\x0c\n\x08INACTIVE\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42%\n\x18\x63om.keepersecurity.protoB\tPublicApib\x06proto3') -_ACTIONTYPE = DESCRIPTOR.enum_types_by_name['ActionType'] -ActionType = enum_type_wrapper.EnumTypeWrapper(_ACTIONTYPE) -_STATUSFILTER = DESCRIPTOR.enum_types_by_name['StatusFilter'] -StatusFilter = enum_type_wrapper.EnumTypeWrapper(_STATUSFILTER) -NONE = 0 -READ = 1 -READ_WRITE = 2 -ACTIVE = 0 -INACTIVE = 1 -ALL = 2 - - -_ROLE = DESCRIPTOR.message_types_by_name['Role'] -_INTEGRATIONREQUEST = DESCRIPTOR.message_types_by_name['IntegrationRequest'] -_GENERATETOKENREQUEST = DESCRIPTOR.message_types_by_name['GenerateTokenRequest'] -_INTEGRATIONS = DESCRIPTOR.message_types_by_name['Integrations'] -_PUBLICAPITOKEN = DESCRIPTOR.message_types_by_name['PublicApiToken'] -_PUBLICAPITOKENRESPONSE = DESCRIPTOR.message_types_by_name['PublicApiTokenResponse'] -_PUBLICAPITOKENRESPONSELIST = DESCRIPTOR.message_types_by_name['PublicApiTokenResponseList'] -_PUBLICAPITOKENS = DESCRIPTOR.message_types_by_name['PublicApiTokens'] -_REVOKETOKENREQUEST = DESCRIPTOR.message_types_by_name['RevokeTokenRequest'] -_REVOKETOKENRESPONSE = DESCRIPTOR.message_types_by_name['RevokeTokenResponse'] -_APIINTEGRATIONTYPE = DESCRIPTOR.message_types_by_name['ApiIntegrationType'] -_APIINTEGRATIONTYPES = DESCRIPTOR.message_types_by_name['ApiIntegrationTypes'] -_TOKEN = DESCRIPTOR.message_types_by_name['Token'] -_LISTPUBLICAPITOKENREQUEST = DESCRIPTOR.message_types_by_name['ListPublicApiTokenRequest'] -Role = _reflection.GeneratedProtocolMessageType('Role', (_message.Message,), { - 'DESCRIPTOR' : _ROLE, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.Role) - }) -_sym_db.RegisterMessage(Role) - -IntegrationRequest = _reflection.GeneratedProtocolMessageType('IntegrationRequest', (_message.Message,), { - 'DESCRIPTOR' : _INTEGRATIONREQUEST, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.IntegrationRequest) - }) -_sym_db.RegisterMessage(IntegrationRequest) - -GenerateTokenRequest = _reflection.GeneratedProtocolMessageType('GenerateTokenRequest', (_message.Message,), { - 'DESCRIPTOR' : _GENERATETOKENREQUEST, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.GenerateTokenRequest) - }) -_sym_db.RegisterMessage(GenerateTokenRequest) - -Integrations = _reflection.GeneratedProtocolMessageType('Integrations', (_message.Message,), { - 'DESCRIPTOR' : _INTEGRATIONS, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.Integrations) - }) -_sym_db.RegisterMessage(Integrations) - -PublicApiToken = _reflection.GeneratedProtocolMessageType('PublicApiToken', (_message.Message,), { - 'DESCRIPTOR' : _PUBLICAPITOKEN, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.PublicApiToken) - }) -_sym_db.RegisterMessage(PublicApiToken) - -PublicApiTokenResponse = _reflection.GeneratedProtocolMessageType('PublicApiTokenResponse', (_message.Message,), { - 'DESCRIPTOR' : _PUBLICAPITOKENRESPONSE, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.PublicApiTokenResponse) - }) -_sym_db.RegisterMessage(PublicApiTokenResponse) - -PublicApiTokenResponseList = _reflection.GeneratedProtocolMessageType('PublicApiTokenResponseList', (_message.Message,), { - 'DESCRIPTOR' : _PUBLICAPITOKENRESPONSELIST, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.PublicApiTokenResponseList) - }) -_sym_db.RegisterMessage(PublicApiTokenResponseList) - -PublicApiTokens = _reflection.GeneratedProtocolMessageType('PublicApiTokens', (_message.Message,), { - 'DESCRIPTOR' : _PUBLICAPITOKENS, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.PublicApiTokens) - }) -_sym_db.RegisterMessage(PublicApiTokens) - -RevokeTokenRequest = _reflection.GeneratedProtocolMessageType('RevokeTokenRequest', (_message.Message,), { - 'DESCRIPTOR' : _REVOKETOKENREQUEST, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.RevokeTokenRequest) - }) -_sym_db.RegisterMessage(RevokeTokenRequest) - -RevokeTokenResponse = _reflection.GeneratedProtocolMessageType('RevokeTokenResponse', (_message.Message,), { - 'DESCRIPTOR' : _REVOKETOKENRESPONSE, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.RevokeTokenResponse) - }) -_sym_db.RegisterMessage(RevokeTokenResponse) - -ApiIntegrationType = _reflection.GeneratedProtocolMessageType('ApiIntegrationType', (_message.Message,), { - 'DESCRIPTOR' : _APIINTEGRATIONTYPE, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.ApiIntegrationType) - }) -_sym_db.RegisterMessage(ApiIntegrationType) - -ApiIntegrationTypes = _reflection.GeneratedProtocolMessageType('ApiIntegrationTypes', (_message.Message,), { - 'DESCRIPTOR' : _APIINTEGRATIONTYPES, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.ApiIntegrationTypes) - }) -_sym_db.RegisterMessage(ApiIntegrationTypes) - -Token = _reflection.GeneratedProtocolMessageType('Token', (_message.Message,), { - 'DESCRIPTOR' : _TOKEN, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.Token) - }) -_sym_db.RegisterMessage(Token) - -ListPublicApiTokenRequest = _reflection.GeneratedProtocolMessageType('ListPublicApiTokenRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTPUBLICAPITOKENREQUEST, - '__module__' : 'publicapi_pb2' - # @@protoc_insertion_point(class_scope:PublicApi.ListPublicApiTokenRequest) - }) -_sym_db.RegisterMessage(ListPublicApiTokenRequest) - -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\030com.keepersecurity.protoB\tPublicApi' - _ACTIONTYPE._serialized_start=1555 - _ACTIONTYPE._serialized_end=1603 - _STATUSFILTER._serialized_start=1605 - _STATUSFILTER._serialized_end=1654 - _ROLE._serialized_start=30 - _ROLE._serialized_end=95 - _INTEGRATIONREQUEST._serialized_start=97 - _INTEGRATIONREQUEST._serialized_end=190 - _GENERATETOKENREQUEST._serialized_start=193 - _GENERATETOKENREQUEST._serialized_end=370 - _INTEGRATIONS._serialized_start=373 - _INTEGRATIONS._serialized_end=510 - _PUBLICAPITOKEN._serialized_start=513 - _PUBLICAPITOKEN._serialized_end=740 - _PUBLICAPITOKENRESPONSE._serialized_start=743 - _PUBLICAPITOKENRESPONSE._serialized_end=942 - _PUBLICAPITOKENRESPONSELIST._serialized_start=944 - _PUBLICAPITOKENRESPONSELIST._serialized_end=1023 - _PUBLICAPITOKENS._serialized_start=1025 - _PUBLICAPITOKENS._serialized_end=1085 - _REVOKETOKENREQUEST._serialized_start=1087 - _REVOKETOKENREQUEST._serialized_end=1136 - _REVOKETOKENRESPONSE._serialized_start=1138 - _REVOKETOKENRESPONSE._serialized_end=1176 - _APIINTEGRATIONTYPE._serialized_start=1178 - _APIINTEGRATIONTYPE._serialized_end=1246 - _APIINTEGRATIONTYPES._serialized_start=1248 - _APIINTEGRATIONTYPES._serialized_end=1329 - _TOKEN._serialized_start=1331 - _TOKEN._serialized_end=1374 - _LISTPUBLICAPITOKENREQUEST._serialized_start=1377 - _LISTPUBLICAPITOKENREQUEST._serialized_end=1553 +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'publicapi_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\030com.keepersecurity.protoB\tPublicApi' + _globals['_ACTIONTYPE']._serialized_start=1555 + _globals['_ACTIONTYPE']._serialized_end=1603 + _globals['_STATUSFILTER']._serialized_start=1605 + _globals['_STATUSFILTER']._serialized_end=1654 + _globals['_ROLE']._serialized_start=30 + _globals['_ROLE']._serialized_end=95 + _globals['_INTEGRATIONREQUEST']._serialized_start=97 + _globals['_INTEGRATIONREQUEST']._serialized_end=190 + _globals['_GENERATETOKENREQUEST']._serialized_start=193 + _globals['_GENERATETOKENREQUEST']._serialized_end=370 + _globals['_INTEGRATIONS']._serialized_start=373 + _globals['_INTEGRATIONS']._serialized_end=510 + _globals['_PUBLICAPITOKEN']._serialized_start=513 + _globals['_PUBLICAPITOKEN']._serialized_end=740 + _globals['_PUBLICAPITOKENRESPONSE']._serialized_start=743 + _globals['_PUBLICAPITOKENRESPONSE']._serialized_end=942 + _globals['_PUBLICAPITOKENRESPONSELIST']._serialized_start=944 + _globals['_PUBLICAPITOKENRESPONSELIST']._serialized_end=1023 + _globals['_PUBLICAPITOKENS']._serialized_start=1025 + _globals['_PUBLICAPITOKENS']._serialized_end=1085 + _globals['_REVOKETOKENREQUEST']._serialized_start=1087 + _globals['_REVOKETOKENREQUEST']._serialized_end=1136 + _globals['_REVOKETOKENRESPONSE']._serialized_start=1138 + _globals['_REVOKETOKENRESPONSE']._serialized_end=1176 + _globals['_APIINTEGRATIONTYPE']._serialized_start=1178 + _globals['_APIINTEGRATIONTYPE']._serialized_end=1246 + _globals['_APIINTEGRATIONTYPES']._serialized_start=1248 + _globals['_APIINTEGRATIONTYPES']._serialized_end=1329 + _globals['_TOKEN']._serialized_start=1331 + _globals['_TOKEN']._serialized_end=1374 + _globals['_LISTPUBLICAPITOKENREQUEST']._serialized_start=1377 + _globals['_LISTPUBLICAPITOKENREQUEST']._serialized_end=1553 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/publicapi_pb2.pyi b/keepercommander/proto/publicapi_pb2.pyi new file mode 100644 index 000000000..b628f7ac7 --- /dev/null +++ b/keepercommander/proto/publicapi_pb2.pyi @@ -0,0 +1,161 @@ +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 + +DESCRIPTOR: _descriptor.FileDescriptor + +class ActionType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + NONE: _ClassVar[ActionType] + READ: _ClassVar[ActionType] + READ_WRITE: _ClassVar[ActionType] + +class StatusFilter(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + ACTIVE: _ClassVar[StatusFilter] + INACTIVE: _ClassVar[StatusFilter] + ALL: _ClassVar[StatusFilter] +NONE: ActionType +READ: ActionType +READ_WRITE: ActionType +ACTIVE: StatusFilter +INACTIVE: StatusFilter +ALL: StatusFilter + +class Role(_message.Message): + __slots__ = ("roleId", "actionType") + ROLEID_FIELD_NUMBER: _ClassVar[int] + ACTIONTYPE_FIELD_NUMBER: _ClassVar[int] + roleId: int + actionType: ActionType + def __init__(self, roleId: _Optional[int] = ..., actionType: _Optional[_Union[ActionType, str]] = ...) -> None: ... + +class IntegrationRequest(_message.Message): + __slots__ = ("apiIntegrationTypeId", "actionType") + APIINTEGRATIONTYPEID_FIELD_NUMBER: _ClassVar[int] + ACTIONTYPE_FIELD_NUMBER: _ClassVar[int] + apiIntegrationTypeId: int + actionType: ActionType + def __init__(self, apiIntegrationTypeId: _Optional[int] = ..., actionType: _Optional[_Union[ActionType, str]] = ...) -> None: ... + +class GenerateTokenRequest(_message.Message): + __slots__ = ("roles", "tokenName", "issuedDate", "expirationDate", "integrationRequests") + ROLES_FIELD_NUMBER: _ClassVar[int] + TOKENNAME_FIELD_NUMBER: _ClassVar[int] + ISSUEDDATE_FIELD_NUMBER: _ClassVar[int] + EXPIRATIONDATE_FIELD_NUMBER: _ClassVar[int] + INTEGRATIONREQUESTS_FIELD_NUMBER: _ClassVar[int] + roles: _containers.RepeatedCompositeFieldContainer[Role] + tokenName: str + issuedDate: int + expirationDate: int + integrationRequests: _containers.RepeatedCompositeFieldContainer[IntegrationRequest] + def __init__(self, roles: _Optional[_Iterable[_Union[Role, _Mapping]]] = ..., tokenName: _Optional[str] = ..., issuedDate: _Optional[int] = ..., expirationDate: _Optional[int] = ..., integrationRequests: _Optional[_Iterable[_Union[IntegrationRequest, _Mapping]]] = ...) -> None: ... + +class Integrations(_message.Message): + __slots__ = ("roleName", "apiIntegrationTypeId", "actionType", "apiIntegrationTypeName") + ROLENAME_FIELD_NUMBER: _ClassVar[int] + APIINTEGRATIONTYPEID_FIELD_NUMBER: _ClassVar[int] + ACTIONTYPE_FIELD_NUMBER: _ClassVar[int] + APIINTEGRATIONTYPENAME_FIELD_NUMBER: _ClassVar[int] + roleName: str + apiIntegrationTypeId: int + actionType: ActionType + apiIntegrationTypeName: str + def __init__(self, roleName: _Optional[str] = ..., apiIntegrationTypeId: _Optional[int] = ..., actionType: _Optional[_Union[ActionType, str]] = ..., apiIntegrationTypeName: _Optional[str] = ...) -> None: ... + +class PublicApiToken(_message.Message): + __slots__ = ("enterprisePublicApiTokenId", "enterprise_id", "name", "token", "active", "issuedDate", "expirationDate", "integrations") + ENTERPRISEPUBLICAPITOKENID_FIELD_NUMBER: _ClassVar[int] + ENTERPRISE_ID_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + TOKEN_FIELD_NUMBER: _ClassVar[int] + ACTIVE_FIELD_NUMBER: _ClassVar[int] + ISSUEDDATE_FIELD_NUMBER: _ClassVar[int] + EXPIRATIONDATE_FIELD_NUMBER: _ClassVar[int] + INTEGRATIONS_FIELD_NUMBER: _ClassVar[int] + enterprisePublicApiTokenId: int + enterprise_id: int + name: str + token: str + active: bool + issuedDate: int + expirationDate: int + integrations: _containers.RepeatedCompositeFieldContainer[Integrations] + def __init__(self, enterprisePublicApiTokenId: _Optional[int] = ..., enterprise_id: _Optional[int] = ..., name: _Optional[str] = ..., token: _Optional[str] = ..., active: bool = ..., issuedDate: _Optional[int] = ..., expirationDate: _Optional[int] = ..., integrations: _Optional[_Iterable[_Union[Integrations, _Mapping]]] = ...) -> None: ... + +class PublicApiTokenResponse(_message.Message): + __slots__ = ("enterprise_id", "name", "token", "active", "issuedDate", "expirationDate", "integrations") + ENTERPRISE_ID_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + TOKEN_FIELD_NUMBER: _ClassVar[int] + ACTIVE_FIELD_NUMBER: _ClassVar[int] + ISSUEDDATE_FIELD_NUMBER: _ClassVar[int] + EXPIRATIONDATE_FIELD_NUMBER: _ClassVar[int] + INTEGRATIONS_FIELD_NUMBER: _ClassVar[int] + enterprise_id: int + name: str + token: str + active: bool + issuedDate: int + expirationDate: int + integrations: _containers.RepeatedCompositeFieldContainer[Integrations] + def __init__(self, enterprise_id: _Optional[int] = ..., name: _Optional[str] = ..., token: _Optional[str] = ..., active: bool = ..., issuedDate: _Optional[int] = ..., expirationDate: _Optional[int] = ..., integrations: _Optional[_Iterable[_Union[Integrations, _Mapping]]] = ...) -> None: ... + +class PublicApiTokenResponseList(_message.Message): + __slots__ = ("tokens",) + TOKENS_FIELD_NUMBER: _ClassVar[int] + tokens: _containers.RepeatedCompositeFieldContainer[PublicApiTokenResponse] + def __init__(self, tokens: _Optional[_Iterable[_Union[PublicApiTokenResponse, _Mapping]]] = ...) -> None: ... + +class PublicApiTokens(_message.Message): + __slots__ = ("tokens",) + TOKENS_FIELD_NUMBER: _ClassVar[int] + tokens: _containers.RepeatedCompositeFieldContainer[PublicApiToken] + def __init__(self, tokens: _Optional[_Iterable[_Union[PublicApiToken, _Mapping]]] = ...) -> None: ... + +class RevokeTokenRequest(_message.Message): + __slots__ = ("name", "token") + NAME_FIELD_NUMBER: _ClassVar[int] + TOKEN_FIELD_NUMBER: _ClassVar[int] + name: str + token: str + def __init__(self, name: _Optional[str] = ..., token: _Optional[str] = ...) -> None: ... + +class RevokeTokenResponse(_message.Message): + __slots__ = ("message",) + MESSAGE_FIELD_NUMBER: _ClassVar[int] + message: str + def __init__(self, message: _Optional[str] = ...) -> None: ... + +class ApiIntegrationType(_message.Message): + __slots__ = ("apiIntegrationTypeId", "roleName") + APIINTEGRATIONTYPEID_FIELD_NUMBER: _ClassVar[int] + ROLENAME_FIELD_NUMBER: _ClassVar[int] + apiIntegrationTypeId: int + roleName: str + def __init__(self, apiIntegrationTypeId: _Optional[int] = ..., roleName: _Optional[str] = ...) -> None: ... + +class ApiIntegrationTypes(_message.Message): + __slots__ = ("apiIntegrationTypes",) + APIINTEGRATIONTYPES_FIELD_NUMBER: _ClassVar[int] + apiIntegrationTypes: _containers.RepeatedCompositeFieldContainer[ApiIntegrationType] + def __init__(self, apiIntegrationTypes: _Optional[_Iterable[_Union[ApiIntegrationType, _Mapping]]] = ...) -> None: ... + +class Token(_message.Message): + __slots__ = ("enterprisePublicApiTokenId",) + ENTERPRISEPUBLICAPITOKENID_FIELD_NUMBER: _ClassVar[int] + enterprisePublicApiTokenId: int + def __init__(self, enterprisePublicApiTokenId: _Optional[int] = ...) -> None: ... + +class ListPublicApiTokenRequest(_message.Message): + __slots__ = ("statusFilter", "sortByName", "nameFilter") + STATUSFILTER_FIELD_NUMBER: _ClassVar[int] + SORTBYNAME_FIELD_NUMBER: _ClassVar[int] + NAMEFILTER_FIELD_NUMBER: _ClassVar[int] + statusFilter: StatusFilter + sortByName: str + nameFilter: str + def __init__(self, statusFilter: _Optional[_Union[StatusFilter, str]] = ..., sortByName: _Optional[str] = ..., nameFilter: _Optional[str] = ...) -> None: ... diff --git a/keepercommander/proto/record_details_pb2.py b/keepercommander/proto/record_details_pb2.py index 6f12d0cbb..39bd1e041 100644 --- a/keepercommander/proto/record_details_pb2.py +++ b/keepercommander/proto/record_details_pb2.py @@ -1,10 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: record_details.proto +# Protobuf Python Version: 5.29.5 +"""Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'record_details.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/record_details_pb2.pyi b/keepercommander/proto/record_details_pb2.pyi index 50f7eefce..dcab92bf5 100644 --- a/keepercommander/proto/record_details_pb2.pyi +++ b/keepercommander/proto/record_details_pb2.pyi @@ -5,8 +5,7 @@ import pagination_pb2 as _pagination_pb2 from google.protobuf.internal import containers as _containers 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor diff --git a/keepercommander/proto/record_endpoints_pb2.py b/keepercommander/proto/record_endpoints_pb2.py index 51cae6af3..55cbe6d3b 100644 --- a/keepercommander/proto/record_endpoints_pb2.py +++ b/keepercommander/proto/record_endpoints_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: record_endpoints.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'record_endpoints.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/record_endpoints_pb2.pyi b/keepercommander/proto/record_endpoints_pb2.pyi index f022be90d..a79e7e9c4 100644 --- a/keepercommander/proto/record_endpoints_pb2.pyi +++ b/keepercommander/proto/record_endpoints_pb2.pyi @@ -3,8 +3,7 @@ import folder_pb2 as _folder_pb2 from google.protobuf.internal import containers as _containers 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor diff --git a/keepercommander/proto/record_pb2.py b/keepercommander/proto/record_pb2.py index 329a522a1..6fb04617e 100644 --- a/keepercommander/proto/record_pb2.py +++ b/keepercommander/proto/record_pb2.py @@ -2,11 +2,21 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: record.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'record.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/record_pb2.pyi b/keepercommander/proto/record_pb2.pyi index 6dfdc6fa3..19b58230f 100644 --- a/keepercommander/proto/record_pb2.pyi +++ b/keepercommander/proto/record_pb2.pyi @@ -2,8 +2,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -177,7 +176,7 @@ class RecordTypesRequest(_message.Message): user: bool enterprise: bool pam: bool - def __init__(self, standard: _Optional[bool] = ..., user: _Optional[bool] = ..., enterprise: _Optional[bool] = ..., pam: _Optional[bool] = ...) -> None: ... + def __init__(self, standard: bool = ..., user: bool = ..., enterprise: bool = ..., pam: bool = ...) -> None: ... class RecordTypesResponse(_message.Message): __slots__ = ("recordTypes", "standardCounter", "userCounter", "enterpriseCounter", "pamCounter") @@ -511,7 +510,7 @@ class File(_message.Message): fileSize: int thumbSize: int is_script: bool - def __init__(self, record_uid: _Optional[bytes] = ..., record_key: _Optional[bytes] = ..., data: _Optional[bytes] = ..., fileSize: _Optional[int] = ..., thumbSize: _Optional[int] = ..., is_script: _Optional[bool] = ...) -> None: ... + def __init__(self, record_uid: _Optional[bytes] = ..., record_key: _Optional[bytes] = ..., data: _Optional[bytes] = ..., fileSize: _Optional[int] = ..., thumbSize: _Optional[int] = ..., is_script: bool = ...) -> None: ... class FilesAddRequest(_message.Message): __slots__ = ("files", "client_time") @@ -553,7 +552,7 @@ class FilesGetRequest(_message.Message): record_uids: _containers.RepeatedScalarFieldContainer[bytes] for_thumbnails: bool emergency_access_account_owner: str - def __init__(self, record_uids: _Optional[_Iterable[bytes]] = ..., for_thumbnails: _Optional[bool] = ..., emergency_access_account_owner: _Optional[str] = ...) -> None: ... + def __init__(self, record_uids: _Optional[_Iterable[bytes]] = ..., for_thumbnails: bool = ..., emergency_access_account_owner: _Optional[str] = ...) -> None: ... class FileGetStatus(_message.Message): __slots__ = ("record_uid", "status", "url", "success_status_code", "fileKeyType") @@ -621,7 +620,7 @@ class UserPermission(_message.Message): accountUid: bytes timerNotificationType: TimerNotificationType rotateOnExpiration: bool - def __init__(self, username: _Optional[str] = ..., owner: _Optional[bool] = ..., shareAdmin: _Optional[bool] = ..., sharable: _Optional[bool] = ..., editable: _Optional[bool] = ..., awaitingApproval: _Optional[bool] = ..., expiration: _Optional[int] = ..., accountUid: _Optional[bytes] = ..., timerNotificationType: _Optional[_Union[TimerNotificationType, str]] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, username: _Optional[str] = ..., owner: bool = ..., shareAdmin: bool = ..., sharable: bool = ..., editable: bool = ..., awaitingApproval: bool = ..., expiration: _Optional[int] = ..., accountUid: _Optional[bytes] = ..., timerNotificationType: _Optional[_Union[TimerNotificationType, str]] = ..., rotateOnExpiration: bool = ...) -> None: ... class SharedFolderPermission(_message.Message): __slots__ = ("sharedFolderUid", "resharable", "editable", "revision", "expiration", "timerNotificationType", "rotateOnExpiration") @@ -639,7 +638,7 @@ class SharedFolderPermission(_message.Message): expiration: int timerNotificationType: TimerNotificationType rotateOnExpiration: bool - def __init__(self, sharedFolderUid: _Optional[bytes] = ..., resharable: _Optional[bool] = ..., editable: _Optional[bool] = ..., revision: _Optional[int] = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[TimerNotificationType, str]] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, sharedFolderUid: _Optional[bytes] = ..., resharable: bool = ..., editable: bool = ..., revision: _Optional[int] = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[TimerNotificationType, str]] = ..., rotateOnExpiration: bool = ...) -> None: ... class RecordData(_message.Message): __slots__ = ("revision", "version", "shared", "encryptedRecordData", "encryptedExtraData", "clientModifiedTime", "nonSharedData", "linkedRecordData", "fileId", "fileSize", "thumbnailSize", "recordKeyType", "recordKey", "recordUid") @@ -671,7 +670,7 @@ class RecordData(_message.Message): recordKeyType: RecordKeyType recordKey: bytes recordUid: bytes - def __init__(self, revision: _Optional[int] = ..., version: _Optional[int] = ..., shared: _Optional[bool] = ..., encryptedRecordData: _Optional[str] = ..., encryptedExtraData: _Optional[str] = ..., clientModifiedTime: _Optional[int] = ..., nonSharedData: _Optional[str] = ..., linkedRecordData: _Optional[_Iterable[_Union[RecordData, _Mapping]]] = ..., fileId: _Optional[_Iterable[bytes]] = ..., fileSize: _Optional[int] = ..., thumbnailSize: _Optional[int] = ..., recordKeyType: _Optional[_Union[RecordKeyType, str]] = ..., recordKey: _Optional[bytes] = ..., recordUid: _Optional[bytes] = ...) -> None: ... + def __init__(self, revision: _Optional[int] = ..., version: _Optional[int] = ..., shared: bool = ..., encryptedRecordData: _Optional[str] = ..., encryptedExtraData: _Optional[str] = ..., clientModifiedTime: _Optional[int] = ..., nonSharedData: _Optional[str] = ..., linkedRecordData: _Optional[_Iterable[_Union[RecordData, _Mapping]]] = ..., fileId: _Optional[_Iterable[bytes]] = ..., fileSize: _Optional[int] = ..., thumbnailSize: _Optional[int] = ..., recordKeyType: _Optional[_Union[RecordKeyType, str]] = ..., recordKey: _Optional[bytes] = ..., recordUid: _Optional[bytes] = ...) -> None: ... class RecordDataWithAccessInfo(_message.Message): __slots__ = ("recordUid", "recordData", "userPermission", "sharedFolderPermission") @@ -701,7 +700,7 @@ class IsObjectShareAdmin(_message.Message): uid: bytes isAdmin: bool objectType: CheckShareAdminObjectType - def __init__(self, uid: _Optional[bytes] = ..., isAdmin: _Optional[bool] = ..., objectType: _Optional[_Union[CheckShareAdminObjectType, str]] = ...) -> None: ... + def __init__(self, uid: _Optional[bytes] = ..., isAdmin: bool = ..., objectType: _Optional[_Union[CheckShareAdminObjectType, str]] = ...) -> None: ... class AmIShareAdmin(_message.Message): __slots__ = ("isObjectShareAdmin",) @@ -749,7 +748,7 @@ class SharedRecord(_message.Message): expiration: int timerNotificationType: TimerNotificationType rotateOnExpiration: bool - def __init__(self, toUsername: _Optional[str] = ..., recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., sharedFolderUid: _Optional[bytes] = ..., teamUid: _Optional[bytes] = ..., editable: _Optional[bool] = ..., shareable: _Optional[bool] = ..., transfer: _Optional[bool] = ..., useEccKey: _Optional[bool] = ..., removeVaultData: _Optional[bool] = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[TimerNotificationType, str]] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, toUsername: _Optional[str] = ..., recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., sharedFolderUid: _Optional[bytes] = ..., teamUid: _Optional[bytes] = ..., editable: bool = ..., shareable: bool = ..., transfer: bool = ..., useEccKey: bool = ..., removeVaultData: bool = ..., expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[TimerNotificationType, str]] = ..., rotateOnExpiration: bool = ...) -> None: ... class RecordShareUpdateResponse(_message.Message): __slots__ = ("addSharedRecordStatus", "updateSharedRecordStatus", "removeSharedRecordStatus") @@ -779,7 +778,7 @@ class GetRecordPermissionsRequest(_message.Message): ISSHAREADMIN_FIELD_NUMBER: _ClassVar[int] recordUids: _containers.RepeatedScalarFieldContainer[bytes] isShareAdmin: bool - def __init__(self, recordUids: _Optional[_Iterable[bytes]] = ..., isShareAdmin: _Optional[bool] = ...) -> None: ... + def __init__(self, recordUids: _Optional[_Iterable[bytes]] = ..., isShareAdmin: bool = ...) -> None: ... class GetRecordPermissionsResponse(_message.Message): __slots__ = ("recordPermissions",) @@ -799,7 +798,7 @@ class RecordPermission(_message.Message): canEdit: bool canShare: bool canTransfer: bool - def __init__(self, recordUid: _Optional[bytes] = ..., owner: _Optional[bool] = ..., canEdit: _Optional[bool] = ..., canShare: _Optional[bool] = ..., canTransfer: _Optional[bool] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., owner: bool = ..., canEdit: bool = ..., canShare: bool = ..., canTransfer: bool = ...) -> None: ... class GetShareObjectsRequest(_message.Message): __slots__ = ("startWith", "contains", "filtered", "sharedFolderUid") @@ -811,7 +810,7 @@ class GetShareObjectsRequest(_message.Message): contains: str filtered: bool sharedFolderUid: bytes - def __init__(self, startWith: _Optional[str] = ..., contains: _Optional[str] = ..., filtered: _Optional[bool] = ..., sharedFolderUid: _Optional[bytes] = ...) -> None: ... + def __init__(self, startWith: _Optional[str] = ..., contains: _Optional[str] = ..., filtered: bool = ..., sharedFolderUid: _Optional[bytes] = ...) -> None: ... class GetShareObjectsResponse(_message.Message): __slots__ = ("shareRelationships", "shareFamilyUsers", "shareEnterpriseUsers", "shareTeams", "shareMCTeams", "shareMCEnterpriseUsers", "shareEnterpriseNames") @@ -847,7 +846,7 @@ class ShareUser(_message.Message): isShareAdmin: bool isAdminOfSharedFolderOwner: bool userAccountUid: bytes - def __init__(self, username: _Optional[str] = ..., fullname: _Optional[str] = ..., enterpriseId: _Optional[int] = ..., status: _Optional[_Union[ShareStatus, str]] = ..., isShareAdmin: _Optional[bool] = ..., isAdminOfSharedFolderOwner: _Optional[bool] = ..., userAccountUid: _Optional[bytes] = ...) -> None: ... + def __init__(self, username: _Optional[str] = ..., fullname: _Optional[str] = ..., enterpriseId: _Optional[int] = ..., status: _Optional[_Union[ShareStatus, str]] = ..., isShareAdmin: bool = ..., isAdminOfSharedFolderOwner: bool = ..., userAccountUid: _Optional[bytes] = ...) -> None: ... class ShareTeam(_message.Message): __slots__ = ("teamname", "enterpriseId", "teamUid") @@ -883,7 +882,7 @@ class TransferRecord(_message.Message): recordUid: bytes recordKey: bytes useEccKey: bool - def __init__(self, username: _Optional[str] = ..., recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., useEccKey: _Optional[bool] = ...) -> None: ... + def __init__(self, username: _Optional[str] = ..., recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., useEccKey: bool = ...) -> None: ... class RecordsOnwershipTransferResponse(_message.Message): __slots__ = ("transferRecordStatus",) diff --git a/keepercommander/proto/record_sharing_pb2.py b/keepercommander/proto/record_sharing_pb2.py index 00b1bd3a3..a7356b30d 100644 --- a/keepercommander/proto/record_sharing_pb2.py +++ b/keepercommander/proto/record_sharing_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: record_sharing.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'record_sharing.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/record_sharing_pb2.pyi b/keepercommander/proto/record_sharing_pb2.pyi index 51887fd6b..c5f25e8db 100644 --- a/keepercommander/proto/record_sharing_pb2.pyi +++ b/keepercommander/proto/record_sharing_pb2.pyi @@ -5,8 +5,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -51,7 +50,7 @@ class Permissions(_message.Message): recordKey: bytes useEccKey: bool rules: _folder_pb2.RecordAccessData - def __init__(self, recipientUid: _Optional[bytes] = ..., recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., useEccKey: _Optional[bool] = ..., rules: _Optional[_Union[_folder_pb2.RecordAccessData, _Mapping]] = ...) -> None: ... + def __init__(self, recipientUid: _Optional[bytes] = ..., recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., useEccKey: bool = ..., rules: _Optional[_Union[_folder_pb2.RecordAccessData, _Mapping]] = ...) -> None: ... class Response(_message.Message): __slots__ = ("createdSharingStatus", "updatedSharingStatus", "revokedSharingStatus") @@ -93,4 +92,4 @@ class RecordSharingState(_message.Message): isDirectlyShared: bool isIndirectlyShared: bool isShared: bool - def __init__(self, recordUid: _Optional[bytes] = ..., isDirectlyShared: _Optional[bool] = ..., isIndirectlyShared: _Optional[bool] = ..., isShared: _Optional[bool] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., isDirectlyShared: bool = ..., isIndirectlyShared: bool = ..., isShared: bool = ...) -> None: ... diff --git a/keepercommander/proto/remove_pb2.py b/keepercommander/proto/remove_pb2.py index f5a48130c..3e2d50dd0 100644 --- a/keepercommander/proto/remove_pb2.py +++ b/keepercommander/proto/remove_pb2.py @@ -2,12 +2,21 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: remove.proto -# Protobuf Python Version: 6.33.4 +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'remove.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/remove_pb2.pyi b/keepercommander/proto/remove_pb2.pyi index c88bbfd0b..093275ccb 100644 --- a/keepercommander/proto/remove_pb2.pyi +++ b/keepercommander/proto/remove_pb2.pyi @@ -3,8 +3,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor diff --git a/keepercommander/proto/rmd_pb2.py b/keepercommander/proto/rmd_pb2.py index 48b23f439..e18861859 100644 --- a/keepercommander/proto/rmd_pb2.py +++ b/keepercommander/proto/rmd_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: rmd.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'rmd.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,13 +29,13 @@ _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'rmd_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\003RMD' _globals['_SECURITYBENCHMARK']._serialized_start=1552 - _globals['_SECURITYBENCHMARK']._serialized_end=2167 - _globals['_SECURITYBENCHMARKSTATUS']._serialized_start=2169 - _globals['_SECURITYBENCHMARKSTATUS']._serialized_end=2250 + _globals['_SECURITYBENCHMARK']._serialized_end=2206 + _globals['_SECURITYBENCHMARKSTATUS']._serialized_start=2208 + _globals['_SECURITYBENCHMARKSTATUS']._serialized_end=2289 _globals['_ENTERPRISESTAT']._serialized_start=18 _globals['_ENTERPRISESTAT']._serialized_end=86 _globals['_ENTERPRISESTATDETAIL']._serialized_start=88 diff --git a/keepercommander/proto/rmd_pb2.pyi b/keepercommander/proto/rmd_pb2.pyi index c7c211e9a..02859c534 100644 --- a/keepercommander/proto/rmd_pb2.pyi +++ b/keepercommander/proto/rmd_pb2.pyi @@ -7,7 +7,7 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class SecurityBenchmark(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () SB_INVALID: _ClassVar[SecurityBenchmark] SB_DEPLOY_ACROSS_ENTIRE_ORGANIZATION: _ClassVar[SecurityBenchmark] SB_PREVENT_INSTALLATION_OF_UNTRUSTED_EXTENSIONS: _ClassVar[SecurityBenchmark] @@ -26,7 +26,7 @@ class SecurityBenchmark(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): SB_ENFORCE_LEAST_PRIVILEGE_POLICY: _ClassVar[SecurityBenchmark] class SecurityBenchmarkStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () INVALID: _ClassVar[SecurityBenchmarkStatus] RESOLVED: _ClassVar[SecurityBenchmarkStatus] IGNORED: _ClassVar[SecurityBenchmarkStatus] @@ -53,7 +53,7 @@ IGNORED: SecurityBenchmarkStatus UNRESOLVED: SecurityBenchmarkStatus class EnterpriseStat(_message.Message): - __slots__ = ["usersLoggedRecent", "usersHasRecords"] + __slots__ = ("usersLoggedRecent", "usersHasRecords") USERSLOGGEDRECENT_FIELD_NUMBER: _ClassVar[int] USERSHASRECORDS_FIELD_NUMBER: _ClassVar[int] usersLoggedRecent: int @@ -61,7 +61,7 @@ class EnterpriseStat(_message.Message): def __init__(self, usersLoggedRecent: _Optional[int] = ..., usersHasRecords: _Optional[int] = ...) -> None: ... class EnterpriseStatDetail(_message.Message): - __slots__ = ["enterpriseUserId", "lastLoggedIn", "hasRecords"] + __slots__ = ("enterpriseUserId", "lastLoggedIn", "hasRecords") ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] LASTLOGGEDIN_FIELD_NUMBER: _ClassVar[int] HASRECORDS_FIELD_NUMBER: _ClassVar[int] @@ -71,7 +71,7 @@ class EnterpriseStatDetail(_message.Message): def __init__(self, enterpriseUserId: _Optional[int] = ..., lastLoggedIn: _Optional[int] = ..., hasRecords: bool = ...) -> None: ... class EnterpriseStatContinuationToken(_message.Message): - __slots__ = ["lastUpdated", "enterpriseUserId"] + __slots__ = ("lastUpdated", "enterpriseUserId") LASTUPDATED_FIELD_NUMBER: _ClassVar[int] ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] lastUpdated: int @@ -79,7 +79,7 @@ class EnterpriseStatContinuationToken(_message.Message): def __init__(self, lastUpdated: _Optional[int] = ..., enterpriseUserId: _Optional[int] = ...) -> None: ... class EnterpriseStatDetailsRequest(_message.Message): - __slots__ = ["lastUpdated", "continuationToken"] + __slots__ = ("lastUpdated", "continuationToken") LASTUPDATED_FIELD_NUMBER: _ClassVar[int] CONTINUATIONTOKEN_FIELD_NUMBER: _ClassVar[int] lastUpdated: int @@ -87,7 +87,7 @@ class EnterpriseStatDetailsRequest(_message.Message): def __init__(self, lastUpdated: _Optional[int] = ..., continuationToken: _Optional[_Union[EnterpriseStatContinuationToken, _Mapping]] = ...) -> None: ... class EnterpriseStatDetailsResponse(_message.Message): - __slots__ = ["enterpriseStatDetails", "lastUpdated", "continuationToken", "hasMore"] + __slots__ = ("enterpriseStatDetails", "lastUpdated", "continuationToken", "hasMore") ENTERPRISESTATDETAILS_FIELD_NUMBER: _ClassVar[int] LASTUPDATED_FIELD_NUMBER: _ClassVar[int] CONTINUATIONTOKEN_FIELD_NUMBER: _ClassVar[int] @@ -99,7 +99,7 @@ class EnterpriseStatDetailsResponse(_message.Message): def __init__(self, enterpriseStatDetails: _Optional[_Iterable[_Union[EnterpriseStatDetail, _Mapping]]] = ..., lastUpdated: _Optional[int] = ..., continuationToken: _Optional[_Union[EnterpriseStatContinuationToken, _Mapping]] = ..., hasMore: bool = ...) -> None: ... class SecurityAlertsSummary(_message.Message): - __slots__ = ["auditEventTypeId", "currentCount", "currentUserCount", "previousCount", "previousUserCount"] + __slots__ = ("auditEventTypeId", "currentCount", "currentUserCount", "previousCount", "previousUserCount") AUDITEVENTTYPEID_FIELD_NUMBER: _ClassVar[int] CURRENTCOUNT_FIELD_NUMBER: _ClassVar[int] CURRENTUSERCOUNT_FIELD_NUMBER: _ClassVar[int] @@ -113,13 +113,13 @@ class SecurityAlertsSummary(_message.Message): def __init__(self, auditEventTypeId: _Optional[int] = ..., currentCount: _Optional[int] = ..., currentUserCount: _Optional[int] = ..., previousCount: _Optional[int] = ..., previousUserCount: _Optional[int] = ...) -> None: ... class SecurityAlertsSummaryResponse(_message.Message): - __slots__ = ["securityAlertsSummary"] + __slots__ = ("securityAlertsSummary",) SECURITYALERTSSUMMARY_FIELD_NUMBER: _ClassVar[int] securityAlertsSummary: _containers.RepeatedCompositeFieldContainer[SecurityAlertsSummary] def __init__(self, securityAlertsSummary: _Optional[_Iterable[_Union[SecurityAlertsSummary, _Mapping]]] = ...) -> None: ... class SecurityAlertsDetailRequest(_message.Message): - __slots__ = ["auditEventTypeId", "continuationToken"] + __slots__ = ("auditEventTypeId", "continuationToken") AUDITEVENTTYPEID_FIELD_NUMBER: _ClassVar[int] CONTINUATIONTOKEN_FIELD_NUMBER: _ClassVar[int] auditEventTypeId: int @@ -127,7 +127,7 @@ class SecurityAlertsDetailRequest(_message.Message): def __init__(self, auditEventTypeId: _Optional[int] = ..., continuationToken: _Optional[int] = ...) -> None: ... class SecurityAlertsDetail(_message.Message): - __slots__ = ["enterpriseUserId", "currentCount", "previousCount", "lastOccurrence"] + __slots__ = ("enterpriseUserId", "currentCount", "previousCount", "lastOccurrence") ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] CURRENTCOUNT_FIELD_NUMBER: _ClassVar[int] PREVIOUSCOUNT_FIELD_NUMBER: _ClassVar[int] @@ -139,7 +139,7 @@ class SecurityAlertsDetail(_message.Message): def __init__(self, enterpriseUserId: _Optional[int] = ..., currentCount: _Optional[int] = ..., previousCount: _Optional[int] = ..., lastOccurrence: _Optional[int] = ...) -> None: ... class SecurityAlertsDetailResponse(_message.Message): - __slots__ = ["securityAlertDetails", "hasMore", "continuationToken"] + __slots__ = ("securityAlertDetails", "hasMore", "continuationToken") SECURITYALERTDETAILS_FIELD_NUMBER: _ClassVar[int] HASMORE_FIELD_NUMBER: _ClassVar[int] CONTINUATIONTOKEN_FIELD_NUMBER: _ClassVar[int] @@ -149,7 +149,7 @@ class SecurityAlertsDetailResponse(_message.Message): def __init__(self, securityAlertDetails: _Optional[_Iterable[_Union[SecurityAlertsDetail, _Mapping]]] = ..., hasMore: bool = ..., continuationToken: _Optional[int] = ...) -> None: ... class EnterpriseSecurityBenchmark(_message.Message): - __slots__ = ["securityBenchmark", "securityBenchmarkStatus", "lastUpdated", "autoResolve"] + __slots__ = ("securityBenchmark", "securityBenchmarkStatus", "lastUpdated", "autoResolve") SECURITYBENCHMARK_FIELD_NUMBER: _ClassVar[int] SECURITYBENCHMARKSTATUS_FIELD_NUMBER: _ClassVar[int] LASTUPDATED_FIELD_NUMBER: _ClassVar[int] @@ -161,13 +161,13 @@ class EnterpriseSecurityBenchmark(_message.Message): def __init__(self, securityBenchmark: _Optional[_Union[SecurityBenchmark, str]] = ..., securityBenchmarkStatus: _Optional[_Union[SecurityBenchmarkStatus, str]] = ..., lastUpdated: _Optional[int] = ..., autoResolve: bool = ...) -> None: ... class SetSecurityBenchmarksRequest(_message.Message): - __slots__ = ["enterpriseSecurityBenchmarks"] + __slots__ = ("enterpriseSecurityBenchmarks",) ENTERPRISESECURITYBENCHMARKS_FIELD_NUMBER: _ClassVar[int] enterpriseSecurityBenchmarks: _containers.RepeatedCompositeFieldContainer[EnterpriseSecurityBenchmark] def __init__(self, enterpriseSecurityBenchmarks: _Optional[_Iterable[_Union[EnterpriseSecurityBenchmark, _Mapping]]] = ...) -> None: ... class GetSecurityBenchmarksResponse(_message.Message): - __slots__ = ["enterpriseSecurityBenchmarks"] + __slots__ = ("enterpriseSecurityBenchmarks",) ENTERPRISESECURITYBENCHMARKS_FIELD_NUMBER: _ClassVar[int] enterpriseSecurityBenchmarks: _containers.RepeatedCompositeFieldContainer[EnterpriseSecurityBenchmark] def __init__(self, enterpriseSecurityBenchmarks: _Optional[_Iterable[_Union[EnterpriseSecurityBenchmark, _Mapping]]] = ...) -> None: ... diff --git a/keepercommander/proto/router_pb2.py b/keepercommander/proto/router_pb2.py index 16b4301dc..1bfa79bb5 100644 --- a/keepercommander/proto/router_pb2.py +++ b/keepercommander/proto/router_pb2.py @@ -1,20 +1,33 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: router.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'router.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -from . import pam_pb2 as pam__pb2 +from . import pam_pb2 as pam__pb2 +from . import APIRequest_pb2 as APIRequest__pb2 +from . import folder_pb2 as folder__pb2 -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') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0crouter.proto\x12\x06Router\x1a\tpam.proto\x1a\x10\x41PIRequest.proto\x1a\x0c\x66older.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\"\xac\x03\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\x12\x1b\n\x0eupdateServices\x18\r \x01(\x08H\x01\x88\x01\x01\x12+\n\x10serviceResources\x18\x0e \x01(\x0b\x32\x0c.PAM.UidListH\x02\x88\x01\x01\x42\x14\n\x12_saasConfigurationB\x11\n\x0f_updateServicesB\x13\n\x11_serviceResources\"<\n\x17UserRecordAccessRequest\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\"w\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\x12\x14\n\x0cisShareAdmin\x18\x03 \x01(\x08\"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\"H\n\x1dUserSharedFolderAccessRequest\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x17\n\x0fsharedFolderUid\x18\x02 \x03(\x0c\"i\n\x1eUserSharedFolderAccessResponse\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12.\n\x0e\x61\x63\x63\x65ssRoleType\x18\x02 \x01(\x0e\x32\x16.Folder.AccessRoleType\"\\\n\x1fUserSharedFolderAccessResponses\x12\x39\n\tresponses\x18\x01 \x03(\x0b\x32&.Router.UserSharedFolderAccessResponse\"A\n\x1cUserFolderPermissionsRequest\x12\x0e\n\x06userId\x18\x01 \x01(\x05\x12\x11\n\tfolderUid\x18\x02 \x03(\x0c\"b\n\x1dUserFolderPermissionsResponse\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12.\n\x0bpermissions\x18\x02 \x01(\x0b\x32\x19.Folder.FolderPermissions\"Z\n\x1eUserFolderPermissionsResponses\x12\x38\n\tresponses\x18\x01 \x03(\x0b\x32%.Router.UserFolderPermissionsResponse\"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\x12\x19\n\x0cidpConfigUid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08\x61\x64minUid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x42\x0f\n\r_idpConfigUidB\x0b\n\t_adminUid\"\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\"[\n\x1c\x43reateEphemeralSecretRequest\x12\x17\n\x0f\x65ncryptedSecret\x18\x01 \x01(\x0c\x12\x15\n\rsecretKeyHash\x18\x02 \x01(\x0c\x12\x0b\n\x03ttl\x18\x03 \x01(\x03*\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) @@ -22,80 +35,94 @@ 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=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 + _globals['_ROUTERRESPONSECODE']._serialized_start=4895 + _globals['_ROUTERRESPONSECODE']._serialized_end=5175 + _globals['_ROUTERROTATIONSTATUS']._serialized_start=5177 + _globals['_ROUTERROTATIONSTATUS']._serialized_end=5284 + _globals['_USERRECORDACCESSLEVEL']._serialized_start=5286 + _globals['_USERRECORDACCESSLEVEL']._serialized_end=5411 + _globals['_SERVICETYPE']._serialized_start=5413 + _globals['_SERVICETYPE']._serialized_end=5459 + _globals['_ROUTERRESPONSE']._serialized_start=67 + _globals['_ROUTERRESPONSE']._serialized_end=181 + _globals['_ROUTERCONTROLLERMESSAGE']._serialized_start=184 + _globals['_ROUTERCONTROLLERMESSAGE']._serialized_end=359 + _globals['_ROUTERUSERAUTH']._serialized_start=362 + _globals['_ROUTERUSERAUTH']._serialized_end=643 + _globals['_ROUTERDEVICEAUTH']._serialized_start=646 + _globals['_ROUTERDEVICEAUTH']._serialized_end=931 + _globals['_ROUTERRECORDROTATION']._serialized_start=934 + _globals['_ROUTERRECORDROTATION']._serialized_end=1065 + _globals['_ROUTERRECORDROTATIONSREQUEST']._serialized_start=1067 + _globals['_ROUTERRECORDROTATIONSREQUEST']._serialized_end=1136 + _globals['_ROUTERRECORDROTATIONSRESPONSE']._serialized_start=1138 + _globals['_ROUTERRECORDROTATIONSRESPONSE']._serialized_end=1235 + _globals['_ROUTERROTATIONINFO']._serialized_start=1238 + _globals['_ROUTERROTATIONINFO']._serialized_end=1475 + _globals['_ROUTERRECORDROTATIONREQUEST']._serialized_start=1478 + _globals['_ROUTERRECORDROTATIONREQUEST']._serialized_end=1906 + _globals['_USERRECORDACCESSREQUEST']._serialized_start=1908 + _globals['_USERRECORDACCESSREQUEST']._serialized_end=1968 + _globals['_USERRECORDACCESSRESPONSE']._serialized_start=1970 + _globals['_USERRECORDACCESSRESPONSE']._serialized_end=2089 + _globals['_USERRECORDACCESSREQUESTS']._serialized_start=2091 + _globals['_USERRECORDACCESSREQUESTS']._serialized_end=2168 + _globals['_USERRECORDACCESSRESPONSES']._serialized_start=2170 + _globals['_USERRECORDACCESSRESPONSES']._serialized_end=2250 + _globals['_USERSHAREDFOLDERACCESSREQUEST']._serialized_start=2252 + _globals['_USERSHAREDFOLDERACCESSREQUEST']._serialized_end=2324 + _globals['_USERSHAREDFOLDERACCESSRESPONSE']._serialized_start=2326 + _globals['_USERSHAREDFOLDERACCESSRESPONSE']._serialized_end=2431 + _globals['_USERSHAREDFOLDERACCESSRESPONSES']._serialized_start=2433 + _globals['_USERSHAREDFOLDERACCESSRESPONSES']._serialized_end=2525 + _globals['_USERFOLDERPERMISSIONSREQUEST']._serialized_start=2527 + _globals['_USERFOLDERPERMISSIONSREQUEST']._serialized_end=2592 + _globals['_USERFOLDERPERMISSIONSRESPONSE']._serialized_start=2594 + _globals['_USERFOLDERPERMISSIONSRESPONSE']._serialized_end=2692 + _globals['_USERFOLDERPERMISSIONSRESPONSES']._serialized_start=2694 + _globals['_USERFOLDERPERMISSIONSRESPONSES']._serialized_end=2784 + _globals['_ROTATIONSCHEDULE']._serialized_start=2786 + _globals['_ROTATIONSCHEDULE']._serialized_end=2842 + _globals['_APICALLBACKREQUEST']._serialized_start=2845 + _globals['_APICALLBACKREQUEST']._serialized_end=2989 + _globals['_APICALLBACKSCHEDULE']._serialized_start=2991 + _globals['_APICALLBACKSCHEDULE']._serialized_end=3044 + _globals['_ROUTERSCHEDULEDACTIONS']._serialized_start=3046 + _globals['_ROUTERSCHEDULEDACTIONS']._serialized_end=3110 + _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_start=3112 + _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_end=3201 + _globals['_CONNECTIONPARAMETERS']._serialized_start=3204 + _globals['_CONNECTIONPARAMETERS']._serialized_end=3337 + _globals['_VALIDATECONNECTIONSREQUEST']._serialized_start=3339 + _globals['_VALIDATECONNECTIONSREQUEST']._serialized_end=3418 + _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_start=3420 + _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_end=3494 + _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_start=3496 + _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_end=3589 + _globals['_GETENFORCEMENTREQUEST']._serialized_start=3591 + _globals['_GETENFORCEMENTREQUEST']._serialized_end=3640 + _globals['_ENFORCEMENTTYPE']._serialized_start=3642 + _globals['_ENFORCEMENTTYPE']._serialized_end=3701 + _globals['_GETENFORCEMENTRESPONSE']._serialized_start=3703 + _globals['_GETENFORCEMENTRESPONSE']._serialized_end=3815 + _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_start=3817 + _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_end=3896 + _globals['_GETPEDMADMININFORESPONSE']._serialized_start=3898 + _globals['_GETPEDMADMININFORESPONSE']._serialized_end=3970 + _globals['_PAMNETWORKSETTINGS']._serialized_start=3972 + _globals['_PAMNETWORKSETTINGS']._serialized_end=4097 + _globals['_PAMNETWORKCONFIGURATIONREQUEST']._serialized_start=4100 + _globals['_PAMNETWORKCONFIGURATIONREQUEST']._serialized_end=4328 + _globals['_PAMDISCOVERYRULESSETREQUEST']._serialized_start=4330 + _globals['_PAMDISCOVERYRULESSETREQUEST']._serialized_end=4412 + _globals['_ROUTER2FAVALIDATEREQUEST']._serialized_start=4414 + _globals['_ROUTER2FAVALIDATEREQUEST']._serialized_end=4502 + _globals['_ROUTER2FASENDPUSHREQUEST']._serialized_start=4504 + _globals['_ROUTER2FASENDPUSHREQUEST']._serialized_end=4630 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGEREQUEST']._serialized_start=4632 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGEREQUEST']._serialized_end=4717 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGERESPONSE']._serialized_start=4719 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGERESPONSE']._serialized_end=4799 + _globals['_CREATEEPHEMERALSECRETREQUEST']._serialized_start=4801 + _globals['_CREATEEPHEMERALSECRETREQUEST']._serialized_end=4892 # @@protoc_insertion_point(module_scope) diff --git a/keepercommander/proto/router_pb2.pyi b/keepercommander/proto/router_pb2.pyi index 486c4628c..c17cc8314 100644 --- a/keepercommander/proto/router_pb2.pyi +++ b/keepercommander/proto/router_pb2.pyi @@ -1,11 +1,11 @@ import pam_pb2 as _pam_pb2 import APIRequest_pb2 as _APIRequest_pb2 +import folder_pb2 as _folder_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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -91,7 +91,7 @@ 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: _Optional[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: bool = ..., payload: _Optional[bytes] = ..., timeout: _Optional[int] = ...) -> None: ... class RouterUserAuth(_message.Message): __slots__ = ("transmissionKey", "sessionToken", "userId", "enterpriseUserId", "deviceName", "deviceToken", "clientVersionId", "needUsername", "username", "mspEnterpriseId", "isPedmAdmin", "mcEnterpriseId") @@ -119,7 +119,7 @@ class RouterUserAuth(_message.Message): mspEnterpriseId: int 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: ... + 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] = ..., isPedmAdmin: bool = ..., mcEnterpriseId: _Optional[int] = ...) -> None: ... class RouterDeviceAuth(_message.Message): __slots__ = ("clientId", "clientVersion", "signature", "enterpriseId", "nodeId", "deviceName", "deviceToken", "controllerName", "controllerUid", "ownerUser", "challenge", "ownerId", "maxInstanceCount") @@ -163,7 +163,7 @@ 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: _Optional[bool] = ...) -> None: ... + def __init__(self, recordUid: _Optional[bytes] = ..., configurationUid: _Optional[bytes] = ..., controllerUid: _Optional[bytes] = ..., resourceUid: _Optional[bytes] = ..., noSchedule: bool = ...) -> None: ... class RouterRecordRotationsRequest(_message.Message): __slots__ = ("enterpriseId", "records") @@ -179,7 +179,7 @@ class RouterRecordRotationsResponse(_message.Message): HASMORE_FIELD_NUMBER: _ClassVar[int] rotations: _containers.RepeatedCompositeFieldContainer[RouterRecordRotation] hasMore: bool - def __init__(self, rotations: _Optional[_Iterable[_Union[RouterRecordRotation, _Mapping]]] = ..., hasMore: _Optional[bool] = ...) -> None: ... + def __init__(self, rotations: _Optional[_Iterable[_Union[RouterRecordRotation, _Mapping]]] = ..., hasMore: bool = ...) -> None: ... class RouterRotationInfo(_message.Message): __slots__ = ("status", "configurationUid", "resourceUid", "nodeId", "controllerUid", "controllerName", "scriptName", "pwdComplexity", "disabled") @@ -201,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: _Optional[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: bool = ...) -> None: ... class RouterRecordRotationRequest(_message.Message): - __slots__ = ("recordUid", "revision", "configurationUid", "resourceUid", "schedule", "enterpriseUserId", "pwdComplexity", "disabled", "remoteAddress", "clientVersionId", "noop", "saasConfiguration") + __slots__ = ("recordUid", "revision", "configurationUid", "resourceUid", "schedule", "enterpriseUserId", "pwdComplexity", "disabled", "remoteAddress", "clientVersionId", "noop", "saasConfiguration", "updateServices", "serviceResources") RECORDUID_FIELD_NUMBER: _ClassVar[int] REVISION_FIELD_NUMBER: _ClassVar[int] CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] @@ -217,6 +217,8 @@ class RouterRecordRotationRequest(_message.Message): CLIENTVERSIONID_FIELD_NUMBER: _ClassVar[int] NOOP_FIELD_NUMBER: _ClassVar[int] SAASCONFIGURATION_FIELD_NUMBER: _ClassVar[int] + UPDATESERVICES_FIELD_NUMBER: _ClassVar[int] + SERVICERESOURCES_FIELD_NUMBER: _ClassVar[int] recordUid: bytes revision: int configurationUid: bytes @@ -229,7 +231,9 @@ class RouterRecordRotationRequest(_message.Message): clientVersionId: int noop: bool 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: ... + updateServices: bool + serviceResources: _pam_pb2.UidList + 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 = ..., saasConfiguration: _Optional[bytes] = ..., updateServices: bool = ..., serviceResources: _Optional[_Union[_pam_pb2.UidList, _Mapping]] = ...) -> None: ... class UserRecordAccessRequest(_message.Message): __slots__ = ("userId", "recordUid") @@ -240,12 +244,14 @@ class UserRecordAccessRequest(_message.Message): def __init__(self, userId: _Optional[int] = ..., recordUid: _Optional[bytes] = ...) -> None: ... class UserRecordAccessResponse(_message.Message): - __slots__ = ("recordUid", "accessLevel") + __slots__ = ("recordUid", "accessLevel", "isShareAdmin") RECORDUID_FIELD_NUMBER: _ClassVar[int] ACCESSLEVEL_FIELD_NUMBER: _ClassVar[int] + ISSHAREADMIN_FIELD_NUMBER: _ClassVar[int] recordUid: bytes accessLevel: UserRecordAccessLevel - def __init__(self, recordUid: _Optional[bytes] = ..., accessLevel: _Optional[_Union[UserRecordAccessLevel, str]] = ...) -> None: ... + isShareAdmin: bool + def __init__(self, recordUid: _Optional[bytes] = ..., accessLevel: _Optional[_Union[UserRecordAccessLevel, str]] = ..., isShareAdmin: bool = ...) -> None: ... class UserRecordAccessRequests(_message.Message): __slots__ = ("requests",) @@ -259,6 +265,50 @@ class UserRecordAccessResponses(_message.Message): responses: _containers.RepeatedCompositeFieldContainer[UserRecordAccessResponse] def __init__(self, responses: _Optional[_Iterable[_Union[UserRecordAccessResponse, _Mapping]]] = ...) -> None: ... +class UserSharedFolderAccessRequest(_message.Message): + __slots__ = ("userId", "sharedFolderUid") + USERID_FIELD_NUMBER: _ClassVar[int] + SHAREDFOLDERUID_FIELD_NUMBER: _ClassVar[int] + userId: int + sharedFolderUid: _containers.RepeatedScalarFieldContainer[bytes] + def __init__(self, userId: _Optional[int] = ..., sharedFolderUid: _Optional[_Iterable[bytes]] = ...) -> None: ... + +class UserSharedFolderAccessResponse(_message.Message): + __slots__ = ("sharedFolderUid", "accessRoleType") + SHAREDFOLDERUID_FIELD_NUMBER: _ClassVar[int] + ACCESSROLETYPE_FIELD_NUMBER: _ClassVar[int] + sharedFolderUid: bytes + accessRoleType: _folder_pb2.AccessRoleType + def __init__(self, sharedFolderUid: _Optional[bytes] = ..., accessRoleType: _Optional[_Union[_folder_pb2.AccessRoleType, str]] = ...) -> None: ... + +class UserSharedFolderAccessResponses(_message.Message): + __slots__ = ("responses",) + RESPONSES_FIELD_NUMBER: _ClassVar[int] + responses: _containers.RepeatedCompositeFieldContainer[UserSharedFolderAccessResponse] + def __init__(self, responses: _Optional[_Iterable[_Union[UserSharedFolderAccessResponse, _Mapping]]] = ...) -> None: ... + +class UserFolderPermissionsRequest(_message.Message): + __slots__ = ("userId", "folderUid") + USERID_FIELD_NUMBER: _ClassVar[int] + FOLDERUID_FIELD_NUMBER: _ClassVar[int] + userId: int + folderUid: _containers.RepeatedScalarFieldContainer[bytes] + def __init__(self, userId: _Optional[int] = ..., folderUid: _Optional[_Iterable[bytes]] = ...) -> None: ... + +class UserFolderPermissionsResponse(_message.Message): + __slots__ = ("folderUid", "permissions") + FOLDERUID_FIELD_NUMBER: _ClassVar[int] + PERMISSIONS_FIELD_NUMBER: _ClassVar[int] + folderUid: bytes + permissions: _folder_pb2.FolderPermissions + def __init__(self, folderUid: _Optional[bytes] = ..., permissions: _Optional[_Union[_folder_pb2.FolderPermissions, _Mapping]] = ...) -> None: ... + +class UserFolderPermissionsResponses(_message.Message): + __slots__ = ("responses",) + RESPONSES_FIELD_NUMBER: _ClassVar[int] + responses: _containers.RepeatedCompositeFieldContainer[UserFolderPermissionsResponse] + def __init__(self, responses: _Optional[_Iterable[_Union[UserFolderPermissionsResponse, _Mapping]]] = ...) -> None: ... + class RotationSchedule(_message.Message): __slots__ = ("record_uid", "schedule") RECORD_UID_FIELD_NUMBER: _ClassVar[int] @@ -357,7 +407,7 @@ class GetEnforcementResponse(_message.Message): enforcementTypes: _containers.RepeatedCompositeFieldContainer[EnforcementType] addOnIds: _containers.RepeatedScalarFieldContainer[int] isInTrial: bool - def __init__(self, enforcementTypes: _Optional[_Iterable[_Union[EnforcementType, _Mapping]]] = ..., addOnIds: _Optional[_Iterable[int]] = ..., isInTrial: _Optional[bool] = ...) -> None: ... + def __init__(self, enforcementTypes: _Optional[_Iterable[_Union[EnforcementType, _Mapping]]] = ..., addOnIds: _Optional[_Iterable[int]] = ..., isInTrial: bool = ...) -> None: ... class PEDMTOTPValidateRequest(_message.Message): __slots__ = ("username", "enterpriseId", "code") @@ -375,13 +425,17 @@ class GetPEDMAdminInfoResponse(_message.Message): PEDMADDONACTIVE_FIELD_NUMBER: _ClassVar[int] isPedmAdmin: bool pedmAddonActive: bool - def __init__(self, isPedmAdmin: _Optional[bool] = ..., pedmAddonActive: _Optional[bool] = ...) -> None: ... + def __init__(self, isPedmAdmin: bool = ..., pedmAddonActive: bool = ...) -> None: ... class PAMNetworkSettings(_message.Message): - __slots__ = ("allowedSettings",) + __slots__ = ("allowedSettings", "idpConfigUid", "adminUid") ALLOWEDSETTINGS_FIELD_NUMBER: _ClassVar[int] + IDPCONFIGUID_FIELD_NUMBER: _ClassVar[int] + ADMINUID_FIELD_NUMBER: _ClassVar[int] allowedSettings: bytes - def __init__(self, allowedSettings: _Optional[bytes] = ...) -> None: ... + idpConfigUid: bytes + adminUid: bytes + def __init__(self, allowedSettings: _Optional[bytes] = ..., idpConfigUid: _Optional[bytes] = ..., adminUid: _Optional[bytes] = ...) -> None: ... class PAMNetworkConfigurationRequest(_message.Message): __slots__ = ("recordUid", "networkSettings", "resources", "rotations") @@ -440,3 +494,13 @@ class Router2FAGetWebAuthnChallengeResponse(_message.Message): challenge: str capabilities: _containers.RepeatedScalarFieldContainer[str] def __init__(self, challenge: _Optional[str] = ..., capabilities: _Optional[_Iterable[str]] = ...) -> None: ... + +class CreateEphemeralSecretRequest(_message.Message): + __slots__ = ("encryptedSecret", "secretKeyHash", "ttl") + ENCRYPTEDSECRET_FIELD_NUMBER: _ClassVar[int] + SECRETKEYHASH_FIELD_NUMBER: _ClassVar[int] + TTL_FIELD_NUMBER: _ClassVar[int] + encryptedSecret: bytes + secretKeyHash: bytes + ttl: int + def __init__(self, encryptedSecret: _Optional[bytes] = ..., secretKeyHash: _Optional[bytes] = ..., ttl: _Optional[int] = ...) -> None: ... diff --git a/keepercommander/proto/ssocloud_pb2.py b/keepercommander/proto/ssocloud_pb2.py index 6e444692a..1ba2362c4 100644 --- a/keepercommander/proto/ssocloud_pb2.py +++ b/keepercommander/proto/ssocloud_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: ssocloud.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'ssocloud.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,22 +25,22 @@ from . import APIRequest_pb2 as APIRequest__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0essocloud.proto\x12\x08SsoCloud\x1a\x10\x41PIRequest.proto\"\xd5\x01\n\x14SsoCloudSettingValue\x12\x11\n\tsettingId\x18\x01 \x01(\x04\x12\x13\n\x0bsettingName\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\r\n\x05value\x18\x04 \x01(\t\x12%\n\tvalueType\x18\x05 \x01(\x0e\x32\x12.SsoCloud.DataType\x12\x14\n\x0clastModified\x18\x07 \x01(\t\x12\x12\n\nisFromFile\x18\x08 \x01(\x08\x12\x12\n\nisEditable\x18\t \x01(\x08\x12\x12\n\nisRequired\x18\n \x01(\x08\"\x89\x01\n\x15SsoCloudSettingAction\x12\x11\n\tsettingId\x18\x01 \x01(\x04\x12\x13\n\x0bsettingName\x18\x02 \x01(\t\x12\x39\n\toperation\x18\x03 \x01(\x0e\x32&.SsoCloud.SsoCloudSettingOperationType\x12\r\n\x05value\x18\x04 \x01(\t\"\xe1\x01\n\x1cSsoCloudConfigurationRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoSpConfigurationId\x18\x02 \x01(\x04\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x37\n\x13ssoAuthProtocolType\x18\x04 \x01(\x0e\x32\x1a.SsoCloud.AuthProtocolType\x12>\n\x15ssoCloudSettingAction\x18\x05 \x03(\x0b\x32\x1f.SsoCloud.SsoCloudSettingAction\"d\n\x13SsoSharedConfigItem\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoServiceProviderId\x18\x02 \x01(\x04\x12\x11\n\tssoNodeId\x18\x03 \x01(\x04\"\xad\x02\n\x1dSsoCloudConfigurationResponse\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoSpConfigurationId\x18\x02 \x01(\x04\x12\x14\n\x0c\x65nterpriseId\x18\x03 \x01(\x04\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x10\n\x08protocol\x18\x05 \x01(\t\x12\x14\n\x0clastModified\x18\x06 \x01(\t\x12<\n\x14ssoCloudSettingValue\x18\x07 \x03(\x0b\x32\x1e.SsoCloud.SsoCloudSettingValue\x12\x10\n\x08isShared\x18\x08 \x01(\x08\x12\x34\n\rsharedConfigs\x18\t \x03(\x0b\x32\x1d.SsoCloud.SsoSharedConfigItem\"E\n\x11SsoIdpTypeRequest\x12\x14\n\x0cssoIdpTypeId\x18\x01 \x01(\r\x12\x0b\n\x03tag\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\"F\n\x12SsoIdpTypeResponse\x12\x14\n\x0cssoIdpTypeId\x18\x01 \x01(\x05\x12\x0b\n\x03tag\x18\x02 \x01(\x05\x12\r\n\x05label\x18\x03 \x01(\x05\"6\n\x16SsoCloudSAMLLogRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\"\xdc\x01\n\x14SsoCloudSAMLLogEntry\x12\x12\n\nserverTime\x18\x01 \x01(\t\x12\x11\n\tdirection\x18\x02 \x01(\t\x12\x13\n\x0bmessageType\x18\x03 \x01(\t\x12\x15\n\rmessageIssued\x18\x04 \x01(\t\x12\x14\n\x0c\x66romEntityId\x18\x05 \x01(\t\x12\x12\n\nsamlStatus\x18\x06 \x01(\t\x12\x12\n\nrelayState\x18\x07 \x01(\t\x12\x13\n\x0bsamlContent\x18\x08 \x01(\t\x12\x10\n\x08isSigned\x18\t \x01(\x08\x12\x0c\n\x04isOK\x18\n \x01(\x08\"f\n\x17SsoCloudSAMLLogResponse\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12-\n\x05\x65ntry\x18\x02 \x03(\x0b\x32\x1e.SsoCloud.SsoCloudSAMLLogEntry\"b\n$SsoCloudServiceProviderUpdateRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoSpConfigurationId\x18\x02 \x01(\x04\"]\n\x1aSsoCloudIdpMetadataRequest\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x01(\x04\x12\x10\n\x08\x66ilename\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"\x9b\x01\n!SsoCloudIdpMetadataSupportRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoSpConfigurationId\x18\x02 \x01(\x04\x12\x17\n\x0fssoEnterpriseId\x18\x03 \x01(\x04\x12\x10\n\x08\x66ilename\x18\x04 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x05 \x01(\x0c\"F\n&SsoCloudConfigurationValidationRequest\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x03(\x04\"]\n\x11ValidationContent\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x01(\x04\x12\x14\n\x0cisSuccessful\x18\x02 \x01(\x08\x12\x14\n\x0c\x65rrorMessage\x18\x03 \x03(\t\"a\n\'SsoCloudConfigurationValidationResponse\x12\x36\n\x11validationContent\x18\x01 \x03(\x0b\x32\x1b.SsoCloud.ValidationContent\"O\n/SsoCloudServiceProviderConfigurationListRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\"u\n\x15\x43onfigurationListItem\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\nisSelected\x18\x03 \x01(\x08\x12\x1c\n\x14ssoServiceProviderId\x18\x04 \x03(\x04\"n\n0SsoCloudServiceProviderConfigurationListResponse\x12:\n\x11\x63onfigurationItem\x18\x01 \x03(\x0b\x32\x1f.SsoCloud.ConfigurationListItem\"\xbf\x01\n\x0fSsoCloudRequest\x12\x19\n\x11messageSessionUid\x18\x01 \x01(\x0c\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x10\n\x08\x65mbedded\x18\x03 \x01(\x08\x12\x0c\n\x04json\x18\x04 \x01(\x08\x12\x0c\n\x04\x64\x65st\x18\x05 \x01(\t\x12\x14\n\x0cidpSessionId\x18\x06 \x01(\t\x12\x12\n\nforceLogin\x18\x07 \x01(\x08\x12\x10\n\x08username\x18\x08 \x01(\t\x12\x10\n\x08\x64\x65tached\x18\t \x01(\x08\"\xc9\x01\n\x10SsoCloudResponse\x12\x0f\n\x07\x63ommand\x18\x01 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x02 \x01(\x0c\x12\r\n\x05\x65mail\x18\x03 \x01(\t\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x04 \x01(\x0c\x12\x14\n\x0cproviderName\x18\x05 \x01(\t\x12\x14\n\x0cidpSessionId\x18\x06 \x01(\t\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x07 \x01(\x0c\x12\x12\n\nerrorToken\x18\x08 \x01(\t\"Z\n\x12SsoCloudLogRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x13\n\x0bserviceName\x18\x02 \x01(\t\x12\x11\n\tserviceId\x18\x03 \x01(\r\"\x88\x02\n\x0eSamlRelayState\x12\x19\n\x11messageSessionUid\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x10\n\x08\x65mbedded\x18\x03 \x01(\x08\x12\x0c\n\x04json\x18\x04 \x01(\x08\x12\x0e\n\x06\x64\x65stId\x18\x05 \x01(\r\x12\r\n\x05keyId\x18\x06 \x01(\x05\x12<\n\x11supportedLanguage\x18\x07 \x01(\x0e\x32!.Authentication.SupportedLanguage\x12\x10\n\x08\x63hecksum\x18\x08 \x01(\x04\x12\x16\n\x0eisGeneratedUid\x18\t \x01(\x08\x12\x10\n\x08\x64\x65viceId\x18\n \x01(\x03\x12\x10\n\x08\x64\x65tached\x18\x0b \x01(\x08\"q\n\x1eSsoCloudMigrationStatusRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x04\x12\x12\n\nfullStatus\x18\x02 \x01(\x08\x12\x1c\n\x14includeMigratedUsers\x18\x03 \x01(\x08\x12\r\n\x05limit\x18\x04 \x01(\x05\"\xe8\x02\n\x1fSsoCloudMigrationStatusResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x04\x12\x14\n\x0cssoConnectId\x18\x04 \x01(\x04\x12\x16\n\x0essoConnectName\x18\x05 \x01(\t\x12\x19\n\x11ssoConnectCloudId\x18\x06 \x01(\x04\x12\x1b\n\x13ssoConnectCloudName\x18\x07 \x01(\t\x12\x17\n\x0ftotalUsersCount\x18\x08 \x01(\r\x12\x1a\n\x12usersMigratedCount\x18\t \x01(\r\x12:\n\rmigratedUsers\x18\n \x03(\x0b\x32#.SsoCloud.SsoCloudMigrationUserInfo\x12<\n\x0funmigratedUsers\x18\x0b \x03(\x0b\x32#.SsoCloud.SsoCloudMigrationUserInfo\"`\n\x19SsoCloudMigrationUserInfo\x12\x0e\n\x06userId\x18\x01 \x01(\r\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x10\n\x08\x66ullName\x18\x03 \x01(\t\x12\x12\n\nisMigrated\x18\x04 \x01(\x08*\x1d\n\x10\x41uthProtocolType\x12\t\n\x05SAML2\x10\x00*\x80\x02\n\x08\x44\x61taType\x12\x07\n\x03\x41NY\x10\x00\x12\x0b\n\x07\x42OOLEAN\x10\x01\x12\x0b\n\x07INTEGER\x10\x02\x12\n\n\x06STRING\x10\x03\x12\t\n\x05\x42YTES\x10\x04\x12\x07\n\x03URL\x10\x05\x12.\n*com_keepersecurity_proto_SsoCloud_DataType\x10\x06\x12\x36\n2com_keepersecurity_proto_SsoCloud_AuthProtocolType\x10\x07\x12\x30\n,com_keepersecurity_proto_SsoCloud_SsoIdpType\x10\x08\x12\x08\n\x04LONG\x10\t\x12\r\n\tTIMESTAMP\x10\n*R\n\x1cSsoCloudSettingOperationType\x12\x07\n\x03SET\x10\x00\x12\x07\n\x03GET\x10\x01\x12\n\n\x06\x44\x45LETE\x10\x02\x12\x14\n\x10RESET_TO_DEFAULT\x10\x03*\xd0\x02\n\nSsoIdpType\x12\r\n\tXX_UNUSED\x10\x00\x12\x0b\n\x07GENERIC\x10\x01\x12\x06\n\x02\x46\x35\x10\x02\x12\n\n\x06GOOGLE\x10\x03\x12\x08\n\x04OKTA\x10\x04\x12\x08\n\x04\x41\x44\x46S\x10\x05\x12\t\n\x05\x41ZURE\x10\x06\x12\x0c\n\x08ONELOGIN\x10\x07\x12\x07\n\x03\x41WS\x10\x08\x12\x0c\n\x08\x43\x45NTRIFY\x10\t\x12\x07\n\x03\x44UO\x10\n\x12\x07\n\x03IBM\x10\x0b\x12\r\n\tJUMPCLOUD\x10\x0c\x12\x08\n\x04PING\x10\r\x12\x0b\n\x07PINGONE\x10\x0e\x12\x07\n\x03RSA\x10\x0f\x12\x0e\n\nSECUREAUTH\x10\x10\x12\n\n\x06THALES\x10\x11\x12\t\n\x05\x41UTH0\x10\x12\x12\n\n\x06\x42\x45YOND\x10\x13\x12\x08\n\x04HYPR\x10\x14\x12\n\n\x06PUREID\x10\x15\x12\x07\n\x03SDO\x10\x16\x12\t\n\x05TRAIT\x10\x17\x12\x0c\n\x08TRANSMIT\x10\x18\x12\x0b\n\x07TRUSONA\x10\x19\x12\x0c\n\x08VERIDIUM\x10\x1a\x12\x07\n\x03\x43\x41S\x10\x1b\x42$\n\x18\x63om.keepersecurity.protoB\x08SsoCloudb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0essocloud.proto\x12\x08SsoCloud\x1a\x10\x41PIRequest.proto\"\xd5\x01\n\x14SsoCloudSettingValue\x12\x11\n\tsettingId\x18\x01 \x01(\x04\x12\x13\n\x0bsettingName\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\r\n\x05value\x18\x04 \x01(\t\x12%\n\tvalueType\x18\x05 \x01(\x0e\x32\x12.SsoCloud.DataType\x12\x14\n\x0clastModified\x18\x07 \x01(\t\x12\x12\n\nisFromFile\x18\x08 \x01(\x08\x12\x12\n\nisEditable\x18\t \x01(\x08\x12\x12\n\nisRequired\x18\n \x01(\x08\"\x89\x01\n\x15SsoCloudSettingAction\x12\x11\n\tsettingId\x18\x01 \x01(\x04\x12\x13\n\x0bsettingName\x18\x02 \x01(\t\x12\x39\n\toperation\x18\x03 \x01(\x0e\x32&.SsoCloud.SsoCloudSettingOperationType\x12\r\n\x05value\x18\x04 \x01(\t\"\xe1\x01\n\x1cSsoCloudConfigurationRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoSpConfigurationId\x18\x02 \x01(\x04\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x37\n\x13ssoAuthProtocolType\x18\x04 \x01(\x0e\x32\x1a.SsoCloud.AuthProtocolType\x12>\n\x15ssoCloudSettingAction\x18\x05 \x03(\x0b\x32\x1f.SsoCloud.SsoCloudSettingAction\"d\n\x13SsoSharedConfigItem\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoServiceProviderId\x18\x02 \x01(\x04\x12\x11\n\tssoNodeId\x18\x03 \x01(\x04\"\xad\x02\n\x1dSsoCloudConfigurationResponse\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoSpConfigurationId\x18\x02 \x01(\x04\x12\x14\n\x0c\x65nterpriseId\x18\x03 \x01(\x04\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x10\n\x08protocol\x18\x05 \x01(\t\x12\x14\n\x0clastModified\x18\x06 \x01(\t\x12<\n\x14ssoCloudSettingValue\x18\x07 \x03(\x0b\x32\x1e.SsoCloud.SsoCloudSettingValue\x12\x10\n\x08isShared\x18\x08 \x01(\x08\x12\x34\n\rsharedConfigs\x18\t \x03(\x0b\x32\x1d.SsoCloud.SsoSharedConfigItem\"E\n\x11SsoIdpTypeRequest\x12\x14\n\x0cssoIdpTypeId\x18\x01 \x01(\r\x12\x0b\n\x03tag\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\"F\n\x12SsoIdpTypeResponse\x12\x14\n\x0cssoIdpTypeId\x18\x01 \x01(\x05\x12\x0b\n\x03tag\x18\x02 \x01(\x05\x12\r\n\x05label\x18\x03 \x01(\x05\"6\n\x16SsoCloudSAMLLogRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\"\xdc\x01\n\x14SsoCloudSAMLLogEntry\x12\x12\n\nserverTime\x18\x01 \x01(\t\x12\x11\n\tdirection\x18\x02 \x01(\t\x12\x13\n\x0bmessageType\x18\x03 \x01(\t\x12\x15\n\rmessageIssued\x18\x04 \x01(\t\x12\x14\n\x0c\x66romEntityId\x18\x05 \x01(\t\x12\x12\n\nsamlStatus\x18\x06 \x01(\t\x12\x12\n\nrelayState\x18\x07 \x01(\t\x12\x13\n\x0bsamlContent\x18\x08 \x01(\t\x12\x10\n\x08isSigned\x18\t \x01(\x08\x12\x0c\n\x04isOK\x18\n \x01(\x08\"f\n\x17SsoCloudSAMLLogResponse\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12-\n\x05\x65ntry\x18\x02 \x03(\x0b\x32\x1e.SsoCloud.SsoCloudSAMLLogEntry\"b\n$SsoCloudServiceProviderUpdateRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoSpConfigurationId\x18\x02 \x01(\x04\"]\n\x1aSsoCloudIdpMetadataRequest\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x01(\x04\x12\x10\n\x08\x66ilename\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"\x9b\x01\n!SsoCloudIdpMetadataSupportRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x1c\n\x14ssoSpConfigurationId\x18\x02 \x01(\x04\x12\x17\n\x0fssoEnterpriseId\x18\x03 \x01(\x04\x12\x10\n\x08\x66ilename\x18\x04 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x05 \x01(\x0c\"F\n&SsoCloudConfigurationValidationRequest\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x03(\x04\"]\n\x11ValidationContent\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x01(\x04\x12\x14\n\x0cisSuccessful\x18\x02 \x01(\x08\x12\x14\n\x0c\x65rrorMessage\x18\x03 \x03(\t\"a\n\'SsoCloudConfigurationValidationResponse\x12\x36\n\x11validationContent\x18\x01 \x03(\x0b\x32\x1b.SsoCloud.ValidationContent\"O\n/SsoCloudServiceProviderConfigurationListRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\"u\n\x15\x43onfigurationListItem\x12\x1c\n\x14ssoSpConfigurationId\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\nisSelected\x18\x03 \x01(\x08\x12\x1c\n\x14ssoServiceProviderId\x18\x04 \x03(\x04\"n\n0SsoCloudServiceProviderConfigurationListResponse\x12:\n\x11\x63onfigurationItem\x18\x01 \x03(\x0b\x32\x1f.SsoCloud.ConfigurationListItem\"\xbf\x01\n\x0fSsoCloudRequest\x12\x19\n\x11messageSessionUid\x18\x01 \x01(\x0c\x12\x15\n\rclientVersion\x18\x02 \x01(\t\x12\x10\n\x08\x65mbedded\x18\x03 \x01(\x08\x12\x0c\n\x04json\x18\x04 \x01(\x08\x12\x0c\n\x04\x64\x65st\x18\x05 \x01(\t\x12\x14\n\x0cidpSessionId\x18\x06 \x01(\t\x12\x12\n\nforceLogin\x18\x07 \x01(\x08\x12\x10\n\x08username\x18\x08 \x01(\t\x12\x10\n\x08\x64\x65tached\x18\t \x01(\x08\"\xc9\x01\n\x10SsoCloudResponse\x12\x0f\n\x07\x63ommand\x18\x01 \x01(\t\x12\x19\n\x11messageSessionUid\x18\x02 \x01(\x0c\x12\r\n\x05\x65mail\x18\x03 \x01(\t\x12\x1b\n\x13\x65ncryptedLoginToken\x18\x04 \x01(\x0c\x12\x14\n\x0cproviderName\x18\x05 \x01(\t\x12\x14\n\x0cidpSessionId\x18\x06 \x01(\t\x12\x1d\n\x15\x65ncryptedSessionToken\x18\x07 \x01(\x0c\x12\x12\n\nerrorToken\x18\x08 \x01(\t\"Z\n\x12SsoCloudLogRequest\x12\x1c\n\x14ssoServiceProviderId\x18\x01 \x01(\x04\x12\x13\n\x0bserviceName\x18\x02 \x01(\t\x12\x11\n\tserviceId\x18\x03 \x01(\r\"\x88\x02\n\x0eSamlRelayState\x12\x19\n\x11messageSessionUid\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x10\n\x08\x65mbedded\x18\x03 \x01(\x08\x12\x0c\n\x04json\x18\x04 \x01(\x08\x12\x0e\n\x06\x64\x65stId\x18\x05 \x01(\r\x12\r\n\x05keyId\x18\x06 \x01(\x05\x12<\n\x11supportedLanguage\x18\x07 \x01(\x0e\x32!.Authentication.SupportedLanguage\x12\x10\n\x08\x63hecksum\x18\x08 \x01(\x04\x12\x16\n\x0eisGeneratedUid\x18\t \x01(\x08\x12\x10\n\x08\x64\x65viceId\x18\n \x01(\x03\x12\x10\n\x08\x64\x65tached\x18\x0b \x01(\x08\"q\n\x1eSsoCloudMigrationStatusRequest\x12\x0e\n\x06nodeId\x18\x01 \x01(\x04\x12\x12\n\nfullStatus\x18\x02 \x01(\x08\x12\x1c\n\x14includeMigratedUsers\x18\x03 \x01(\x08\x12\r\n\x05limit\x18\x04 \x01(\x05\"\xe8\x02\n\x1fSsoCloudMigrationStatusResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0e\n\x06nodeId\x18\x03 \x01(\x04\x12\x14\n\x0cssoConnectId\x18\x04 \x01(\x04\x12\x16\n\x0essoConnectName\x18\x05 \x01(\t\x12\x19\n\x11ssoConnectCloudId\x18\x06 \x01(\x04\x12\x1b\n\x13ssoConnectCloudName\x18\x07 \x01(\t\x12\x17\n\x0ftotalUsersCount\x18\x08 \x01(\r\x12\x1a\n\x12usersMigratedCount\x18\t \x01(\r\x12:\n\rmigratedUsers\x18\n \x03(\x0b\x32#.SsoCloud.SsoCloudMigrationUserInfo\x12<\n\x0funmigratedUsers\x18\x0b \x03(\x0b\x32#.SsoCloud.SsoCloudMigrationUserInfo\"`\n\x19SsoCloudMigrationUserInfo\x12\x0e\n\x06userId\x18\x01 \x01(\r\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x10\n\x08\x66ullName\x18\x03 \x01(\t\x12\x12\n\nisMigrated\x18\x04 \x01(\x08*&\n\x10\x41uthProtocolType\x12\t\n\x05SAML2\x10\x00\x12\x07\n\x03JWT\x10\x01*\x80\x02\n\x08\x44\x61taType\x12\x07\n\x03\x41NY\x10\x00\x12\x0b\n\x07\x42OOLEAN\x10\x01\x12\x0b\n\x07INTEGER\x10\x02\x12\n\n\x06STRING\x10\x03\x12\t\n\x05\x42YTES\x10\x04\x12\x07\n\x03URL\x10\x05\x12.\n*com_keepersecurity_proto_SsoCloud_DataType\x10\x06\x12\x36\n2com_keepersecurity_proto_SsoCloud_AuthProtocolType\x10\x07\x12\x30\n,com_keepersecurity_proto_SsoCloud_SsoIdpType\x10\x08\x12\x08\n\x04LONG\x10\t\x12\r\n\tTIMESTAMP\x10\n*R\n\x1cSsoCloudSettingOperationType\x12\x07\n\x03SET\x10\x00\x12\x07\n\x03GET\x10\x01\x12\n\n\x06\x44\x45LETE\x10\x02\x12\x14\n\x10RESET_TO_DEFAULT\x10\x03*\xd0\x02\n\nSsoIdpType\x12\r\n\tXX_UNUSED\x10\x00\x12\x0b\n\x07GENERIC\x10\x01\x12\x06\n\x02\x46\x35\x10\x02\x12\n\n\x06GOOGLE\x10\x03\x12\x08\n\x04OKTA\x10\x04\x12\x08\n\x04\x41\x44\x46S\x10\x05\x12\t\n\x05\x41ZURE\x10\x06\x12\x0c\n\x08ONELOGIN\x10\x07\x12\x07\n\x03\x41WS\x10\x08\x12\x0c\n\x08\x43\x45NTRIFY\x10\t\x12\x07\n\x03\x44UO\x10\n\x12\x07\n\x03IBM\x10\x0b\x12\r\n\tJUMPCLOUD\x10\x0c\x12\x08\n\x04PING\x10\r\x12\x0b\n\x07PINGONE\x10\x0e\x12\x07\n\x03RSA\x10\x0f\x12\x0e\n\nSECUREAUTH\x10\x10\x12\n\n\x06THALES\x10\x11\x12\t\n\x05\x41UTH0\x10\x12\x12\n\n\x06\x42\x45YOND\x10\x13\x12\x08\n\x04HYPR\x10\x14\x12\n\n\x06PUREID\x10\x15\x12\x07\n\x03SDO\x10\x16\x12\t\n\x05TRAIT\x10\x17\x12\x0c\n\x08TRANSMIT\x10\x18\x12\x0b\n\x07TRUSONA\x10\x19\x12\x0c\n\x08VERIDIUM\x10\x1a\x12\x07\n\x03\x43\x41S\x10\x1b\x42$\n\x18\x63om.keepersecurity.protoB\x08SsoCloudb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ssocloud_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\010SsoCloud' _globals['_AUTHPROTOCOLTYPE']._serialized_start=3826 - _globals['_AUTHPROTOCOLTYPE']._serialized_end=3855 - _globals['_DATATYPE']._serialized_start=3858 - _globals['_DATATYPE']._serialized_end=4114 - _globals['_SSOCLOUDSETTINGOPERATIONTYPE']._serialized_start=4116 - _globals['_SSOCLOUDSETTINGOPERATIONTYPE']._serialized_end=4198 - _globals['_SSOIDPTYPE']._serialized_start=4201 - _globals['_SSOIDPTYPE']._serialized_end=4537 + _globals['_AUTHPROTOCOLTYPE']._serialized_end=3864 + _globals['_DATATYPE']._serialized_start=3867 + _globals['_DATATYPE']._serialized_end=4123 + _globals['_SSOCLOUDSETTINGOPERATIONTYPE']._serialized_start=4125 + _globals['_SSOCLOUDSETTINGOPERATIONTYPE']._serialized_end=4207 + _globals['_SSOIDPTYPE']._serialized_start=4210 + _globals['_SSOIDPTYPE']._serialized_end=4546 _globals['_SSOCLOUDSETTINGVALUE']._serialized_start=47 _globals['_SSOCLOUDSETTINGVALUE']._serialized_end=260 _globals['_SSOCLOUDSETTINGACTION']._serialized_start=263 diff --git a/keepercommander/proto/ssocloud_pb2.pyi b/keepercommander/proto/ssocloud_pb2.pyi index a64e0f0e1..af152a2de 100644 --- a/keepercommander/proto/ssocloud_pb2.pyi +++ b/keepercommander/proto/ssocloud_pb2.pyi @@ -8,11 +8,12 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class AuthProtocolType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () SAML2: _ClassVar[AuthProtocolType] + JWT: _ClassVar[AuthProtocolType] class DataType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () ANY: _ClassVar[DataType] BOOLEAN: _ClassVar[DataType] INTEGER: _ClassVar[DataType] @@ -26,14 +27,14 @@ class DataType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): TIMESTAMP: _ClassVar[DataType] class SsoCloudSettingOperationType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () SET: _ClassVar[SsoCloudSettingOperationType] GET: _ClassVar[SsoCloudSettingOperationType] DELETE: _ClassVar[SsoCloudSettingOperationType] RESET_TO_DEFAULT: _ClassVar[SsoCloudSettingOperationType] class SsoIdpType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = [] + __slots__ = () XX_UNUSED: _ClassVar[SsoIdpType] GENERIC: _ClassVar[SsoIdpType] F5: _ClassVar[SsoIdpType] @@ -63,6 +64,7 @@ class SsoIdpType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): VERIDIUM: _ClassVar[SsoIdpType] CAS: _ClassVar[SsoIdpType] SAML2: AuthProtocolType +JWT: AuthProtocolType ANY: DataType BOOLEAN: DataType INTEGER: DataType @@ -108,7 +110,7 @@ VERIDIUM: SsoIdpType CAS: SsoIdpType class SsoCloudSettingValue(_message.Message): - __slots__ = ["settingId", "settingName", "label", "value", "valueType", "lastModified", "isFromFile", "isEditable", "isRequired"] + __slots__ = ("settingId", "settingName", "label", "value", "valueType", "lastModified", "isFromFile", "isEditable", "isRequired") SETTINGID_FIELD_NUMBER: _ClassVar[int] SETTINGNAME_FIELD_NUMBER: _ClassVar[int] LABEL_FIELD_NUMBER: _ClassVar[int] @@ -130,7 +132,7 @@ class SsoCloudSettingValue(_message.Message): def __init__(self, settingId: _Optional[int] = ..., settingName: _Optional[str] = ..., label: _Optional[str] = ..., value: _Optional[str] = ..., valueType: _Optional[_Union[DataType, str]] = ..., lastModified: _Optional[str] = ..., isFromFile: bool = ..., isEditable: bool = ..., isRequired: bool = ...) -> None: ... class SsoCloudSettingAction(_message.Message): - __slots__ = ["settingId", "settingName", "operation", "value"] + __slots__ = ("settingId", "settingName", "operation", "value") SETTINGID_FIELD_NUMBER: _ClassVar[int] SETTINGNAME_FIELD_NUMBER: _ClassVar[int] OPERATION_FIELD_NUMBER: _ClassVar[int] @@ -142,7 +144,7 @@ class SsoCloudSettingAction(_message.Message): def __init__(self, settingId: _Optional[int] = ..., settingName: _Optional[str] = ..., operation: _Optional[_Union[SsoCloudSettingOperationType, str]] = ..., value: _Optional[str] = ...) -> None: ... class SsoCloudConfigurationRequest(_message.Message): - __slots__ = ["ssoServiceProviderId", "ssoSpConfigurationId", "name", "ssoAuthProtocolType", "ssoCloudSettingAction"] + __slots__ = ("ssoServiceProviderId", "ssoSpConfigurationId", "name", "ssoAuthProtocolType", "ssoCloudSettingAction") SSOSERVICEPROVIDERID_FIELD_NUMBER: _ClassVar[int] SSOSPCONFIGURATIONID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] @@ -156,7 +158,7 @@ class SsoCloudConfigurationRequest(_message.Message): def __init__(self, ssoServiceProviderId: _Optional[int] = ..., ssoSpConfigurationId: _Optional[int] = ..., name: _Optional[str] = ..., ssoAuthProtocolType: _Optional[_Union[AuthProtocolType, str]] = ..., ssoCloudSettingAction: _Optional[_Iterable[_Union[SsoCloudSettingAction, _Mapping]]] = ...) -> None: ... class SsoSharedConfigItem(_message.Message): - __slots__ = ["ssoSpConfigurationId", "ssoServiceProviderId", "ssoNodeId"] + __slots__ = ("ssoSpConfigurationId", "ssoServiceProviderId", "ssoNodeId") SSOSPCONFIGURATIONID_FIELD_NUMBER: _ClassVar[int] SSOSERVICEPROVIDERID_FIELD_NUMBER: _ClassVar[int] SSONODEID_FIELD_NUMBER: _ClassVar[int] @@ -166,7 +168,7 @@ class SsoSharedConfigItem(_message.Message): def __init__(self, ssoSpConfigurationId: _Optional[int] = ..., ssoServiceProviderId: _Optional[int] = ..., ssoNodeId: _Optional[int] = ...) -> None: ... class SsoCloudConfigurationResponse(_message.Message): - __slots__ = ["ssoServiceProviderId", "ssoSpConfigurationId", "enterpriseId", "name", "protocol", "lastModified", "ssoCloudSettingValue", "isShared", "sharedConfigs"] + __slots__ = ("ssoServiceProviderId", "ssoSpConfigurationId", "enterpriseId", "name", "protocol", "lastModified", "ssoCloudSettingValue", "isShared", "sharedConfigs") SSOSERVICEPROVIDERID_FIELD_NUMBER: _ClassVar[int] SSOSPCONFIGURATIONID_FIELD_NUMBER: _ClassVar[int] ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] @@ -188,7 +190,7 @@ class SsoCloudConfigurationResponse(_message.Message): def __init__(self, ssoServiceProviderId: _Optional[int] = ..., ssoSpConfigurationId: _Optional[int] = ..., enterpriseId: _Optional[int] = ..., name: _Optional[str] = ..., protocol: _Optional[str] = ..., lastModified: _Optional[str] = ..., ssoCloudSettingValue: _Optional[_Iterable[_Union[SsoCloudSettingValue, _Mapping]]] = ..., isShared: bool = ..., sharedConfigs: _Optional[_Iterable[_Union[SsoSharedConfigItem, _Mapping]]] = ...) -> None: ... class SsoIdpTypeRequest(_message.Message): - __slots__ = ["ssoIdpTypeId", "tag", "label"] + __slots__ = ("ssoIdpTypeId", "tag", "label") SSOIDPTYPEID_FIELD_NUMBER: _ClassVar[int] TAG_FIELD_NUMBER: _ClassVar[int] LABEL_FIELD_NUMBER: _ClassVar[int] @@ -198,7 +200,7 @@ class SsoIdpTypeRequest(_message.Message): def __init__(self, ssoIdpTypeId: _Optional[int] = ..., tag: _Optional[str] = ..., label: _Optional[str] = ...) -> None: ... class SsoIdpTypeResponse(_message.Message): - __slots__ = ["ssoIdpTypeId", "tag", "label"] + __slots__ = ("ssoIdpTypeId", "tag", "label") SSOIDPTYPEID_FIELD_NUMBER: _ClassVar[int] TAG_FIELD_NUMBER: _ClassVar[int] LABEL_FIELD_NUMBER: _ClassVar[int] @@ -208,13 +210,13 @@ class SsoIdpTypeResponse(_message.Message): def __init__(self, ssoIdpTypeId: _Optional[int] = ..., tag: _Optional[int] = ..., label: _Optional[int] = ...) -> None: ... class SsoCloudSAMLLogRequest(_message.Message): - __slots__ = ["ssoServiceProviderId"] + __slots__ = ("ssoServiceProviderId",) SSOSERVICEPROVIDERID_FIELD_NUMBER: _ClassVar[int] ssoServiceProviderId: int def __init__(self, ssoServiceProviderId: _Optional[int] = ...) -> None: ... class SsoCloudSAMLLogEntry(_message.Message): - __slots__ = ["serverTime", "direction", "messageType", "messageIssued", "fromEntityId", "samlStatus", "relayState", "samlContent", "isSigned", "isOK"] + __slots__ = ("serverTime", "direction", "messageType", "messageIssued", "fromEntityId", "samlStatus", "relayState", "samlContent", "isSigned", "isOK") SERVERTIME_FIELD_NUMBER: _ClassVar[int] DIRECTION_FIELD_NUMBER: _ClassVar[int] MESSAGETYPE_FIELD_NUMBER: _ClassVar[int] @@ -238,7 +240,7 @@ class SsoCloudSAMLLogEntry(_message.Message): def __init__(self, serverTime: _Optional[str] = ..., direction: _Optional[str] = ..., messageType: _Optional[str] = ..., messageIssued: _Optional[str] = ..., fromEntityId: _Optional[str] = ..., samlStatus: _Optional[str] = ..., relayState: _Optional[str] = ..., samlContent: _Optional[str] = ..., isSigned: bool = ..., isOK: bool = ...) -> None: ... class SsoCloudSAMLLogResponse(_message.Message): - __slots__ = ["ssoServiceProviderId", "entry"] + __slots__ = ("ssoServiceProviderId", "entry") SSOSERVICEPROVIDERID_FIELD_NUMBER: _ClassVar[int] ENTRY_FIELD_NUMBER: _ClassVar[int] ssoServiceProviderId: int @@ -246,7 +248,7 @@ class SsoCloudSAMLLogResponse(_message.Message): def __init__(self, ssoServiceProviderId: _Optional[int] = ..., entry: _Optional[_Iterable[_Union[SsoCloudSAMLLogEntry, _Mapping]]] = ...) -> None: ... class SsoCloudServiceProviderUpdateRequest(_message.Message): - __slots__ = ["ssoServiceProviderId", "ssoSpConfigurationId"] + __slots__ = ("ssoServiceProviderId", "ssoSpConfigurationId") SSOSERVICEPROVIDERID_FIELD_NUMBER: _ClassVar[int] SSOSPCONFIGURATIONID_FIELD_NUMBER: _ClassVar[int] ssoServiceProviderId: int @@ -254,7 +256,7 @@ class SsoCloudServiceProviderUpdateRequest(_message.Message): def __init__(self, ssoServiceProviderId: _Optional[int] = ..., ssoSpConfigurationId: _Optional[int] = ...) -> None: ... class SsoCloudIdpMetadataRequest(_message.Message): - __slots__ = ["ssoSpConfigurationId", "filename", "content"] + __slots__ = ("ssoSpConfigurationId", "filename", "content") SSOSPCONFIGURATIONID_FIELD_NUMBER: _ClassVar[int] FILENAME_FIELD_NUMBER: _ClassVar[int] CONTENT_FIELD_NUMBER: _ClassVar[int] @@ -264,7 +266,7 @@ class SsoCloudIdpMetadataRequest(_message.Message): def __init__(self, ssoSpConfigurationId: _Optional[int] = ..., filename: _Optional[str] = ..., content: _Optional[bytes] = ...) -> None: ... class SsoCloudIdpMetadataSupportRequest(_message.Message): - __slots__ = ["ssoServiceProviderId", "ssoSpConfigurationId", "ssoEnterpriseId", "filename", "content"] + __slots__ = ("ssoServiceProviderId", "ssoSpConfigurationId", "ssoEnterpriseId", "filename", "content") SSOSERVICEPROVIDERID_FIELD_NUMBER: _ClassVar[int] SSOSPCONFIGURATIONID_FIELD_NUMBER: _ClassVar[int] SSOENTERPRISEID_FIELD_NUMBER: _ClassVar[int] @@ -278,13 +280,13 @@ class SsoCloudIdpMetadataSupportRequest(_message.Message): def __init__(self, ssoServiceProviderId: _Optional[int] = ..., ssoSpConfigurationId: _Optional[int] = ..., ssoEnterpriseId: _Optional[int] = ..., filename: _Optional[str] = ..., content: _Optional[bytes] = ...) -> None: ... class SsoCloudConfigurationValidationRequest(_message.Message): - __slots__ = ["ssoSpConfigurationId"] + __slots__ = ("ssoSpConfigurationId",) SSOSPCONFIGURATIONID_FIELD_NUMBER: _ClassVar[int] ssoSpConfigurationId: _containers.RepeatedScalarFieldContainer[int] def __init__(self, ssoSpConfigurationId: _Optional[_Iterable[int]] = ...) -> None: ... class ValidationContent(_message.Message): - __slots__ = ["ssoSpConfigurationId", "isSuccessful", "errorMessage"] + __slots__ = ("ssoSpConfigurationId", "isSuccessful", "errorMessage") SSOSPCONFIGURATIONID_FIELD_NUMBER: _ClassVar[int] ISSUCCESSFUL_FIELD_NUMBER: _ClassVar[int] ERRORMESSAGE_FIELD_NUMBER: _ClassVar[int] @@ -294,19 +296,19 @@ class ValidationContent(_message.Message): def __init__(self, ssoSpConfigurationId: _Optional[int] = ..., isSuccessful: bool = ..., errorMessage: _Optional[_Iterable[str]] = ...) -> None: ... class SsoCloudConfigurationValidationResponse(_message.Message): - __slots__ = ["validationContent"] + __slots__ = ("validationContent",) VALIDATIONCONTENT_FIELD_NUMBER: _ClassVar[int] validationContent: _containers.RepeatedCompositeFieldContainer[ValidationContent] def __init__(self, validationContent: _Optional[_Iterable[_Union[ValidationContent, _Mapping]]] = ...) -> None: ... class SsoCloudServiceProviderConfigurationListRequest(_message.Message): - __slots__ = ["ssoServiceProviderId"] + __slots__ = ("ssoServiceProviderId",) SSOSERVICEPROVIDERID_FIELD_NUMBER: _ClassVar[int] ssoServiceProviderId: int def __init__(self, ssoServiceProviderId: _Optional[int] = ...) -> None: ... class ConfigurationListItem(_message.Message): - __slots__ = ["ssoSpConfigurationId", "name", "isSelected", "ssoServiceProviderId"] + __slots__ = ("ssoSpConfigurationId", "name", "isSelected", "ssoServiceProviderId") SSOSPCONFIGURATIONID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] ISSELECTED_FIELD_NUMBER: _ClassVar[int] @@ -318,13 +320,13 @@ class ConfigurationListItem(_message.Message): def __init__(self, ssoSpConfigurationId: _Optional[int] = ..., name: _Optional[str] = ..., isSelected: bool = ..., ssoServiceProviderId: _Optional[_Iterable[int]] = ...) -> None: ... class SsoCloudServiceProviderConfigurationListResponse(_message.Message): - __slots__ = ["configurationItem"] + __slots__ = ("configurationItem",) CONFIGURATIONITEM_FIELD_NUMBER: _ClassVar[int] configurationItem: _containers.RepeatedCompositeFieldContainer[ConfigurationListItem] def __init__(self, configurationItem: _Optional[_Iterable[_Union[ConfigurationListItem, _Mapping]]] = ...) -> None: ... class SsoCloudRequest(_message.Message): - __slots__ = ["messageSessionUid", "clientVersion", "embedded", "json", "dest", "idpSessionId", "forceLogin", "username", "detached"] + __slots__ = ("messageSessionUid", "clientVersion", "embedded", "json", "dest", "idpSessionId", "forceLogin", "username", "detached") MESSAGESESSIONUID_FIELD_NUMBER: _ClassVar[int] CLIENTVERSION_FIELD_NUMBER: _ClassVar[int] EMBEDDED_FIELD_NUMBER: _ClassVar[int] @@ -346,7 +348,7 @@ class SsoCloudRequest(_message.Message): def __init__(self, messageSessionUid: _Optional[bytes] = ..., clientVersion: _Optional[str] = ..., embedded: bool = ..., json: bool = ..., dest: _Optional[str] = ..., idpSessionId: _Optional[str] = ..., forceLogin: bool = ..., username: _Optional[str] = ..., detached: bool = ...) -> None: ... class SsoCloudResponse(_message.Message): - __slots__ = ["command", "messageSessionUid", "email", "encryptedLoginToken", "providerName", "idpSessionId", "encryptedSessionToken", "errorToken"] + __slots__ = ("command", "messageSessionUid", "email", "encryptedLoginToken", "providerName", "idpSessionId", "encryptedSessionToken", "errorToken") COMMAND_FIELD_NUMBER: _ClassVar[int] MESSAGESESSIONUID_FIELD_NUMBER: _ClassVar[int] EMAIL_FIELD_NUMBER: _ClassVar[int] @@ -366,7 +368,7 @@ class SsoCloudResponse(_message.Message): def __init__(self, command: _Optional[str] = ..., messageSessionUid: _Optional[bytes] = ..., email: _Optional[str] = ..., encryptedLoginToken: _Optional[bytes] = ..., providerName: _Optional[str] = ..., idpSessionId: _Optional[str] = ..., encryptedSessionToken: _Optional[bytes] = ..., errorToken: _Optional[str] = ...) -> None: ... class SsoCloudLogRequest(_message.Message): - __slots__ = ["ssoServiceProviderId", "serviceName", "serviceId"] + __slots__ = ("ssoServiceProviderId", "serviceName", "serviceId") SSOSERVICEPROVIDERID_FIELD_NUMBER: _ClassVar[int] SERVICENAME_FIELD_NUMBER: _ClassVar[int] SERVICEID_FIELD_NUMBER: _ClassVar[int] @@ -376,7 +378,7 @@ class SsoCloudLogRequest(_message.Message): def __init__(self, ssoServiceProviderId: _Optional[int] = ..., serviceName: _Optional[str] = ..., serviceId: _Optional[int] = ...) -> None: ... class SamlRelayState(_message.Message): - __slots__ = ["messageSessionUid", "username", "embedded", "json", "destId", "keyId", "supportedLanguage", "checksum", "isGeneratedUid", "deviceId", "detached"] + __slots__ = ("messageSessionUid", "username", "embedded", "json", "destId", "keyId", "supportedLanguage", "checksum", "isGeneratedUid", "deviceId", "detached") MESSAGESESSIONUID_FIELD_NUMBER: _ClassVar[int] USERNAME_FIELD_NUMBER: _ClassVar[int] EMBEDDED_FIELD_NUMBER: _ClassVar[int] @@ -402,7 +404,7 @@ class SamlRelayState(_message.Message): def __init__(self, messageSessionUid: _Optional[bytes] = ..., username: _Optional[str] = ..., embedded: bool = ..., json: bool = ..., destId: _Optional[int] = ..., keyId: _Optional[int] = ..., supportedLanguage: _Optional[_Union[_APIRequest_pb2.SupportedLanguage, str]] = ..., checksum: _Optional[int] = ..., isGeneratedUid: bool = ..., deviceId: _Optional[int] = ..., detached: bool = ...) -> None: ... class SsoCloudMigrationStatusRequest(_message.Message): - __slots__ = ["nodeId", "fullStatus", "includeMigratedUsers", "limit"] + __slots__ = ("nodeId", "fullStatus", "includeMigratedUsers", "limit") NODEID_FIELD_NUMBER: _ClassVar[int] FULLSTATUS_FIELD_NUMBER: _ClassVar[int] INCLUDEMIGRATEDUSERS_FIELD_NUMBER: _ClassVar[int] @@ -414,7 +416,7 @@ class SsoCloudMigrationStatusRequest(_message.Message): def __init__(self, nodeId: _Optional[int] = ..., fullStatus: bool = ..., includeMigratedUsers: bool = ..., limit: _Optional[int] = ...) -> None: ... class SsoCloudMigrationStatusResponse(_message.Message): - __slots__ = ["success", "message", "nodeId", "ssoConnectId", "ssoConnectName", "ssoConnectCloudId", "ssoConnectCloudName", "totalUsersCount", "usersMigratedCount", "migratedUsers", "unmigratedUsers"] + __slots__ = ("success", "message", "nodeId", "ssoConnectId", "ssoConnectName", "ssoConnectCloudId", "ssoConnectCloudName", "totalUsersCount", "usersMigratedCount", "migratedUsers", "unmigratedUsers") SUCCESS_FIELD_NUMBER: _ClassVar[int] MESSAGE_FIELD_NUMBER: _ClassVar[int] NODEID_FIELD_NUMBER: _ClassVar[int] @@ -440,7 +442,7 @@ class SsoCloudMigrationStatusResponse(_message.Message): def __init__(self, success: bool = ..., message: _Optional[str] = ..., nodeId: _Optional[int] = ..., ssoConnectId: _Optional[int] = ..., ssoConnectName: _Optional[str] = ..., ssoConnectCloudId: _Optional[int] = ..., ssoConnectCloudName: _Optional[str] = ..., totalUsersCount: _Optional[int] = ..., usersMigratedCount: _Optional[int] = ..., migratedUsers: _Optional[_Iterable[_Union[SsoCloudMigrationUserInfo, _Mapping]]] = ..., unmigratedUsers: _Optional[_Iterable[_Union[SsoCloudMigrationUserInfo, _Mapping]]] = ...) -> None: ... class SsoCloudMigrationUserInfo(_message.Message): - __slots__ = ["userId", "email", "fullName", "isMigrated"] + __slots__ = ("userId", "email", "fullName", "isMigrated") USERID_FIELD_NUMBER: _ClassVar[int] EMAIL_FIELD_NUMBER: _ClassVar[int] FULLNAME_FIELD_NUMBER: _ClassVar[int] diff --git a/keepercommander/proto/tla_pb2.py b/keepercommander/proto/tla_pb2.py index afb757bdf..340b81b81 100644 --- a/keepercommander/proto/tla_pb2.py +++ b/keepercommander/proto/tla_pb2.py @@ -1,12 +1,23 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: tla.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder - +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'tla.proto' +) +# @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/tla_pb2.pyi b/keepercommander/proto/tla_pb2.pyi index 7075a1c31..08e7a2c3b 100644 --- a/keepercommander/proto/tla_pb2.pyi +++ b/keepercommander/proto/tla_pb2.pyi @@ -22,4 +22,4 @@ class TLAProperties(_message.Message): expiration: int timerNotificationType: TimerNotificationType rotateOnExpiration: bool - def __init__(self, expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[TimerNotificationType, str]] = ..., rotateOnExpiration: _Optional[bool] = ...) -> None: ... + def __init__(self, expiration: _Optional[int] = ..., timerNotificationType: _Optional[_Union[TimerNotificationType, str]] = ..., rotateOnExpiration: bool = ...) -> None: ... diff --git a/keepercommander/proto/version_pb2.py b/keepercommander/proto/version_pb2.py index 73c390ca2..03a8e37fc 100644 --- a/keepercommander/proto/version_pb2.py +++ b/keepercommander/proto/version_pb2.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: version.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'version.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -18,8 +29,8 @@ _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'version_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\017SemanticVersion' _globals['_VERSION']._serialized_start=34 _globals['_VERSION']._serialized_end=103 diff --git a/keepercommander/proto/version_pb2.pyi b/keepercommander/proto/version_pb2.pyi index b3acf70a6..6adf41c77 100644 --- a/keepercommander/proto/version_pb2.pyi +++ b/keepercommander/proto/version_pb2.pyi @@ -5,7 +5,7 @@ from typing import ClassVar as _ClassVar, Optional as _Optional DESCRIPTOR: _descriptor.FileDescriptor class Version(_message.Message): - __slots__ = ["major", "minor", "patch", "build"] + __slots__ = ("major", "minor", "patch", "build") MAJOR_FIELD_NUMBER: _ClassVar[int] MINOR_FIELD_NUMBER: _ClassVar[int] PATCH_FIELD_NUMBER: _ClassVar[int] diff --git a/keepercommander/proto/workflow_pb2.py b/keepercommander/proto/workflow_pb2.py index 82869ae91..c24a4f19c 100644 --- a/keepercommander/proto/workflow_pb2.py +++ b/keepercommander/proto/workflow_pb2.py @@ -1,11 +1,23 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: workflow.proto +# Protobuf Python Version: 5.29.5 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 5, + '', + 'workflow.proto' +) +# @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() diff --git a/keepercommander/proto/workflow_pb2.pyi b/keepercommander/proto/workflow_pb2.pyi index 8a82cb70f..6091808ed 100644 --- a/keepercommander/proto/workflow_pb2.pyi +++ b/keepercommander/proto/workflow_pb2.pyi @@ -4,8 +4,7 @@ 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 +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -73,7 +72,7 @@ class WorkflowApprover(_message.Message): 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: ... + def __init__(self, user: _Optional[str] = ..., userId: _Optional[int] = ..., teamUid: _Optional[bytes] = ..., escalation: bool = ..., escalationAfterMs: _Optional[int] = ...) -> None: ... class WorkflowParameters(_message.Message): __slots__ = ("resource", "approvalsNeeded", "checkoutNeeded", "startAccessOnApproval", "requireReason", "requireTicket", "requireMFA", "accessLength", "allowedTimes") @@ -95,7 +94,7 @@ class WorkflowParameters(_message.Message): 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: ... + def __init__(self, resource: _Optional[_Union[_GraphSync_pb2.GraphSyncRef, _Mapping]] = ..., approvalsNeeded: _Optional[int] = ..., checkoutNeeded: bool = ..., startAccessOnApproval: bool = ..., requireReason: bool = ..., requireTicket: bool = ..., requireMFA: bool = ..., accessLength: _Optional[int] = ..., allowedTimes: _Optional[_Union[TemporalAccessFilter, _Mapping]] = ...) -> None: ... class WorkflowConfig(_message.Message): __slots__ = ("parameters", "approvers", "createdOn") @@ -125,7 +124,7 @@ class WorkflowStatus(_message.Message): escalated: bool checkedOutBy: str canForceCheckIn: 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] = ..., checkedOutBy: _Optional[str] = ..., canForceCheckIn: _Optional[bool] = ...) -> None: ... + 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: bool = ..., checkedOutBy: _Optional[str] = ..., canForceCheckIn: bool = ...) -> None: ... class WorkflowProcess(_message.Message): __slots__ = ("flowUid", "userId", "resource", "startedOn", "expiresOn", "reason", "mfaVerified", "externalRef", "user", "workflowParameters", "escalated") @@ -151,7 +150,7 @@ class WorkflowProcess(_message.Message): user: str workflowParameters: _containers.RepeatedCompositeFieldContainer[_NotificationCenter_pb2.NotificationParameter] escalated: bool - 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] = ..., user: _Optional[str] = ..., workflowParameters: _Optional[_Iterable[_Union[_NotificationCenter_pb2.NotificationParameter, _Mapping]]] = ..., escalated: _Optional[bool] = ...) -> None: ... + 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: bool = ..., externalRef: _Optional[bytes] = ..., user: _Optional[str] = ..., workflowParameters: _Optional[_Iterable[_Union[_NotificationCenter_pb2.NotificationParameter, _Mapping]]] = ..., escalated: bool = ...) -> None: ... class WorkflowApproval(_message.Message): __slots__ = ("userId", "user", "flowUid", "approvedOn") @@ -205,7 +204,7 @@ class WorkflowApprovalOrDenial(_message.Message): flowUid: bytes deny: bool denialReason: bytes - def __init__(self, flowUid: _Optional[bytes] = ..., deny: _Optional[bool] = ..., denialReason: _Optional[bytes] = ...) -> None: ... + def __init__(self, flowUid: _Optional[bytes] = ..., deny: bool = ..., denialReason: _Optional[bytes] = ...) -> None: ... class UserAccessState(_message.Message): __slots__ = ("workflows",) diff --git a/requirements.txt b/requirements.txt index 7af34666c..592ec6fb2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ websockets fido2>=2.0.0; python_version>='3.10' requests>=2.31.0 cryptography>=46.0.6 -protobuf>=5.29.6 +protobuf>=5.29.6,<6 keeper-secrets-manager-core>=16.6.0 keeper_pam_webrtc_rs>=2.1.18 pydantic>=2.6.4 diff --git a/setup.cfg b/setup.cfg index 6c52380c5..51de347d0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -41,7 +41,7 @@ install_requires = flask-limiter keeper-secrets-manager-core>=16.6.0 prompt_toolkit - protobuf>=5.29.6 + protobuf>=5.29.6,<6 googleapis-common-protos psutil pycryptodomex>=3.20.0 From edcee4ded10659ee4613d6920fa5ca93e924ec6c Mon Sep 17 00:00:00 2001 From: idimov-keeper <78815270+idimov-keeper@users.noreply.github.com> Date: Tue, 2 Jun 2026 18:42:40 -0500 Subject: [PATCH 09/23] fix(tunnel): close and unregister tube on connection_state_changed=closed (#2109) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the WebRTC peer connection reached the "closed" state without a preceding channel_closed signal (ICE timeout, network drop, etc.), Commander logged the state change and returned — leaving the Rust tube running and any active forwarded TCP sessions (SSH, MySQL, etc.) still proxying while Commander stopped reporting the tunnel entirely. Mirror the channel_closed cleanup: call close_tube (idempotent if Rust already closed), stop the dedicated WebSocket, and unregister the tunnel session so the tunnel is fully cleaned up regardless of which path triggered the close. Co-authored-by: Micah Roberts --- .../tunnel/port_forward/tunnel_helpers.py | 19 ++ tests/test_tunnel_close_leak.py | 208 ++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 tests/test_tunnel_close_leak.py diff --git a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py index 1349f71d2..d597cd8c4 100644 --- a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py +++ b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py @@ -1723,6 +1723,25 @@ def _signal_from_rust_inner(self, response: dict): elif new_state == "closed": logging.debug(f"Connection closed for tube {tube_id}") + # The WebRTC peer connection reached "closed" state. This can happen + # without a preceding channel_closed signal (e.g. ICE timeout, network + # drop). If we don't clean up here the Rust tube keeps running and any + # active forwarded TCP sessions (SSH, MySQL, etc.) keep flowing while + # Commander stops reporting the tunnel entirely. + if tube_id and self.tube_registry: + try: + self.tube_registry.close_tube(tube_id, reason=CloseConnectionReasons.Normal) + except Exception: + pass # Already closed — idempotent + if tube_id: + session = get_tunnel_session(tube_id) + if session: + if hasattr(session, 'signal_handler') and session.signal_handler: + session.signal_handler.cleanup() + if session.websocket_stop_event and session.websocket_thread: + session.websocket_stop_event.set() + session.websocket_thread.join(timeout=5.0) + unregister_tunnel_session(tube_id) else: logging.debug(f"Connection state for tube {tube_id}: {new_state}") diff --git a/tests/test_tunnel_close_leak.py b/tests/test_tunnel_close_leak.py new file mode 100644 index 000000000..877677523 --- /dev/null +++ b/tests/test_tunnel_close_leak.py @@ -0,0 +1,208 @@ +""" +Test: connection_state_changed=closed must close tube and unregister session. + +Reproduces the leak where Commander stopped reporting a tunnel (session removed +from _GLOBAL_TUNNEL_SESSIONS) while the Rust tube kept running because +connection_state_changed=closed didn't call close_tube. + +Steps: + 1. Build a loopback WebRTC tunnel → SSH container (port 2222) + 2. Open a real SSH session through it (ssh sleep 60) + 3. Fire connection_state_changed=closed directly into the signal handler + (simulates ICE timeout / network drop — the path that previously leaked) + 4. Assert the SSH process dies within CLOSE_TIMEOUT_S + 5. Assert the tunnel session is unregistered from _GLOBAL_TUNNEL_SESSIONS +""" +import subprocess +import sys +import threading +import time +import logging + +import keeper_pam_connections +from keepercommander.commands.tunnel.port_forward.tunnel_helpers import ( + TunnelSignalHandler, + register_tunnel_session, + get_tunnel_session, + TunnelSession, + CloseConnectionReasons, +) + +SSH_HOST = "127.0.0.1" +SSH_PORT = 2222 +SSH_USER = "linuxuser" +SSH_PASS = "alpine" +CLOSE_TIMEOUT_S = 5 + +TEST_CALLBACK_TOKEN = "TEST_MODE_CALLBACK_TOKEN" +TEST_KSM_CONFIG = "TEST_MODE_KSM_CONFIG" + + +def build_loopback_tunnel(registry, peer_map, lock): + def signal_cb(sig): + try: + with lock: + kind = sig.get("kind") + tid = sig.get("tube_id") or sig.get("conversation_id") + peer = peer_map.get(tid) + if kind == "ice_candidate" and peer: + cand = sig.get("candidate") + if cand: + try: + registry.add_ice_candidate(peer, cand) + except Exception: + pass + except Exception: + pass + + server_info = registry.create_tube( + conversation_id="leak-test-server", + settings={"conversationType": "tunnel", "local_listen_addr": "127.0.0.1:0"}, + trickle_ice=False, + callback_token=TEST_CALLBACK_TOKEN, + krelay_server="test.relay.server.com", + client_version="ms16.5.0", + ksm_config=TEST_KSM_CONFIG, + signal_callback=signal_cb, + ) + server_id = server_info["tube_id"] + + client_info = registry.create_tube( + conversation_id="leak-test-client", + settings={ + "conversationType": "tunnel", + "target_host": SSH_HOST, + "target_port": str(SSH_PORT), + }, + trickle_ice=False, + callback_token=TEST_CALLBACK_TOKEN, + krelay_server="test.relay.server.com", + client_version="ms16.5.0", + ksm_config=TEST_KSM_CONFIG, + offer=server_info["offer"], + signal_callback=signal_cb, + ) + client_id = client_info["tube_id"] + + with lock: + peer_map[server_id] = client_id + peer_map[client_id] = server_id + + registry.set_remote_description(server_id, client_info["answer"], is_answer=True) + + deadline = time.time() + 20 + while time.time() < deadline: + try: + s = registry.get_tube_status(server_id) + c = registry.get_tube_status(client_id) + if s in ("ready", "active") and c in ("ready", "active"): + break + except Exception: + pass + time.sleep(0.1) + else: + print("FAIL: tunnel did not connect within 20s") + sys.exit(1) + + listen_addr = server_info.get("actual_local_listen_addr") + return server_id, client_id, listen_addr + + +def main(): + logging.basicConfig(level=logging.WARNING) + + registry = keeper_pam_connections.PyTubeRegistry() + peer_map = {} + lock = threading.Lock() + + print("[1] Building WebRTC loopback tunnel ...") + server_id, client_id, listen_addr = build_loopback_tunnel(registry, peer_map, lock) + tunnel_host, tunnel_port = listen_addr.rsplit(":", 1) + print(f" Tunnel ready — {listen_addr}") + time.sleep(0.3) + + print(f"[2] Opening SSH session through tunnel ...") + ssh_proc = subprocess.Popen( + [ + "sshpass", f"-p{SSH_PASS}", + "ssh", + "-p", tunnel_port, + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "ServerAliveInterval=5", + f"{SSH_USER}@{tunnel_host}", + "sleep 60", + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + time.sleep(2) + if ssh_proc.poll() is not None: + print(f"FAIL: SSH exited immediately (rc={ssh_proc.returncode})") + print(ssh_proc.stderr.read().decode()) + sys.exit(1) + print(f" SSH alive (pid={ssh_proc.pid})") + + # Register a minimal tunnel session so the signal handler can look it up + session = TunnelSession( + tube_id=server_id, + conversation_id="leak-test-server", + gateway_uid="test-gateway", + symmetric_key=None, + record_uid="test-record", + record_title="Test", + target_host=SSH_HOST, + target_port=str(SSH_PORT), + host=tunnel_host, + port=int(tunnel_port), + ) + register_tunnel_session(server_id, session) + + # Build the signal handler the same way the tunnel stack does + handler = TunnelSignalHandler( + params=None, + record_uid="test-record", + gateway_uid="test-gateway", + symmetric_key=None, + base64_nonce=None, + conversation_id="leak-test-server", + tube_registry=registry, + tube_id=server_id, + trickle_ice=False, + ) + session.signal_handler = handler + + print("[3] Firing connection_state_changed=closed into signal handler ...") + t_fire = time.time() + handler.signal_from_rust({ + "kind": "connection_state_changed", + "tube_id": server_id, + "data": "closed", + "conversation_id": "leak-test-server", + }) + + # Check 1: tunnel session must be unregistered + remaining_session = get_tunnel_session(server_id) + if remaining_session is not None: + print(f"FAIL: tunnel session still registered after connection_state_changed=closed") + ssh_proc.kill() + sys.exit(2) + print(f" Session unregistered ✓") + + # Check 2: SSH process must die within CLOSE_TIMEOUT_S + try: + ssh_proc.wait(timeout=CLOSE_TIMEOUT_S) + elapsed = time.time() - t_fire + print(f" SSH exited (rc={ssh_proc.returncode}) {elapsed:.1f}s after signal ✓") + except subprocess.TimeoutExpired: + elapsed = time.time() - t_fire + print(f"\nFAIL: SSH still alive {elapsed:.1f}s after connection_state_changed=closed — tunnel leaked") + ssh_proc.kill() + sys.exit(2) + + print("\nPASSED") + sys.exit(0) + + +if __name__ == "__main__": + main() From 94b49b80419787f14737b09c70034daa38de94a8 Mon Sep 17 00:00:00 2001 From: lthievenaz-keeper Date: Wed, 3 Jun 2026 13:54:27 +0100 Subject: [PATCH 10/23] Disable logging with silent flag (#2103) * Add --silent flag that disables all logging Some customers use the Keeper CLI within PowerShell scripts (not native PS module), and want to avoid noise in their logging. Added a --silent flag that launches Keeper with logging statements disabled. * Add silent flag to print statements --- keepercommander/__main__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/keepercommander/__main__.py b/keepercommander/__main__.py index cb69ef2c4..b627a7947 100644 --- a/keepercommander/__main__.py +++ b/keepercommander/__main__.py @@ -116,6 +116,7 @@ def show_brief_help(): print(' --password, -kp PASSWORD Master password for the account') print(' --config CONFIG Config file to use') print(' --debug Turn on debug mode') + print(' --silent Turn off all logging statements') print(' --batch-mode Run in batch/non-interactive mode') print(' --proxy PROXY Proxy server') print(' --new-login Force full login (bypass persistent login)') @@ -149,6 +150,7 @@ def show_brief_help(): parser.add_argument('--version', dest='version', action='store_true', help='Display version') parser.add_argument('--config', dest='config', action='store', help='Config file to use') parser.add_argument('--debug', dest='debug', action='store_true', help='Turn on debug mode') +parser.add_argument('--silent', dest='silent', action='store_true', help='Turn off all logging statements') parser.add_argument('--batch-mode', dest='batch_mode', action='store_true', help='Run commander in batch or basic UI mode.') parser.add_argument('--launched-with-shortcut', '-lwsc', dest='launched_with_shortcut', action='store', help='Indicates that the app was launched using a shortcut, for example using Mac App or from ' @@ -223,7 +225,7 @@ def main(from_package=False): value_main_parser_args = ['--config', '--server', '--user', '--password', '--launched-with-shortcut', '--proxy', '--data-dir', '-ks', '-ku', '-kp', '-lwsc'] - bool_main_parser_args = ['--version', '--debug', '--batch-mode', + bool_main_parser_args = ['--version', '--debug', '--silent', '--batch-mode', '--unmask-all', '--fail-on-throttle', '--new-login', '--config-file'] main_parser_args = value_main_parser_args + bool_main_parser_args @@ -250,6 +252,9 @@ def main(from_package=False): if opts.launched_with_shortcut: os.chdir(Path.home()) + if opts.silent: + logging.disable(logging.CRITICAL) + params = get_params_from_config(opts.config, opts.launched_with_shortcut, opts.data_dir) if opts.batch_mode: From 70682dbf45ad6dc21ba9eb8267c090404a9ad3ef Mon Sep 17 00:00:00 2001 From: sshrushanth-ks Date: Wed, 3 Jun 2026 19:51:15 +0530 Subject: [PATCH 11/23] KC-1261, KC-1267 & KC-1291: Fix: consistent JSON response across get, ls, and nsf-get commands (#2105) * KC-1261, KC-1267 & KC-1291: Fix: consistent folder JSON response across get, ls, and nsf-get commands (#2092) * Fix: consistent folder JSON response across get, ls, and nsf-get commands * enrich ls and folder get responses with location block and record names * addressed PR review comments * --include-dag flag update * added source discriminator to record get JSON response * added folder location block to record get and nsf-get JSON responses, and fixed --include-dag flag issue * included NSF records in ls output and standardize record source to nested/classic --- keepercommander/commands/folder.py | 95 +++++++++++++------ .../nested_share_folder/display_commands.py | 73 ++++++++++++-- .../commands/nested_share_folder/helpers.py | 23 +++-- .../commands/nested_share_folder/parsers.py | 3 + keepercommander/commands/record.py | 65 ++++++++++--- unit-tests/test_nested_share_folder.py | 12 ++- 6 files changed, 213 insertions(+), 58 deletions(-) diff --git a/keepercommander/commands/folder.py b/keepercommander/commands/folder.py index 8a8e33321..f473eb754 100644 --- a/keepercommander/commands/folder.py +++ b/keepercommander/commands/folder.py @@ -188,6 +188,56 @@ def chunk_list(l, n): for i in range(0, len(l), n): yield l[i:i + n] + @staticmethod + def _collect_nsf_record_uids(params, folder_uid, recursive_search): + nsf_folders = getattr(params, 'nested_share_folders', {}) + nsf_folder_records = getattr(params, 'nested_share_folder_records', {}) + nsf_records = getattr(params, 'nested_share_records', {}) + if not nsf_records: + return set() + + if folder_uid in nsf_folders: + from .nested_share_folder.helpers import collect_records_in_folder + return set(collect_records_in_folder(params, folder_uid, recursive=bool(recursive_search))) + + record_uids = set() + if recursive_search: + record_uids.update(nsf_records.keys()) + elif not folder_uid: + for fuid, rec_set in nsf_folder_records.items(): + if fuid not in nsf_folders: + record_uids.update(rec_set) + return record_uids + + @staticmethod + def _load_record_for_ls(params, uid): + for cache in (getattr(params, 'nested_share_records', {}), params.record_cache or {}): + if uid not in cache: + continue + cached = cache[uid] + if cached.get('version', 0) not in (2, 3): + continue + r = vault.KeeperRecord.load(params, cached) + if r: + return r + + nsf_record_data = getattr(params, 'nested_share_record_data', {}) + if uid in nsf_record_data and 'data_json' in nsf_record_data[uid]: + dj = nsf_record_data[uid]['data_json'] + rec = vault.TypedRecord(version=3) + rec.record_uid = uid + rec.title = dj.get('title', uid) + rec.record_type = dj.get('type', '') + rec.load_record_data(dj, None) + return rec + return None + + @staticmethod + def _record_source(params, record_uid): + if hasattr(params, 'nested_share_records') and record_uid in params.nested_share_records: + return 'nested' + return 'classic' + def get_parser(self): return ls_parser @@ -225,25 +275,20 @@ def execute(self, params, **kwargs): if any(filter(lambda x: regex(x) is not None, FolderListCommand.folder_match_strings(f))) if regex is not None else True: folders.append(f) - if show_records and params.record_cache: - if folder_uid in params.subfolder_record_cache or recursive_search: + if show_records: + record_uids = set() + if params.record_cache and (folder_uid in params.subfolder_record_cache or recursive_search): record_uids_by_folder = get_contained_record_uids(params, folder_uid, not recursive_search) - record_uids = {rec_uid for recs in record_uids_by_folder.values() for rec_uid in recs} - for uid in record_uids: - if uid not in params.record_cache: - continue - rec = params.record_cache[uid] - rv = rec.get('version', 0) - if rv not in (2, 3): - continue # skip fileRef and application records - they use file-report command - - r = vault.KeeperRecord.load(params, rec) - if not r: - continue + record_uids.update(rec for recs in record_uids_by_folder.values() for rec in recs) + record_uids.update(FolderListCommand._collect_nsf_record_uids(params, folder_uid, recursive_search)) - if regex and not regex(r.title): - continue - records.append(r) + for uid in record_uids: + r = FolderListCommand._load_record_for_ls(params, uid) + if not r: + continue + if regex and not regex(r.title): + continue + records.append(r) if len(folders) == 0 and len(records) == 0: if pattern: @@ -252,7 +297,7 @@ def execute(self, params, **kwargs): if show_detail: # Helper function to get folder flags def folder_flags(f): - if f.type == 'shared_folder': + if f.type == BaseFolderNode.SharedFolderType: flags = 'S' else: flags = '' @@ -266,15 +311,13 @@ def folder_flags(f): for f in folders: # Check if folder is from Nested Share Folder is_nested_share = hasattr(params, 'nested_share_folders') and f.uid in params.nested_share_folders - source = 'Nested Share Folder' if is_nested_share else 'Legacy' + source = 'nested_share_folder' if is_nested_share else 'classic_folder' row = ['folder', f.uid, f.name, f'Flags: {folder_flags(f)}, Parent: {f.parent_uid or "/"}', source] combined_table.append(row) if len(records) > 0: for record in records: - # Check if record is from Nested Share Folder - is_nested_share = hasattr(params, 'nested_share_records') and record.record_uid in params.nested_share_records - source = 'Nested' if is_nested_share else 'Legacy' + source = FolderListCommand._record_source(params, record.record_uid) row = ['record', record.record_uid, record.title, f'Type: {record.record_type}, Description: {vault_extensions.get_record_description(record)}', source] combined_table.append(row) @@ -292,7 +335,7 @@ def folder_flags(f): colors[f.name] = f.color # Check if folder is from Nested Share Folder is_nested_share = hasattr(params, 'nested_share_folders') and f.uid in params.nested_share_folders - source = 'Nested Share Folder' if is_nested_share else 'Legacy' + source = 'nested_share_folder' if is_nested_share else 'classic_folder' row = [f.uid, f.name, folder_flags(f), f.parent_uid or '/', source] table.append(row) table.sort(key=lambda x: (x[1] or '').lower()) @@ -308,9 +351,7 @@ def folder_flags(f): table = [] headers = ['record_uid', 'type', 'title', 'description', 'source'] for record in records: - # Check if record is from Nested Share Folder - is_nested_share = hasattr(params, 'nested_share_records') and record.record_uid in params.nested_share_records - source = 'Nested' if is_nested_share else 'Legacy' + source = FolderListCommand._record_source(params, record.record_uid) row = [record.record_uid, record.record_type, record.title, vault_extensions.get_record_description(record), source] table.append(row) table.sort(key=lambda x: (x[2] or '').lower()) @@ -1269,7 +1310,7 @@ def execute(self, params, **kwargs): for f_uid in params.root_folder.subfolders: if folder_uid: f = params.folder_cache[folder_uid] - if f.type != 'user_folder': + if f.type != BaseFolderNode.UserFolderType: raise CommandError(self.get_parser().prog, f'\"{f.name}\" cannot be shared folder') break if f_uid == folder: diff --git a/keepercommander/commands/nested_share_folder/display_commands.py b/keepercommander/commands/nested_share_folder/display_commands.py index 733628f98..7f67039c1 100644 --- a/keepercommander/commands/nested_share_folder/display_commands.py +++ b/keepercommander/commands/nested_share_folder/display_commands.py @@ -20,13 +20,15 @@ import logging from ..base import Command +from ..record import RecordGetUidCommand from ...error import CommandError from ... import nested_share_folder as _nsf +from ... import vault from .helpers import ( RECORD_PERM_LABELS, FOLDER_PERM_LABELS, get_access_role_label, format_role_display, format_timestamp, load_record_metadata, command_error_handler, - ensure_nested_share_record, + ensure_nested_share_record, collect_records_in_folder, ROOT_FOLDER_UID, ) from .parsers import ( nested_share_record_get_details_parser, @@ -91,10 +93,11 @@ def get_parser(self): return nested_share_get_parser def execute(self, params, **kwargs): - uid = (kwargs.get('uid') or '').strip() - fmt = kwargs.get('format') or 'detail' - verbose = kwargs.get('verbose', False) - unmask = kwargs.get('unmask', False) + uid = (kwargs.get('uid') or '').strip() + fmt = kwargs.get('format') or 'detail' + verbose = kwargs.get('verbose', False) + unmask = kwargs.get('unmask', False) + include_dag = kwargs.get('include_dag', False) if not uid: raise CommandError('nsf-get', 'UID parameter is required') @@ -115,8 +118,10 @@ def execute(self, params, **kwargs): "sensitive field values will be displayed on stdout only.", resolved, ) - (self._record_json if fmt == 'json' else self._record_detail)( - params, resolved, verbose, unmask) + if fmt == 'json': + self._record_json(params, resolved, verbose, unmask, include_dag=include_dag) + else: + self._record_detail(params, resolved, verbose, unmask) return raise CommandError('nsf-get', f'Cannot find any Nested Share Folder object with UID or title: {uid}') @@ -231,7 +236,7 @@ def _extract_field_value(fields, field_type): ', '.join(f'{k}: {v}' for k, v in val.items() if v) return '' - def _record_json(self, params, record_uid, verbose, _unmask=False): + def _record_json(self, params, record_uid, verbose, _unmask=False, include_dag=False): meta = load_record_metadata(params, record_uid) ro = { 'record_uid': record_uid, 'title': meta['title'], @@ -275,7 +280,15 @@ def _record_json(self, params, record_uid, verbose, _unmask=False): if share_admins: ro['share_admins'] = share_admins except Exception as e: - logger.debug('Could not retrieve share admins: %s', e) + logger.error('Could not retrieve share admins: %s', e) + + if include_dag: + try: + r = vault.KeeperRecord.load(params, record_uid) + if r: + RecordGetUidCommand().include_dag(params, ro, r) + except Exception as e: + logger.error('Could not retrieve DAG info for record %s: %s', record_uid, e) print(json.dumps(ro, indent=2)) @@ -391,7 +404,34 @@ def _folder_json(params, folder_uid, verbose): owner_username = fobj.get('owner_username') owner_account_uid = fobj.get('owner_account_uid') - fo = {'nested_share_folder_uid': folder_uid, 'name': name} + nsf_folders = getattr(params, 'nested_share_folders', {}) + raw_parent = fobj.get('parent_uid') or '' + # Treat the parent as root if it is absent, a known root sentinel, or not + # a real NSF folder entry (some vaults use server-specific root UIDs). + is_root_parent = not raw_parent or raw_parent == ROOT_FOLDER_UID or raw_parent not in nsf_folders + parent_uid = None if is_root_parent else raw_parent + + def _nsf_folder_path(uid): + """Build display path for an NSF folder by walking up the hierarchy.""" + parts = [] + cur = uid + while cur and cur in nsf_folders: + obj = nsf_folders[cur] + parts.append(obj.get('name', cur)) + p = obj.get('parent_uid') or '' + cur = None if (not p or p not in nsf_folders) else p + return '/'.join(reversed(parts)) + + fo = { + 'folder_uid': folder_uid, + 'type': 'nested_share_folder', + 'name': name, + 'parent_uid': parent_uid, + 'folder': { + 'uid': parent_uid, + 'path': _nsf_folder_path(parent_uid) if parent_uid else '/' + } + } if owner_username: fo['owner'] = owner_username @@ -436,6 +476,19 @@ def _folder_json(params, folder_uid, verbose): except Exception as e: logger.debug('Could not retrieve folder access: %s', e) + try: + record_uids = collect_records_in_folder(params, folder_uid, recursive=False) + records_list = [] + for r_uid in record_uids: + entry = {'record_uid': r_uid} + rec = vault.KeeperRecord.load(params, r_uid) + if rec: + entry['record_name'] = rec.title + records_list.append(entry) + fo['records'] = records_list + except Exception as e: + logger.debug('Could not retrieve records for NSF folder %s: %s', folder_uid, e) + print(json.dumps(fo, indent=2)) @staticmethod diff --git a/keepercommander/commands/nested_share_folder/helpers.py b/keepercommander/commands/nested_share_folder/helpers.py index 1f6626a47..c6624b137 100644 --- a/keepercommander/commands/nested_share_folder/helpers.py +++ b/keepercommander/commands/nested_share_folder/helpers.py @@ -241,17 +241,26 @@ def classify_share_recipient(params, recipient): def find_folder_location(params, record_uid): - """Return the display name of the first folder containing *record_uid*.""" + """Return a {uid, path} dict for the first NSF folder containing *record_uid*.""" nsf_folder_records = getattr(params, 'nested_share_folder_records', {}) nsf_folders = getattr(params, 'nested_share_folders', {}) + + def _build_path(fuid): + parts = [] + cur = fuid + while cur and cur in nsf_folders and cur != ROOT_FOLDER_UID: + obj = nsf_folders[cur] + parts.append(obj.get('name', cur)) + p = obj.get('parent_uid') or '' + cur = None if (not p or p not in nsf_folders) else p + return '/'.join(reversed(parts)) + for fuid, rec_set in nsf_folder_records.items(): if record_uid in rec_set: - if fuid == ROOT_FOLDER_UID: - return 'root' - if fuid in nsf_folders: - return nsf_folders[fuid].get('name', fuid) - return fuid - return '' + if fuid == ROOT_FOLDER_UID or fuid not in nsf_folders: + return {'uid': None, 'path': '/'} + return {'uid': fuid, 'path': _build_path(fuid)} + return None def collect_records_in_folder(params, folder_uid, recursive=False): diff --git a/keepercommander/commands/nested_share_folder/parsers.py b/keepercommander/commands/nested_share_folder/parsers.py index 0d0e821b0..9d09692be 100644 --- a/keepercommander/commands/nested_share_folder/parsers.py +++ b/keepercommander/commands/nested_share_folder/parsers.py @@ -351,3 +351,6 @@ def _make_parser(prog, description): nested_share_get_parser.add_argument( '--unmask', dest='unmask', action='store_true', default=False, help='Reveal masked field values (passwords, secrets)') +nested_share_get_parser.add_argument( + '--include-dag', dest='include_dag', action='store_true', default=False, + help='Include DAG/GraphSync information in json output (PAM record types only)') diff --git a/keepercommander/commands/record.py b/keepercommander/commands/record.py index 422243d21..bf1d70875 100644 --- a/keepercommander/commands/record.py +++ b/keepercommander/commands/record.py @@ -307,10 +307,18 @@ def execute(self, params, **kwargs): sf = api.get_shared_folder(params, uid) if fmt == 'json': path = get_folder_path(params, sf.shared_folder_uid, delimiter=os.sep) if sf.shared_folder_uid else '' + sf_node = params.folder_cache.get(sf.shared_folder_uid) if sf.shared_folder_uid else None + parent_uid = sf_node.parent_uid if sf_node and sf_node.parent_uid else None sfo = { - "shared_folder_uid": sf.shared_folder_uid, + "folder_uid": sf.shared_folder_uid, + "type": "classic_folder", "name": sf.name, "path": path, + "parent_uid": parent_uid, + "folder": { + "uid": parent_uid, + "path": get_folder_path(params, parent_uid) if parent_uid else "/" + }, "manage_users": sf.default_manage_users, "manage_records": sf.default_manage_records, "can_edit": sf.default_can_edit, @@ -364,20 +372,42 @@ def _format_expiration(expiration_value): if uid in params.folder_cache: f = params.folder_cache[uid] if fmt == 'json': + folder_type = 'nested_share_folder' if f.type == BaseFolderNode.NestedShareFolderType else 'classic_folder' + parent_uid = f.parent_uid or None fo = { 'folder_uid': f.uid, - 'type': f.type, - 'name': f.name + 'type': folder_type, + 'name': f.name, + 'path': get_folder_path(params, f.uid), + 'parent_uid': parent_uid, + 'folder': { + 'uid': parent_uid, + 'path': get_folder_path(params, parent_uid) if parent_uid else '/' + } } - if isinstance(f, (subfolder.SharedFolderFolderNode, subfolder.SharedFolderNode)): - fo['shared_folder_uid'] = f.shared_folder_uid if isinstance(f, subfolder.SharedFolderFolderNode) \ - else f.uid - if f.parent_uid: - fo['parent_folder_uid'] = f.parent_uid + if isinstance(f, subfolder.SharedFolderFolderNode): + fo['shared_folder_uid'] = f.shared_folder_uid if f.type == BaseFolderNode.UserFolderType: - fo['path'] = get_folder_path(params, f.uid) record_uids = params.subfolder_record_cache.get(f.uid, set()) - fo['records'] = [{'record_uid': r} for r in record_uids] + records_list = [] + for r_uid in record_uids: + entry = {'record_uid': r_uid} + rec = vault.KeeperRecord.load(params, r_uid) + if rec: + entry['record_name'] = rec.title + records_list.append(entry) + fo['records'] = records_list + elif f.type == BaseFolderNode.NestedShareFolderType: + from .nested_share_folder.helpers import collect_records_in_folder + record_uids = collect_records_in_folder(params, f.uid, recursive=False) + records_list = [] + for r_uid in record_uids: + entry = {'record_uid': r_uid} + rec = vault.KeeperRecord.load(params, r_uid) + if rec: + entry['record_name'] = rec.title + records_list.append(entry) + fo['records'] = records_list print(json.dumps(fo, indent=2)) else: f.display() @@ -448,9 +478,11 @@ def _format_expiration(expiration_value): if r: params.queue_audit_event('open_record', record_uid=uid) if fmt == 'json': - + is_nsf_record = (hasattr(params, 'nested_share_records') + and uid in getattr(params, 'nested_share_records', {})) ro = { 'record_uid': uid, + 'source': 'nested' if is_nsf_record else 'classic', } if version < 3 or kwargs.get('legacy') is True: ro['title'] = r.title @@ -524,6 +556,17 @@ def _format_expiration(expiration_value): if version == 3 and kwargs.get('include_dag') is True: self.include_dag(params, ro, r) + if is_nsf_record: + from .nested_share_folder.helpers import find_folder_location + folder_loc = find_folder_location(params, uid) + ro['folder'] = folder_loc if folder_loc else {'uid': None, 'path': '/'} + else: + folder_uid = next(find_folders(params, uid), None) + ro['folder'] = { + 'uid': folder_uid, + 'path': get_folder_path(params, folder_uid) if folder_uid else '/' + } + print(json.dumps(ro, indent=2)) elif fmt == 'password': if r.password: diff --git a/unit-tests/test_nested_share_folder.py b/unit-tests/test_nested_share_folder.py index ca16cdb7a..02d2817ec 100644 --- a/unit-tests/test_nested_share_folder.py +++ b/unit-tests/test_nested_share_folder.py @@ -156,10 +156,16 @@ def test_find_folder_location(self): nested_share_folder_records={fuid: {ruid}}, nested_share_folders={fuid: fobj}, ) - self.assertEqual(find_folder_location(params, ruid), 'Docs') + result = find_folder_location(params, ruid) + self.assertIsInstance(result, dict) + self.assertEqual(result['uid'], fuid) + self.assertEqual(result['path'], 'Docs') params2 = _make_params(nested_share_folder_records={ROOT_FOLDER_UID: {ruid}}) - self.assertEqual(find_folder_location(params2, ruid), 'root') - self.assertEqual(find_folder_location(_make_params(), 'missing'), '') + result2 = find_folder_location(params2, ruid) + self.assertIsInstance(result2, dict) + self.assertIsNone(result2['uid']) + self.assertEqual(result2['path'], '/') + self.assertIsNone(find_folder_location(_make_params(), 'missing')) def test_load_record_metadata_from_cache(self): from keepercommander.commands.nested_share_folder.helpers import load_record_metadata From bffc0de172be17b0d79642f2635bca133038676d Mon Sep 17 00:00:00 2001 From: pvagare-ks Date: Wed, 3 Jun 2026 19:53:04 +0530 Subject: [PATCH 12/23] Import users and teams in cyberark and download membership support for cyberark (#2112) --- keepercommander/importer/commands.py | 5 +- keepercommander/importer/cyberark/__init__.py | 4 +- keepercommander/importer/cyberark/cyberark.py | 2238 ++++++++++++++++- .../cyberark_portal/cyberark_portal.py | 511 ++-- 4 files changed, 2503 insertions(+), 255 deletions(-) diff --git a/keepercommander/importer/commands.py b/keepercommander/importer/commands.py index 312717d99..8563dd0c4 100644 --- a/keepercommander/importer/commands.py +++ b/keepercommander/importer/commands.py @@ -105,7 +105,7 @@ def register_command_info(aliases, command_info): download_membership_parser = argparse.ArgumentParser(prog='download-membership', description='Unload shared folder membership to a JSON file') -download_membership_parser.add_argument('--source', dest='source', choices=['keeper', 'lastpass', 'thycotic'], required=True, help='Shared folder membership source') +download_membership_parser.add_argument('--source', dest='source', choices=['keeper', 'lastpass', 'thycotic', 'cyberark'], required=True, help='Shared folder membership source') download_membership_parser.add_argument('--folder', dest='folder', action='store', help='import into a separate folder.') download_membership_parser.add_argument('-p', '--permissions', dest='permissions', action='store', help='force shared folder permissions: manage (U)sers, manage (R)ecords') download_membership_parser.add_argument('-r', '--restrictions', dest='restrictions', action='store', help='force shared folder restrictions: manage (U)sers, manage (R)ecords') @@ -463,6 +463,9 @@ def get_parser(self): def execute(self, params, **kwargs): file_name = kwargs.get('name') or 'shared_folder_membership.json' + if not os.path.exists(file_name): + logging.warning('Shared folder membership file "%s" not found', file_name) + return shared_folders = [] # type: List[SharedFolder] teams = [] # type: List[Team] diff --git a/keepercommander/importer/cyberark/__init__.py b/keepercommander/importer/cyberark/__init__.py index e2ab88fc7..a11f2378f 100644 --- a/keepercommander/importer/cyberark/__init__.py +++ b/keepercommander/importer/cyberark/__init__.py @@ -1,3 +1,3 @@ -from .cyberark import CyberArkImporter as Importer +from .cyberark import CyberArkImporter as Importer, CyberArkMembershipDownload as MembershipDownload -__all__ = ["Importer"] +__all__ = ["Importer", "MembershipDownload"] diff --git a/keepercommander/importer/cyberark/cyberark.py b/keepercommander/importer/cyberark/cyberark.py index d5669d97a..2620d17ce 100644 --- a/keepercommander/importer/cyberark/cyberark.py +++ b/keepercommander/importer/cyberark/cyberark.py @@ -1,5 +1,13 @@ +import asyncio +import json +import logging +import os import re import requests +import stat +import tempfile +import warnings +from contextlib import contextmanager from http import HTTPStatus from os import environ, path from prompt_toolkit import HTML, print_formatted_text, prompt @@ -7,10 +15,246 @@ from prompt_toolkit.styles import Style from tabulate import tabulate from time import sleep +from typing import List from urllib.parse import parse_qsl +from urllib3.exceptions import InsecureRequestWarning +from ... import api, crypto, utils +from ...commands.enterprise_common import EnterpriseCommand +from ...constants import EMAIL_PATTERN +from ..importer import ( + BaseDownloadMembership, + BaseImporter, + Permission, + Record, + RecordField, + SharedFolder, + Team, +) -from ..importer import BaseImporter, SharedFolder, Record, RecordField + +class _ExecutorShutdownLogFilter(logging.Filter): + """Drop the harmless ``Executor shutdown has been called`` records. + """ + + _NEEDLE = "Executor shutdown has been called" + + def filter(self, record): # noqa: D401 - logging.Filter API + try: + if self._NEEDLE in record.getMessage(): + return False + exc_info = record.exc_info + if exc_info and exc_info[1] is not None and self._NEEDLE in str(exc_info[1]): + return False + except Exception: + # Never let the filter itself break logging. + return True + return True + + +@contextmanager +def _suppress_progressbar_executor_noise(): + """Silence the harmless ``Executor shutdown has been called`` tracebacks. + """ + + def _filter_handler(loop, context): + exc = context.get("exception") + message = context.get("message", "") or "" + if isinstance(exc, RuntimeError) and "Executor shutdown" in str(exc): + return + if "Executor shutdown" in message: + return + loop.default_exception_handler(context) + + previous_handler = None + loop = None + log_filter = _ExecutorShutdownLogFilter() + asyncio_logger = logging.getLogger("asyncio") + asyncio_logger.addFilter(log_filter) + try: + try: + loop = asyncio.get_event_loop() + except RuntimeError: + loop = None + if loop is not None: + previous_handler = loop.get_exception_handler() + loop.set_exception_handler(_filter_handler) + yield + finally: + if loop is not None: + loop.set_exception_handler(previous_handler) + asyncio_logger.removeFilter(log_filter) + + +# CyberArk user types that identify non-human / service accounts. +_SERVICE_ACCOUNT_USER_TYPES = frozenset( + t.lower() + for t in ( + "Built-InAdmins", + "CyberArkServiceUser", + "SaaSSRV", + "AppProvider", + "CPM", + "PVWAGWAccounts", + "PVWAGWUser", + "PSM", + "PSMUser", + "PSMAppUser", + "PSMGWUser", + "NotificationEngine", + "ENE", + "AIMAccount", + "AIMWebService", + ) +) + +# Strings that identify non-human / service accounts in ``userType`` and ``source``. +_SERVICE_ACCOUNT_SUBSTRINGS = ( + "service", + "component", + "gateway", + "appuser", + "appprovider", + "psm", + "cpm", + "pvwa", + "telemetry", +) + + +def _is_service_account_user(user): + """Heuristically decide whether a CyberArk user dict is a service account. + + Accepts either the normalised dict produced by + :meth:`CyberArkImporter.fetch_cyberark_users` (lowercase keys) or the raw + PVWA payload (PascalCase keys), so it is safe to call from any code path. + """ + + if not isinstance(user, dict): + return False + + if user.get("componentUser") is True or user.get("ComponentUser") is True: + return True + + user_type = ( + user.get("type") + or user.get("userType") + or user.get("UserType") + or "" + ) + source = user.get("source") or user.get("Source") or "" + + user_type_lc = str(user_type).strip().lower() + source_lc = str(source).strip().lower() + + if user_type_lc and user_type_lc in _SERVICE_ACCOUNT_USER_TYPES: + return True + if source_lc and source_lc in _SERVICE_ACCOUNT_USER_TYPES: + return True + + for needle in _SERVICE_ACCOUNT_SUBSTRINGS: + if needle in user_type_lc or needle in source_lc: + return True + + + username = (user.get("username") or user.get("UserName") or "").strip() + if username.endswith("$"): + return True + + return False + + +class PermissionMapper: + """Maps CyberArk safe member permissions to Keeper shared folder permission tiers. + + """ + + ALL_PERMISSIONS = { + "useAccounts", "retrieveAccounts", "listAccounts", + "addAccounts", "updateAccountContent", "updateAccountProperties", + "renameAccounts", "deleteAccounts", + "manageSafe", "manageSafeMembers", "viewSafeMembers", + "viewAuditLog", "backupSafe", "unlockAccounts", + "initiateCPMAccountManagementOperations", "specifyNextAccountContent", + "createFolders", "deleteFolders", "moveAccountsAndFolders", + "accessWithoutConfirmation", + "requestsAuthorizationLevel1", "requestsAuthorizationLevel2", + "isExpiredMembershipEnable", "isReadOnly", + } + + UNMAPPED_PERMISSIONS = { + "accessWithoutConfirmation", + "requestsAuthorizationLevel1", + "requestsAuthorizationLevel2", + } + + @staticmethod + def map_permissions(perms): + """Map CyberArk permission booleans to Keeper shared folder permissions. + + Returns dict with keys: manage_users, manage_records, can_edit, can_share. + """ + if not isinstance(perms, dict): + return {"manage_users": False, "manage_records": False, + "can_edit": False, "can_share": False} + + # Tier 1: View — can list and use accounts + has_view = (perms.get("useAccounts", False) + and perms.get("listAccounts", False)) + + # Tier 2: Edit — can modify account content + has_edit = (has_view + and perms.get("addAccounts", False) + and (perms.get("updateAccountContent", False) + or perms.get("updateAccountProperties", False))) + + # Tier 3: Manage — full safe administration + has_manage = (has_edit + and perms.get("manageSafe", False) + and perms.get("manageSafeMembers", False)) + + return { + "manage_users": has_manage, + "manage_records": has_edit or has_manage, + "can_edit": has_edit or has_manage, + "can_share": has_manage, + } + + @staticmethod + def get_unmapped_permissions(perms): + """Return list of CyberArk permissions that have no Keeper equivalent.""" + if not isinstance(perms, dict): + return [] + return [ + p for p in PermissionMapper.UNMAPPED_PERMISSIONS + if perms.get(p, False) + ] + + @staticmethod + def map_member(member): + """Map a CyberArk safe member to a Keeper shared folder permission entry. + + Returns dict with: name, member_type ('user'|'team'), permissions dict, + unmapped list, and the raw memberName for matching. + """ + name = member.get("memberName", "") + member_type = member.get("memberType", "User") + perms = member.get("permissions", {}) + + # Warn on unexpected member types (CyberArk currently uses "User" and "Group") + if member_type not in ("User", "Group"): + logging.warning('Unexpected member type "%s" for member "%s" — treating as user', + member_type, name) + + keeper_perms = PermissionMapper.map_permissions(perms) + unmapped = PermissionMapper.get_unmapped_permissions(perms) + + return { + "name": name, + "member_type": "team" if member_type == "Group" else "user", + "permissions": keeper_perms, + "unmapped_permissions": unmapped, + } class CyberArkImporter(BaseImporter): @@ -22,138 +266,842 @@ class CyberArkImporter(BaseImporter): "account_password": "Accounts/{account_id}/Password/Retrieve", "logon": "Auth/{type}/Logon", "safes": "Safes", + "user_groups": "UserGroups", + "user_group": "UserGroups/{group_id}", + "users": "Users", + "user": "Users/{user_id}", } # Request timeout in seconds TIMEOUT = 10 + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self._client_cert = None + self._tmp_cert_files = [] + # ``verify`` value used for every PVWA request. Defaults to False (self-hosted PVWAs typically use a private CA), but can be overridden by the ``_CYBERARK_CA_BUNDLE`` env var to point at a CA file/dir. + self._verify_tls = False + @classmethod def get_url(cls, pvwa_host, endpoint): return f"https://{pvwa_host}/PasswordVault/API/{cls.ENDPOINTS[endpoint]}" - @classmethod - def get_response(cls, url, authorization_token, query_params): - return requests.get( - url, - headers={ - "Authorization": authorization_token, - "Content-Type": "application/json", - }, - params=query_params, - timeout=cls.TIMEOUT, + @staticmethod + def _request(method, url, **kwargs): + """Send a request, scoping private-CA TLS warning suppression per call.""" + if kwargs.get("verify") is False: + # Self-hosted PVWAs often use a private CA; verify=False is intentional there. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", InsecureRequestWarning) + return requests.request(method, url, **kwargs) + return requests.request(method, url, **kwargs) + + def _load_p12_client_cert(self, p12_path, p12_password): + """Convert a PKCS#12 bundle to a temporary PEM cert+key pair for ``requests``. + + ``requests`` does not accept ``.p12``/``.pfx`` files directly — it needs + a PEM certificate and a PEM private key. We use the ``cryptography`` + library (already a Commander dependency) to decrypt the P12 in memory + and then write the cert chain and unencrypted private key to two + temporary files. The files are tracked on ``self._tmp_cert_files`` so + that ``do_import`` can delete them when the import finishes. + + Returns the ``(certfile, keyfile)`` tuple on success or ``None`` on + any failure (bad path, wrong passphrase, malformed P12, missing private + key, etc.) — the caller logs and aborts in that case. + """ + try: + from cryptography.hazmat.primitives import serialization + from cryptography.hazmat.primitives.serialization import pkcs12 + except ImportError as e: + print_formatted_text( + HTML( + "cryptography package is required for P12 client certificates: " + f"{e}" + ) + ) + return None + try: + with open(p12_path, "rb") as fh: + p12_bytes = fh.read() + except OSError as e: + print_formatted_text( + HTML(f"Could not read P12 file {p12_path}: {e}") + ) + return None + password_bytes = p12_password.encode("utf-8") if p12_password else None + try: + private_key, cert, additional_certs = pkcs12.load_key_and_certificates( + p12_bytes, password_bytes + ) + except ValueError as e: + print_formatted_text( + HTML( + "Failed to decode P12 bundle — check the file " + f"and passphrase: {e}" + ) + ) + return None + if private_key is None or cert is None: + print_formatted_text( + HTML( + "P12 bundle is missing a private key or certificate; " + "client-certificate authentication requires both." + ) + ) + return None + + + cert_fd, cert_path = tempfile.mkstemp(prefix="ca_pvwa_", suffix=".pem") + key_fd, key_path = tempfile.mkstemp(prefix="ca_pvwa_", suffix=".key.pem") + try: + with os.fdopen(cert_fd, "wb") as cf: + cf.write(cert.public_bytes(serialization.Encoding.PEM)) + for extra in additional_certs or []: + cf.write(extra.public_bytes(serialization.Encoding.PEM)) + with os.fdopen(key_fd, "wb") as kf: + kf.write( + private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + ) + try: + os.chmod(cert_path, stat.S_IRUSR | stat.S_IWUSR) + os.chmod(key_path, stat.S_IRUSR | stat.S_IWUSR) + except OSError: + pass + except OSError as e: + print_formatted_text( + HTML(f"Failed to materialise P12 to PEM: {e}") + ) + for p in (cert_path, key_path): + try: + os.remove(p) + except OSError: + pass + return None + + self._tmp_cert_files.extend([cert_path, key_path]) + return cert_path, key_path + + def _cleanup_tmp_cert_files(self): + """Delete every temporary PEM file written by ``_load_p12_client_cert``.""" + for p in self._tmp_cert_files: + try: + os.remove(p) + except OSError: + pass + self._tmp_cert_files = [] + self._client_cert = None + + def _maybe_configure_client_cert(self, pvwa_host): + """Prompt for / load a P12 client cert when targeting a self-hosted PVWA. + """ + if pvwa_host.endswith(".cyberark.cloud"): + self._verify_tls = True + return True + + ca_bundle = environ.get("_CYBERARK_CA_BUNDLE") + if ca_bundle: + if not path.isfile(ca_bundle) and not path.isdir(ca_bundle): + print_formatted_text( + HTML( + f"_CYBERARK_CA_BUNDLE points to {ca_bundle} " + "which does not exist; falling back to verify=False." + ) + ) + else: + self._verify_tls = ca_bundle + + p12_path = environ.get("_CYBERARK_CLIENT_CERT_P12") + if p12_path is None: + try: + entered = prompt( + "CyberArk PVWA client certificate P12 path (leave empty if none): " + ).strip() + except (EOFError, KeyboardInterrupt): + return False + p12_path = entered or None + if not p12_path: + return True + if not path.isfile(p12_path): + print_formatted_text( + HTML(f"P12 file not found: {p12_path}") + ) + return False + p12_password = environ.get("_CYBERARK_CLIENT_CERT_PASSWORD") + if p12_password is None: + p12_password = prompt("P12 passphrase: ", is_password=True) + cert_tuple = self._load_p12_client_cert(p12_path, p12_password) + if cert_tuple is None: + return False + self._client_cert = cert_tuple + print_formatted_text( + HTML("Loaded PVWA client certificate from P12 — using mutual TLS.") + ) + return True + + def get_response(self, url, authorization_token, query_params): + """GET helper that surfaces connection errors as a graceful warning instead of a stack trace. + + Returns the ``requests.Response`` on success, or ``None`` if the request could not be sent + (DNS failure, connection refused, timeout, etc.). Callers should treat ``None`` the same as + a non-200 response and stop processing. + """ + try: + return self._request( + "GET", + url, + headers={ + "Authorization": authorization_token, + "Content-Type": "application/json", + }, + params=query_params, + timeout=self.TIMEOUT, + cert=self._client_cert, + verify=self._verify_tls, + ) + except requests.exceptions.RequestException as e: + print_formatted_text( + HTML(f"Request to {url} failed: {e}") + ) + return None + + @staticmethod + def _extract_user_email(user): + """Return the best available email for a CyberArk user dict, or empty string.""" + internet = user.get("internet") or user.get("Internet") or {} + for key in ("businessEmail", "homeEmail", "otherEmail"): + value = internet.get(key) + if value and str(value).strip(): + return str(value).strip() + # Some endpoints (e.g. Get logged on user) expose a flat Email field. + for key in ("email", "Email"): + value = user.get(key) + if value and str(value).strip(): + return str(value).strip() + return "" + + def fetch_cyberark_users(self, pvwa_host, authorization_token, fetch_groups_membership=False): + """Fetch all CyberArk vault users (excluding component / service accounts). + + Returns a list of dicts: + ``{"id", "username", "email", "type", "source", "groups_membership"}`` + — empty list on any error. + """ + include_component = environ.get("_CYBERARK_INCLUDE_COMPONENT_USERS", "").lower() in ( + "1", "true", "yes", + ) + query_params = {"ExtendedDetails": "True"} + if not include_component: + # CyberArk's documented filter - drops CPM/PSM/PVWA/AppProvider/etc. + query_params["componentUser"] = "false" + sleep(self.DELAY) + response = self.get_response( + self.get_url(pvwa_host, "users"), + authorization_token, + query_params, + ) + if response is None: + return [] + if response.status_code != 200: + print_formatted_text( + HTML( + f"Getting users from server {pvwa_host} failed " + f"with status {response.status_code}" + ) + ) + return [] + try: + payload = response.json() + except ValueError: + print_formatted_text(HTML("Users response was not valid JSON")) + return [] + + # CyberArk returns users under "Users" (PAM Self-Hosted) or "value" (Privilege Cloud) + users = payload.get("Users") or payload.get("value") or [] + if not isinstance(users, list) or not users: + return [] + + # Optional username filter + filter_env = environ.get("_CYBERARK_USERS_FILTER") or "" + username_filter = {x.strip().lower() for x in filter_env.split(",") if x.strip()} + + result = [] + for u in users: + username = u.get("username") or u.get("UserName") or "" + if username_filter and username.lower() not in username_filter: + continue + user_id = u.get("id") or u.get("ID") + user_type = u.get("userType") or u.get("UserType") or "" + source = u.get("source") or u.get("Source") or "" + email = self._extract_user_email(u) + groups_membership = u.get("groupsMembership") or [] + + + need_detail = (not email) or (fetch_groups_membership and not groups_membership) + if need_detail and user_id is not None: + sleep(self.DELAY) + detail = self.get_response( + self.get_url(pvwa_host, "user").format(user_id=user_id), + authorization_token, + {}, + ) + if detail is not None and detail.status_code == 200: + try: + detail_json = detail.json() + except ValueError: + detail_json = {} + if not email: + email = self._extract_user_email(detail_json) + if fetch_groups_membership and not groups_membership: + groups_membership = detail_json.get("groupsMembership") or [] + + result.append( + { + "id": user_id, + "username": username, + "email": email, + "type": user_type, + "source": source, + "groups_membership": groups_membership, + } + ) + return result + + def fetch_all_safes(self, pvwa_host, authorization_token, safes_filter=None): + """Return full safe objects from PVWA (not just names). + + ``safes_filter`` is an optional set of safe names to restrict the result. + """ + safes_file = environ.get("_CYBERARK_SAFES_PATH", "safes.txt") + if path.isfile(safes_file): + with open(safes_file, "r", encoding="utf-8") as f: + names = [line.strip() for line in f if line.strip()] + return [{"safeName": n, "safeUrlId": n} for n in names] + if "_CYBERARK_SAFES" in environ: + names = [x.strip() for x in environ.get("_CYBERARK_SAFES").split(",") if x.strip()] + return [{"safeName": n, "safeUrlId": n} for n in names] + + safes = [] + offset = 0 + limit = 200 + while True: + sleep(self.DELAY) + response = self.get_response( + self.get_url(pvwa_host, "safes"), + authorization_token, + {"offset": offset, "limit": limit}, + ) + if response is None or response.status_code != 200: + break + try: + payload = response.json() + except ValueError: + break + chunk = payload.get("value") or [] + safes.extend(chunk) + count = payload.get("count", len(safes)) + offset += len(chunk) + if offset >= count or not chunk: + break + + if safes_filter: + safes = [ + s for s in safes + if s.get("safeName") in safes_filter or s.get("safeUrlId") in safes_filter + ] + return safes + + def fetch_safe_members(self, pvwa_host, authorization_token, safe_url_id): + """Fetch all members of a CyberArk safe (excluding predefined system members).""" + if not safe_url_id: + return [] + if not re.match(r"^[a-zA-Z0-9][a-zA-Z0-9_. -]*$", safe_url_id): + logging.warning("Invalid safe URL ID format: %s — skipping member fetch", safe_url_id) + return [] + url = f"{self.get_url(pvwa_host, 'safes')}/{safe_url_id}/Members" + members = [] + offset = 0 + limit = 100 + while True: + sleep(self.DELAY) + response = self.get_response( + url, authorization_token, {"offset": offset, "limit": limit}, + ) + if response is None or response.status_code != 200: + break + try: + payload = response.json() + except ValueError: + break + chunk = payload.get("value") or [] + for m in chunk: + if not m.get("isPredefinedUser"): + members.append(m) + count = payload.get("count", 0) + offset += len(chunk) + if offset >= count or not chunk: + break + return members + + def print_cyberark_users(self, pvwa_host, authorization_token, users=None): + """Print a table of CyberArk users and their emails (no Keeper changes). + """ + if users is None: + print_formatted_text(HTML("\nFetching CyberArk Users...")) + users = self.fetch_cyberark_users(pvwa_host, authorization_token) + if not users: + print_formatted_text(HTML("No users returned by CyberArk")) + return + + rows = [] + users_missing_email = [] + for u in users: + user_id = u.get("id") + username = u.get("username") or "" + email = u.get("email") or "" + if not email: + users_missing_email.append(username or str(user_id)) + rows.append( + { + "ID": user_id if user_id is not None else "", + "Username": username, + "Email": email, + "Type": u.get("type") or "", + "Source": u.get("source") or "", + } + ) + + if not rows: + logging.debug("CyberArk users: no users matched the filter") + return + + logging.debug( + "CyberArk Users (%d — informational only, NOT imported to Keeper):\n%s", + len(rows), + tabulate(rows, headers="keys"), ) + if users_missing_email: + preview = ", ".join(users_missing_email[:20]) + ( + "..." if len(users_missing_email) > 20 else "" + ) + logging.debug( + "%d CyberArk user(s) had no email: %s", + len(users_missing_email), + preview, + ) + + # Optional CSV dump for downstream tooling. + out_path = environ.get("_CYBERARK_USERS_PATH") + if out_path: + try: + import csv + with open(out_path, "w", encoding="utf-8", newline="") as f: + writer = csv.DictWriter(f, fieldnames=["ID", "Username", "Email", "Type", "Source"]) + writer.writeheader() + writer.writerows(rows) + print_formatted_text(HTML(f"Wrote user list to {out_path}")) + except OSError as e: + print_formatted_text( + HTML(f"Failed to write users CSV to {out_path}: {e}") + ) + def do_import(self, filename, **kwargs): + try: + return self._do_import_inner(filename, **kwargs) + finally: + self._cleanup_tmp_cert_files() + + def _authenticate_pvwa(self, filename): + """Authenticate to a CyberArk PVWA and return session details. + + Returns ``(pvwa_host, authorization_token, query_params)`` on success, + or ``None`` if authentication failed or was cancelled. + """ pvwa_host = filename.removeprefix("https://") query_params = {} if "?" in pvwa_host: pvwa_host, query_string = pvwa_host.split("?", 1) if "=" in query_string: - # Override the query parameters query_params = dict(parse_qsl(query_string)) else: - # Treat the entire query string as the search query parameter query_params["search"] = query_string + if not self._maybe_configure_client_cert(pvwa_host): + return None if pvwa_host.endswith(".cyberark.cloud"): - # CyberArk Privilege Cloud uses an OAuth2 client_credentials grant for authentication pvwa_host = f"{pvwa_host.split('.')[0]}.privilegecloud.cyberark.cloud" - id_tenant = environ.get("KEEPER_CYBERARK_ID_TENANT") or prompt("CyberArk Identity Tenant ID: ") + self._verify_tls = True + id_tenant = environ.get("_CYBERARK_ID_TENANT") or prompt("CyberArk Identity Tenant ID: ") if re.match(r"^[A-Za-z]{3}\d{4}$", id_tenant): - # Append the ".id" suffix to the tenant ID if it matches the expected format id_tenant += ".id" - client_id = environ.get("KEEPER_CYBERARK_USERNAME") or prompt("CyberArk service user name: ") - client_secret = environ.get("KEEPER_CYBERARK_PASSWORD") or prompt( + client_id = environ.get("_CYBERARK_USERNAME") or prompt("CyberArk service user name: ") + client_secret = environ.get("_CYBERARK_PASSWORD") or prompt( "CyberArk service user password: ", is_password=True ) - response = requests.post( - f"https://{id_tenant}.cyberark.cloud/oauth2/platformtoken", - data={ - "grant_type": "client_credentials", - "client_id": client_id, - "client_secret": client_secret, - }, - timeout=self.TIMEOUT, - ) + token_url = f"https://{id_tenant}.cyberark.cloud/oauth2/platformtoken" + try: + response = self._request( + "POST", + token_url, + data={ + "grant_type": "client_credentials", + "client_id": client_id, + "client_secret": client_secret, + }, + timeout=self.TIMEOUT, + ) + except requests.exceptions.ConnectionError as e: + print_formatted_text( + HTML( + "OAuth2 authorization token request failed: " + f"could not connect to {id_tenant}.cyberark.cloud.\n" + "Verify the CyberArk Identity Tenant ID is correct (check the CyberArk Identity " + "Admin Portal URL — the first label of the hostname is your tenant ID) and that " + "your machine has network/DNS access to it." + ) + ) + print_formatted_text(HTML(f"Details: {e}")) + return None + except requests.exceptions.RequestException as e: + print_formatted_text( + HTML(f"OAuth2 authorization token request failed: {e}") + ) + return None if response.status_code != 200: print_formatted_text( HTML( f"OAuth2 authorization token request failed with status code {response.status_code}" ) ) - return - access_token = response.json()["access_token"] + try: + print_formatted_text(HTML(f"Response: {response.text[:500]}")) + except Exception: + pass + return None + try: + access_token = response.json()["access_token"] + except (ValueError, KeyError) as e: + print_formatted_text( + HTML(f"OAuth2 response did not contain an access_token: {e}") + ) + return None authorization_token = f"Bearer {access_token}" else: - # CyberArk self-hosted PVWA uses a logon API to authenticate - login_type = environ.get("KEEPER_CYBERARK_LOGON_TYPE") or prompt( + login_type = environ.get("_CYBERARK_LOGON_TYPE") or prompt( "CyberArk logon type (Cyberark, LDAP, RADIUS or Windows): " ) - username = environ.get("KEEPER_CYBERARK_USERNAME") or prompt("CyberArk username: ") - password = environ.get("KEEPER_CYBERARK_PASSWORD") or prompt("CyberArk password: ", is_password=True) - response = requests.post( - self.get_url(pvwa_host, "logon").format(type=login_type), - json={"username": username, "password": password}, - timeout=self.TIMEOUT, - verify=False, - ) + username = environ.get("_CYBERARK_USERNAME") or prompt("CyberArk username: ") + password = environ.get("_CYBERARK_PASSWORD") or prompt("CyberArk password: ", is_password=True) + try: + response = self._request( + "POST", + self.get_url(pvwa_host, "logon").format(type=login_type), + json={"username": username, "password": password}, + timeout=self.TIMEOUT, + verify=self._verify_tls, + cert=self._client_cert, + ) + except requests.exceptions.ConnectionError as e: + print_formatted_text( + HTML( + f"CyberArk Log on failed: could not connect to {pvwa_host}.\n" + "Verify the PVWA hostname is correct and reachable from this machine." + ) + ) + print_formatted_text(HTML(f"Details: {e}")) + return None + except requests.exceptions.RequestException as e: + print_formatted_text(HTML(f"CyberArk Log on failed: {e}")) + return None if response.status_code != 200: print_formatted_text( HTML(f"CyberArk Log on failed with status code {response.status_code}") ) - return + return None authorization_token = response.text.strip('"') print_formatted_text(HTML("Log on successful")) - # Get a list of safes, either from a file, the environment variable KEEPER_CYBERARK_SAFES, or from the API - safes_file = environ.get("KEEPER_CYBERARK_SAFES_PATH", "safes.txt") + return pvwa_host, authorization_token, query_params + + def _resolve_safes(self, pvwa_host, authorization_token): + """Resolve the list of safe names to import. + """ + safes_file = environ.get("_CYBERARK_SAFES_PATH", "safes.txt") if path.isfile(safes_file): with open(safes_file, "r", encoding="utf-8") as f: safes = [line.strip() for line in f if line.strip()] - if len(safes) == 0: - print_formatted_text(HTML(f"Safes file {safes_file} is empty")) - return - print_formatted_text(HTML(f"Safes from file {safes_file}: {', '.join(safes)}")) - elif "KEEPER_CYBERARK_SAFES" in environ: - safes = [x.strip() for x in environ.get("KEEPER_CYBERARK_SAFES").split(",") if x.strip()] - print_formatted_text(HTML(f"Safes from environment variable KEEPER_CYBERARK_SAFES: {', '.join(safes)}")) - else: - safes = [ - x.strip() - for x in prompt( - "CyberArk safes as a comma-separated list (leave empty to get safes from the server): " - ).split(",") - if x.strip() - ] if len(safes) == 0: - print_formatted_text(HTML("Getting safes from the server...")) - response = self.get_response(self.get_url(pvwa_host, "safes"), authorization_token, {}) - if response.status_code != 200: - print_formatted_text( - HTML( - f"Getting safes from server {pvwa_host} failed with status {response.status_code}" - ) - ) - return - safes = [x["safeName"] for x in response.json().get("value", [])] - if len(safes) == 0: - print_formatted_text(HTML(f"No Safes on server {pvwa_host}")) - return - print_formatted_text(HTML(f"Safes: {', '.join(safes)}")) - # Get the accounts out of each safe + print_formatted_text(HTML(f"Safes file {safes_file} is empty")) + return None + print_formatted_text(HTML(f"Safes from file {safes_file}: {', '.join(safes)}")) + return safes + if "_CYBERARK_SAFES" in environ: + safes = [x.strip() for x in environ.get("_CYBERARK_SAFES").split(",") if x.strip()] + print_formatted_text(HTML(f"Safes from environment variable _CYBERARK_SAFES: {', '.join(safes)}")) + return safes + safes = [ + x.strip() + for x in prompt( + "CyberArk safes as a comma-separated list (leave empty to get safes from the server): " + ).split(",") + if x.strip() + ] + if len(safes) > 0: + return safes + print_formatted_text(HTML("Getting safes from the server...")) + response = self.get_response(self.get_url(pvwa_host, "safes"), authorization_token, {}) + if response is None: + return None + if response.status_code != 200: + print_formatted_text( + HTML( + f"Getting safes from server {pvwa_host} failed with status {response.status_code}" + ) + ) + return None + safes = [x["safeName"] for x in response.json().get("value", [])] + if len(safes) == 0: + print_formatted_text(HTML(f"No Safes on server {pvwa_host}")) + return None + print_formatted_text(HTML(f"Safes: {', '.join(safes)}")) + return safes + + def _preview_user_group_names(self, pvwa_host, authorization_token): + """Return the CyberArk user-group names that would become Keeper Teams/Roles. + + Read-only preview helper that honors the same ``_CYBERARK_GROUPS`` / + ``_CYBERARK_GROUPS_PATH`` filter as :meth:`import_user_groups`. Returns + an empty list on any error. + """ + groups_filter = None + groups_file = environ.get("_CYBERARK_GROUPS_PATH", "groups.txt") + if path.isfile(groups_file): + with open(groups_file, "r", encoding="utf-8") as f: + groups_filter = {line.strip() for line in f if line.strip()} + elif "_CYBERARK_GROUPS" in environ: + groups_filter = {x.strip() for x in environ.get("_CYBERARK_GROUPS").split(",") if x.strip()} + sleep(self.DELAY) + response = self.get_response(self.get_url(pvwa_host, "user_groups"), authorization_token, {}) + if response is None or response.status_code != 200: + return [] + try: + groups = response.json().get("value", []) + except ValueError: + return [] + names = [] + for g in groups: + name = g.get("groupName") or g.get("name") + if not name: + continue + if groups_filter and name not in groups_filter: + continue + names.append(name) + return names + + def _eligible_user_previews(self, cyberark_users): + """Return ``[{"Username", "Email"}]`` for users eligible for Keeper provisioning. + """ + accepted = [] + seen = set() + for u in cyberark_users or []: + email = (u.get("email") or "").strip() + if not self._is_valid_cyberark_user_email(email): + continue + key = email.lower() + if key in seen: + continue + seen.add(key) + accepted.append({"Username": u.get("username") or "", "Email": email}) + return accepted + + @staticmethod + def _enterprise_existing(params): + """Return ``(team_names, role_names, user_status)`` already in the enterprise. + + ``team_names`` / ``role_names`` are sets of lowercase names; ``user_status`` + maps lowercase email -> Keeper status (e.g. ``"active"`` / ``"invited"``). + All are empty when enterprise data is unavailable. + """ + teams, roles, users = set(), set(), {} + if not (params and getattr(params, "enterprise", None)): + return teams, roles, users + try: + api.query_enterprise(params) + except Exception as e: + logging.debug("Preview enterprise refresh failed: %s", e) + for t in (params.enterprise.get("teams") or []): + if t.get("name"): + teams.add(t["name"].lower()) + for t in (params.enterprise.get("queued_teams") or []): + if t.get("name"): + teams.add(t["name"].lower()) + for r in (params.enterprise.get("roles") or []): + display = (r.get("data") or {}).get("displayname") or "" + if display: + roles.add(display.lower()) + for u in (params.enterprise.get("users") or []): + uname = (u.get("username") or "").lower() + if uname: + users[uname] = u.get("status") or "active" + return teams, roles, users + + @staticmethod + def _confirm_import(pvwa_host, summary=None, question="Proceed with the import? (yes/no): "): + """Ask the user to confirm before reading/importing CyberArk data into Keeper. + + Returns ``True`` to proceed, ``False`` to cancel. The prompt can be + bypassed for non-interactive runs by setting ``_CYBERARK_ASSUME_YES`` + to a truthy value (``1``/``true``/``yes``). + """ + if environ.get("_CYBERARK_ASSUME_YES", "").lower() in ("1", "true", "yes"): + return True + + if summary is None: + summary = ( + f"\nYou are about to import data from CyberArk PVWA {pvwa_host} into Keeper.\n" + "This will create Keeper records, and (unless skipped) teams, " + "roles and users." + ) + print_formatted_text(HTML(summary)) + try: + answer = prompt(question).strip().lower() + except (EOFError, KeyboardInterrupt): + return False + return answer in ("y", "yes") + + def _do_import_inner(self, filename, **kwargs): + auth = self._authenticate_pvwa(filename) + if auth is None: + return + pvwa_host, authorization_token, query_params = auth + + safes = self._resolve_safes(pvwa_host, authorization_token) + if not safes: + return + + + print_formatted_text(HTML("\nScanning CyberArk safes for accounts to migrate...")) + safe_accounts = {} for safe in safes: sleep(self.DELAY) response = self.get_response( self.get_url(pvwa_host, "accounts"), authorization_token, query_params | {"filter": f"safeName eq {safe}"} ) - count = response.json().get("count", 0) - if count == 0: + if response is None: + print_formatted_text(HTML(f"Skipping safe {safe} due to network error")) + continue + if response.status_code != 200: + print_formatted_text( + HTML( + f"Skipping safe {safe}: status {response.status_code}" + ) + ) + continue + try: + payload = response.json() + except ValueError: + print_formatted_text( + HTML(f"Skipping safe {safe}: accounts response was not valid JSON") + ) + continue + accounts = payload.get("value", []) + if not accounts: print_formatted_text(HTML(f"No accounts in safe {safe}")) continue - accounts = response.json().get("value", []) + safe_accounts[safe] = accounts + + params = kwargs.get("params") + will_teams = environ.get("_CYBERARK_SKIP_TEAMS", "").lower() not in ("1", "true", "yes") + will_create_users = environ.get("_CYBERARK_SKIP_CREATE_USERS", "").lower() not in ("1", "true", "yes") + will_print_users = environ.get("_CYBERARK_SKIP_USERS_LIST", "").lower() not in ("1", "true", "yes") + + # Gather the CyberArk identities (groups + users) that will become Keeper + # teams, roles and users so they can be previewed before the import. The + # fetched users are reused after the import (no second fetch). + group_names = self._preview_user_group_names(pvwa_host, authorization_token) if will_teams else [] + cyberark_users = [] + if will_print_users or will_create_users: + print_formatted_text(HTML("\nFetching CyberArk Users...")) + cyberark_users = self.fetch_cyberark_users( + pvwa_host, authorization_token, + fetch_groups_membership=will_create_users, + ) + eligible_users = self._eligible_user_previews(cyberark_users) if will_create_users else [] + existing_teams, existing_roles, existing_users = self._enterprise_existing(params) + + total_accounts = sum(len(a) for a in safe_accounts.values()) + if total_accounts == 0 and not group_names and not eligible_users: + print_formatted_text(HTML("\nNothing to migrate from CyberArk.")) + return + + # Show exactly what will be migrated (records, teams, roles, users), then confirm. + if safe_accounts: + account_rows = [] + for safe, accounts in safe_accounts.items(): + for x in accounts: + account_rows.append({"ID": x.get("id"), "Safe": x.get("safeName") or safe, "Account": x.get("name")}) + print_formatted_text( + HTML("\nCyberArk accounts to import as Keeper records:\n"), + tabulate(account_rows, headers="keys"), + end="\n\n", + ) + if group_names: + team_rows = [ + {"Team": g, "Status": "exists" if g.lower() in existing_teams else "new"} + for g in group_names + ] + print_formatted_text( + HTML("\nCyberArk user groups to import as Keeper teams:\n"), + tabulate(team_rows, headers="keys"), + end="\n\n", + ) + role_rows = [ + {"Role": g, "Status": "exists" if g.lower() in existing_roles else "new"} + for g in group_names + ] + print_formatted_text( + HTML("\nCyberArk user groups to import as Keeper roles:\n"), + tabulate(role_rows, headers="keys"), + end="\n\n", + ) + if eligible_users: + user_rows = [ + { + "Username": a["Username"], + "Email": a["Email"], + "Status": existing_users.get(a["Email"].lower(), "new"), + } + for a in eligible_users + ] + print_formatted_text( + HTML("\nCyberArk users to provision as Keeper users:\n"), + tabulate(user_rows, headers="keys"), + end="\n\n", + ) + + summary_lines = [ + f"\nYou are about to import data from CyberArk PVWA {pvwa_host} into Keeper:", + f" - {total_accounts} account(s) across {len(safe_accounts)} safe(s) as Keeper records", + ] + if group_names: + summary_lines.append(f" - {len(group_names)} user group(s) as Keeper teams and roles") + if eligible_users: + summary_lines.append(f" - {len(eligible_users)} user(s) provisioned as Keeper users") + if not self._confirm_import(pvwa_host, summary="\n".join(summary_lines)): + print_formatted_text(HTML("\nImport cancelled by user")) + return + + # Import the accounts we already gathered above. + for safe, accounts in safe_accounts.items(): print_formatted_text( HTML(f"Importing {len(accounts)} accounts from safe {safe}:\n"), tabulate([{"ID": x["id"], "Safe": x["safeName"], "Account": x["name"]} for x in accounts], headers="keys"), end="\n\n", ) - with ProgressBar() as pb: + with _suppress_progressbar_executor_noise(), ProgressBar() as pb: skip_all = {} skipped_accounts = [] for r in pb(accounts, total=len(accounts)): @@ -177,16 +1125,38 @@ def do_import(self, filename, **kwargs): record.login_url = r["platformAccountProperties"]["URL"] retry = True while retry is True: - response = requests.post( - self.get_url(pvwa_host, "account_password").format(account_id=r["id"]), - headers={ - "Authorization": authorization_token, - "Content-Type": "application/json", - }, - json={"reason": "Keeper Commander Import"}, - timeout=self.TIMEOUT, - verify=True if pvwa_host.endswith(".cyberark.cloud") else False, - ) + try: + response = self._request( + "POST", + self.get_url(pvwa_host, "account_password").format(account_id=r["id"]), + headers={ + "Authorization": authorization_token, + "Content-Type": "application/json", + }, + json={"reason": "Keeper Commander Import"}, + timeout=self.TIMEOUT, + verify=True if pvwa_host.endswith(".cyberark.cloud") else self._verify_tls, + cert=None if pvwa_host.endswith(".cyberark.cloud") else self._client_cert, + ) + except requests.exceptions.RequestException as e: + print_formatted_text( + HTML( + f"\nNetwork error retrieving password for " + f"{r['name']} in safe {r['safeName']}: {e}" + ) + ) + skipped_accounts.append( + { + "ID": r["id"], + "Safe": r["safeName"], + "Account": r["name"], + "Status": "N/A", + "Error": "NetworkError", + "Message": str(e), + } + ) + retry = False + continue if response.status_code == 200: record.password = response.text.strip('"') retry = False @@ -231,4 +1201,1100 @@ def do_import(self, filename, **kwargs): tabulate(skipped_accounts, headers="keys"), end="\n\n" ) + + # Print CyberArk users with their usernames and emails (already fetched for the + # preview above; no Keeper users are created here). + if will_print_users: + self.print_cyberark_users(pvwa_host, authorization_token, users=cyberark_users) + + # Import CyberArk User Groups as Keeper Enterprise Teams + Roles, then optionally + # create Keeper users (using their real CyberArk business emails) and + # assign them to the matching Keeper Roles. + if will_teams: + self.import_user_groups( + pvwa_host, authorization_token, params, + cyberark_users=cyberark_users, + ) + print_formatted_text(HTML("\nImport completed")) + + def import_user_groups(self, pvwa_host, authorization_token, params, cyberark_users=None): + """Fetch CyberArk User Groups and create them as Keeper Enterprise Teams. + + This mirrors the ``enterprise-team --add`` command flow: for each + CyberArk user group we build a ``team_add`` request (with a freshly + generated team UID, AES team key, EC key pair and — if RSA is + permitted — an RSA key pair) and submit them as a single batch via + ``api.execute_batch``. + """ + if params is None: + print_formatted_text( + HTML( + "Cannot create Keeper Teams: Keeper session is not " + "available to the importer (no params)." + ) + ) + return + if not getattr(params, "enterprise", None): + print_formatted_text( + HTML( + "Cannot create Keeper Teams: the logged-in account " + "is not an enterprise admin (no enterprise data loaded)." + ) + ) + return + + # Determine which groups to import (similar pattern to safes) + groups_filter = None + groups_file = environ.get("_CYBERARK_GROUPS_PATH", "groups.txt") + if path.isfile(groups_file): + with open(groups_file, "r", encoding="utf-8") as f: + groups_filter = {line.strip() for line in f if line.strip()} + if groups_filter: + print_formatted_text( + HTML(f"User groups from file {groups_file}: {', '.join(sorted(groups_filter))}") + ) + elif "_CYBERARK_GROUPS" in environ: + groups_filter = {x.strip() for x in environ.get("_CYBERARK_GROUPS").split(",") if x.strip()} + if groups_filter: + print_formatted_text( + HTML( + "User groups from environment variable _CYBERARK_GROUPS: " + f"{', '.join(sorted(groups_filter))}" + ) + ) + + print_formatted_text(HTML("\nFetching CyberArk User Groups...")) + sleep(self.DELAY) + response = self.get_response( + self.get_url(pvwa_host, "user_groups"), + authorization_token, + {"includeMembers": "True"}, + ) + if response is None: + return + if response.status_code != 200: + print_formatted_text( + HTML( + f"Getting user groups from server {pvwa_host} failed " + f"with status {response.status_code}" + ) + ) + return + groups = response.json().get("value", []) + if groups_filter: + groups = [g for g in groups if g.get("groupName") in groups_filter or g.get("name") in groups_filter] + if not groups: + print_formatted_text(HTML("No user groups to import")) + return + + + try: + api.query_enterprise(params) + except Exception as e: + logging.debug("Pre-team-add enterprise refresh failed: %s", e) + + + existing_team_names = set() + for team in params.enterprise.get("teams", []) or []: + if team.get("name"): + existing_team_names.add(team["name"].lower()) + for team in params.enterprise.get("queued_teams", []) or []: + if team.get("name"): + existing_team_names.add(team["name"].lower()) + + # Determine the target node id (same default as enterprise-team --add): + # the first user-root node when no --node was specified. + node_id = None + for nid in params.enterprise.get("user_root_nodes", []) or []: + node_id = nid + break + if node_id is None: + # Fall back to the first node in the tree (root has parent_id=0) + for n in params.enterprise.get("nodes", []) or []: + if not n.get("parent_id"): + node_id = n["node_id"] + break + if node_id is None: + print_formatted_text( + HTML( + "Cannot create Keeper Teams: no root node found in the " + "enterprise tree." + ) + ) + return + + print_formatted_text( + HTML(f"Importing {len(groups)} user groups as Keeper Teams (members not provisioned):\n"), + tabulate( + [ + { + "ID": g.get("id"), + "Name": g.get("groupName") or g.get("name"), + "CyberArk Members": len(g.get("members") or []), + } + for g in groups + ], + headers="keys", + ), + end="\n\n", + ) + + request_batch = [] + request_team_names = [] # parallel list for reporting per-batch results + skipped_existing = [] + for g in groups: + group_id = g.get("id") + group_name = g.get("groupName") or g.get("name") + if not group_name: + continue + members = g.get("members") or [] + # Some CyberArk versions return members only via the per-group detail endpoint + if not members and group_id is not None: + sleep(self.DELAY) + detail = self.get_response( + self.get_url(pvwa_host, "user_group").format(group_id=group_id), + authorization_token, + {"includeMembers": "True"}, + ) + if detail is not None and detail.status_code == 200: + try: + detail_payload = detail.json() + except ValueError: + detail_payload = {} + members = detail_payload.get("members") or [] + g["members"] = members + + + member_names = [ + m.get("username") or m.get("userName") or str(m.get("id") or m.get("userId") or "") + for m in members + ] + member_names = [n for n in member_names if n] + logging.debug( + "Team %s (CyberArk group id %s) - %d CyberArk member(s), not imported as users%s", + group_name, + group_id, + len(member_names), + (": " + ", ".join(member_names)) if member_names else "", + ) + + if group_name.lower() in existing_team_names: + skipped_existing.append(group_name) + continue + + # Build a team_add request - same shape as EnterpriseTeamCommand.execute + team_uid = api.generate_record_uid() + team_key = api.generate_aes_key() + encrypted_team_key = crypto.encrypt_aes_v2(team_key, params.enterprise["unencrypted_tree_key"]) + rq = { + "command": "team_add", + "team_uid": team_uid, + "team_name": group_name, + "restrict_edit": False, + "restrict_share": False, + "restrict_view": False, + "node_id": node_id, + "team_key": utils.base64_url_encode(crypto.encrypt_aes_v1(team_key, params.data_key)), + "encrypted_team_key": utils.base64_url_encode(encrypted_team_key), + "manage_only": True, + } + ec_private_key, ec_public_key = crypto.generate_ec_key() + encrypted_ec_private_key = crypto.encrypt_aes_v2( + crypto.unload_ec_private_key(ec_private_key), team_key + ) + rq["ecc_private_key"] = utils.base64_url_encode(encrypted_ec_private_key) + rq["ecc_public_key"] = utils.base64_url_encode(crypto.unload_ec_public_key(ec_public_key)) + if not getattr(params, "forbid_rsa", False): + rsa_private_key, rsa_public_key = crypto.generate_rsa_key() + encrypted_rsa_private_key = crypto.encrypt_aes_v1( + crypto.unload_rsa_private_key(rsa_private_key), team_key + ) + rq["public_key"] = utils.base64_url_encode(crypto.unload_rsa_public_key(rsa_public_key)) + rq["private_key"] = utils.base64_url_encode(encrypted_rsa_private_key) + + request_batch.append(rq) + request_team_names.append(group_name) + # Track locally so duplicates within the same run are also skipped + existing_team_names.add(group_name.lower()) + + if skipped_existing: + print_formatted_text( + HTML( + f"\nSkipped {len(skipped_existing)} group(s) that already exist as " + f"Keeper Teams: {', '.join(skipped_existing)}" + ) + ) + + if not request_batch: + print_formatted_text(HTML("\nNo new Keeper Teams to create.")) + else: + try: + responses = api.execute_batch(params, request_batch) + except Exception as e: + print_formatted_text(HTML(f"\nFailed to create Keeper Teams: {e}")) + responses = [] + + created = 0 + failed = [] + for team_name, rs in zip(request_team_names, responses or []): + result = (rs or {}).get("result") + if result == "success": + created += 1 + else: + failed.append( + { + "Team": team_name, + "Code": (rs or {}).get("result_code"), + "Message": (rs or {}).get("message"), + } + ) + + print_formatted_text( + HTML(f"\nCreated {created} of {len(request_batch)} Keeper Teams") + ) + if failed: + print_formatted_text( + HTML("\nSome teams could not be created:\n"), + tabulate(failed, headers="keys"), + end="\n\n", + ) + + # Also create a Keeper Enterprise Role for each user group (mirrors enterprise-role --add) + if environ.get("_CYBERARK_SKIP_ROLES", "").lower() not in ("1", "true", "yes"): + self._create_keeper_roles(groups, params, node_id) + + # Provision Keeper users (using their real CyberArk business emails) + # and assign them to matching Roles. + if environ.get("_CYBERARK_SKIP_CREATE_USERS", "").lower() not in ("1", "true", "yes"): + if cyberark_users is None: + print_formatted_text(HTML("\nFetching CyberArk Users for provisioning...")) + cyberark_users = self.fetch_cyberark_users( + pvwa_host, authorization_token, fetch_groups_membership=True, + ) + self._create_keeper_users_and_assign_roles(groups, cyberark_users, params, node_id) + + def _create_keeper_roles(self, groups, params, node_id): + """Create one Keeper Enterprise Role per CyberArk user group. + + Mirrors the ``enterprise-role --add`` command flow: allocates a fresh + enterprise id for each new role, encrypts ``{"displayname": }`` + with the enterprise tree key, and submits a ``role_add`` request per + role via ``api.communicate`` (one-by-one so a single failure does not + hide the others — matching ``EnterpriseRoleCommand``'s behavior, which + also logs each error individually). + + + """ + + try: + api.query_enterprise(params) + except Exception as e: + print_formatted_text( + HTML(f"\nWarning: could not refresh enterprise data before role creation: {e}") + ) + + if not getattr(params, "enterprise", None): + print_formatted_text( + HTML( + "\nCannot create Keeper Roles: enterprise data is " + "not available (are you logged in as an enterprise administrator?)." + ) + ) + return + + tree_key = params.enterprise.get("unencrypted_tree_key") + if not tree_key: + print_formatted_text( + HTML( + "\nCannot create Keeper Roles: enterprise tree key is " + "not available." + ) + ) + return + + # Build a lookup of existing role display names (case-insensitive) + existing_role_names = set() + for r in params.enterprise.get("roles", []) or []: + display = (r.get("data") or {}).get("displayname") or "" + if display: + existing_role_names.add(display.lower()) + + new_role_names = [] + skipped_existing = [] + seen_in_batch = set() + for g in groups: + group_name = g.get("groupName") or g.get("name") + if not group_name: + continue + key = group_name.lower() + if key in existing_role_names or key in seen_in_batch: + skipped_existing.append(group_name) + continue + seen_in_batch.add(key) + new_role_names.append(group_name) + + if skipped_existing: + print_formatted_text( + HTML( + f"\nSkipped {len(skipped_existing)} group(s) that already exist as " + f"Keeper Roles: {', '.join(skipped_existing)}" + ) + ) + + if not new_role_names: + print_formatted_text(HTML("\nNo new Keeper Roles to create.")) + return + + print_formatted_text( + HTML(f"\nCreating {len(new_role_names)} Keeper Roles...") + ) + + created = 0 + failed = [] + for role_name in new_role_names: + # Allocate a role id (one call per role - same as EnterpriseRoleCommand). + try: + role_id = EnterpriseCommand.get_enterprise_id(params) + except Exception as e: + logging.exception("Failed to allocate role id for '%s'", role_name) + failed.append({"Role": role_name, "Code": "id-alloc", "Message": str(e)}) + continue + if not role_id: + failed.append({"Role": role_name, "Code": "id-alloc", "Message": "no id returned"}) + continue + + data = json.dumps({"displayname": role_name}).encode("utf-8") + rq = { + "command": "role_add", + "role_id": role_id, + "node_id": node_id, + "encrypted_data": utils.base64_url_encode(crypto.encrypt_aes_v1(data, tree_key)), + "visible_below": False, + "new_user_inherit": False, + "role_name": role_name, + } + + try: + rs = api.communicate(params, rq) + except Exception as e: + logging.exception("role_add request failed for '%s'", role_name) + failed.append({"Role": role_name, "Code": "exception", "Message": str(e)}) + continue + + if (rs or {}).get("result") == "success": + created += 1 + else: + failed.append( + { + "Role": role_name, + "Code": (rs or {}).get("result_code") or "unknown", + "Message": (rs or {}).get("message") or "no message", + } + ) + + print_formatted_text( + HTML(f"\nCreated {created} of {len(new_role_names)} Keeper Roles") + ) + if failed: + print_formatted_text( + HTML("\nSome roles could not be created:\n"), + tabulate(failed, headers="keys"), + end="\n\n", + ) + + # Refresh enterprise data so the new roles appear in subsequent commands + # (mirrors EnterpriseRoleCommand which calls query_enterprise after role_add). + if created > 0: + try: + api.query_enterprise(params, force=True) + except Exception as e: + logging.debug("Post-role-add enterprise refresh failed: %s", e) + + # Default domains to reject (the email's domain, not the username). + + DEFAULT_EMAIL_DOMAIN_BLOCKLIST = ("cyberark.cloud", "cyberark.com", "id.cyberark.cloud") + + @classmethod + def _is_valid_cyberark_user_email(cls, email): + """Return True if ``email`` looks like a real, non-CyberArk-internal mailbox.""" + if not email: + return False + # Must match Keeper's email regex. + if not re.match(EMAIL_PATTERN, email): + return False + # Block emails whose DOMAIN matches the blocklist. We deliberately ignore + + try: + domain = email.split("@", 1)[1].strip().lower() + except IndexError: + return False + blocklist_env = environ.get("_CYBERARK_USER_EMAIL_DOMAIN_BLOCKLIST") + if blocklist_env is not None: + blocked = {x.strip().lower() for x in blocklist_env.split(",") if x.strip()} + else: + blocked = set(cls.DEFAULT_EMAIL_DOMAIN_BLOCKLIST) + for bad in blocked: + if domain == bad or domain.endswith("." + bad): + return False + return True + + def _create_keeper_users_and_assign_roles(self, groups, cyberark_users, params, node_id): + """Provision Keeper Enterprise users from valid CyberArk users and add them to matching Roles. + """ + if not cyberark_users: + print_formatted_text(HTML("\nNo CyberArk users available - skipping user creation.")) + return + if params is None or not getattr(params, "enterprise", None): + print_formatted_text( + HTML( + "\nCannot create Keeper users: the logged-in account " + "is not an enterprise admin (no enterprise data loaded)." + ) + ) + return + + + accepted = [] # list of dicts: {cyberark_username, cyberark_email, keeper_email, displayname, groups_membership} + rejected = [] # list of dicts: {Username, Email, Reason} + seen_emails = {} # lowercase business email -> index in ``accepted`` + for u in cyberark_users: + cyberark_username = u.get("username") or "" + cyberark_email = u.get("email") or "" + if not self._is_valid_cyberark_user_email(cyberark_email): + if not cyberark_email: + reason = "no email" + elif not re.match(EMAIL_PATTERN, cyberark_email): + reason = "invalid email format" + else: + reason = "email domain is in CyberArk-internal blocklist" + rejected.append({"Username": cyberark_username, "Email": cyberark_email, "Reason": reason}) + continue + + email_key = cyberark_email.strip().lower() + existing_idx = seen_emails.get(email_key) + if existing_idx is not None: + + primary = accepted[existing_idx] + for g in (u.get("groups_membership") or []): + if g not in primary["groups_membership"]: + primary["groups_membership"].append(g) + rejected.append( + { + "Username": cyberark_username, + "Email": cyberark_email, + "Reason": ( + f"duplicate of CyberArk user '{primary['cyberark_username']}' " + "(same business email)" + ), + } + ) + continue + + seen_emails[email_key] = len(accepted) + accepted.append( + { + "cyberark_username": cyberark_username, + "cyberark_email": cyberark_email, + "keeper_email": cyberark_email, + "displayname": cyberark_username or cyberark_email, + "groups_membership": list(u.get("groups_membership") or []), + } + ) + + if rejected: + print_formatted_text( + HTML(f"\nSkipped {len(rejected)} CyberArk user(s) (invalid for Keeper provisioning):\n"), + tabulate(rejected, headers="keys"), + end="\n\n", + ) + + if not accepted: + print_formatted_text(HTML("\nNo CyberArk users were eligible for Keeper provisioning.")) + return + + # Show the mapping so it's auditable. + print_formatted_text( + HTML(f"\nProvisioning {len(accepted)} Keeper user(s):\n"), + tabulate( + [ + { + "CyberArk Username": a["cyberark_username"], + "CyberArk Email": a["cyberark_email"], + "Keeper Email": a["keeper_email"], + } + for a in accepted + ], + headers="keys", + ), + end="\n\n", + ) + + # Refresh enterprise so we see current users + roles before checking for duplicates. + try: + api.query_enterprise(params) + except Exception as e: + logging.debug("Pre-user-invite enterprise refresh failed: %s", e) + + # Build a lookup of existing users so we don't try to invite the same email twice. + existing_user_by_email = {} + for u in (params.enterprise.get("users") or []): + uname = (u.get("username") or "").lower() + if uname: + existing_user_by_email[uname] = u + + # Determine the target node (root node) for new invitations. + invite_node_id = None + for nid in params.enterprise.get("user_root_nodes", []) or []: + invite_node_id = nid + break + if invite_node_id is None: + for n in params.enterprise.get("nodes", []) or []: + if not n.get("parent_id"): + invite_node_id = n["node_id"] + break + if invite_node_id is None: + print_formatted_text( + HTML( + "\nCannot invite Keeper users: no root node found in " + "the enterprise tree." + ) + ) + return + + tree_key = params.enterprise.get("unencrypted_tree_key") + if not tree_key: + print_formatted_text( + HTML( + "\nCannot invite Keeper users: enterprise tree key is " + "not available." + ) + ) + return + + # Build the enterprise_user_add batch (mirrors enterprise-user --add / --invite). + users_to_invite = [] # list of accepted entries that need a new invite + users_to_resend = [] # accepted entries whose Keeper account is still 'invited' + skipped_existing = [] # accepted entries whose Keeper account already exists + for a in accepted: + keeper_email = a["keeper_email"] + existing = existing_user_by_email.get(keeper_email.lower()) + if existing is None: + users_to_invite.append(a) + elif existing.get("status") == "invited": + users_to_resend.append((a, existing)) + else: + skipped_existing.append(keeper_email) + + # Allocate one enterprise id per new invitation. + new_user_ids = [] + if users_to_invite: + try: + new_user_ids = EnterpriseCommand.get_enterprise_ids(params, len(users_to_invite)) + except Exception as e: + print_formatted_text( + HTML(f"\nFailed to allocate user ids: {e}") + ) + return + if ( + not new_user_ids + or len(new_user_ids) < len(users_to_invite) + or any(uid is None for uid in new_user_ids[: len(users_to_invite)]) + ): + print_formatted_text( + HTML( + "\nCould not allocate enterprise ids for all users. " + "Aborting user invitation." + ) + ) + return + + request_batch = [] + request_descriptions = [] # parallel: (email, "invited"/"resent") for reporting + for i, a in enumerate(users_to_invite): + displayname = a["displayname"] or a["keeper_email"] + encrypted_data = utils.base64_url_encode( + crypto.encrypt_aes_v1( + json.dumps({"displayname": displayname}).encode("utf-8"), + tree_key, + ) + ) + rq = { + "command": "enterprise_user_add", + "enterprise_user_id": new_user_ids[i], + "node_id": invite_node_id, + "encrypted_data": encrypted_data, + "enterprise_user_username": a["keeper_email"], + } + request_batch.append(rq) + request_descriptions.append((a["keeper_email"], "invited")) + + for a, existing in users_to_resend: + rq = { + "command": "resend_enterprise_invite", + "enterprise_user_id": existing["enterprise_user_id"], + } + request_batch.append(rq) + request_descriptions.append((a["keeper_email"], "resent")) + + if skipped_existing: + print_formatted_text( + HTML( + f"\n{len(skipped_existing)} user(s) already exist in Keeper " + f"- skipping invitation but will still assign roles: " + f"{', '.join(skipped_existing)}" + ) + ) + + if not request_batch: + print_formatted_text(HTML("\nNo new Keeper invitations to send.")) + else: + try: + responses = api.execute_batch(params, request_batch) + except Exception as e: + print_formatted_text(HTML(f"\nFailed to send invitations: {e}")) + responses = [] + + invited = 0 + resent = 0 + failed_users = [] + for (email, action), rs in zip(request_descriptions, responses or []): + if (rs or {}).get("result") == "success": + if action == "invited": + invited += 1 + else: + resent += 1 + else: + failed_users.append( + { + "Email": email, + "Action": action, + "Code": (rs or {}).get("result_code") or "unknown", + "Message": (rs or {}).get("message") or "no message", + } + ) + + if invited: + print_formatted_text( + HTML(f"\nInvited {invited} new Keeper user(s)") + ) + if resent: + print_formatted_text( + HTML(f"Resent invitation to {resent} previously-invited user(s)") + ) + if failed_users: + print_formatted_text( + HTML("\nSome invitations failed:\n"), + tabulate(failed_users, headers="keys"), + end="\n\n", + ) + + # Refresh enterprise data so newly invited users + the roles we just made are in cache. + try: + api.query_enterprise(params, force=True) + except Exception as e: + logging.debug("Pre-role-assignment enterprise refresh failed: %s", e) + + # Build lookups from the freshest enterprise state. + user_id_by_email = {} + for u in params.enterprise.get("users", []) or []: + uname = (u.get("username") or "").lower() + if uname: + user_id_by_email[uname] = u.get("enterprise_user_id") + + role_id_by_name = {} + for r in params.enterprise.get("roles", []) or []: + display = ((r.get("data") or {}).get("displayname") or "").strip().lower() + if display and display not in role_id_by_name: + # If multiple roles share a name (across nodes) we just use the first one, + # matching how `enterprise-role` warns about ambiguity. For our case roles + # were just created at the root node so collisions are unlikely. + role_id_by_name[display] = r.get("role_id") + + # Existing role-user assignments so we don't re-add (avoids spurious failures). + existing_role_users = {(x.get("role_id"), x.get("enterprise_user_id")) + for x in (params.enterprise.get("role_users") or [])} + + + groups_by_cyberark_username = {} + for g in groups or []: + group_name = g.get("groupName") or g.get("name") or "" + if not group_name: + continue + for m in g.get("members") or []: + m_username = (m.get("username") or m.get("userName") or "").strip().lower() + if not m_username: + continue + groups_by_cyberark_username.setdefault(m_username, []).append(group_name) + + # Build the role_user_add batch. + request_batch = [] + request_descriptions = [] # parallel: (keeper_email, role_name) for reporting + assignments_skipped = [] # already a member + assignments_unmappable = [] # no role for that group name + users_without_groups = [] # accepted user but not in any CyberArk group + + for a in accepted: + keeper_email = a["keeper_email"] + cyberark_username = a["cyberark_username"] + enterprise_user_id = user_id_by_email.get(keeper_email.lower()) + if not enterprise_user_id: + # User creation must have failed (e.g. domain not reserved). Skip role assignment. + continue + + their_groups = [] + for gm in a.get("groups_membership") or []: + gname = (gm.get("groupName") or "").strip() + if gname: + their_groups.append(gname) + if not their_groups: + their_groups = list(groups_by_cyberark_username.get(cyberark_username.lower(), [])) + if not their_groups: + users_without_groups.append(keeper_email) + continue + # Deduplicate while preserving order. + seen = set() + their_groups = [g for g in their_groups if not (g.lower() in seen or seen.add(g.lower()))] + for group_name in their_groups: + role_id = role_id_by_name.get(group_name.strip().lower()) + if not role_id: + assignments_unmappable.append({"User": keeper_email, "Group": group_name}) + continue + if (role_id, enterprise_user_id) in existing_role_users: + assignments_skipped.append({"User": keeper_email, "Role": group_name}) + continue + request_batch.append( + { + "command": "role_user_add", + "role_id": role_id, + "enterprise_user_id": enterprise_user_id, + } + ) + request_descriptions.append((keeper_email, group_name)) + existing_role_users.add((role_id, enterprise_user_id)) + + if assignments_skipped: + print_formatted_text( + HTML( + f"\nSkipped {len(assignments_skipped)} role assignment(s) " + f"that already exist." + ) + ) + if assignments_unmappable: + print_formatted_text( + HTML( + f"\n{len(assignments_unmappable)} role assignment(s) could not " + "find a matching Keeper Role by name:\n" + ), + tabulate(assignments_unmappable, headers="keys"), + end="\n\n", + ) + if users_without_groups: + print_formatted_text( + HTML( + f"\n{len(users_without_groups)} user(s) were not a member of " + f"any CyberArk group: {', '.join(users_without_groups)}" + ) + ) + + if not request_batch: + print_formatted_text(HTML("\nNo new role assignments to send.")) + return + + try: + responses = api.execute_batch(params, request_batch) + except Exception as e: + print_formatted_text(HTML(f"\nFailed to assign users to roles: {e}")) + return + + added = 0 + failed_assignments = [] + for (email, role_name), rs in zip(request_descriptions, responses or []): + if (rs or {}).get("result") == "success": + added += 1 + else: + failed_assignments.append( + { + "User": email, + "Role": role_name, + "Code": (rs or {}).get("result_code") or "unknown", + "Message": (rs or {}).get("message") or "no message", + } + ) + + print_formatted_text( + HTML(f"\nAssigned {added} of {len(request_batch)} user-to-role memberships") + ) + if failed_assignments: + print_formatted_text( + HTML("\nSome role assignments failed:\n"), + tabulate(failed_assignments, headers="keys"), + end="\n\n", + ) + + # Final refresh so subsequent commands see the new memberships. + try: + api.query_enterprise(params, force=True) + except Exception as e: + logging.debug("Post-role-assignment enterprise refresh failed: %s", e) + + +class CyberArkMembershipDownload(CyberArkImporter, BaseDownloadMembership): + """Download CyberArk safe and user-group membership for ``download-membership``. + + Maps CyberArk Safes → Keeper ``SharedFolder`` objects (with per-member + ``Permission`` entries) and CyberArk User Groups → Keeper ``Team`` objects. + """ + + @staticmethod + def _resolve_pvwa_host(): + """Prompt for (or read from env) the PVWA hostname / URL.""" + host = environ.get("_CYBERARK_PVWA_HOST") + if host: + return host + try: + entered = prompt( + "CyberArk PVWA hostname or URL (e.g. pvwa.example.com or https://tenant.cyberark.cloud): " + ).strip() + except (EOFError, KeyboardInterrupt): + return None + return entered or None + + @staticmethod + def _build_user_email_lookup(cyberark_users): + """Map CyberArk username (lowercase) → best available email.""" + lookup = {} + for u in cyberark_users or []: + username = (u.get("username") or "").strip() + email = (u.get("email") or "").strip() + if username and email: + lookup[username.lower()] = email + return lookup + + @staticmethod + def _build_service_account_username_set(cyberark_users): + """Return the set of lowercase usernames that are service accounts. + """ + return { + (u.get("username") or "").strip().lower() + for u in (cyberark_users or []) + if (u.get("username") or "").strip() and _is_service_account_user(u) + } + + @staticmethod + def _member_permission_name(member, user_email_lookup): + """Return the Keeper permission ``name`` for a CyberArk safe member.""" + member_name = (member.get("memberName") or "").strip() + member_type = member.get("memberType", "User") + if member_type == "Group": + return member_name + email = user_email_lookup.get(member_name.lower()) + return email or member_name + + def download_membership(self, params, **kwargs): + folders_only = kwargs.get("folders_only") is True + + include_service_accounts = environ.get( + "_CYBERARK_INCLUDE_COMPONENT_USERS", "" + ).lower() in ("1", "true", "yes") + pvwa_input = self._resolve_pvwa_host() + if not pvwa_input: + print_formatted_text(HTML("PVWA hostname is required")) + return + + try: + auth = self._authenticate_pvwa(pvwa_input) + if auth is None: + return + pvwa_host, authorization_token, _query_params = auth + + # Confirm before downloading CyberArk membership into Keeper. + if not self._confirm_import( + pvwa_host, + summary=( + f"\nYou are about to download membership data from CyberArk PVWA " + f"{pvwa_host} into Keeper.\n" + "This maps CyberArk safes to Keeper shared folders and " + "user groups to Keeper teams." + ), + question="Proceed with the membership download? (yes/no): ", + ): + print_formatted_text(HTML("\nMembership download cancelled by user")) + return + + print_formatted_text(HTML("\nFetching CyberArk users for email resolution...")) + cyberark_users = self.fetch_cyberark_users(pvwa_host, authorization_token) + user_email_lookup = self._build_user_email_lookup(cyberark_users) + service_account_usernames = ( + set() + if include_service_accounts + else self._build_service_account_username_set(cyberark_users) + ) + + groups_filter = None + groups_file = environ.get("_CYBERARK_GROUPS_PATH", "groups.txt") + if path.isfile(groups_file): + with open(groups_file, "r", encoding="utf-8") as f: + groups_filter = {line.strip() for line in f if line.strip()} + elif "_CYBERARK_GROUPS" in environ: + groups_filter = { + x.strip() for x in environ.get("_CYBERARK_GROUPS").split(",") if x.strip() + } + + safes_filter = None + safes_file = environ.get("_CYBERARK_SAFES_PATH", "safes.txt") + if path.isfile(safes_file): + with open(safes_file, "r", encoding="utf-8") as f: + safes_filter = {line.strip() for line in f if line.strip()} + elif "_CYBERARK_SAFES" in environ: + safes_filter = {x.strip() for x in environ.get("_CYBERARK_SAFES").split(",") if x.strip()} + + print_formatted_text(HTML("\nFetching CyberArk Safes...")) + safes = self.fetch_all_safes(pvwa_host, authorization_token, safes_filter=safes_filter) + if not safes: + print_formatted_text(HTML("No safes returned by CyberArk")) + else: + print_formatted_text( + HTML(f"Downloading membership for {len(safes)} safe(s)...") + ) + + sf_group_ids = set() + for safe in safes: + safe_name = safe.get("safeName") or safe.get("safeUrlId") or "" + safe_url_id = safe.get("safeUrlId") or safe.get("safeName") or "" + if not safe_name: + continue + + members = self.fetch_safe_members(pvwa_host, authorization_token, safe_url_id) + if not members: + continue + + shared_folder = SharedFolder() + shared_folder.uid = str(safe.get("id") or safe_url_id) + shared_folder.path = safe_name + shared_folder.permissions = [] + + skipped_service = 0 + for member in members: + member_type = member.get("memberType", "User") + member_name = (member.get("memberName") or "").strip() + # Skip non-human safe members (gateway/service/component users). + # Groups are kept — group membership is filtered separately below. + if ( + not include_service_accounts + and member_type != "Group" + and member_name + and ( + member_name.lower() in service_account_usernames + or _is_service_account_user(member) + ) + ): + skipped_service += 1 + continue + + perm_name = self._member_permission_name(member, user_email_lookup) + if not perm_name: + continue + keeper_perms = PermissionMapper.map_permissions(member.get("permissions") or {}) + perm = Permission() + perm.name = perm_name + perm.manage_users = keeper_perms.get("manage_users", False) + perm.manage_records = keeper_perms.get("manage_records", False) + shared_folder.permissions.append(perm) + if member_type == "Group": + sf_group_ids.add(perm_name) + + if skipped_service: + logging.debug( + "Safe %s: skipped %d service-account member(s)", + safe_name, + skipped_service, + ) + + if shared_folder.permissions: + yield shared_folder + + if folders_only: + return + + print_formatted_text(HTML("\nFetching CyberArk User Groups...")) + sleep(self.DELAY) + response = self.get_response( + self.get_url(pvwa_host, "user_groups"), + authorization_token, + {"includeMembers": "True"}, + ) + if response is None: + return + if response.status_code != 200: + print_formatted_text( + HTML( + f"Getting user groups failed with status " + f"{response.status_code}" + ) + ) + return + + groups = response.json().get("value", []) + if groups_filter: + groups = [ + g for g in groups + if g.get("groupName") in groups_filter or g.get("name") in groups_filter + ] + + if not sf_group_ids: + return + + for g in groups: + group_id = g.get("id") + group_name = g.get("groupName") or g.get("name") or "" + if not group_name or group_name not in sf_group_ids: + continue + + members = g.get("members") or [] + if not members and group_id is not None: + sleep(self.DELAY) + detail = self.get_response( + self.get_url(pvwa_host, "user_group").format(group_id=group_id), + authorization_token, + {"includeMembers": "True"}, + ) + if detail is not None and detail.status_code == 200: + try: + detail_payload = detail.json() + except ValueError: + detail_payload = {} + members = detail_payload.get("members") or [] + g["members"] = members + + emails = [] + for m in members: + username = (m.get("username") or m.get("userName") or "").strip() + if not username: + continue + # Skip service-account members of the group as well. + if ( + not include_service_accounts + and ( + username.lower() in service_account_usernames + or _is_service_account_user(m) + ) + ): + continue + email = user_email_lookup.get(username.lower()) or self._extract_user_email(m) + if email: + emails.append(email) + elif username: + emails.append(username) + + if not emails: + continue + + team = Team() + team.uid = str(group_id) if group_id is not None else group_name + team.name = group_name + team.members = list(dict.fromkeys(emails)) + yield team + + finally: + self._cleanup_tmp_cert_files() diff --git a/keepercommander/importer/cyberark_portal/cyberark_portal.py b/keepercommander/importer/cyberark_portal/cyberark_portal.py index 92e7e2e99..fb9601d92 100644 --- a/keepercommander/importer/cyberark_portal/cyberark_portal.py +++ b/keepercommander/importer/cyberark_portal/cyberark_portal.py @@ -1,7 +1,5 @@ import base64 import hashlib -import json -import os import re from http import HTTPStatus from http.server import HTTPServer, BaseHTTPRequestHandler @@ -17,11 +15,94 @@ from tabulate import tabulate -from ..importer import BaseImporter, Record, RecordField, Folder +from ..importer import BaseImporter, Folder, Record, RecordField import secrets import string +_SHARING_FIELD_NAMES = ( + "IsShared", + "Shared", + "IsOwner", + "Owner", + "OwnerLogin", + "OwnerEmail", + "OwnerDisplayName", + "SharedBy", + "SharedByName", + "SharedByDisplayName", + "SharedWith", + "SharedWithCount", + "SharingCount", + "ShareCount", + "AdminTag", + "IsAdminCreated", + "IsAdminAssigned", + "Permission", + "Permissions", + "EffectivePermissions", + "ItemPermissions", + "AccessLevel", + "AccessRights", + "Role", + "Roles", + "AssignedRoles", + "StartTime", + "EndTime", +) + + +def _format_sharing_value(value): + """Render a sharing/access value for display in a Keeper text field.""" + if isinstance(value, bool): + return "yes" if value else "no" + if isinstance(value, (list, tuple)): + parts = [] + for v in value: + if isinstance(v, dict): + # Prefer human-readable names if available + name = (v.get("Name") or v.get("DisplayName") or v.get("PrincipalName") + or v.get("UserName") or v.get("Login") or v.get("Email") + or v.get("ID") or "") + perm = v.get("Permission") or v.get("Right") or v.get("AccessLevel") or "" + if name and perm: + parts.append(f"{name} ({perm})") + elif name: + parts.append(str(name)) + else: + parts.append(str(v)) + elif v is not None and v != "": + parts.append(str(v)) + return ", ".join(parts) + if isinstance(value, dict): + return ", ".join(f"{k}={v}" for k, v in value.items() if v not in (None, "", False)) + return str(value) + + +def _collect_sharing_fields(item, prefix=""): + """Return a list of (label, value) tuples for sharing/access metadata + present on the given item dict. ``prefix`` is prepended to labels for + nested contexts (e.g. folder-level sharing on a record). + """ + out = [] + if not isinstance(item, dict): + return out + for key in _SHARING_FIELD_NAMES: + if key not in item: + continue + v = item.get(key) + if v in (None, "", [], {}): + continue + formatted = _format_sharing_value(v) + if not formatted or formatted in ("no", "0"): + # Skip explicit false/empty signals so we don't clutter records + # with negative facts. + continue + label = f"{prefix}{key}" if prefix else key + out.append((label, formatted)) + return out + + class CyberArkPortalImporter(BaseImporter): """CyberArk Portal Importer This importer enables users to import Applications and SecuredItems from the CyberArk User Portal. @@ -44,6 +125,231 @@ class CyberArkPortalImporter(BaseImporter): def get_url(identity_base_url, endpoint): return f'{identity_base_url.rstrip("/")}/{endpoint.lstrip("/").rstrip("/")}' + def _try_get_json(self, identity_base_url, endpoint, authentication_token, *, + params=None, payload=None, method="POST", + missing_endpoint_cache=None, quiet_statuses=(404, 403, 501)): + """Best-effort call to a CyberArk Identity endpoint that may or may + not be available on a given tenant. + + """ + url = self.get_url(identity_base_url, endpoint) + if missing_endpoint_cache is not None and endpoint in missing_endpoint_cache: + return None + try: + if method == "GET": + response = requests.get( + url, + headers={"Authorization": f"Bearer {authentication_token}"}, + params=params, + timeout=self.TIMEOUT, + ) + else: + response = requests.post( + url, + headers={"Authorization": f"Bearer {authentication_token}"}, + params=params, + json=payload if payload is not None else {}, + timeout=self.TIMEOUT, + ) + except requests.RequestException as e: + logging.debug(f"Sharing endpoint {endpoint} request failed: {e}") + return None + + if response.status_code in quiet_statuses: + if missing_endpoint_cache is not None and endpoint not in missing_endpoint_cache: + missing_endpoint_cache.add(endpoint) + logging.debug( + f"CyberArk Identity endpoint {endpoint} not available on this tenant " + f"(HTTP {response.status_code}); skipping sharing details for this resource type." + ) + return None + if response.status_code != HTTPStatus.OK: + logging.debug( + f"Sharing endpoint {endpoint} returned HTTP {response.status_code}: {response.text[:200]}" + ) + return None + try: + body = response.json() + except ValueError: + return None + if isinstance(body, dict) and body.get("success") is False: + logging.debug( + f"Sharing endpoint {endpoint} reported failure: {body.get('Message') or body.get('MessageID')}" + ) + return None + if isinstance(body, dict) and "Result" in body: + return body.get("Result") + return body + + def _fetch_app_sharing(self, identity_base_url, authentication_token, app_key, + missing_endpoint_cache): + """Try several CyberArk Identity endpoints that may surface app + sharing info. Returns a dict (possibly empty) of sharing metadata. + """ + if not app_key: + return {} + # /UPRest/GetAppByKey returns the same record as in GetUPData but + # some tenants include extra sharing/owner fields here. + result = self._try_get_json( + identity_base_url, + f"/UPRest/GetAppByKey?appkey={app_key}", + authentication_token, + missing_endpoint_cache=missing_endpoint_cache, + ) + if isinstance(result, dict): + return result + return {} + + def _fetch_secured_item_sharing(self, identity_base_url, authentication_token, + item_key, missing_endpoint_cache): + """Best-effort fetch of per-secured-item sharing details. + """ + if not item_key: + return {} + candidates = ( + f"/UPRest/GetSecuredItemShareInfo?sItemKey={item_key}", + f"/UPRest/GetSecuredItem?sItemKey={item_key}", + ) + for endpoint in candidates: + result = self._try_get_json( + identity_base_url, endpoint, authentication_token, + missing_endpoint_cache=missing_endpoint_cache, + ) + if isinstance(result, dict) and result: + return result + return {} + + def _fetch_folders(self, identity_base_url, authentication_token, + missing_endpoint_cache): + """Fetch the user's CyberArk Identity folders (a.k.a. Collections). + """ + candidates = ( + "/UPRest/GetCollectionList", + "/UPRest/GetCollections", + "/UPRest/GetFolders", + ) + for endpoint in candidates: + result = self._try_get_json( + identity_base_url, endpoint, authentication_token, + missing_endpoint_cache=missing_endpoint_cache, + ) + if result is None: + continue + # Different endpoints can return slightly different shapes; we + # normalize a few common ones. + if isinstance(result, list): + return [f for f in result if isinstance(f, dict)] + if isinstance(result, dict): + for key in ("Collections", "Folders", "Results"): + items = result.get(key) + if isinstance(items, list): + return [f for f in items if isinstance(f, dict)] + return [] + + @staticmethod + def _build_folder_index(folders): + """Build a mapping item_key -> list[folder_dict] for every secured + item / app that's listed as a member of any folder. + """ + index = {} + for folder in folders: + if not isinstance(folder, dict): + continue + member_keys = [] + for k in ("AppKeys", "SecuredItemKeys", "ItemKeys"): + v = folder.get(k) + if isinstance(v, list): + member_keys.extend(str(x) for x in v if x) + for k in ("Items", "Members"): + v = folder.get(k) + if isinstance(v, list): + for member in v: + if isinstance(member, dict): + mk = (member.get("AppKey") or member.get("ItemKey") + or member.get("Key") or member.get("ID") + or member.get("_RowKey")) + if mk: + member_keys.append(str(mk)) + elif member: + member_keys.append(str(member)) + for mk in member_keys: + index.setdefault(mk, []).append(folder) + return index + + @staticmethod + def _folder_display_name(folder): + return (folder.get("Name") or folder.get("DisplayName") + or folder.get("Title") or folder.get("CollectionName") + or folder.get("ID") or "") + + def _apply_sharing_and_folders(self, *, record, item, item_key, folder_index, + identity_base_url, authentication_token, + missing_endpoint_cache, item_kind): + """Attach sharing/access metadata and folder placement to ``record``. + """ + # 1) Surface sharing fields already on the bulk-response item. + for label, value in _collect_sharing_fields(item): + record.fields.append(RecordField(type="text", label=f"CyberArk {label}", value=value)) + + # 2) Best-effort per-item fetch for richer sharing details. + detail = {} + if item_kind == "app": + detail = self._fetch_app_sharing( + identity_base_url, authentication_token, item_key, missing_endpoint_cache, + ) + elif item_kind == "secured_item": + detail = self._fetch_secured_item_sharing( + identity_base_url, authentication_token, item_key, missing_endpoint_cache, + ) + if detail: + # Avoid duplicating labels we already wrote from the bulk response. + already = {f.label for f in record.fields if f.label} + for label, value in _collect_sharing_fields(detail): + full_label = f"CyberArk {label}" + if full_label in already: + continue + record.fields.append(RecordField(type="text", label=full_label, value=value)) + already.add(full_label) + + # 3) Folder placement + folder-level sharing. + if folder_index and item_key: + owning_folders = folder_index.get(str(item_key)) or [] + if owning_folders: + record.folders = [] + folder_share_lines = [] + for f in owning_folders: + fname = self._folder_display_name(f) + if not fname: + continue + rec_folder = Folder() + rec_folder.path = fname + # Map CyberArk's coarse folder permissions onto Keeper's + perm = (f.get("Permission") or f.get("AccessLevel") + or f.get("EffectivePermission") or "") + perm_lower = str(perm).lower() + if perm_lower in ("owner", "manage"): + rec_folder.can_edit = True + rec_folder.can_share = True + elif perm_lower in ("edit", "modify", "write"): + rec_folder.can_edit = True + rec_folder.can_share = False + elif perm_lower in ("view", "read"): + rec_folder.can_edit = False + rec_folder.can_share = False + record.folders.append(rec_folder) + + fshare = _collect_sharing_fields(f, prefix=f"Folder[{fname}] ") + folder_share_lines.extend(f"{label}: {value}" for label, value in fshare) + + if folder_share_lines: + record.fields.append( + RecordField( + type="text", + label="CyberArk Folder Sharing", + value="\n".join(folder_share_lines), + ) + ) + @staticmethod def discover_identity_url(tenant_name): """Discover the actual CyberArk Identity URL for a tenant. @@ -89,63 +395,7 @@ def discover_identity_url(tenant_name): ) return default_url - @staticmethod - def _create_empty_keeper_folder(params, folder_name, is_shared): - """Create an empty folder directly in the user's Keeper vault. - - The Keeper importer pipeline creates user folders only implicitly when a record is placed - inside them, so a CyberArk folder with zero items would otherwise never appear in Keeper. - We bypass that limitation by calling the folder_add Keeper API directly. If `params` is - not available (e.g. the importer is being exercised in dry-run mode or a context that does - not forward params), we silently skip the creation instead of failing. - """ - if not params or not folder_name: - return - try: - from ... import api, crypto, utils - except Exception as e: - logging.debug(f"Unable to import Keeper api helpers for folder creation: {e}") - return - - try: - # Skip if a top-level folder with the same name already exists to avoid duplicates - # on repeated imports. - existing_names = set() - root_subfolders = getattr(params.root_folder, "subfolders", None) or [] - for sub_uid in root_subfolders: - sub = params.folder_cache.get(sub_uid) - if sub and sub.name: - existing_names.add(sub.name.lower()) - if folder_name.lower() in existing_names: - logging.debug(f"Folder '{folder_name}' already exists in vault; skipping creation.") - return - - folder_uid = api.generate_record_uid() - folder_key = os.urandom(32) - request = { - "command": "folder_add", - "folder_uid": folder_uid, - "folder_type": "shared_folder" if is_shared else "user_folder", - "key": utils.base64_url_encode(crypto.encrypt_aes_v1(folder_key, params.data_key)), - } - - data = json.dumps({"name": folder_name}) - request["data"] = utils.base64_url_encode( - crypto.encrypt_aes_v1(data.encode("utf-8"), folder_key) - ) - if is_shared: - request["name"] = utils.base64_url_encode( - crypto.encrypt_aes_v1(folder_name.encode("utf-8"), folder_key) - ) - - api.communicate(params, request) - params.sync_data = True - logging.info(f"Created empty Keeper folder '{folder_name}' (is_shared={is_shared}).") - except Exception as e: - logging.warning(f"Failed to create empty folder '{folder_name}': {e}") - def do_import(self, filename, **kwargs): - params = kwargs.get("params") name = filename.removeprefix("https://").removeprefix("http://") host_part = name.split("/")[0] @@ -450,103 +700,10 @@ def complete(self): print_formatted_text(HTML("Authentication successful")) - auth_headers = {"Authorization": f"Bearer {authentication_token}"} - - - app_folder_map = {} # type: dict - item_folder_map = {} # type: dict - - folders_response = requests.post( - self.get_url(identity_base_url, "/Folder/GetFolders"), - headers=auth_headers, - json={}, - timeout=self.TIMEOUT, - ) - - if folders_response.status_code == HTTPStatus.OK: - folders_result = folders_response.json().get("Result") or [] - logging.debug(f"Folders: {folders_result}") - for folder in folders_result: - folder_uuid = folder.get("FolderUuid") - folder_name = (folder.get("Name") or "").strip() - if not folder_name: - continue - share_option = folder.get("ShareOption", "NotShared") - is_shared = share_option != "NotShared" - folder_meta = { - "uuid": folder_uuid, - "name": folder_name, - "is_shared": is_shared, - } - - app_keys = set(folder.get("Apps") or []) - item_keys = set(folder.get("SecuredItems") or []) - - if folder_uuid: - items_response = requests.post( - self.get_url( - identity_base_url, - f"/Folder/GetFolderItems?folderUuid={folder_uuid}", - ), - headers=auth_headers, - json={}, - timeout=self.TIMEOUT, - ) - if items_response.status_code == HTTPStatus.OK: - items_result = items_response.json().get("Result") or {} - app_keys.update(items_result.get("Apps") or []) - item_keys.update(items_result.get("SecuredItems") or []) - for it in items_result.get("Items") or []: - key = it.get("ItemKey") or it.get("_RowKey") - if not key: - continue - it_type = it.get("Type") or "" - if it_type == "App" or "AppType" in it: - app_keys.add(key) - else: - item_keys.add(key) - else: - logging.warning( - f"HTTP {items_response.status_code} getting items for folder " - f"{folder_name} ({folder_uuid}): {items_response.text[:200]}" - ) - - for k in app_keys: - app_folder_map[k] = folder_meta - for k in item_keys: - item_folder_map[k] = folder_meta - - - if not app_keys and not item_keys: - self._create_empty_keeper_folder( - params, folder_meta["name"], folder_meta["is_shared"] - ) - else: - logging.warning( - f"HTTP {folders_response.status_code} getting folders: {folders_response.text[:200]}; " - f"records will be imported at the vault root." - ) - - def _attach_folder(record, folder_meta): - """Attach a single Folder reference to the record if the item lives inside a CyberArk folder. - - - Shared folder -> Folder.domain = folder name (Keeper creates a Shared Folder) - - Personal folder -> Folder.path = folder name (Keeper creates a user folder) - - No mapping -> no folder is attached, record goes to the vault root - """ - if not folder_meta or not folder_meta.get("name"): - return - f = Folder() - if folder_meta["is_shared"]: - f.domain = folder_meta["name"] - else: - f.path = folder_meta["name"] - record.folders = [f] - # Get all the application data (except the password, of course) in one API call response = requests.post( self.get_url(identity_base_url, "/UPRest/GetUPData"), - headers=auth_headers, + headers={"Authorization": f"Bearer {authentication_token}"}, json={}, timeout=self.TIMEOUT, ) @@ -556,6 +713,16 @@ def _attach_folder(record, folder_meta): return apps = response.json()["Result"]["Apps"] + + missing_endpoint_cache = set() + folders = self._fetch_folders(identity_base_url, authentication_token, missing_endpoint_cache) + folder_index = self._build_folder_index(folders) if folders else {} + if folders: + print_formatted_text( + HTML(f"Discovered {len(folders)} CyberArk folder(s) for the current user."), + end="\n\n", + ) + if len(apps) > 0: print_formatted_text( HTML(f"Importing {len(apps)} Applications:\n"), @@ -576,7 +743,6 @@ def _attach_folder(record, folder_meta): record.login = app.get("Username", "") record.login_url = app.get("Url", "") record.notes = app.get("Notes", "") - _attach_folder(record, app_folder_map.get(app_key)) if app.get("IsTotpSet"): record.notes += "The CyberArk Application had a TOTP that Keeper could not access to import." @@ -586,9 +752,20 @@ def _attach_folder(record, folder_meta): RecordField(type="text", label="Tags", value=", ".join(str(tag) for tag in app["Tags"])) ) + self._apply_sharing_and_folders( + record=record, + item=app, + item_key=app_key, + folder_index=folder_index, + identity_base_url=identity_base_url, + authentication_token=authentication_token, + missing_endpoint_cache=missing_endpoint_cache, + item_kind="app", + ) + response = requests.post( self.get_url(identity_base_url, f"/UPRest/GetMCFA?appkey={app_key}"), - headers=auth_headers, + headers={"Authorization": f"Bearer {authentication_token}"}, json={}, timeout=self.TIMEOUT, ) @@ -602,19 +779,12 @@ def _attach_folder(record, folder_meta): logging.warning(f"No password found for app {app_key}; response: {response.text}") else: record.password = response.json()["Result"].get("p", "") - record.username = response.json()["Result"].get("u", "") - record.notes = response.json()["Result"].get("n", "") - record.fields.append(RecordField(type="text", label="Tags", value=", ".join(str(tag) for tag in response.json()["Result"].get("t", [])))) - record.fields.append(RecordField(type="text", label="Category", value=response.json()["Result"].get("c", ""))) - record.fields.append(RecordField(type="text", label="Description", value=response.json()["Result"].get("d", ""))) - record.fields.append(RecordField(type="text", label="Registration Message", value=response.json()["Result"].get("rm", ""))) - record.fields.append(RecordField(type="text", label="Registration Link Message", value=response.json()["Result"].get("rrm", ""))) yield record response = requests.post( self.get_url(identity_base_url, "/UPRest/GetSecuredItemsData"), - headers=auth_headers, + headers={"Authorization": f"Bearer {authentication_token}"}, json={}, timeout=self.TIMEOUT, ) @@ -638,17 +808,26 @@ def _attach_folder(record, folder_meta): item_key = item["ItemKey"] record = Record() record.title = item["Name"] - _attach_folder(record, item_folder_map.get(item_key)) if item.get("Tags"): record.fields.append( RecordField(type="text", label="Tags", value=", ".join(str(tag) for tag in item["Tags"])) ) - + + self._apply_sharing_and_folders( + record=record, + item=item, + item_key=item_key, + folder_index=folder_index, + identity_base_url=identity_base_url, + authentication_token=authentication_token, + missing_endpoint_cache=missing_endpoint_cache, + item_kind="secured_item", + ) response = requests.post( self.get_url(identity_base_url, f"/UPRest/GetCredsForSecuredItem?sItemKey={item_key}"), - headers=auth_headers, + headers={"Authorization": f"Bearer {authentication_token}"}, json={}, timeout=self.TIMEOUT, ) From 7abf62f7ea8d955ffb22fac624fc4d8588d7862a Mon Sep 17 00:00:00 2001 From: Matthew Ford Date: Wed, 3 Jun 2026 07:29:49 -0700 Subject: [PATCH 13/23] KC-1299: cloud secret import to keeper drive (#2107) * Use batch v3 endpoint to create records from cloud secrets in Keeper Drive folders. --- .../commands/_cloud_import_base.py | 134 ++++++++++++++++-- 1 file changed, 119 insertions(+), 15 deletions(-) diff --git a/keepercommander/commands/_cloud_import_base.py b/keepercommander/commands/_cloud_import_base.py index 8bd18ed89..aaadaf331 100644 --- a/keepercommander/commands/_cloud_import_base.py +++ b/keepercommander/commands/_cloud_import_base.py @@ -13,6 +13,7 @@ import json import logging +import os import re from typing import Callable, Dict, List, Optional, Tuple @@ -272,7 +273,8 @@ def _validate_folder(params, folder_uid, command_name): 'Use "list-sf" to find the correct shared folder UID, ' 'or run "sync-down" if the folder was recently shared.' ) - if not isinstance(folder, (SharedFolderNode, SharedFolderFolderNode)): + if not (isinstance(folder, (SharedFolderNode, SharedFolderFolderNode)) or + (hasattr(folder, 'type') and folder.type == 'nested_share_folder')): raise CommandError( command_name, f'"{folder_uid}" is a personal folder. ' @@ -290,8 +292,12 @@ def _run_import(self, params, secrets, folder_uid, record_type, filter_name, # type: (KeeperParams, list, str, str, Optional[str], Optional[str], Optional[str], Optional[str], List[Tuple[str, str]], bool, str, Optional[Callable]) -> None """ Iterate *secrets* (list of dicts with at minimum 'name' and 'tags'), - apply all filters, then create Keeper records via batched vault/records_add - calls (up to BATCH_SIZE records per request). + apply all filters, then create Keeper records via batched API calls + (up to BATCH_SIZE records per request). + + When *folder_uid* belongs to a nested_share_folder the records are + created via ``vault/records/v3/add``; otherwise the legacy + ``vault/records_add`` endpoint is used. *value_fetcher*, when provided, is called as ``value_fetcher(name) -> str`` for each secret that passes filters and is not a dry-run. This enables @@ -320,10 +326,9 @@ def _run_import(self, params, secrets, folder_uid, record_type, filter_name, if dry_run: return - # Phase 2 – fetch values (if needed) and build TypedRecord + protobuf - # objects without touching the Keeper API yet. + # Phase 2 – fetch values (if needed) and build TypedRecord objects. skipped = 0 - pending = [] # type: List[Tuple[vault.TypedRecord, record_pb2.RecordAdd]] + typed_records = [] # type: List[vault.TypedRecord] for item in matched: name = item['name'] @@ -339,17 +344,118 @@ def _run_import(self, params, secrets, folder_uid, record_type, filter_name, value = item.get('value', '') fields = self._parse_secret_string(value) - record = self._build_keeper_record(name, fields, record_type) - pb = record_management.add_record_to_folder(params, record, folder_uid, pb_only=True) - if pb is not None: - pending.append((record, pb)) + typed_records.append(self._build_keeper_record(name, fields, record_type)) - if not pending: + if not typed_records: print(f'{command_name}: 0 record(s) created, {skipped} skipped.') return - # Phase 3 – send in batches of up to BATCH_SIZE to vault/records_add. + # Phase 3 – send in batches, routing to the appropriate API endpoint. + is_nsf = folder_uid in getattr(params, 'nested_share_folders', {}) + if is_nsf: + created, nsf_skipped = self._send_nsf_batches( + params, typed_records, folder_uid, command_name) + skipped += nsf_skipped + else: + created, legacy_skipped = self._send_legacy_batches( + params, typed_records, folder_uid, command_name) + skipped += legacy_skipped + + if created: + params.sync_data = True + print(f'{command_name}: {created} record(s) created, {skipped} skipped.') + + # ------------------------------------------------------------------ + # Phase-3 dispatch helpers + # ------------------------------------------------------------------ + + @staticmethod + def _typed_record_to_data(record): + # type: (vault.TypedRecord) -> dict + """Serialise a TypedRecord to the dict format expected by the v3 record API.""" + data = { + 'type': record.type_name, + 'title': record.title, + 'fields': [ + {'type': f.type, 'label': f.label or '', 'value': list(f.value)} + for f in record.fields + ], + 'custom': [ + {'type': f.type, 'label': f.label or '', 'value': list(f.value)} + for f in record.custom + ], + } + if record.notes: + data['notes'] = record.notes + return data + + @staticmethod + def _send_nsf_batches(params, typed_records, folder_uid, command_name): + # type: (KeeperParams, List[vault.TypedRecord], str, str) -> Tuple[int, int] + """Send *typed_records* to a nested_share_folder via ``vault/records/v3/add``.""" + from ..nested_share_folder.common import get_folder_key + from ..nested_share_folder.record_api import create_record_data_v3, record_add_v3 + + folder_key = get_folder_key(params, folder_uid, raise_on_missing=True) + + created = 0 + skipped = 0 + + for batch_start in range(0, len(typed_records), BATCH_SIZE): + batch = typed_records[batch_start:batch_start + BATCH_SIZE] + batch_num = batch_start // BATCH_SIZE + 1 + logging.info('%s: sending batch %d (%d record(s))', command_name, batch_num, len(batch)) + + uid_to_title = {} + adds = [] + for record in batch: + uid = utils.generate_uid() + rk = os.urandom(32) + data = CloudImportMixin._typed_record_to_data(record) + ra = create_record_data_v3( + record_uid=uid, record_key=rk, data=data, + folder_uid=folder_uid, folder_key=folder_key, + data_key=params.data_key, + client_modified_time=utils.current_milli_time(), + ) + adds.append(ra) + uid_to_title[uid] = record.title + + try: + rs = record_add_v3(params, adds) + except Exception as exc: + logging.warning('%s: batch %d failed: %s', command_name, batch_num, exc) + skipped += len(batch) + continue + + for r in rs.records: + uid = utils.base64_url_encode(r.record_uid) + title = uid_to_title.get(uid, uid) + if r.status == record_pb2.RS_SUCCESS: + logging.debug('%s: created record "%s"', command_name, title) + created += 1 + else: + logging.warning('%s: failed to create record "%s": status=%s', + command_name, title, r.status) + skipped += 1 + + return created, skipped + + @staticmethod + def _send_legacy_batches(params, typed_records, folder_uid, command_name): + # type: (KeeperParams, List[vault.TypedRecord], str, str) -> Tuple[int, int] + """Send *typed_records* to a regular shared folder via ``vault/records_add``.""" created = 0 + skipped = 0 + + pending = [] # type: List[Tuple[vault.TypedRecord, record_pb2.RecordAdd]] + for record in typed_records: + pb = record_management.add_record_to_folder(params, record, folder_uid, pb_only=True) + if pb is not None: + pending.append((record, pb)) + + if not pending: + return 0, 0 for batch_start in range(0, len(pending), BATCH_SIZE): batch = pending[batch_start:batch_start + BATCH_SIZE] @@ -385,6 +491,4 @@ def _run_import(self, params, secrets, folder_uid, record_type, filter_name, command_name, record.title, rs_rec.status) skipped += 1 - if created: - params.sync_data = True - print(f'{command_name}: {created} record(s) created, {skipped} skipped.') + return created, skipped From 163ea38e4be4c8c7f17283f30b86adb3ac779c00 Mon Sep 17 00:00:00 2001 From: lthievenaz-keeper Date: Wed, 3 Jun 2026 18:50:44 +0100 Subject: [PATCH 14/23] Fix circular KSM import (#2115) * Fix circular KSM import Moved KSM, gateway_helper and router_helper into their respective functions to avoid an issue with circular imports when importing commands, eg: `from keepercommander.commands.utils import WhoamiCommand` `from keepercommander.commands.discoveryrotation import PAMGatewayListCommand` * Changed KSM import in pam.gateway_helper KSMCommand import in pam.gateway_helper doesn't point to the core file where KSMCommand is defined. While this doesn't appear to cause an issue, fixed to avoid any future circular import problems. --- keepercommander/commands/pam/gateway_helper.py | 4 ++-- keepercommander/commands/record.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/keepercommander/commands/pam/gateway_helper.py b/keepercommander/commands/pam/gateway_helper.py index ec87111e9..aafed1c6e 100644 --- a/keepercommander/commands/pam/gateway_helper.py +++ b/keepercommander/commands/pam/gateway_helper.py @@ -5,7 +5,7 @@ from keeper_secrets_manager_core.utils import url_safe_str_to_bytes from ... import api, utils -from ...commands.utils import KSMCommand +from ...commands.ksm import KSMCommand from ...loginv3 import CommonHelperMethods from ...params import KeeperParams from ...proto import pam_pb2, enterprise_pb2 @@ -119,4 +119,4 @@ def edit_gateway(params, gateway_uid, gateway_name, node_id): rq.controllerName = gateway_name rq.nodeId = node_id api.communicate_rest(params, rq, 'pam/modify_controller') - \ No newline at end of file + diff --git a/keepercommander/commands/record.py b/keepercommander/commands/record.py index bf1d70875..0c8d152de 100644 --- a/keepercommander/commands/record.py +++ b/keepercommander/commands/record.py @@ -27,9 +27,6 @@ from . import record_edit, base, record_totp, record_file_report from .base import Command, GroupCommand, RecordMixin, FolderMixin, fields_to_titles -from .ksm import KSMCommand -from .pam import gateway_helper -from .pam.router_helper import router_get_connected_gateways from .. import api, display, crypto, utils, vault, vault_extensions, subfolder, record_types from ..breachwatch import BreachWatch from ..display import bcolors @@ -1347,6 +1344,8 @@ def display_rotation_info(self, params, r): def _resolve_gateways_by_pattern(params, pattern): + from .pam import gateway_helper + from .pam.router_helper import router_get_connected_gateways """Resolve a `--device` pattern to a list of Gateway controller objects. Matches both: exact controllerUid AND case-insensitive substring on @@ -1417,6 +1416,7 @@ def _find_pam_configs_for_gateway(params, gateway_uid_str): def _build_gateway_info(c, connected_instances, params, is_router_down, is_verbose): + from .ksm import KSMCommand """Build a dict describing one Gateway, suitable for table or JSON rendering.""" gateway_uid_str = utils.base64_url_encode(c.controllerUid) ksm_app_uid_str = utils.base64_url_encode(c.applicationUid) From 0221d43e333c649ef08757dce721e6d8eee2ae10 Mon Sep 17 00:00:00 2001 From: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:23:04 -0500 Subject: [PATCH 15/23] Re-apply DAG-migration local-only changes onto release base (per-graph read path, local-vault config_uid, set_launch_credentials, discovery_common+keeper_dag parity, tests) --- docker_ksm_utility.py | 7 +- .../biometric/utils/error_handler.py | 5 +- .../commands/credential_provision.py | 2 +- .../commands/discover/job_remove.py | 2 +- .../commands/discover/job_start.py | 2 +- .../commands/discover/job_status.py | 18 +- .../commands/discover/result_process.py | 6 +- keepercommander/commands/discover/rule_add.py | 2 +- .../commands/discover/rule_list.py | 2 +- .../commands/discover/rule_remove.py | 2 +- .../commands/discover/rule_update.py | 2 +- keepercommander/commands/discoveryrotation.py | 16 +- keepercommander/commands/pam/_layer_b.py | 219 ++++ keepercommander/commands/pam/router_helper.py | 23 +- .../commands/pam_debug/__init__.py | 7 +- keepercommander/commands/pam_debug/acl.py | 2 +- keepercommander/commands/pam_debug/dump.py | 38 +- keepercommander/commands/pam_debug/gateway.py | 6 +- keepercommander/commands/pam_debug/graph.py | 40 +- keepercommander/commands/pam_debug/info.py | 8 +- keepercommander/commands/pam_debug/krouter.py | 87 ++ keepercommander/commands/pam_debug/link.py | 2 +- .../commands/pam_debug/rotation_setting.py | 2 +- keepercommander/commands/pam_debug/vertex.py | 2 +- .../commands/pam_import/kcm_import.py | 25 +- .../commands/pam_import/keeper_ai_settings.py | 401 +++--- keepercommander/commands/pam_service/add.py | 4 +- keepercommander/commands/pam_service/list.py | 2 +- .../commands/pam_service/remove.py | 2 +- .../tunnel/port_forward/TunnelGraph.py | 275 +++- .../discovery_common/__version__.py | 2 +- .../discovery_common/infrastructure.py | 56 +- keepercommander/discovery_common/jobs.py | 49 +- .../discovery_common/record_link.py | 20 +- keepercommander/discovery_common/rm_types.py | 236 +++- keepercommander/discovery_common/rule.py | 43 +- keepercommander/discovery_common/types.py | 4 +- .../discovery_common/user_service.py | 51 +- keepercommander/discovery_common/utils.py | 6 +- keepercommander/email_service.py | 29 +- keepercommander/keeper_dag/__version__.py | 2 +- .../keeper_dag/connection/__init__.py | 142 ++- keepercommander/keeper_dag/dag.py | 101 +- keepercommander/keeper_dag/struct/__init__.py | 31 + keepercommander/keeper_dag/struct/default.py | 63 +- keepercommander/keeper_dag/struct/protobuf.py | 77 +- keepercommander/keeper_dag/types.py | 19 +- pytest.ini | 2 + tests/test_credential_provision_execute.py | 13 +- tests/test_dag_integration.py | 175 +++ tests/test_security_audit_refresh.py | 20 +- unit-tests/pam/_dag_fixtures.py | 68 + unit-tests/pam/test_dag_cross_cutting.py | 178 +++ .../pam/test_dag_graph_sync_endpoints.py | 203 +++ .../test_dag_layer_b_configure_resource.py | 248 ++++ unit-tests/pam/test_dag_layer_b_fallback.py | 359 ++++++ unit-tests/pam/test_dag_layer_b_migration.py | 1122 +++++++++++++++++ .../test_dag_layer_b_set_record_rotation.py | 218 ++++ unit-tests/pam/test_dag_multi_sync_load.py | 232 ++++ .../test_discovery_common_per_graph_flag.py | 232 ++++ .../pam/test_get_config_uid_local_vault.py | 175 +++ unit-tests/pam/test_kcm_import.py | 36 +- unit-tests/pam/test_set_launch_credentials.py | 275 ++++ unit-tests/test_email_service.py | 22 +- 64 files changed, 5328 insertions(+), 392 deletions(-) create mode 100644 keepercommander/commands/pam/_layer_b.py create mode 100644 keepercommander/commands/pam_debug/krouter.py create mode 100644 tests/test_dag_integration.py create mode 100644 unit-tests/pam/_dag_fixtures.py create mode 100644 unit-tests/pam/test_dag_cross_cutting.py create mode 100644 unit-tests/pam/test_dag_graph_sync_endpoints.py create mode 100644 unit-tests/pam/test_dag_layer_b_configure_resource.py create mode 100644 unit-tests/pam/test_dag_layer_b_fallback.py create mode 100644 unit-tests/pam/test_dag_layer_b_migration.py create mode 100644 unit-tests/pam/test_dag_layer_b_set_record_rotation.py create mode 100644 unit-tests/pam/test_dag_multi_sync_load.py create mode 100644 unit-tests/pam/test_discovery_common_per_graph_flag.py create mode 100644 unit-tests/pam/test_get_config_uid_local_vault.py create mode 100644 unit-tests/pam/test_set_launch_credentials.py diff --git a/docker_ksm_utility.py b/docker_ksm_utility.py index 4b5088593..ff80926c1 100755 --- a/docker_ksm_utility.py +++ b/docker_ksm_utility.py @@ -6,6 +6,7 @@ records, including config processing, file upload/download, and monitoring. """ +import importlib import sys import os import argparse @@ -70,7 +71,7 @@ def check_ksm_dependency(): bool: True if installed, False otherwise """ try: - import keeper_secrets_manager_core # noqa: F401 + importlib.import_module('keeper_secrets_manager_core') return True except ImportError: print("ERROR: keeper_secrets_manager_core is not installed") @@ -180,7 +181,7 @@ def _decode_and_save_config(base64_input, config_path): os.chmod(config_path, 0o600) return True - except Exception as e: + except Exception: print("ERROR: Failed to decode and save config") return False @@ -598,7 +599,7 @@ def monitor_config(ksm_config_path, ksm_token, record_identifier, config_file_pa return ksm_config_path = validated_ksm_config_path - print(f"Monitoring config file for changes") + print("Monitoring config file for changes") last_hash = _get_secure_file_hash(config_file_path) monitor_interval = 30 # Check every 30 seconds diff --git a/keepercommander/biometric/utils/error_handler.py b/keepercommander/biometric/utils/error_handler.py index f41c6b578..d1f015b12 100644 --- a/keepercommander/biometric/utils/error_handler.py +++ b/keepercommander/biometric/utils/error_handler.py @@ -12,6 +12,7 @@ """ Centralized error handling utilities for biometric authentication """ +import importlib import logging from ...error import CommandError @@ -62,7 +63,7 @@ def handle_credential_creation_error(error: Exception, platform_name: str = "Bio return Exception(f"{platform_name} registration cancelled") elif ("object already exists" in error_msg or ("oserror" in error_msg and "22" in error_msg and "object already exists" in error_msg)): - return Exception(f"A biometric credential for this account already exists") + return Exception("A biometric credential for this account already exists") elif "timeout" in error_msg: return Exception(f"{platform_name} registration timed out") elif "not available" in error_msg: @@ -107,7 +108,7 @@ def validate_dependencies(required_modules: list, platform_name: str = "Platform missing_modules = [] for module in required_modules: try: - __import__(module) + importlib.import_module(module) except ImportError: missing_modules.append(module) diff --git a/keepercommander/commands/credential_provision.py b/keepercommander/commands/credential_provision.py index b1a9008bd..d19711948 100644 --- a/keepercommander/commands/credential_provision.py +++ b/keepercommander/commands/credential_provision.py @@ -1773,7 +1773,7 @@ def _create_dag_link( pam_config_record = vault.KeeperRecord.load(params, pam_config_uid) # Create RecordLink instance - record_link = RecordLink(record=pam_config_record, params=params, fail_on_corrupt=False) + record_link = RecordLink(record=pam_config_record, params=params, fail_on_corrupt=False, use_per_graph_endpoints=True) # Create belongs_to relationship: PAM User belongs_to PAM Configuration record_link.belongs_to( diff --git a/keepercommander/commands/discover/job_remove.py b/keepercommander/commands/discover/job_remove.py index fed280496..9a15a1755 100644 --- a/keepercommander/commands/discover/job_remove.py +++ b/keepercommander/commands/discover/job_remove.py @@ -39,7 +39,7 @@ def execute(self, params, **kwargs): all_gateways = GatewayContext.all_gateways(params) def _find_job(configuration_record) -> Optional[Dict]: - jobs_obj = Jobs(record=configuration_record, params=params) + jobs_obj = Jobs(record=configuration_record, params=params, use_per_graph_endpoints=True) job_item = jobs_obj.get_job(job_id) if job_item is not None: return { diff --git a/keepercommander/commands/discover/job_start.py b/keepercommander/commands/discover/job_start.py index 1093ec402..4c80016e8 100644 --- a/keepercommander/commands/discover/job_start.py +++ b/keepercommander/commands/discover/job_start.py @@ -112,7 +112,7 @@ def execute(self, params, **kwargs): multi_conf_msg(gateway, err) return - jobs = Jobs(record=gateway_context.configuration, params=params) + jobs = Jobs(record=gateway_context.configuration, params=params, use_per_graph_endpoints=True) current_job_item = jobs.current_job removed_prior_job = None if current_job_item is not None: diff --git a/keepercommander/commands/discover/job_status.py b/keepercommander/commands/discover/job_status.py index 8ad2bddd3..f4e8c046d 100644 --- a/keepercommander/commands/discover/job_status.py +++ b/keepercommander/commands/discover/job_status.py @@ -3,18 +3,16 @@ import logging from . import PAMGatewayActionDiscoverCommandBase, GatewayContext from ..pam.router_helper import router_get_connected_gateways -from ... import vault_extensions from ...display import bcolors from ...discovery_common.jobs import Jobs from ...discovery_common.infrastructure import Infrastructure -from ...discovery_common.constants import DIS_INFRA_GRAPH_ID +from ...keeper_dag.types import PamEndpoints from ...discovery_common.types import DiscoveryDelta, DiscoveryObject from ...keeper_dag.dag import DAG from typing import Optional, Dict, List, TYPE_CHECKING if TYPE_CHECKING: from ...params import KeeperParams - from ...discovery_common.jobs import JobItem def _h(text): @@ -148,7 +146,7 @@ def print_job_table(jobs: List[Dict], print(_f(f" pam action discover status -j {job_id}")) print("") if len(failed_jobs) == 1: - print(f"To remove the job, use the following command.") + print("To remove the job, use the following command.") else: print(f"To remove the {_f('FAILED')} job, use one of the following commands.") for job_id in failed_jobs: @@ -162,7 +160,7 @@ def print_job_detail(params: KeeperParams, job_id: str): def _find_job(configuration_record) -> Optional[Dict]: - jobs_obj = Jobs(record=configuration_record, params=params) + jobs_obj = Jobs(record=configuration_record, params=params, use_per_graph_endpoints=True) job_item = jobs_obj.get_job(job_id) if job_item is not None: return { @@ -177,7 +175,7 @@ def _find_job(configuration_record) -> Optional[Dict]: if gateway_context is not None: jobs = payload["jobs"] job = jobs.get_job(job_id) # type: JobItem - infra = Infrastructure(record=gateway_context.configuration, params=params) + infra = Infrastructure(record=gateway_context.configuration, params=params, use_per_graph_endpoints=True) color = bcolors.OKBLUE status = "RUNNING" @@ -242,7 +240,7 @@ def _find_job(configuration_record) -> Optional[Dict]: discovery_object = DiscoveryObject.get_discovery_object(vertex) print(f" * {discovery_object.description}") if item.changes is None: - print(f" no changed, may be a object not added in prior discoveries.") + print(" no changed, may be a object not added in prior discoveries.") else: for key, value in item.changes.items(): print(f" - {key} = {value}") @@ -258,7 +256,9 @@ def _find_job(configuration_record) -> Optional[Dict]: print(f"{_f('Could not load delta from infrastructure: ' + str(err))}") print("Fall back to raw graph.") print("") - dag = DAG(conn=infra.conn, record=infra.record, graph_id=DIS_INFRA_GRAPH_ID) + dag = DAG(conn=infra.conn, record=infra.record, + read_endpoint=PamEndpoints.INFRASTRUCTURE, + write_endpoint=PamEndpoints.INFRASTRUCTURE) print(dag.to_dot_raw(sync_point=job.sync_point, rank_dir="RL")) else: @@ -325,7 +325,7 @@ def execute(self, params, **kwargs): if len(gateway_context.gateway_name) > max_gateway_name: max_gateway_name = len(gateway_context.gateway_name) - jobs = Jobs(record=configuration_record, params=params) + jobs = Jobs(record=configuration_record, params=params, use_per_graph_endpoints=True) if show_history is True: job_list = reversed(jobs.history) else: diff --git a/keepercommander/commands/discover/result_process.py b/keepercommander/commands/discover/result_process.py index 4f44b0d21..5fa7ede00 100644 --- a/keepercommander/commands/discover/result_process.py +++ b/keepercommander/commands/discover/result_process.py @@ -1310,7 +1310,7 @@ def _get_directory_info(domain: str, def remove_job(params: KeeperParams, configuration_record: KeeperRecord, job_id: str): try: - jobs = Jobs(record=configuration_record, params=params) + jobs = Jobs(record=configuration_record, params=params, use_per_graph_endpoints=True) jobs.cancel(job_id) print(f"{bcolors.OKGREEN}No items left to process. Removing completed discovery job.{bcolors.ENDC}") except Exception as err: @@ -1328,7 +1328,7 @@ def preview(self, job_item: JobItem, params: KeeperParams, gateway_context: Gate infra = Infrastructure(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) infra.load(sync_point) configuration = None @@ -1487,7 +1487,7 @@ def execute(self, params: KeeperParams, **kwargs): # Get the current job. # There can only be one active job. # This will give us the sync point for the delta - jobs = Jobs(record=configuration_record, params=params, logger=logging, debug_level=debug_level) + jobs = Jobs(record=configuration_record, params=params, logger=logging, debug_level=debug_level, use_per_graph_endpoints=True) job_item = jobs.current_job if job_item is None: continue diff --git a/keepercommander/commands/discover/rule_add.py b/keepercommander/commands/discover/rule_add.py index 2bf1d7bb9..a472c3dd8 100644 --- a/keepercommander/commands/discover/rule_add.py +++ b/keepercommander/commands/discover/rule_add.py @@ -134,7 +134,7 @@ def execute(self, params, **kwargs): return # If the rule passes its validation, then add control DAG - rules = Rules(record=gateway_context.configuration, params=params) + rules = Rules(record=gateway_context.configuration, params=params, use_per_graph_endpoints=True) new_rule = ActionRuleItem( name=kwargs.get("name"), action=kwargs.get("rule_action"), diff --git a/keepercommander/commands/discover/rule_list.py b/keepercommander/commands/discover/rule_list.py index 9819fc361..6f2f4145d 100644 --- a/keepercommander/commands/discover/rule_list.py +++ b/keepercommander/commands/discover/rule_list.py @@ -101,7 +101,7 @@ def execute(self, params, **kwargs): multi_conf_msg(gateway, err) return - rules = Rules(record=gateway_context.configuration, params=params) + rules = Rules(record=gateway_context.configuration, params=params, use_per_graph_endpoints=True) rule_list = rules.rule_list(rule_type=RuleTypeEnum.ACTION, search=kwargs.get("search")) # type: List[RuleItem] if len(rule_list) == 0: diff --git a/keepercommander/commands/discover/rule_remove.py b/keepercommander/commands/discover/rule_remove.py index b093dfa57..ad081d3e4 100644 --- a/keepercommander/commands/discover/rule_remove.py +++ b/keepercommander/commands/discover/rule_remove.py @@ -46,7 +46,7 @@ def execute(self, params, **kwargs): return try: - rules = Rules(record=gateway_context.configuration, params=params) + rules = Rules(record=gateway_context.configuration, params=params, use_per_graph_endpoints=True) if remove_all: rules.remove_all(RuleTypeEnum.ACTION) print(f"{bcolors.OKGREEN}All rules removed.{bcolors.ENDC}") diff --git a/keepercommander/commands/discover/rule_update.py b/keepercommander/commands/discover/rule_update.py index b3e063bf5..0f8aeef5d 100644 --- a/keepercommander/commands/discover/rule_update.py +++ b/keepercommander/commands/discover/rule_update.py @@ -64,7 +64,7 @@ def execute(self, params, **kwargs): try: rule_id = kwargs.get("rule_id") - rules = Rules(record=gateway_context.configuration, params=params) + rules = Rules(record=gateway_context.configuration, params=params, use_per_graph_endpoints=True) rule_item = rules.get_rule_item(rule_type=RuleTypeEnum.ACTION, rule_id=rule_id) if rule_item is None: raise ValueError("Rule Id does not exist.") diff --git a/keepercommander/commands/discoveryrotation.py b/keepercommander/commands/discoveryrotation.py index 621b40b05..78edeb618 100644 --- a/keepercommander/commands/discoveryrotation.py +++ b/keepercommander/commands/discoveryrotation.py @@ -17,7 +17,6 @@ import re import time from datetime import datetime -from typing import Dict, Optional, Any, Set, List from urllib.parse import urlparse, urlunparse import requests @@ -73,6 +72,7 @@ from .pam_debug.gateway import PAMDebugGatewayCommand from .pam_debug.graph import PAMDebugGraphCommand from .pam_debug.info import PAMDebugInfoCommand +from .pam_debug.krouter import PAMDebugKRouterCommand from .pam_debug.link import PAMDebugLinkCommand from .pam_debug.rotation_setting import PAMDebugRotationSettingsCommand from .pam_debug.vertex import PAMDebugVertexCommand @@ -314,6 +314,7 @@ def __init__(self): super(PAMDebugCommand, self).__init__() self.register_command('info', PAMDebugInfoCommand(), 'Debug a record', 'i') self.register_command('gateway', PAMDebugGatewayCommand(), 'Debug a gateway', 'g') + self.register_command('krouter', PAMDebugKRouterCommand(), 'Show connected krouter version', 'k') self.register_command('graph', PAMDebugGraphCommand(), 'Render graphs', 'r') # Disable for now. Needs more work. @@ -1408,7 +1409,7 @@ def execute(self, params, **kwargs): try: enterprise_controllers_connected = router_get_connected_gateways(params) - except requests.exceptions.ConnectionError as errc: + except requests.exceptions.ConnectionError: is_router_down = True if not is_force: logging.warning(f"Looks like router is down. Use '{bcolors.OKGREEN}-f{bcolors.ENDC}' flag to " @@ -1419,7 +1420,7 @@ def execute(self, params, **kwargs): logging.info(f"{bcolors.WARNING}Looks like router is down. Router URL [{krouter_url}]{bcolors.ENDC}") except Exception as e: - logging.warning(f"Unhandled error during retrieval of the connected gateways.") + logging.warning("Unhandled error during retrieval of the connected gateways.") raise e enterprise_controllers_all = gateway_helper.get_all_gateways(params) @@ -1970,10 +1971,10 @@ def print_root_rotation_setting(params, is_verbose=False, format_type='table'): table.append(row) else: - logging.warning(f'Following configuration is not in the shared folder: UID: %s, Title: %s', + logging.warning('Following configuration is not in the shared folder: UID: %s, Title: %s', c.record_uid, c.title) else: - logging.warning(f'Following configuration has unsupported type: UID: %s, Title: %s', c.record_uid, + logging.warning('Following configuration has unsupported type: UID: %s, Title: %s', c.record_uid, c.title) if format_type == 'json': @@ -3146,7 +3147,6 @@ def execute(self, params, **kwargs): record_uid = kwargs.get('record_uid', '') folder = kwargs.get('folder', '') recursive = kwargs.get('recursive', False) - pattern = kwargs.get('pattern', '') # additional record title match pattern dry_run = kwargs.get('dry_run', False) # Store email/share arguments as instance variables @@ -3215,7 +3215,7 @@ def execute(self, params, **kwargs): folders = list(set(folders)) # Remove duplicate UIDs # 2. pattern could match both parent and child - drop all children (w/ a matching parent) if recursive and len(folders) > 1: - roots: Dict[str, list] = {} # group by shared_folder_uid + roots: dict[str, list] = {} # group by shared_folder_uid for fuid in folders: # no shf inside shf yet roots.setdefault(params.folder_cache.get(fuid).shared_folder_uid, []).append(fuid) uniq = [] @@ -3366,7 +3366,7 @@ def record_rotate(self, params, record_uid, slient: bool = False): # Check the graph for the noop setting. record_link = RecordLink(record=pam_config, params=params, - fail_on_corrupt=False) + fail_on_corrupt=False, use_per_graph_endpoints=True) acl = record_link.get_acl(record_uid, pam_config.record_uid) if acl is not None and acl.rotation_settings is not None: is_noop = acl.rotation_settings.noop diff --git a/keepercommander/commands/pam/_layer_b.py b/keepercommander/commands/pam/_layer_b.py new file mode 100644 index 000000000..aa4e27bba --- /dev/null +++ b/keepercommander/commands/pam/_layer_b.py @@ -0,0 +1,219 @@ +""" +Layer-B (permission-checked configure_resource / set_record_rotation / +configure_network_graph) fallback infrastructure. Isolated in its own module so +it can be imported without triggering the pre-existing circular import in +`pam/router_helper.py` -> `gateway_helper` -> `commands.utils` -> `ksm` -> +`record` -> `ksm`. + +Hosts: +- `RouterResponseError` — raised by `_post_request_to_router` on non-OK + `RouterResponseCode`. Subclass of Exception for backward compat. +- `should_fallback_on_layer_b_error()` — the per-endpoint fallback decision. +- `(host, endpoint)` cache — marked only on HTTP 404 (endpoint not deployed + on this krouter build). RRC permission codes are NOT cached because tenant + admins can flip features and per-user denials are call-specific. + +Policy (default semantics): + +| Endpoint | HTTP 404 | RRC denial | Transient (5xx, conn) | +|---------------------------|------------------------------|-------------------------------------|-----------------------| +| configure_resource | propagate (env=1 → fallback) | propagate (env=1 → fallback) | propagate | +| set_record_rotation | propagate | propagate (no fallback ever) | propagate | +| configure_network_graph | propagate (env=1 → fallback) | propagate (env=1 → fallback) | propagate | + +All three Layer-B endpoints honor the same env-var policy. The +`_LB_ALWAYS_DOWNGRADE_ENDPOINTS` frozenset is preserved (empty) as scaffolding +for future endpoints whose rollout window genuinely needs an unconditional +auto-downgrade safety net. + +Env-var opt-in (`KEEPER_DAG_LB_FALLBACK=1`) extends "fall back on 404 + RRC" +to every endpoint — use during the rollout of any future new endpoint that +isn't worth adding to the always-downgrade set. +""" +import os +import threading +import time +from typing import Optional + + +class RouterResponseError(Exception): + """Raised when krouter responds with a non-OK `RouterResponseCode`. + + Carries `.response_code` (int) and `.response_code_name` (str) so callers + can branch on specific failure modes — most importantly, the permission-denial + codes that drive the Layer-B fallback policy. + """ + def __init__(self, response_code: int, response_code_name: str, message: str): + super().__init__(f'{message} Response code: {response_code_name}') + self.response_code = response_code + self.response_code_name = response_code_name + + +# Codes that warrant fallback when fallback is in effect. Includes the per-user +# RRC_NOT_ALLOWED — when this fires under fallback, the legacy DAG-write reopens +# the credential-link gap for that single call. That's the documented trade-off +# of enabling fallback during rollout. +_LB_PERMISSION_DENIED_CODES = frozenset({ + 'RRC_NOT_ALLOWED', + 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', + 'RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED', +}) + + +# Per-endpoint override: endpoints listed here always allow auto-downgrade +# (404 + RRC) regardless of KEEPER_DAG_LB_FALLBACK. Used for endpoints whose +# krouter rollout window is long (DEV → QA → PROD → GovCloud) so Commander +# has to work against both upgraded and not-yet-upgraded krouters in the field. +# +# **When to ADD an endpoint:** during the rollout of a new krouter endpoint +# that ships incrementally across environments. Add here so Commander gracefully +# falls back to legacy DAG-write on krouters that haven't received the update. +# +# **When to REMOVE an endpoint:** once it's deployed to every production +# krouter AND the Vault is using it in anger. After removal, denials propagate +# loudly and per-feature enforcement checks fire by default. +_LB_ALWAYS_DOWNGRADE_ENDPOINTS = frozenset({ + # Currently empty — kept as scaffolding for future endpoints whose krouter + # rollout window genuinely needs unconditional auto-downgrade (e.g. an + # endpoint that ships incrementally across DEV → QA → PROD → GovCloud and + # Commander has to keep working against all of them at once). To use: + # add the endpoint name as a string here. + # + # `configure_network_graph` was a candidate during its initial rollout + # (krouter release/1.7.1+, Vault adoption still in progress) — leaving + # the example below as a template: + # + # 'configure_network_graph', +}) + + +def _layer_b_fallback_enabled() -> bool: + """Whether KEEPER_DAG_LB_FALLBACK env var opts INTO legacy fallback. + + **Default OFF (strict mode)** — the mature Layer-B endpoints + (`configure_resource`, `set_record_rotation`) are deployed everywhere; + denials are real and should propagate so the caller sees them. + + Set to '1' / 'true' / 'yes' / 'on' to opt INTO fallback during the rollout + of a new endpoint that isn't (yet) on `_LB_ALWAYS_DOWNGRADE_ENDPOINTS`. + Endpoints on that set auto-downgrade regardless of this flag. + """ + raw = os.environ.get("KEEPER_DAG_LB_FALLBACK", "").strip().lower() + return raw in ('1', 'true', 'yes', 'on') + + +def _feature_cache_ttl_sec() -> int: + """TTL (seconds) for the (host, endpoint) feature-disabled cache. + + Default 300s (5 min). `KEEPER_DAG_LB_FEATURE_CACHE_TTL=0` disables caching + entirely (every call hits the new API). Negative or non-integer → 0. + """ + raw = os.environ.get("KEEPER_DAG_LB_FEATURE_CACHE_TTL", "300").strip() + try: + ttl = int(raw) + return ttl if ttl > 0 else 0 + except ValueError: + return 0 + + +_feature_cache_lock = threading.Lock() +# (host, endpoint) -> epoch seconds at which the cache entry expires. +_feature_cache = {} # type: dict + + +def mark_layer_b_feature_disabled(host: Optional[str], endpoint: Optional[str]) -> None: + """Remember that (host, endpoint) reported HTTP 404 (endpoint not deployed). + + Subsequent `is_layer_b_feature_disabled(host, endpoint)` calls return True + until the TTL expires, letting callers skip the new API entirely. + + Only HTTP 404 marks the cache — RRC tenant-level codes are NOT cached + because admins can flip features mid-session; per-user RRC_NOT_ALLOWED is + NEVER cached because the answer is per-call. + """ + ttl = _feature_cache_ttl_sec() + if ttl <= 0 or not host or not endpoint: + return + with _feature_cache_lock: + _feature_cache[(host, endpoint)] = time.time() + ttl + + +def is_layer_b_feature_disabled(host: Optional[str], endpoint: Optional[str]) -> bool: + """True if a recent call to (host, endpoint) returned HTTP 404 AND the + TTL has not yet expired.""" + if not host or not endpoint: + return False + if _feature_cache_ttl_sec() <= 0: + return False + now = time.time() + with _feature_cache_lock: + expiry = _feature_cache.get((host, endpoint)) + if expiry is None: + return False + if expiry <= now: + _feature_cache.pop((host, endpoint), None) + return False + return True + + +def clear_layer_b_feature_cache() -> None: + """Test helper — wipe the (host, endpoint) cache.""" + with _feature_cache_lock: + _feature_cache.clear() + + +def _is_http_404(err: Exception) -> bool: + """KeeperApiError carries the HTTP status code as `result_code` (or as a + leading "404:" in str(err) on older paths).""" + code = getattr(err, 'result_code', None) + if isinstance(code, int) and code == 404: + return True + if isinstance(code, str) and code == '404': + return True + return str(err).startswith('404') + + +def _fallback_allowed(endpoint: Optional[str]) -> bool: + """Per-endpoint policy: True if this endpoint allows legacy fallback right + now. Always-downgrade endpoints return True regardless of env var; others + follow KEEPER_DAG_LB_FALLBACK.""" + if endpoint and endpoint in _LB_ALWAYS_DOWNGRADE_ENDPOINTS: + return True + return _layer_b_fallback_enabled() + + +def should_fallback_on_layer_b_error(err: Exception, host: Optional[str] = None, endpoint: Optional[str] = None) -> bool: + """Return True iff `err` warrants legacy fallback for this endpoint. + + Decision matrix (see module docstring for full policy table): + - HTTP 404 on always-downgrade endpoint → fall back + mark cache. + - HTTP 404 on other endpoint with env var on → fall back + mark cache. + - HTTP 404 on other endpoint with env var off → propagate (strict). + - RRC permission code on always-downgrade endpoint → fall back (no cache). + - RRC permission code on other endpoint with env var on → fall back (no cache). + - RRC permission code on other endpoint with env var off → propagate. + - Transient errors (5xx, connection, unrelated exceptions) → propagate. + + The (host, endpoint) cache is marked ONLY on 404; tenant-level RRC codes + are not cached because admins can flip features at runtime. + """ + fallback_active = _fallback_allowed(endpoint) + + # HTTP 404 → endpoint not deployed on this krouter build. + if _is_http_404(err): + if fallback_active: + mark_layer_b_feature_disabled(host, endpoint) + return True + return False + + if not fallback_active: + return False + + # RRC permission codes + if isinstance(err, RouterResponseError): + return err.response_code_name in _LB_PERMISSION_DENIED_CODES + + # Transitional: legacy paths may still raise plain Exception with the code + # embedded in str(err). + msg = str(err) + return any(code in msg for code in _LB_PERMISSION_DENIED_CODES) diff --git a/keepercommander/commands/pam/router_helper.py b/keepercommander/commands/pam/router_helper.py index 1cae08992..1ccc11d49 100644 --- a/keepercommander/commands/pam/router_helper.py +++ b/keepercommander/commands/pam/router_helper.py @@ -22,6 +22,12 @@ VERIFY_SSL = bool(os.environ.get("VERIFY_SSL", "TRUE") == "TRUE") +# `RouterResponseError` lives in `_layer_b` to avoid the pre-existing circular +# import chain (gateway_helper -> commands.utils -> ksm -> record). It's raised +# below from `_post_request_to_router` on non-OK RouterResponseCode. +from ._layer_b import RouterResponseError + + def get_router_url(params: KeeperParams): krouter_env_var_name = "KROUTER_URL" if os.getenv(krouter_env_var_name): @@ -96,6 +102,15 @@ def router_configure_resource(params, proto_request, transmission_key=None, return rs +def router_configure_network_graph(params, proto_request, transmission_key=None, + encrypted_transmission_key=None, encrypted_session_token=None): + rs = _post_request_to_router(params, 'configure_network_graph', proto_request, transmission_key=transmission_key, + encrypted_transmission_key=encrypted_transmission_key, + encrypted_session_token=encrypted_session_token) + + return rs + + def router_get_rotation_schedules(params, proto_request): return _post_request_to_router(params, 'get_rotation_schedules', rq_proto=proto_request, rs_type=pam_pb2.PAMRotationSchedulesResponse) @@ -166,7 +181,7 @@ def _post_request_to_router(params, path, rq_proto=None, rs_type=None, method='p rrc = router_pb2.RouterResponseCode.Name(router_response.responseCode) if router_response.responseCode != router_pb2.RRC_OK: - raise Exception(router_response.errorMessage + ' Response code: ' + rrc) + raise RouterResponseError(router_response.responseCode, rrc, router_response.errorMessage) if router_response.encryptedPayload: payload_encrypted = router_response.encryptedPayload @@ -203,7 +218,7 @@ def router_send_action_to_gateway(params, gateway_action: GatewayAction, message router_enterprise_controllers_connected = \ [x.controllerUid for x in router_get_connected_gateways(params).controllers] - except requests.exceptions.ConnectionError as errc: + except requests.exceptions.ConnectionError: logging.info(f"{bcolors.WARNING}Looks like router is down. Router URL [{krouter_host}]{bcolors.ENDC}") return except Exception as e: @@ -452,7 +467,7 @@ def print_router_response(router_response, response_type, original_conversation_ exec_status = job_info.get('status') exec_exception = job_info.get('execException') - print(f'Execution Details\n-------------------------') + print('Execution Details\n-------------------------') if exec_status == 'finished': font_color = bcolors.OKGREEN @@ -478,7 +493,7 @@ def print_router_response(router_response, response_type, original_conversation_ print(f'\t\t{font_color}stdout:\n---\n{bcolors.OKBLUE}{el.get("stdout")}{font_color}\n---{bcolors.ENDC}') if el.get("stderr"): print(f'\t\t{font_color}stderr:\n---\n{bcolors.WARNING}{el.get("stderr")}{font_color}\n---{bcolors.ENDC}') - print(f'\n') + print() if exec_exception: print(f'\t{font_color}Execution Exception : {exec_exception}{bcolors.ENDC}') diff --git a/keepercommander/commands/pam_debug/__init__.py b/keepercommander/commands/pam_debug/__init__.py index b41fd4018..eaa0276d3 100644 --- a/keepercommander/commands/pam_debug/__init__.py +++ b/keepercommander/commands/pam_debug/__init__.py @@ -11,7 +11,12 @@ def get_connection(params: KeeperParams) -> ConnectionBase: if value_to_boolean(os.environ.get("USE_LOCAL_DAG", False)) is False: from ...keeper_dag.connection.commander import Connection as CommanderConnection - return CommanderConnection(params=params) + # New per-graph endpoints (`/api/user/graph-sync//`) are + # protobuf-only — JSON reads on those routes return empty. Match the + # default in `discovery_common.utils.get_connection`. + return CommanderConnection(params=params, + use_read_protobuf=True, + use_write_protobuf=True) else: from ...keeper_dag.connection.local import Connection as LocalConnection return LocalConnection() \ No newline at end of file diff --git a/keepercommander/commands/pam_debug/acl.py b/keepercommander/commands/pam_debug/acl.py index 9a7bca7d2..cdf1918eb 100644 --- a/keepercommander/commands/pam_debug/acl.py +++ b/keepercommander/commands/pam_debug/acl.py @@ -57,7 +57,7 @@ def execute(self, params: KeeperParams, **kwargs): record_link = RecordLink(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) user_record = vault.KeeperRecord.load(params, user_uid) # type: Optional[TypedRecord] if user_record is None: diff --git a/keepercommander/commands/pam_debug/dump.py b/keepercommander/commands/pam_debug/dump.py index aabe2814f..1f9f6c2bc 100644 --- a/keepercommander/commands/pam_debug/dump.py +++ b/keepercommander/commands/pam_debug/dump.py @@ -11,7 +11,7 @@ from ...subfolder import get_folder_uids from ... import vault, api from ...keeper_dag import DAG, EdgeType -from ...keeper_dag.types import PamGraphId +from ...keeper_dag.types import GRAPH_ID_TO_ENDPOINT, PamGraphId from ..pam_import.keeper_ai_settings import get_resource_settings from ...keeper_dag.crypto import decrypt_aes from . import get_connection @@ -28,7 +28,7 @@ class PAMDebugDumpCommand(Command): - parser = argparse.ArgumentParser(prog='pam action debug dump') + parser = argparse.ArgumentParser(prog=':') parser.add_argument('folder_uid', action='store', help='Folder UID or path. Use empty string for the root folder.') parser.add_argument('--recursive', '-r', required=False, dest='recursive', action='store_true', @@ -125,14 +125,30 @@ def _on_folder(f): continue rotation = params.record_rotation_cache.get(rec_uid) - if rotation is not None: - config_uid = rotation.get('configuration_uid') - if config_uid: - config_to_records.setdefault(config_uid, []).append(rec_uid) - record_config_map[rec_uid] = config_uid - continue + config_uid = rotation.get('configuration_uid') if rotation else None + + # Two fallback tiers when rotation cache has no entry (e.g. records that + # are only linked via launch-credential / ACL, never rotated): + # 1. Local vault scan of `pamResources.resourceRef`. + # 2. krouter `/api/user/graph-sync/pam/get_leafs` — same call WV uses; + # finds the stream root even when the local resourceRef is missing. + if not config_uid: + from ..tunnel.port_forward.tunnel_helpers import ( + get_config_uid_from_local_vault, + get_config_uid_from_krouter, + ) + config_uid = ( + get_config_uid_from_local_vault(params, rec_uid) + or get_config_uid_from_krouter(params, rec_uid) + ) + + if config_uid: + config_to_records.setdefault(config_uid, []).append(rec_uid) + record_config_map[rec_uid] = config_uid + continue - logging.debug('Record %s not found in rotation cache; rotation config unavailable, ', rec_uid) + logging.debug('Record %s: no rotation entry, no local vault config, ' + 'no krouter leafs match; graph data unavailable.', rec_uid) record_config_map[rec_uid] = None if not valid_uids: @@ -154,7 +170,9 @@ def _on_folder(f): for graph_id in ALL_GRAPH_IDS: try: - dag = DAG(conn=conn, record=config_record, graph_id=graph_id, + endpoint = GRAPH_ID_TO_ENDPOINT[graph_id] + dag = DAG(conn=conn, record=config_record, + read_endpoint=endpoint, write_endpoint=endpoint, fail_on_corrupt=False, logger=logging) dag.load(sync_point=0) dag_cache[(config_uid, graph_id)] = dag diff --git a/keepercommander/commands/pam_debug/gateway.py b/keepercommander/commands/pam_debug/gateway.py index 3642668ce..2f53a620a 100644 --- a/keepercommander/commands/pam_debug/gateway.py +++ b/keepercommander/commands/pam_debug/gateway.py @@ -49,11 +49,11 @@ def execute(self, params: KeeperParams, **kwargs): multi_conf_msg(gateway, err) return - infra = Infrastructure(record=gateway_context.configuration, params=params, fail_on_corrupt=False) + infra = Infrastructure(record=gateway_context.configuration, params=params, fail_on_corrupt=False, use_per_graph_endpoints=True) infra.load() - record_link = RecordLink(record=gateway_context.configuration, params=params, fail_on_corrupt=False) - user_service = UserService(record=gateway_context.configuration, params=params, fail_on_corrupt=False) + record_link = RecordLink(record=gateway_context.configuration, params=params, fail_on_corrupt=False, use_per_graph_endpoints=True) + user_service = UserService(record=gateway_context.configuration, params=params, fail_on_corrupt=False, use_per_graph_endpoints=True) if gateway_context is None: print(f" {self._f('Cannot get gateway information. Gateway may not be up.')}") diff --git a/keepercommander/commands/pam_debug/graph.py b/keepercommander/commands/pam_debug/graph.py index 877245eb0..8daeb4838 100644 --- a/keepercommander/commands/pam_debug/graph.py +++ b/keepercommander/commands/pam_debug/graph.py @@ -18,13 +18,12 @@ from ...keeper_dag import DAG from ...keeper_dag.connection.commander import Connection as CommanderConnection from ...keeper_dag.connection.local import Connection as LocalConnection +from ...keeper_dag.types import GRAPH_ID_TO_ENDPOINT, PamEndpoints from ...keeper_dag.vertex import DAGVertex -from ...keeper_dag.edge import DAGEdge from typing import Optional, Union, TYPE_CHECKING Connection = Union[CommanderConnection, LocalConnection] if TYPE_CHECKING: - from ...vault import TypedRecord from ...params import KeeperParams @@ -79,7 +78,7 @@ def _do_text_list_infra(self, params: KeeperParams, gateway_context: GatewayCont indent: int = 0): infra = Infrastructure(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) infra.load(sync_point=0) try: @@ -165,7 +164,7 @@ def _do_text_list_rl(self, params: KeeperParams, gateway_context: GatewayContext record_link = RecordLink(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) configuration = record_link.dag.get_root record = vault.KeeperRecord.load(params, configuration.uid) # type: Optional[TypedRecord] @@ -317,7 +316,7 @@ def _do_text_list_service(self, params: KeeperParams, gateway_context: GatewayCo indent: int = 0): user_service = UserService(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) configuration = user_service.dag.get_root def _handle(current_vertex: DAGVertex, parent_vertex: Optional[DAGVertex] = None, indent: int = 0): @@ -365,7 +364,7 @@ def _do_text_list_jobs(self, params: KeeperParams, gateway_context: GatewayConte indent: int = 0): infra = Infrastructure(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level, fail_on_corrupt=False) + debug_level=debug_level, fail_on_corrupt=False, use_per_graph_endpoints=True) infra.load(sync_point=0) pad = "" @@ -374,22 +373,23 @@ def _do_text_list_jobs(self, params: KeeperParams, gateway_context: GatewayConte conn = get_connection(params) graph_sync = DAG(conn=conn, record=gateway_context.configuration, logger=logging, debug_level=debug_level, - graph_id=DIS_JOBS_GRAPH_ID) + read_endpoint=PamEndpoints.DISCOVERY_JOBS, + write_endpoint=PamEndpoints.DISCOVERY_JOBS) graph_sync.load(0) configuration = graph_sync.get_root vertices = configuration.has_vertices() if len(vertices) == 0: - print(self._f(f"The jobs graph has not been initialized. Only has root vertex.")) + print(self._f("The jobs graph has not been initialized. Only has root vertex.")) return vertex = vertices[0] if not vertex.has_data: - print(self._f(f"The job vertex does not contain any data")) + print(self._f("The job vertex does not contain any data")) return current_json = vertex.content_as_str if current_json is None: - print(self._f(f"The current job vertex content is None")) + print(self._f("The current job vertex content is None")) return content = JobContent.model_validate_json(current_json) @@ -461,7 +461,7 @@ def _do_render_infra(self, params: KeeperParams, gateway_context: GatewayContext debug_level: int = 0): infra = Infrastructure(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) infra.load(sync_point=0) print("") @@ -487,7 +487,7 @@ def _do_render_rl(self, params: KeeperParams, gateway_context: GatewayContext, f rl = RecordLink(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) print("") dot_instance = rl.to_dot( @@ -510,7 +510,7 @@ def _do_render_service(self, params: KeeperParams, gateway_context: GatewayConte graph_format: str, debug_level: int = 0): service = UserService(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) print("") dot_instance = service.to_dot( @@ -532,7 +532,7 @@ def _do_render_service(self, params: KeeperParams, gateway_context: GatewayConte def _do_render_jobs(self, params: KeeperParams, gateway_context: GatewayContext, filepath: str, graph_format: str, debug_level: int = 0): - jobs = Jobs(record=gateway_context.configuration, params=params, logger=logging, debug_level=debug_level) + jobs = Jobs(record=gateway_context.configuration, params=params, logger=logging, debug_level=debug_level, use_per_graph_endpoints=True) print("") dot_instance = jobs.dag.to_dot() @@ -553,8 +553,10 @@ def _do_raw_text_list(self, params: KeeperParams, gateway_context: GatewayContex logging.debug(f"loading graph id {graph_id}, for record uid {gateway_context.configuration.record_uid}") conn = get_connection(params=params) - dag = DAG(conn=conn, record=gateway_context.configuration, graph_id=graph_id, fail_on_corrupt=False, - logger=logging, debug_level=debug_level) + endpoint = GRAPH_ID_TO_ENDPOINT[graph_id] + dag = DAG(conn=conn, record=gateway_context.configuration, + read_endpoint=endpoint, write_endpoint=endpoint, + fail_on_corrupt=False, logger=logging, debug_level=debug_level) dag.load(sync_point=0) print("") if dag.is_corrupt is True: @@ -626,8 +628,10 @@ def _do_raw_render_graph(self, params: KeeperParams, gateway_context: GatewayCon graph_format: str, graph_id: int = 0, debug_level: int = 0): conn = get_connection(params=params) - dag = DAG(conn=conn, record=gateway_context.configuration, graph_id=graph_id, fail_on_corrupt=False, - logger=logging, debug_level=debug_level) + endpoint = GRAPH_ID_TO_ENDPOINT[graph_id] + dag = DAG(conn=conn, record=gateway_context.configuration, + read_endpoint=endpoint, write_endpoint=endpoint, + fail_on_corrupt=False, logger=logging, debug_level=debug_level) dag.load(sync_point=0) dot = dag.to_dot(graph_format=graph_format) if graph_format == "raw": diff --git a/keepercommander/commands/pam_debug/info.py b/keepercommander/commands/pam_debug/info.py index 09d9bcfa6..c8f690518 100644 --- a/keepercommander/commands/pam_debug/info.py +++ b/keepercommander/commands/pam_debug/info.py @@ -65,7 +65,7 @@ def execute(self, params: KeeperParams, **kwargs): for configuration_record in configuration_records: - record_link = RecordLink(record=configuration_record, params=params) + record_link = RecordLink(record=configuration_record, params=params, use_per_graph_endpoints=True) record_vertex = record_link.dag.get_vertex(record.record_uid) if record_vertex is not None and record_vertex.active is True: controller_uid = configuration_record.record_uid @@ -95,10 +95,10 @@ def execute(self, params: KeeperParams, **kwargs): print(f"{bcolors.FAIL}Could not find the gateway for configuration record.{controller_uid}{bcolors.ENDC}") return - infra = Infrastructure(record=configuration_record, params=params) + infra = Infrastructure(record=configuration_record, params=params, use_per_graph_endpoints=True) infra.load() - record_link = RecordLink(record=configuration_record, params=params) - user_service = UserService(record=configuration_record, params=params) + record_link = RecordLink(record=configuration_record, params=params, use_per_graph_endpoints=True) + user_service = UserService(record=configuration_record, params=params, use_per_graph_endpoints=True) print("") print(self._h("Record Information")) diff --git a/keepercommander/commands/pam_debug/krouter.py b/keepercommander/commands/pam_debug/krouter.py new file mode 100644 index 000000000..bc1a28ac3 --- /dev/null +++ b/keepercommander/commands/pam_debug/krouter.py @@ -0,0 +1,87 @@ +from __future__ import annotations +import argparse +import json +import os +from typing import TYPE_CHECKING + +import requests + +from ..base import Command +from ...display import bcolors + +if TYPE_CHECKING: + from ...params import KeeperParams + + +class PAMDebugKRouterCommand(Command): + """`pam action debug krouter` — show the connected krouter build version. + + Calls krouter's unauthenticated `GET /healthcheck` endpoint and prints the + `version` field. Useful for verifying which krouter build a Commander + session is talking to (e.g. to confirm whether a newly-shipped Layer-B + endpoint such as `configure_network_graph` has been deployed to the + tenant's krouter, or to compare against the floor version required by the + PR description). + """ + + parser = argparse.ArgumentParser( + prog='pam action debug krouter', + description='Show the connected krouter build version (calls /healthcheck).' + ) + parser.add_argument('--url', '-u', required=False, dest='url', action='store', + help='Override the krouter base URL. Defaults to the URL Commander would normally use ' + '(KROUTER_URL env var, else derived from params.rest_context.server_base).') + parser.add_argument('--timeout', '-t', required=False, dest='timeout', type=float, default=10.0, + help='HTTP timeout in seconds (default: 10).') + parser.add_argument('--raw', '-r', required=False, dest='raw', action='store_true', + help='Print the raw healthcheck response body instead of formatted output.') + + def get_parser(self): + return PAMDebugKRouterCommand.parser + + def execute(self, params: KeeperParams, **kwargs): + url = kwargs.get('url') + if not url: + from ..pam.router_helper import get_router_url + url = get_router_url(params) + url = url.rstrip('/') + '/healthcheck' + + verify_ssl = os.environ.get('VERIFY_SSL', 'TRUE') == 'TRUE' + timeout = kwargs.get('timeout') or 10.0 + raw = bool(kwargs.get('raw')) + + try: + rs = requests.get(url, verify=verify_ssl, timeout=timeout) + rs.raise_for_status() + except requests.exceptions.SSLError as err: + print(f"{bcolors.FAIL}SSL verification failed: {err}{bcolors.ENDC}") + print(' Set VERIFY_SSL=FALSE to bypass for development krouter builds.') + return + except requests.exceptions.ConnectionError as err: + print(f"{bcolors.FAIL}Cannot reach krouter at {url}: {err}{bcolors.ENDC}") + return + except requests.exceptions.Timeout: + print(f"{bcolors.FAIL}Timed out after {timeout}s connecting to {url}.{bcolors.ENDC}") + return + except requests.exceptions.HTTPError as err: + status = err.response.status_code if err.response is not None else '?' + body = err.response.text if err.response is not None else '' + print(f"{bcolors.FAIL}HTTP {status} from {url}: {body!r}{bcolors.ENDC}") + return + + if raw: + print(rs.text) + return + + try: + data = rs.json() + except ValueError: + print(f"{bcolors.FAIL}Non-JSON response from {url}: {rs.text!r}{bcolors.ENDC}") + return + + version = data.get('version') or '' + print(f"{bcolors.OKGREEN}krouter version : {version}{bcolors.ENDC}") + print(f" URL : {url}") + extras = {k: v for k, v in data.items() if k != 'version'} + if extras: + print(f" Extras : {json.dumps(extras)}") diff --git a/keepercommander/commands/pam_debug/link.py b/keepercommander/commands/pam_debug/link.py index 766d27d16..f2489d2f0 100644 --- a/keepercommander/commands/pam_debug/link.py +++ b/keepercommander/commands/pam_debug/link.py @@ -53,7 +53,7 @@ def execute(self, params: KeeperParams, **kwargs): record_link = RecordLink(record=gateway_context.configuration, params=params, logger=logging, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) resource_record = vault.KeeperRecord.load(params, resource_uid) # type: Optional[TypedRecord] if resource_record is None: diff --git a/keepercommander/commands/pam_debug/rotation_setting.py b/keepercommander/commands/pam_debug/rotation_setting.py index 8e475b487..fd2d6ed19 100644 --- a/keepercommander/commands/pam_debug/rotation_setting.py +++ b/keepercommander/commands/pam_debug/rotation_setting.py @@ -164,7 +164,7 @@ def execute(self, params: KeeperParams, **kwargs): f"It's a {resource_record.record_type}.{bcolors.ENDC}") return - record_link = RecordLink(record=configuration_record, params=params) + record_link = RecordLink(record=configuration_record, params=params, use_per_graph_endpoints=True) parent_uid = resource_record_uid or configuration_record_uid parent_vertex = record_link.get_record_link(parent_uid) diff --git a/keepercommander/commands/pam_debug/vertex.py b/keepercommander/commands/pam_debug/vertex.py index 3d37e61dc..35bd26acd 100644 --- a/keepercommander/commands/pam_debug/vertex.py +++ b/keepercommander/commands/pam_debug/vertex.py @@ -53,7 +53,7 @@ def execute(self, params: KeeperParams, **kwargs): return infra = Infrastructure(record=gateway_context.configuration, params=params, fail_on_corrupt=False, - debug_level=debug_level) + debug_level=debug_level, use_per_graph_endpoints=True) infra.load() vertex_uid = kwargs.get("vertex_uid") diff --git a/keepercommander/commands/pam_import/kcm_import.py b/keepercommander/commands/pam_import/kcm_import.py index 5bc4e5f6e..c5e02f167 100644 --- a/keepercommander/commands/pam_import/kcm_import.py +++ b/keepercommander/commands/pam_import/kcm_import.py @@ -27,8 +27,6 @@ import threading import time -from typing import Dict, List, Tuple - from ..base import Command from ...display import bcolors from ...error import CommandError @@ -1367,6 +1365,10 @@ def execute(self, params, **kwargs): fd = os.open(output_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) with os.fdopen(fd, 'w') as f: json.dump(out_data, f, indent=2) + # On Windows os.open() ignores POSIX mode bits; apply the cross-platform + # owner-only helper (POSIX chmod 0o600 / Windows icacls grant owner:RW). + from ... import utils as _ku + _ku.set_file_permissions(output_file) redact_note = '' if include_creds else ' (credentials redacted)' logging.warning('JSON written to %s (%d resources, %d users)%s', output_file, num_resources, num_users, redact_note) @@ -1423,9 +1425,9 @@ def execute(self, params, **kwargs): gw_label, existing_project) else: print(f'\n{gw_label} belongs to project "{existing_project}".') - print(f' [1] Create a NEW project with its own folders and gateway (recommended)') + print(' [1] Create a NEW project with its own folders and gateway (recommended)') print(f' [2] Add records into "{existing_project}" existing folders') - print(f' [3] Cancel import') + print(' [3] Cancel import') choice = input('\n Select [1]: ').strip() if choice == '2': config_uid = resolved_config @@ -1764,8 +1766,6 @@ def _run_extend_batch(params, config_uid, tmp_path, batch_items, """ from .extend import PAMProjectExtendCommand - pre_cache = set(params.record_cache.keys()) - stdout_trap = io.StringIO() with _STDOUT_LOCK: original_stdout = sys.stdout @@ -1787,9 +1787,6 @@ def _run_extend_batch(params, config_uid, tmp_path, batch_items, except (BrokenPipeError, OSError): pass - post_cache = set(params.record_cache.keys()) - new_uids = post_cache - pre_cache - # Parse output for per-record info # Common patterns from extend.py output: # "PAM User is missing required field `password`" @@ -2014,7 +2011,7 @@ def _interactive_group_picker(groups, resolver, resources, users): total_res = len(resources) total_usr = len(users) print(f'\n Total: {total_res} resources, {total_usr} users') - print(f'\n [A] Import ALL groups') + print('\n [A] Import ALL groups') print() try: @@ -2530,8 +2527,8 @@ def _resolve_ksm_tokens(params, resources, users): f'matching vault record was found (the KSM shared folder may ' f'not be accessible to your account):\n' + '\n'.join( f' - {line}' for line in unresolved_lines) + - f'\n Action: add passwords to these records after import, ' - f'or share the KSM app folder with your vault and re-import.') + '\n Action: add passwords to these records after import, ' + 'or share the KSM app folder with your vault and re-import.') return warnings, resolved_details, unresolved_details @@ -3440,7 +3437,7 @@ def _resolve_gateway(params, gateway_arg): for i, g in enumerate(online, 1): uid_str = utils.base64_url_encode(g.controllerUid) print(f' [{i}] {g.controllerName} ({uid_str})') - print(f'\n [N] Create a new gateway') + print('\n [N] Create a new gateway') print() choice = input(' Select gateway [N]: ').strip() if choice and choice.upper() != 'N': @@ -3672,7 +3669,7 @@ def _resolve_db_password(self, params, kwargs): for i, (rec, title) in enumerate(candidates, 1): uid = rec.record_uid if hasattr(rec, 'record_uid') else '?' print(f' [{i}] {title} ({uid})') - print(f'\n [M] Enter password manually') + print('\n [M] Enter password manually') print() choice = input(' Select [M]: ').strip() if choice and choice.upper() != 'M': diff --git a/keepercommander/commands/pam_import/keeper_ai_settings.py b/keepercommander/commands/pam_import/keeper_ai_settings.py index 652870684..13a767a15 100644 --- a/keepercommander/commands/pam_import/keeper_ai_settings.py +++ b/keepercommander/commands/pam_import/keeper_ai_settings.py @@ -19,8 +19,11 @@ from ...vault import PasswordRecord from ... import vault from ...display import bcolors -from ..tunnel.port_forward.tunnel_helpers import get_config_uid, generate_random_bytes, get_keeper_tokens +from ...proto import pam_pb2 +from ..pam._layer_b import should_fallback_on_layer_b_error, is_layer_b_feature_disabled +from ..tunnel.port_forward.tunnel_helpers import get_config_uid, get_keeper_tokens from ...keeper_dag.crypto import encrypt_aes +from keeper_secrets_manager_core.utils import url_safe_str_to_bytes def list_resource_data_edges( @@ -80,6 +83,7 @@ def list_resource_data_edges( encrypted_transmission_key=encrypted_transmission_key, encrypted_session_token=encrypted_session_token, transmission_key=transmission_key, + use_read_protobuf=True, use_write_protobuf=True ) @@ -88,6 +92,7 @@ def list_resource_data_edges( conn=conn, record=dag_record, graph_id=0, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM ) linking_dag.load() @@ -179,6 +184,7 @@ def get_resource_settings( encrypted_transmission_key=encrypted_transmission_key, encrypted_session_token=encrypted_session_token, transmission_key=transmission_key, + use_read_protobuf=True, use_write_protobuf=True ) @@ -187,6 +193,7 @@ def get_resource_settings( conn=conn, record=dag_record, graph_id=0, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM ) linking_dag.load() @@ -388,15 +395,17 @@ def set_resource_keeper_ai_settings( config_uid: Optional[str] = None ) -> bool: """ - Save KeeperAI settings to the DAG DATA edge with path 'ai_settings' for a resource. + Save KeeperAI settings on a PAM resource. - The settings are stored as encrypted JSON in a DATA edge on the resource vertex - with path 'ai_settings'. This function: - 1. Loads the DAG for the resource - 2. Finds and deactivates any existing 'ai_settings' DATA edge - 3. Adds a new DATA edge with the provided settings - 4. Encrypts the content using the record key - 5. Saves the DAG + Primary path: POSTs the encrypted settings to krouter's + `/api/user/configure_resource` (Layer-B, permission-checked). krouter + validates caller access then writes the `ai_settings` DAG DATA edge on the + resource server-side. + + Fallback (env var `KEEPER_DAG_LB_FALLBACK=1`, default ON): on + `RRC_NOT_ALLOWED*` from krouter, fall back to the legacy direct + DAG-write path (`_set_resource_keeper_ai_settings_legacy`). Gateway then + enforces at runtime. Set the env var to `0` for strict mode (denials propagate). Args: params: KeeperParams instance @@ -407,102 +416,145 @@ def set_resource_keeper_ai_settings( Returns: True if settings were saved successfully, False otherwise. """ - try: - # Get the record to access the record key - record = vault.KeeperRecord.load(params, resource_uid) - if not record: - logging.warning(f"Record {resource_uid} not found") - return False + # Common setup — needed by both the new and legacy paths. + common = _resolve_resource_settings_inputs(params, resource_uid, settings, config_uid) + if common is None: + return False + record_key, resolved_config_uid = common + + encrypted_content = encrypt_aes(json.dumps(settings).encode(), record_key) + + # Primary: Layer-B configure_resource (permission-checked). + from ..pam.router_helper import router_configure_resource, get_router_url + host = get_router_url(params) + endpoint = 'configure_resource' + if not is_layer_b_feature_disabled(host, endpoint): + rq = pam_pb2.PAMResourceConfig( + recordUid=url_safe_str_to_bytes(resource_uid), + networkUid=url_safe_str_to_bytes(resolved_config_uid), + keeperAiSettings=encrypted_content, + ) + try: + router_configure_resource(params, rq) + logging.debug(f"Saved KeeperAI settings via configure_resource for {resource_uid}") + return True + except Exception as err: + if not should_fallback_on_layer_b_error(err, host=host, endpoint=endpoint): + logging.error(f"configure_resource failed (no fallback): {err}", exc_info=True) + return False + logging.warning( + f"configure_resource denied/unavailable for {resource_uid}; falling back to legacy " + f"DAG-write (KEEPER_DAG_LB_FALLBACK enabled): {err}" + ) + + # Fallback: legacy direct DAG-write path. + return _set_resource_keeper_ai_settings_legacy( + params, resource_uid, settings, resolved_config_uid, record_key, encrypted_content + ) + + +def _resolve_resource_settings_inputs( + params: KeeperParams, + resource_uid: str, + settings: Dict[str, Any], + config_uid: Optional[str], +): + """Validate inputs and resolve record_key + config_uid. Returns (record_key, config_uid) or None on failure. - # Get record key for encryption - record_key = None - if resource_uid in params.record_cache: - record_data = params.record_cache[resource_uid] - record_key = record_data.get('record_key_unencrypted') + Shared by the new and legacy paths so behavior on bad input (missing record, + bad settings dict, etc.) stays identical. + """ + record = vault.KeeperRecord.load(params, resource_uid) + if not record: + logging.warning(f"Record {resource_uid} not found") + return None - if not record_key: - logging.warning(f"Record key not available for {resource_uid}") - return False + record_key = None + if resource_uid in params.record_cache: + record_data = params.record_cache[resource_uid] + record_key = record_data.get('record_key_unencrypted') + if not record_key: + logging.warning(f"Record key not available for {resource_uid}") + return None - # Validate settings structure - if not isinstance(settings, dict): - logging.warning("Settings must be a dictionary") - return False + if not isinstance(settings, dict): + logging.warning("Settings must be a dictionary") + return None - # Get config UID if not provided + if not config_uid: + encrypted_session_token, encrypted_transmission_key, _ = get_keeper_tokens(params) + config_uid = get_config_uid(params, encrypted_session_token, encrypted_transmission_key, resource_uid) if not config_uid: - encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) - config_uid = get_config_uid(params, encrypted_session_token, encrypted_transmission_key, resource_uid) - if not config_uid: - config_uid = resource_uid + config_uid = resource_uid - # Create DAG connection + return record_key, config_uid + + +def _set_resource_keeper_ai_settings_legacy( + params: KeeperParams, + resource_uid: str, + settings: Dict[str, Any], + config_uid: str, + record_key: bytes, + encrypted_content: bytes, +) -> bool: + """Legacy direct DAG-write path. Used as fallback when configure_resource is denied. + + Loads the resource's DAG, deactivates any prior `ai_settings` edge, writes a new + one with the pre-encrypted content, and saves. Pre-Phase-2 behavior, preserved verbatim. + """ + try: encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) - # Create a dummy record for DAG (uses config UID as record UID) dag_record = PasswordRecord() dag_record.record_uid = config_uid - dag_record.record_key = record_key # Use actual record key for encryption + dag_record.record_key = record_key conn = Connection( params=params, encrypted_transmission_key=encrypted_transmission_key, encrypted_session_token=encrypted_session_token, transmission_key=transmission_key, - use_write_protobuf=True + use_read_protobuf=True, + use_write_protobuf=True, ) - # Load the DAG with decryption enabled linking_dag = DAG( conn=conn, record=dag_record, - graph_id=0, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM, - decrypt=True # Enable decryption so we can encrypt on save + decrypt=True, ) linking_dag.load() - # Get the resource vertex resource_vertex = linking_dag.get_vertex(resource_uid) if not resource_vertex: logging.warning(f"Resource vertex {resource_uid} not found in DAG") return False # Find and deactivate existing 'ai_settings' edge for proper versioning - existing_edge = None for edge in resource_vertex.edges: if edge and (edge.edge_type == EdgeType.DATA and edge.path == 'ai_settings' and edge.active): - existing_edge = edge + edge.active = False + logging.debug(f"Deactivated existing 'ai_settings' edge (version {edge.version})") break - if existing_edge: - # Deactivate the existing edge - existing_edge.active = False - logging.debug(f"Deactivated existing 'ai_settings' edge (version {existing_edge.version})") - - # Pre-encrypt content with record key (matches Web Vault: encrypted=True, needs_encryption=False) - content_bytes = json.dumps(settings).encode() - encrypted_content = encrypt_aes(content_bytes, record_key) - - # Add new DATA edge with pre-encrypted content resource_vertex.add_data( content=encrypted_content, path='ai_settings', needs_encryption=False, is_encrypted=True, - modified=True + modified=True, ) - - # Save the DAG linking_dag.save() - logging.debug(f"Successfully saved KeeperAI settings for resource {resource_uid}") + logging.debug(f"Saved KeeperAI settings via legacy DAG-write for {resource_uid}") return True - except Exception as e: - logging.error(f"Error saving KeeperAI settings for {resource_uid}: {e}", exc_info=True) + logging.error(f"Error saving KeeperAI settings (legacy path) for {resource_uid}: {e}", exc_info=True) return False @@ -514,123 +566,116 @@ def set_resource_jit_settings( allow_empty: bool = False ) -> bool: """ - Save JIT settings to the DAG DATA edge with path 'jit_settings' for a resource. + Save JIT settings on a PAM resource. - The settings are stored as encrypted JSON in a DATA edge on the resource vertex - with path 'jit_settings'. This function: - 1. Loads the DAG for the resource - 2. Finds and deactivates any existing 'jit_settings' DATA edge - 3. Adds a new DATA edge with the provided settings - 4. Encrypts the content using the record key - 5. Saves the DAG - - Args: - params: KeeperParams instance - resource_uid: UID of the PAM resource (pamMachine, pamDatabase, pamDirectory) - settings: Dictionary containing JIT settings to save - config_uid: Optional PAM config UID. If not provided, will be looked up. - - Returns: - True if settings were saved successfully, False otherwise. + Primary path: POSTs the encrypted settings to krouter's + `/api/user/configure_resource` (Layer-B, permission-checked). Fallback + behavior matches `set_resource_keeper_ai_settings` — see its docstring + and `KEEPER_DAG_LB_FALLBACK`. Same shape, `jit_settings` instead of + `ai_settings`. """ - try: - if not isinstance(settings, dict): - logging.debug(f"JIT settings invalid for {resource_uid}, skipping") - return False - if not settings and not allow_empty: - logging.debug(f"JIT settings empty for {resource_uid}, skipping") - return False - - # Get the record to access the record key - record = vault.KeeperRecord.load(params, resource_uid) - if not record: - logging.warning(f"Record {resource_uid} not found") - return False - - # Get record key for encryption - record_key = None - if resource_uid in params.record_cache: - record_data = params.record_cache[resource_uid] - record_key = record_data.get('record_key_unencrypted') - - if not record_key: - logging.warning(f"Record key not available for {resource_uid}") - return False - - # Get config UID if not provided - if not config_uid: - encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) - config_uid = get_config_uid(params, encrypted_session_token, encrypted_transmission_key, resource_uid) - if not config_uid: - config_uid = resource_uid + # Empty-settings short-circuit retains the legacy semantics. + if not isinstance(settings, dict): + logging.debug(f"JIT settings invalid for {resource_uid}, skipping") + return False + if not settings and not allow_empty: + logging.debug(f"JIT settings empty for {resource_uid}, skipping") + return False - # Create DAG connection + common = _resolve_resource_settings_inputs(params, resource_uid, settings, config_uid) + if common is None: + return False + record_key, resolved_config_uid = common + + encrypted_content = encrypt_aes(json.dumps(settings).encode(), record_key) + + # Primary: Layer-B configure_resource (permission-checked). + from ..pam.router_helper import router_configure_resource, get_router_url + host = get_router_url(params) + endpoint = 'configure_resource' + if not is_layer_b_feature_disabled(host, endpoint): + rq = pam_pb2.PAMResourceConfig( + recordUid=url_safe_str_to_bytes(resource_uid), + networkUid=url_safe_str_to_bytes(resolved_config_uid), + jitSettings=encrypted_content, + ) + try: + router_configure_resource(params, rq) + logging.debug(f"Saved JIT settings via configure_resource for {resource_uid}") + return True + except Exception as err: + if not should_fallback_on_layer_b_error(err, host=host, endpoint=endpoint): + logging.error(f"configure_resource failed (no fallback): {err}", exc_info=True) + return False + logging.warning( + f"configure_resource denied/unavailable for {resource_uid}; falling back to legacy " + f"DAG-write (KEEPER_DAG_LB_FALLBACK enabled): {err}" + ) + + return _set_resource_jit_settings_legacy( + params, resource_uid, resolved_config_uid, record_key, encrypted_content + ) + + +def _set_resource_jit_settings_legacy( + params: KeeperParams, + resource_uid: str, + config_uid: str, + record_key: bytes, + encrypted_content: bytes, +) -> bool: + """Legacy direct DAG-write path for JIT settings. See AI-settings analog for shape.""" + try: encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) - # Create a dummy record for DAG (uses config UID as record UID) dag_record = PasswordRecord() dag_record.record_uid = config_uid - dag_record.record_key = record_key # Use actual record key for encryption + dag_record.record_key = record_key conn = Connection( params=params, encrypted_transmission_key=encrypted_transmission_key, encrypted_session_token=encrypted_session_token, transmission_key=transmission_key, - use_write_protobuf=True + use_read_protobuf=True, + use_write_protobuf=True, ) - # Load the DAG with decryption enabled linking_dag = DAG( conn=conn, record=dag_record, - graph_id=0, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM, - decrypt=True # Enable decryption so we can encrypt on save + decrypt=True, ) linking_dag.load() - # Get the resource vertex resource_vertex = linking_dag.get_vertex(resource_uid) if not resource_vertex: logging.warning(f"Resource vertex {resource_uid} not found in DAG") return False - # Find and deactivate existing 'jit_settings' edge for proper versioning - existing_edge = None for edge in resource_vertex.edges: if edge and (edge.edge_type == EdgeType.DATA and edge.path == 'jit_settings' and edge.active): - existing_edge = edge + edge.active = False + logging.debug(f"Deactivated existing 'jit_settings' edge (version {edge.version})") break - if existing_edge: - # Deactivate the existing edge - existing_edge.active = False - logging.debug(f"Deactivated existing 'jit_settings' edge (version {existing_edge.version})") - - # Pre-encrypt content with record key (matches Web Vault: encrypted=True, needs_encryption=False) - content_bytes = json.dumps(settings).encode() - encrypted_content = encrypt_aes(content_bytes, record_key) - - # Add new DATA edge with pre-encrypted content resource_vertex.add_data( content=encrypted_content, path='jit_settings', needs_encryption=False, is_encrypted=True, - modified=True + modified=True, ) - - # Save the DAG linking_dag.save() - logging.debug(f"Successfully saved JIT settings for resource {resource_uid}") + logging.debug(f"Saved JIT settings via legacy DAG-write for {resource_uid}") return True - except Exception as e: - logging.error(f"Error saving JIT settings for {resource_uid}: {e}", exc_info=True) + logging.error(f"Error saving JIT settings (legacy path) for {resource_uid}: {e}", exc_info=True) return False @@ -668,12 +713,14 @@ def refresh_meta_to_latest( encrypted_transmission_key=encrypted_transmission_key, encrypted_session_token=encrypted_session_token, transmission_key=transmission_key, + use_read_protobuf=True, use_write_protobuf=True ) linking_dag = DAG( conn=conn, record=dag_record, graph_id=0, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM, decrypt=True ) @@ -736,12 +783,14 @@ def refresh_link_to_config_to_latest( encrypted_transmission_key=encrypted_transmission_key, encrypted_session_token=encrypted_session_token, transmission_key=transmission_key, + use_read_protobuf=True, use_write_protobuf=True ) linking_dag = DAG( conn=conn, record=dag_record, graph_id=0, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM, decrypt=True ) @@ -872,12 +921,14 @@ def inspect_resource_in_graph( encrypted_transmission_key=encrypted_transmission_key, encrypted_session_token=encrypted_session_token, transmission_key=transmission_key, + use_read_protobuf=True, use_write_protobuf=True ) linking_dag = DAG( conn=conn, record=dag_record, graph_id=0, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM, decrypt=not show_raw_content ) @@ -972,12 +1023,14 @@ def get_resource_domain_dir_uid( encrypted_transmission_key=encrypted_transmission_key, encrypted_session_token=encrypted_session_token, transmission_key=transmission_key, + use_read_protobuf=True, use_write_protobuf=True ) linking_dag = DAG( conn=conn, record=dag_record, graph_id=0, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM ) linking_dag.load() @@ -1005,28 +1058,57 @@ def set_resource_domain_dir( config_uid: Optional[str] = None ) -> bool: """ - Add or replace the LINK edge from resource to pamDirectory with path 'domain'. - If a domain LINK to a different pamDirectory already exists, it is disconnected first. - """ - try: - record = vault.KeeperRecord.load(params, resource_uid) - if not record: - logging.warning(f"Record {resource_uid} not found") - return False + Add or replace the LINK edge from resource to pamDirectory. - record_key = None - if resource_uid in params.record_cache: - record_key = params.record_cache[resource_uid].get('record_key_unencrypted') - if not record_key: - logging.warning(f"Record key not available for {resource_uid}") - return False - - if not config_uid: - encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) - config_uid = get_config_uid(params, encrypted_session_token, encrypted_transmission_key, resource_uid) - if not config_uid: - config_uid = resource_uid + Primary path: POSTs a `PAMResourceConfig` with `domainUid` set to krouter's + `/api/user/configure_resource`. krouter handles "replace existing domain link" + server-side (so the legacy disconnect-old-first dance is no longer needed). + Fallback: same `KEEPER_DAG_LB_FALLBACK` policy as the AI/JIT helpers (see + `set_resource_keeper_ai_settings`). + """ + common = _resolve_resource_settings_inputs(params, resource_uid, {}, config_uid) + if common is None: + return False + record_key, resolved_config_uid = common + + # Primary: Layer-B configure_resource (permission-checked). + from ..pam.router_helper import router_configure_resource, get_router_url + host = get_router_url(params) + endpoint = 'configure_resource' + if not is_layer_b_feature_disabled(host, endpoint): + rq = pam_pb2.PAMResourceConfig( + recordUid=url_safe_str_to_bytes(resource_uid), + networkUid=url_safe_str_to_bytes(resolved_config_uid), + domainUid=url_safe_str_to_bytes(dir_uid), + ) + try: + router_configure_resource(params, rq) + logging.debug(f"Set domain dir link {resource_uid} -> {dir_uid} via configure_resource") + return True + except Exception as err: + if not should_fallback_on_layer_b_error(err, host=host, endpoint=endpoint): + logging.error(f"configure_resource failed (no fallback): {err}", exc_info=True) + return False + logging.warning( + f"configure_resource denied/unavailable for {resource_uid}; falling back to legacy " + f"DAG-write (KEEPER_DAG_LB_FALLBACK enabled): {err}" + ) + + return _set_resource_domain_dir_legacy( + params, resource_uid, dir_uid, resolved_config_uid, record_key + ) + + +def _set_resource_domain_dir_legacy( + params: KeeperParams, + resource_uid: str, + dir_uid: str, + config_uid: str, + record_key: bytes, +) -> bool: + """Legacy direct DAG-write path: disconnect-old-then-link. Used as fallback only.""" + try: encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) dag_record = PasswordRecord() dag_record.record_uid = config_uid @@ -1037,14 +1119,15 @@ def set_resource_domain_dir( encrypted_transmission_key=encrypted_transmission_key, encrypted_session_token=encrypted_session_token, transmission_key=transmission_key, - use_write_protobuf=True + use_read_protobuf=True, + use_write_protobuf=True, ) linking_dag = DAG( conn=conn, record=dag_record, - graph_id=0, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM, - decrypt=True + decrypt=True, ) linking_dag.load() @@ -1053,14 +1136,13 @@ def set_resource_domain_dir( logging.warning(f"Resource vertex {resource_uid} not found in DAG") return False - # If a domain LINK to a different pamDirectory exists, disconnect it first + # If a domain LINK to a different pamDirectory exists, disconnect it first. old_dir_uid = None for edge in resource_vertex.edges: if (edge and edge.edge_type == EdgeType.LINK and edge.path == 'domain' and edge.active): old_dir_uid = edge.head_uid break - if old_dir_uid and old_dir_uid != dir_uid: old_dir_vertex = linking_dag.get_vertex(old_dir_uid) if old_dir_vertex: @@ -1075,11 +1157,10 @@ def set_resource_domain_dir( resource_vertex.belongs_to(dir_vertex, EdgeType.LINK, path="domain", content={}) linking_dag.save() - logging.debug(f"Successfully set domain dir link for resource {resource_uid} -> {dir_uid}") + logging.debug(f"Set domain dir link {resource_uid} -> {dir_uid} via legacy DAG-write") return True - except Exception as e: - logging.error(f"Error setting domain dir for {resource_uid}: {e}", exc_info=True) + logging.error(f"Error setting domain dir (legacy path) for {resource_uid}: {e}", exc_info=True) return False diff --git a/keepercommander/commands/pam_service/add.py b/keepercommander/commands/pam_service/add.py index e471a5c4f..c03441e3b 100644 --- a/keepercommander/commands/pam_service/add.py +++ b/keepercommander/commands/pam_service/add.py @@ -60,9 +60,9 @@ def execute(self, params: KeeperParams, **kwargs): return user_service = UserService(record=gateway_context.configuration, params=params, fail_on_corrupt=False, - agent=f"Cmdr/{__version__}") + agent=f"Cmdr/{__version__}", use_per_graph_endpoints=True) record_link = RecordLink(record=gateway_context.configuration, params=params, fail_on_corrupt=False, - agent=f"Cmdr/{__version__}") + agent=f"Cmdr/{__version__}", use_per_graph_endpoints=True) ############### diff --git a/keepercommander/commands/pam_service/list.py b/keepercommander/commands/pam_service/list.py index 995be7d1d..b19b34283 100644 --- a/keepercommander/commands/pam_service/list.py +++ b/keepercommander/commands/pam_service/list.py @@ -42,7 +42,7 @@ def execute(self, params: KeeperParams, **kwargs): return user_service = UserService(record=gateway_context.configuration, params=params, fail_on_corrupt=False, - agent=f"Cmdr/{__version__}") + agent=f"Cmdr/{__version__}", use_per_graph_endpoints=True) service_map = {} for resource_vertex in user_service.dag.get_root.has_vertices(edge_type=EdgeType.LINK): diff --git a/keepercommander/commands/pam_service/remove.py b/keepercommander/commands/pam_service/remove.py index e4b68d25f..58f545592 100644 --- a/keepercommander/commands/pam_service/remove.py +++ b/keepercommander/commands/pam_service/remove.py @@ -57,7 +57,7 @@ def execute(self, params: KeeperParams, **kwargs): return user_service = UserService(record=gateway_context.configuration, params=params, fail_on_corrupt=False, - agent=f"Cmdr/{__version__}") + agent=f"Cmdr/{__version__}", use_per_graph_endpoints=True) machine_record = vault.KeeperRecord.load(params, machine_uid) # type: Optional[TypedRecord] if machine_record is None: diff --git a/keepercommander/commands/tunnel/port_forward/TunnelGraph.py b/keepercommander/commands/tunnel/port_forward/TunnelGraph.py index 027a4d822..150b22916 100644 --- a/keepercommander/commands/tunnel/port_forward/TunnelGraph.py +++ b/keepercommander/commands/tunnel/port_forward/TunnelGraph.py @@ -1,3 +1,5 @@ +import json +import logging from .tunnel_helpers import generate_random_bytes, get_config_uid from ....keeper_dag import DAG, EdgeType from ....keeper_dag.connection.commander import Connection @@ -5,6 +7,9 @@ from ....keeper_dag.vertex import DAGVertex from ....display import bcolors from ....vault import PasswordRecord +from ....proto import pam_pb2, router_pb2 +from ...pam._layer_b import should_fallback_on_layer_b_error +from keeper_secrets_manager_core.utils import url_safe_str_to_bytes def get_vertex_content(vertex): @@ -76,6 +81,7 @@ def __init__(self, params, encrypted_session_token, encrypted_transmission_key, config_uid = get_config_uid(params, encrypted_session_token, encrypted_transmission_key, record_uid) if not config_uid: config_uid = record_uid + self.params = params # retained for Layer-B router_configure_resource calls self.record = PasswordRecord() self.record.record_uid = config_uid self.record.record_key = generate_random_bytes(32) @@ -86,9 +92,11 @@ def __init__(self, params, encrypted_session_token, encrypted_transmission_key, encrypted_transmission_key=self.encrypted_transmission_key, encrypted_session_token=self.encrypted_session_token, transmission_key=self.transmission_key, + use_read_protobuf=True, use_write_protobuf=True ) - self.linking_dag = DAG(conn=self.conn, record=self.record, graph_id=0, write_endpoint=PamEndpoints.PAM) + self.linking_dag = DAG(conn=self.conn, record=self.record, + read_endpoint=PamEndpoints.PAM, write_endpoint=PamEndpoints.PAM) try: self.linking_dag.load() except Exception as e: @@ -250,6 +258,44 @@ def edit_tunneling_config(self, connections=None, tunneling=None, allowed_settings["aiSessionTerminate"] = ai_session_terminate if dirty: + # Primary: Layer-B configure_network_graph (permission-checked). + # The configuration record's allowedSettings belong on the network + # endpoint, not configure_resource — the latter bypasses per-feature + # enforcement checks for remoteBrowserIsolation / rotation / + # connections that the network endpoint enforces server-side. + # + # Fallback policy matches the other Layer-B endpoints: strict by + # default; 404 / RRC denials fall back to legacy DAG-write only + # when KEEPER_DAG_LB_FALLBACK=1 (per `should_fallback_on_layer_b_error`). + # Transient errors (5xx, connection, timeout) always propagate. + from ...pam.router_helper import router_configure_network_graph, get_router_url + from ...pam._layer_b import is_layer_b_feature_disabled + host = get_router_url(self.params) + endpoint = 'configure_network_graph' + if not is_layer_b_feature_disabled(host, endpoint): + try: + config_uid_bytes = url_safe_str_to_bytes(self.record.record_uid) + rq = router_pb2.PAMNetworkConfigurationRequest( + recordUid=config_uid_bytes, + networkSettings=router_pb2.PAMNetworkSettings( + allowedSettings=json.dumps(allowed_settings).encode() + ), + ) + router_configure_network_graph(self.params, rq) + logging.debug( + f"edit_tunneling_config: applied via configure_network_graph for {self.record.record_uid}" + ) + return + except Exception as err: + if not should_fallback_on_layer_b_error(err, host=host, endpoint=endpoint): + logging.error(f"configure_network_graph failed (no fallback): {err}", exc_info=True) + raise + logging.warning( + f"configure_network_graph denied/unavailable for {self.record.record_uid}; " + f"falling back to legacy DAG-write: {err}" + ) + + # Fallback: legacy direct DAG-write. config_vertex.add_data(content=content, path='meta', needs_encryption=False) self.linking_dag.save() @@ -298,14 +344,45 @@ def link_user_to_config(self, user_uid): config_vertex = self.linking_dag.get_vertex(self.record.record_uid) if config_vertex is None: config_vertex = self.linking_dag.add_vertex(uid=self.record.record_uid) + # IAM-user link. Permission-check via set_record_rotation BEFORE the legacy + # DAG write (server-side set_record_rotation with no + # resource/saasConfiguration and noop=False permission-checks edit-access + # on the pamUser record and sets is_iam_user atomically). No legacy fallback + # - if the call fails, propagate so the unauthorized link is never written. + self._permission_check_iam_user_link(user_uid) self.link_user(user_uid, config_vertex, belongs_to=True, is_iam_user=True) + def _permission_check_iam_user_link(self, user_uid): + """Call set_record_rotation(recordUid=user_uid, noop=False) to permission-check + an is_iam_user link write. Raises on permission denial; no fallback. The + server enforces edit-access on the pamUser record at the call boundary, so + the per-flag check that other Layer-B endpoints do is not needed here.""" + from ...pam.router_helper import router_set_record_rotation_information + rq = router_pb2.RouterRecordRotationRequest( + recordUid=url_safe_str_to_bytes(user_uid), + noop=False, + ) + router_set_record_rotation_information(self.params, rq) + logging.debug( + f"is_iam_user link permission-checked via set_record_rotation for {user_uid}" + ) + def link_user_to_config_with_options(self, user_uid, is_admin=None, belongs_to=None, is_iam_user=None): config_vertex = self.linking_dag.get_vertex(self.record.record_uid) if config_vertex is None: config_vertex = self.linking_dag.add_vertex(uid=self.record.record_uid) - # self.link_user(user_uid, config_vertex, is_admin, belongs_to, is_iam_user) + # is_iam_user: route through set_record_rotation for the permission check. + # No legacy fallback for this flag — if the server rejects, + # propagate rather than write an unauthorized ACL edge locally. + # is_admin (domain-controller admin on the config vertex): in development + # server-side but has no new krouter API yet — legacy DAG-write only. When + # the new API ships, gate the local write below behind it. + # belongs_to: bare membership, no security-sensitive permission decision; + # legacy write only. + if is_iam_user is True: + self._permission_check_iam_user_link(user_uid) + source_vertex = config_vertex user_vertex = self.linking_dag.get_vertex(user_uid) if user_vertex is None: @@ -378,6 +455,39 @@ def link_user_to_resource(self, user_uid, resource_uid, is_admin=None, belongs_t if resource_vertex is None or not self.resource_belongs_to_config(resource_uid): print(f"{bcolors.FAIL}Resource {resource_uid} does not belong to the configuration{bcolors.ENDC}") return False + + # Layer-B: when setting an admin credential, route through configure_resource + # for the permission check (addresses the "link unauthorized record as credentials" + # security finding). On RRC_NOT_ALLOWED* with `KEEPER_DAG_LB_FALLBACK` enabled, + # fall through to the legacy in-memory + save path. + # Other flag-only paths (belongs_to / is_launch_credential without is_admin) stay + # legacy because PAMResourceConfig doesn't model those flags independently. + if is_admin is True: + from ...pam.router_helper import router_configure_resource, get_router_url + from ...pam._layer_b import is_layer_b_feature_disabled + host = get_router_url(self.params) + endpoint = 'configure_resource' + if not is_layer_b_feature_disabled(host, endpoint): + try: + rq = pam_pb2.PAMResourceConfig( + recordUid=url_safe_str_to_bytes(resource_uid), + networkUid=url_safe_str_to_bytes(self.record.record_uid), + adminUid=url_safe_str_to_bytes(user_uid), + ) + router_configure_resource(self.params, rq) + logging.debug( + f"link_user_to_resource: admin {user_uid} set on {resource_uid} via configure_resource" + ) + return None + except Exception as err: + if not should_fallback_on_layer_b_error(err, host=host, endpoint=endpoint): + logging.error(f"configure_resource failed (no fallback): {err}", exc_info=True) + raise + logging.warning( + f"configure_resource denied/unavailable for {resource_uid}; falling back to legacy " + f"DAG-write (KEEPER_DAG_LB_FALLBACK enabled): {err}" + ) + self.link_user(user_uid, resource_vertex, is_admin, belongs_to, is_launch_credential=is_launch_credential) return None @@ -479,6 +589,67 @@ def clear_launch_credential_for_resource(self, resource_uid, exclude_user_uid=No if dirty: self.linking_dag.save() + def set_launch_credentials(self, resource_uid, launch_uid=None): + """ + Set or clear the launch credential for a resource via Layer-B configure_resource. + + krouter's connectUsers field has replacement semantics + (UserRest.kt:generateResourceConnectionEdges): sending [launch_uid] sets + is_launch_credential=true on that user AND clears it from every other user on + the resource; sending [] clears it from every user. The meta field carries + the v1 upgrade in the same call. This replaces the legacy 2-3 op sequence + (clear + link + meta-upgrade) with one permission-checked round-trip. + + For a fresh launch_uid (no existing edge), krouter creates the new edge with + belongs_to=null; a follow-up local DAG-write of belongs_to=True preserves + legacy parity. For existing edges, krouter preserves belongs_to already so + the follow-up is a no-op. + + Fallback on RRC_NOT_ALLOWED* (or feature-disabled) with KEEPER_DAG_LB_FALLBACK + enabled: legacy clear + link (if set) + meta-upgrade. + """ + resource_vertex = self.linking_dag.get_vertex(resource_uid) + if resource_vertex is None: + return + + upgraded_meta = ensure_resource_meta_v1(get_vertex_content(resource_vertex)) + uids = [url_safe_str_to_bytes(launch_uid)] if launch_uid is not None else [] + + from ...pam.router_helper import router_configure_resource, get_router_url + from ...pam._layer_b import is_layer_b_feature_disabled + host = get_router_url(self.params) + endpoint = 'configure_resource' + if not is_layer_b_feature_disabled(host, endpoint): + try: + rq = pam_pb2.PAMResourceConfig( + recordUid=url_safe_str_to_bytes(resource_uid), + networkUid=url_safe_str_to_bytes(self.record.record_uid), + connectUsers=pam_pb2.UidList(uids=uids), + meta=json.dumps(upgraded_meta).encode(), + ) + router_configure_resource(self.params, rq) + logging.debug( + f"set_launch_credentials: resource={resource_uid} " + f"launch_uid={launch_uid} via configure_resource" + ) + if launch_uid is not None: + self.link_user(launch_uid, resource_vertex, belongs_to=True) + return + except Exception as err: + if not should_fallback_on_layer_b_error(err, host=host, endpoint=endpoint): + logging.error(f"configure_resource failed (no fallback): {err}", exc_info=True) + raise + logging.warning( + f"configure_resource denied/unavailable for {resource_uid}; " + f"falling back to legacy DAG-write: {err}" + ) + + self.clear_launch_credential_for_resource(resource_uid, exclude_user_uid=launch_uid) + if launch_uid is not None: + self.link_user_to_resource(launch_uid, resource_uid, + is_launch_credential=True, belongs_to=True) + self.upgrade_resource_meta_to_v1(resource_uid) + def upgrade_resource_meta_to_v1(self, resource_uid): """Ensure resource vertex meta has version >= 1 so vault reads ACL launch credentials.""" resource_vertex = self.linking_dag.get_vertex(resource_uid) @@ -488,6 +659,38 @@ def upgrade_resource_meta_to_v1(self, resource_uid): if content and content.get('version', 0) >= RESOURCE_META_VERSION_V1: return upgraded = ensure_resource_meta_v1(content) + + # Primary: Layer-B configure_resource (permission-checked). Same meta + # shape we use in set_resource_allowed(meta_version=1): the versioned + # payload {version, allowedSettings, rotateOnTermination} is sent as + # the proto's `meta` field. krouter's mergeJson honors version with the + # `oldMetaVersion <= newMetaVersion` upgrade check. + from ...pam.router_helper import router_configure_resource, get_router_url + from ...pam._layer_b import is_layer_b_feature_disabled + host = get_router_url(self.params) + endpoint = 'configure_resource' + if not is_layer_b_feature_disabled(host, endpoint): + try: + rq = pam_pb2.PAMResourceConfig( + recordUid=url_safe_str_to_bytes(resource_uid), + networkUid=url_safe_str_to_bytes(self.record.record_uid), + meta=json.dumps(upgraded).encode(), + ) + router_configure_resource(self.params, rq) + logging.debug( + f"upgrade_resource_meta_to_v1: applied to {resource_uid} via configure_resource" + ) + return + except Exception as err: + if not should_fallback_on_layer_b_error(err, host=host, endpoint=endpoint): + logging.error(f"configure_resource failed (no fallback): {err}", exc_info=True) + raise + logging.warning( + f"configure_resource denied/unavailable for {resource_uid}; " + f"falling back to legacy DAG-write: {err}" + ) + + # Fallback: legacy direct DAG-write. resource_vertex.add_data(content=upgraded, path='meta', needs_encryption=False) self.linking_dag.save() @@ -637,13 +840,79 @@ def set_resource_allowed(self, resource_uid, tunneling=None, connections=None, r content["rotateOnTermination"] = bool(rotate_on_termination) if dirty: - # Legacy: missing or meta_version=0 -> write content as-is (no version in meta) + # Compute the meta payload (same shape legacy would write). if meta_version is not None and meta_version != 0: meta_payload = build_resource_meta( meta_version, content.get(allowed_settings_name, {}), rotate_on_termination=bool(content.get("rotateOnTermination", False)), ) + else: + meta_payload = content + + # Primary: Layer-B (permission-checked). is_config=True writes the + # config record's network-level allowedSettings via configure_network_graph + # (the same endpoint edit_tunneling_config uses). is_config=False writes + # a resource's meta via configure_resource(meta=...); the server's + # mergeJson handles any meta key (allowedSettings, pamRemoteBrowserSettings, + # rotation, etc.). Same fallback policy as edit_tunneling_config and + # link_user_to_resource: strict by default, opt INTO legacy fallback + # via KEEPER_DAG_LB_FALLBACK=1. + from ...pam.router_helper import ( + router_configure_resource, router_configure_network_graph, get_router_url + ) + from ...pam._layer_b import is_layer_b_feature_disabled + host = get_router_url(self.params) + + if is_config: + endpoint = 'configure_network_graph' + if not is_layer_b_feature_disabled(host, endpoint): + try: + inner_settings = meta_payload.get(allowed_settings_name, {}) + rq = router_pb2.PAMNetworkConfigurationRequest( + recordUid=url_safe_str_to_bytes(resource_uid), + networkSettings=router_pb2.PAMNetworkSettings( + allowedSettings=json.dumps(inner_settings).encode() + ), + ) + router_configure_network_graph(self.params, rq) + logging.debug( + f"set_resource_allowed: applied to config {resource_uid} via configure_network_graph" + ) + return + except Exception as err: + if not should_fallback_on_layer_b_error(err, host=host, endpoint=endpoint): + logging.error(f"configure_network_graph failed (no fallback): {err}", exc_info=True) + raise + logging.warning( + f"configure_network_graph denied/unavailable for config {resource_uid}; " + f"falling back to legacy DAG-write: {err}" + ) + else: + endpoint = 'configure_resource' + if not is_layer_b_feature_disabled(host, endpoint): + try: + rq = pam_pb2.PAMResourceConfig( + recordUid=url_safe_str_to_bytes(resource_uid), + networkUid=url_safe_str_to_bytes(self.record.record_uid), + meta=json.dumps(meta_payload).encode(), + ) + router_configure_resource(self.params, rq) + logging.debug( + f"set_resource_allowed: applied to resource {resource_uid} via configure_resource" + ) + return + except Exception as err: + if not should_fallback_on_layer_b_error(err, host=host, endpoint=endpoint): + logging.error(f"configure_resource failed (no fallback): {err}", exc_info=True) + raise + logging.warning( + f"configure_resource denied/unavailable for resource {resource_uid}; " + f"falling back to legacy DAG-write: {err}" + ) + + # Fallback: legacy direct DAG-write. + if meta_version is not None and meta_version != 0: resource_vertex.add_data(content=meta_payload, path='meta', needs_encryption=False) else: resource_vertex.add_data(content=content, path='meta', needs_encryption=False) diff --git a/keepercommander/discovery_common/__version__.py b/keepercommander/discovery_common/__version__.py index 1bdaf4702..d14671611 100644 --- a/keepercommander/discovery_common/__version__.py +++ b/keepercommander/discovery_common/__version__.py @@ -1 +1 @@ -__version__ = '1.1.10' +__version__ = '1.1.13' diff --git a/keepercommander/discovery_common/infrastructure.py b/keepercommander/discovery_common/infrastructure.py index ee7ad7fed..b358c8215 100644 --- a/keepercommander/discovery_common/infrastructure.py +++ b/keepercommander/discovery_common/infrastructure.py @@ -4,7 +4,7 @@ from ..keeper_dag import DAG, EdgeType from ..keeper_dag.exceptions import DAGVertexException from ..keeper_dag.crypto import urlsafe_str_to_bytes -from ..keeper_dag.types import PamGraphId +from ..keeper_dag.types import PamEndpoints, PamGraphId from .types import DiscoveryObject import os import importlib @@ -36,7 +36,16 @@ class Infrastructure: def __init__(self, record: Any, logger: Optional[Any] = None, history_level: int = 0, debug_level: int = 0, fail_on_corrupt: bool = True, log_prefix: str = "GS Infrastructure", save_batch_count: int = 200, agent: Optional[str] = None, + use_per_graph_endpoints: bool = False, **kwargs): + """ + :param use_per_graph_endpoints: If True, use the per-graph URL transport + (`/api/user/graph-sync/infrastructure/...`) via `read_endpoint` / + `write_endpoint`. If False (default), use the legacy single-endpoint + transport via `graph_id=PamGraphId.INFRASTRUCTURE`. The legacy + default is preserved for backward compatibility; it will be removed + in a future major version once all consumers have migrated. + """ # This will either be a KSM Record, or Commander KeeperRecord self.record = record @@ -49,6 +58,7 @@ def __init__(self, record: Any, logger: Optional[Any] = None, history_level: int self.debug_level = debug_level self.fail_on_corrupt = fail_on_corrupt self.save_batch_count = save_batch_count + self.use_per_graph_endpoints = use_per_graph_endpoints self.auto_save = False self.delta_graph = True @@ -66,22 +76,38 @@ def __init__(self, record: Any, logger: Optional[Any] = None, history_level: int def dag(self) -> DAG: if self._dag is None: - self.logger.debug(f"loading the dag graph {PamGraphId.INFRASTRUCTURE.value}") + self.logger.debug("loading the dag graph " + + (PamEndpoints.INFRASTRUCTURE.value if self.use_per_graph_endpoints + else str(PamGraphId.INFRASTRUCTURE.value))) self.logger.debug(f"setting graph save batch count to {self.save_batch_count}") - self._dag = DAG(conn=self.conn, - record=self.record, - # endpoint=PamEndpoints.INFRASTRUCTURE, - graph_id=PamGraphId.INFRASTRUCTURE, - auto_save=self.auto_save, - logger=self.logger, - history_level=self.history_level, - debug_level=self.debug_level, - name="Discovery Infrastructure", - fail_on_corrupt=self.fail_on_corrupt, - log_prefix=self.log_prefix, - save_batch_count=self.save_batch_count, - agent=self.agent) + if self.use_per_graph_endpoints: + self._dag = DAG(conn=self.conn, + record=self.record, + read_endpoint=PamEndpoints.INFRASTRUCTURE, + write_endpoint=PamEndpoints.INFRASTRUCTURE, + auto_save=self.auto_save, + logger=self.logger, + history_level=self.history_level, + debug_level=self.debug_level, + name="Discovery Infrastructure", + fail_on_corrupt=self.fail_on_corrupt, + log_prefix=self.log_prefix, + save_batch_count=self.save_batch_count, + agent=self.agent) + else: + self._dag = DAG(conn=self.conn, + record=self.record, + graph_id=PamGraphId.INFRASTRUCTURE, + auto_save=self.auto_save, + logger=self.logger, + history_level=self.history_level, + debug_level=self.debug_level, + name="Discovery Infrastructure", + fail_on_corrupt=self.fail_on_corrupt, + log_prefix=self.log_prefix, + save_batch_count=self.save_batch_count, + agent=self.agent) # Do not load the DAG here. # We don't know if we are using a sync point yet. diff --git a/keepercommander/discovery_common/jobs.py b/keepercommander/discovery_common/jobs.py index 098179da0..cf5a7dad3 100644 --- a/keepercommander/discovery_common/jobs.py +++ b/keepercommander/discovery_common/jobs.py @@ -2,7 +2,7 @@ from .utils import get_connection, make_agent from .types import JobContent, JobItem, Settings, DiscoveryDelta from ..keeper_dag import DAG, EdgeType -from ..keeper_dag.types import PamGraphId +from ..keeper_dag.types import PamEndpoints, PamGraphId import logging import os import base64 @@ -36,7 +36,16 @@ class Jobs: def __init__(self, record: Any, logger: Optional[Any] = None, debug_level: int = 0, fail_on_corrupt: bool = True, log_prefix: str = "GS Jobs", save_batch_count: int = 200, agent: Optional[str] = None, + use_per_graph_endpoints: bool = False, **kwargs): + """ + :param use_per_graph_endpoints: If True, use the per-graph URL transport + (`/api/user/graph-sync/discovery_jobs/...`) via `read_endpoint` / + `write_endpoint`. If False (default), use the legacy single-endpoint + transport via `graph_id=PamGraphId.DISCOVERY_JOBS`. The legacy + default is preserved for backward compatibility; it will be removed + in a future major version once all consumers have migrated. + """ self.conn = get_connection(logger=logger, **kwargs) @@ -51,6 +60,7 @@ def __init__(self, record: Any, logger: Optional[Any] = None, debug_level: int = self.debug_level = debug_level self.fail_on_corrupt = fail_on_corrupt self.save_batch_count = save_batch_count + self.use_per_graph_endpoints = use_per_graph_endpoints self.agent = make_agent("jobs") if agent is not None: @@ -60,18 +70,31 @@ def __init__(self, record: Any, logger: Optional[Any] = None, debug_level: int = def dag(self) -> DAG: if self._dag is None: - self._dag = DAG(conn=self.conn, - record=self.record, - # endpoint=PamEndpoints.DISCOVERY_JOBS, - graph_id=PamGraphId.DISCOVERY_JOBS, - auto_save=False, - logger=self.logger, - debug_level=self.debug_level, - name="Discovery Jobs", - fail_on_corrupt=self.fail_on_corrupt, - log_prefix=self.log_prefix, - save_batch_count=self.save_batch_count, - agent=self.agent) + if self.use_per_graph_endpoints: + self._dag = DAG(conn=self.conn, + record=self.record, + read_endpoint=PamEndpoints.DISCOVERY_JOBS, + write_endpoint=PamEndpoints.DISCOVERY_JOBS, + auto_save=False, + logger=self.logger, + debug_level=self.debug_level, + name="Discovery Jobs", + fail_on_corrupt=self.fail_on_corrupt, + log_prefix=self.log_prefix, + save_batch_count=self.save_batch_count, + agent=self.agent) + else: + self._dag = DAG(conn=self.conn, + record=self.record, + graph_id=PamGraphId.DISCOVERY_JOBS, + auto_save=False, + logger=self.logger, + debug_level=self.debug_level, + name="Discovery Jobs", + fail_on_corrupt=self.fail_on_corrupt, + log_prefix=self.log_prefix, + save_batch_count=self.save_batch_count, + agent=self.agent) ts = time() self._dag.load() diff --git a/keepercommander/discovery_common/record_link.py b/keepercommander/discovery_common/record_link.py index 705c57c8f..6818a8327 100644 --- a/keepercommander/discovery_common/record_link.py +++ b/keepercommander/discovery_common/record_link.py @@ -21,9 +21,20 @@ def __init__(self, log_prefix: str = "GS Record Linking", save_batch_count: int = 200, agent: Optional[str] = None, - use_read_protobuf: bool = False, + use_read_protobuf: bool = True, use_write_protobuf: bool = True, + use_per_graph_endpoints: bool = False, **kwargs): + """ + :param use_per_graph_endpoints: If True, force the per-graph URL transport + (`/api/user/graph-sync/pam/...`) by setting both `read_endpoint` and + `write_endpoint` to `PamEndpoints.PAM`, regardless of the protobuf + flags. If False (default), the endpoints are still set when the + connection itself has protobuf enabled (preserving existing + behavior). The `use_per_graph_endpoints` opt-in lets callers + decouple from the protobuf flags and migrate to the new transport + on their own schedule. + """ self.conn = get_connection(logger=logger, use_read_protobuf=use_read_protobuf, @@ -39,16 +50,19 @@ def __init__(self, self.log_prefix = log_prefix self.debug_level = debug_level self.save_batch_count = save_batch_count + self.use_per_graph_endpoints = use_per_graph_endpoints # Based on the connection type, use_write_protobuf might be set to False is True was passed. # Use self.conn.use_write_protobuf; don't use passed in use_write_protobuf. # If using protobuf to write, then use the endpoint. + # `use_per_graph_endpoints=True` also forces the endpoints on, independent + # of the protobuf flags. self.write_endpoint = None - if self.conn.use_write_protobuf: + if use_per_graph_endpoints or self.conn.use_write_protobuf: self.write_endpoint = PamEndpoints.PAM self.read_endpoint = None - if self.conn.use_read_protobuf: + if use_per_graph_endpoints or self.conn.use_read_protobuf: self.read_endpoint = PamEndpoints.PAM self.agent = make_agent("record_linking") diff --git a/keepercommander/discovery_common/rm_types.py b/keepercommander/discovery_common/rm_types.py index 38057ba18..a647ca933 100644 --- a/keepercommander/discovery_common/rm_types.py +++ b/keepercommander/discovery_common/rm_types.py @@ -61,9 +61,13 @@ class RmNewUser(BaseModel): desc: Optional[str] = None password: Optional[str] = None private_key: Optional[str] = None + public_key: Optional[str] = None private_key_passphrase: Optional[str] = None connect_database: Optional[str] = None + mode: Optional[str] = None dn: Optional[str] = None + uid: Optional[str] = None + home_dir: Optional[str] = None class RmRole(BaseModel): @@ -168,7 +172,84 @@ class RmDomainUserAddMeta(RmMetaBase): # DATABASE -class RmMySQLUserAddMeta(RmMetaBase): +class RmMySQLBaseAddMeta(RmMetaBase): + + """ + Shared model for MySQL and MariaDB of the common GRANT attributes. + """ + + # MariaDB will give all expect GRANT OPTION + grant_all_privileges: List[str] = [] + + # DATA + grant_select: List[str] = ["*"] + grant_insert: List[str] = ["*"] + grant_update: List[str] = ["*"] + grant_delete: List[str] = ["*"] + + # STRUCTURE + grant_create: List[str] = [] + grant_alter: List[str] = [] + grant_drop: List[str] = [] + grant_index: List[str] = [] + grant_create_view: List[str] = [] + grant_show_view: List[str] = [] + grant_create_routine: List[str] = [] + grant_alter_routine: List[str] = [] + grant_trigger: List[str] = [] + grant_references: List[str] = [] + + # TEMP TABLE + grant_create_temp_tables: List[str] = [] + + # EVENTS AND PROCEDURES + grant_event: List[str] = [] + grant_execute: List[str] = [] + + # LOCK + grant_lock_tables: List[str] = [] + + # ADMIN + grant_grant_option: List[str] = [] + grant_create_user: List[str] = [] + grant_reload: List[str] = [] + grant_shutdown: List[str] = [] + grant_process: List[str] = [] + grant_file: List[str] = [] + grant_show_databases: List[str] = [] + grant_super: List[str] = [] + grant_rep_client: List[str] = [] + grant_rep_slave: List[str] = [] + grant_create_tablespace: List[str] = [] + + +class RmMySQLUserAddMeta(RmMySQLBaseAddMeta): + """ + MySQL user add meta information + + engine_type: mysql + + :param failed_login_attempts: Number of fail login attempts before locking the account. + :param password_lockout_time: Number of days to wait before unlocking the account. + Set to 0 to prevent unlock. + Set to UNBOUNDED is lock forever. + :param password_expire_days: Number of days before password is expired. + :param password_history_count: Number of prior passwords that cannot be reused. + :param password_history_days: Cannot reuse passwords used within N days. + :param password_req_current: Current password is required to change password. + :param password_req_ssl: Connection requires SSL. + :param authentication_plugin: Authentication plugin. + :param authentication_value: For the authentication plugin, additional required value. + """ + + failed_login_attempts: Optional[int] = None + # days or UNBOUNDED + password_lockout_time: Optional[str] = None + password_expire_days: Optional[int] = None + password_history_count: Optional[int] = None + password_history_days: Optional[int] = None + password_req_current: bool = False + password_req_ssl: bool = False authentication_plugin: Optional[str] = None authentication_value: Optional[str] = None roles: List[str] = [] @@ -179,17 +260,33 @@ class RmMySQLRoleAddMeta(RmMetaBase): class RmMariaDbRoleAddMeta(RmMetaBase): + with_admin: str = "CURRENT_USER" grant_script: Optional[str] = None -class RmMariaDbLUserAddMeta(RmMetaBase): +class RmMariaDbUserAddMeta(RmMySQLBaseAddMeta): authentication_plugin: Optional[str] = None authentication_value: Optional[str] = None roles: List[str] = [] +# TODO: H +class RmMariaDbLUserAddMeta(RmMariaDbUserAddMeta): + pass + + class RmPostgreSqlUserAddMeta(RmMetaBase): + """ + PostgreSQL user add meta information + + engine_type: postgres + + :param superuser: Make the user a superuser. + :param create_db: Make can create databases. + :param create_role: Make can create roles. + """ + superuser: Optional[bool] = False create_db: Optional[bool] = False create_role: Optional[bool] = False @@ -248,9 +345,122 @@ class RmSqlServerRoleAddMeta(RmMetaBase): grant_script: Optional[str] = None +class RmOracleGrant(BaseModel): + """ + For system and role grants, there is an option to have ADMIN OPTION + """ + + allow: bool = False + admin_option: bool = False + + +class RmOracleGrantObject(BaseModel): + """ + For object grants. + Allows object columns. + """ + + columns: List[str] = [] + object: str + + class RmOracleUserAddMeta(RmMetaBase): - allow_login: bool = True - allow_resource: bool = True + + """ + Oracle user add meta information + + :param profile: User uses this profile for resource limits and password policies. + :param identified_externally: User uses OS authentication (IDENTIFIED EXTERNALLY). + :param identified_globally: User uses directory authentication (IDENTIFIED GLOBALLY AS 'directory_DN'). + :param identified_globally_as: The 'directory_DN' used for identified_globally. + + """ + profile: Optional[str] = None + identified_externally: bool = False + identified_globally: bool = False + identified_globally_as: Optional[str] = None + + # SYSTEM + grant_create_session: RmOracleGrant = RmOracleGrant() + grant_create_table: RmOracleGrant = RmOracleGrant() + grant_create_view: RmOracleGrant = RmOracleGrant() + grant_create_procedure: RmOracleGrant = RmOracleGrant() + grant_create_sequence: RmOracleGrant = RmOracleGrant() + grant_create_trigger: RmOracleGrant = RmOracleGrant() + grant_create_synonym: RmOracleGrant = RmOracleGrant() + grant_create_public_synonym: RmOracleGrant = RmOracleGrant() + grant_create_materialized_view: RmOracleGrant = RmOracleGrant() + grant_create_index: RmOracleGrant = RmOracleGrant() + grant_create_type: RmOracleGrant = RmOracleGrant() + grant_create_role: RmOracleGrant = RmOracleGrant() + grant_create_user: RmOracleGrant = RmOracleGrant() + grant_alter_user: RmOracleGrant = RmOracleGrant() + grant_drop_user: RmOracleGrant = RmOracleGrant() + grant_alter_system: RmOracleGrant = RmOracleGrant() + grant_alter_database: RmOracleGrant = RmOracleGrant() + grant_create_tablespace: RmOracleGrant = RmOracleGrant() + grant_alter_tablespace: RmOracleGrant = RmOracleGrant() + grant_drop_tablespace: RmOracleGrant = RmOracleGrant() + grant_select_any_table: RmOracleGrant = RmOracleGrant() + grant_insert_any_table: RmOracleGrant = RmOracleGrant() + grant_update_any_table: RmOracleGrant = RmOracleGrant() + grant_delete_any_table: RmOracleGrant = RmOracleGrant() + grant_drop_any_table: RmOracleGrant = RmOracleGrant() + grant_create_any_table: RmOracleGrant = RmOracleGrant() + grant_alter_any_table: RmOracleGrant = RmOracleGrant() + grant_create_any_index: RmOracleGrant = RmOracleGrant() + grant_drop_any_index: RmOracleGrant = RmOracleGrant() + grant_create_any_view: RmOracleGrant = RmOracleGrant() + grant_drop_any_view: RmOracleGrant = RmOracleGrant() + grant_execute_any_procedure: RmOracleGrant = RmOracleGrant() + grant_create_any_procedure: RmOracleGrant = RmOracleGrant() + grant_drop_any_procedure: RmOracleGrant = RmOracleGrant() + grant_create_any_sequence: RmOracleGrant = RmOracleGrant() + grant_drop_any_sequence: RmOracleGrant = RmOracleGrant() + grant_grant_any_privilege: RmOracleGrant = RmOracleGrant() + grant_grant_any_role: RmOracleGrant = RmOracleGrant() + grant_grant_any_object_privilege: RmOracleGrant = RmOracleGrant() + grant_unlimited_tablespace: RmOracleGrant = RmOracleGrant() + grant_manage_tablespace: RmOracleGrant = RmOracleGrant() + grant_audit_any: RmOracleGrant = RmOracleGrant() + grant_analyze_any: RmOracleGrant = RmOracleGrant() + grant_comment_any_table: RmOracleGrant = RmOracleGrant() + grant_flashback_any_table: RmOracleGrant = RmOracleGrant() + grant_debug_any_procedure: RmOracleGrant = RmOracleGrant() + grant_administer_database_trigger: RmOracleGrant = RmOracleGrant() + + # OBJECT + grant_select: List[RmOracleGrantObject] = [] + grant_insert: List[RmOracleGrantObject] = [] + grant_update: List[RmOracleGrantObject] = [] + grant_delete: List[RmOracleGrantObject] = [] + grant_alter: List[RmOracleGrantObject] = [] + grant_index: List[RmOracleGrantObject] = [] + grant_references: List[RmOracleGrantObject] = [] + grant_execute: List[RmOracleGrantObject] = [] + grant_read: List[RmOracleGrantObject] = [] + grant_write: List[RmOracleGrantObject] = [] + grant_debug: List[RmOracleGrantObject] = [] + grant_flashback: List[RmOracleGrantObject] = [] + grant_on_commit_refresh: List[RmOracleGrantObject] = [] + grant_query_rewrite: List[RmOracleGrantObject] = [] + grant_under: List[RmOracleGrantObject] = [] + + # PREDEFINED + grant_connect: RmOracleGrant = RmOracleGrant(allow=True) + grant_resource: RmOracleGrant = RmOracleGrant(allow=True) + grant_dba: RmOracleGrant = RmOracleGrant() + grant_select_catalog_role: RmOracleGrant = RmOracleGrant() + grant_execute_catalog_role: RmOracleGrant = RmOracleGrant() + grant_delete_catalog_role: RmOracleGrant = RmOracleGrant() + grant_exp_full_database: RmOracleGrant = RmOracleGrant() + grant_imp_full_database: RmOracleGrant = RmOracleGrant() + grant_recovery_catalog_owner: RmOracleGrant = RmOracleGrant() + grant_scheduler_admin: RmOracleGrant = RmOracleGrant() + grant_aq_administrator_role: RmOracleGrant = RmOracleGrant() + grant_datapump_exp_full_database: RmOracleGrant = RmOracleGrant() + grant_datapump_imp_full_database: RmOracleGrant = RmOracleGrant() + roles: List[str] = [] @@ -295,6 +505,15 @@ class RmMachineUserAddMeta(RmMetaBase): private_key_passphrase: Optional[str] = None authorized_keys: List[str] = [] + # SSH certificate + # This is used if SSH, on the machine, has a CA private key + key_id: Optional[str] = None + serial: int = 0 + valid_principles: List[str] = ["keeper_jit"] + extensions: Optional[str] = None + critical_options: Optional[str] = None + expire_days: int = 30 + class RmLinuxUserAddMeta(RmMachineUserAddMeta): system_user: Optional[bool] = False @@ -379,6 +598,9 @@ class RmOpenLdapUserAddMeta(RmBaseLdapUserAddMeta): """ Parameters for creating a user for OpenLDAP + object_type: directories + engine_type: openldap + :type: directory,openldap,create,user :param object_class: Object classes for the entry. :param dn: Full distinguished name for the user. @@ -396,7 +618,11 @@ class RmOpenLdapUserAddMeta(RmBaseLdapUserAddMeta): class RmAdUserAddMeta(RmBaseLdapUserAddMeta): """ - Parameters for creating a user for Active Directory + Parameters for creating a user for Active Directory. + + object_type: directories + + engine_type: active_directory :type: directory,active_directory,create,user :param object_class: Object classes for the entry. diff --git a/keepercommander/discovery_common/rule.py b/keepercommander/discovery_common/rule.py index 328ae289f..66b3e9590 100644 --- a/keepercommander/discovery_common/rule.py +++ b/keepercommander/discovery_common/rule.py @@ -4,7 +4,7 @@ from .utils import value_to_boolean, get_connection, make_agent from ..keeper_dag import DAG, EdgeType from ..keeper_dag.exceptions import DAGException -from ..keeper_dag.types import PamGraphId +from ..keeper_dag.types import PamEndpoints, PamGraphId from time import time import base64 import os @@ -82,7 +82,17 @@ class Rules: } def __init__(self, record: Any, logger: Optional[Any] = None, debug_level: int = 0, fail_on_corrupt: bool = True, - agent: Optional[str] = None, **kwargs): + agent: Optional[str] = None, + use_per_graph_endpoints: bool = False, + **kwargs): + """ + :param use_per_graph_endpoints: If True, use the per-graph URL transport + (`/api/user/graph-sync/discovery_rules/...`) via `read_endpoint` / + `write_endpoint`. If False (default), use the legacy single-endpoint + transport via `graph_id=PamGraphId.DISCOVERY_RULES`. The legacy + default is preserved for backward compatibility; it will be removed + in a future major version once all consumers have migrated. + """ self.conn = get_connection(**kwargs) @@ -92,6 +102,7 @@ def __init__(self, record: Any, logger: Optional[Any] = None, debug_level: int self.logger = logger self.debug_level = debug_level self.fail_on_corrupt = fail_on_corrupt + self.use_per_graph_endpoints = use_per_graph_endpoints self.agent = make_agent("rules") if agent is not None: @@ -103,15 +114,25 @@ def dag(self) -> DAG: # Turn auto_save on after the DAG has been created. # No need to call it six times in a row to initialize it. - self._dag = DAG(conn=self.conn, - record=self.record, - # endpoint=PamEndpoints.DISCOVERY_RULES, - graph_id=PamGraphId.DISCOVERY_RULES, - auto_save=False, - logger=self.logger, - debug_level=self.debug_level, - fail_on_corrupt=self.fail_on_corrupt, - agent=self.agent) + if self.use_per_graph_endpoints: + self._dag = DAG(conn=self.conn, + record=self.record, + read_endpoint=PamEndpoints.DISCOVERY_RULES, + write_endpoint=PamEndpoints.DISCOVERY_RULES, + auto_save=False, + logger=self.logger, + debug_level=self.debug_level, + fail_on_corrupt=self.fail_on_corrupt, + agent=self.agent) + else: + self._dag = DAG(conn=self.conn, + record=self.record, + graph_id=PamGraphId.DISCOVERY_RULES, + auto_save=False, + logger=self.logger, + debug_level=self.debug_level, + fail_on_corrupt=self.fail_on_corrupt, + agent=self.agent) self._dag.load() # Has the status been initialized? diff --git a/keepercommander/discovery_common/types.py b/keepercommander/discovery_common/types.py index d4d17d953..ef6de0f08 100644 --- a/keepercommander/discovery_common/types.py +++ b/keepercommander/discovery_common/types.py @@ -34,13 +34,15 @@ def find_enum(cls, value: Union[Enum, str, int], default: Optional[Enum] = None) class CredentialBase(BaseModel): - # Use Any because it might be a str or Secret, but Secret is defined to discover-and_rotation. + # Use Any because it might be a str or Secret, but Secret is defined to discover-and-rotation-kdnrm. user: Optional[Any] = None dn: Optional[Any] = None password: Optional[Any] = None private_key: Optional[Any] = None + public_key: Optional[Any] = None private_key_passphrase: Optional[Any] = None database: Optional[Any] = None + mode: Optional[Any] = None class Settings(BaseModel): diff --git a/keepercommander/discovery_common/user_service.py b/keepercommander/discovery_common/user_service.py index cf58675a2..6a4358aa8 100644 --- a/keepercommander/discovery_common/user_service.py +++ b/keepercommander/discovery_common/user_service.py @@ -8,7 +8,7 @@ from .infrastructure import Infrastructure from .record_link import RecordLink from ..keeper_dag import DAG, EdgeType -from ..keeper_dag.types import PamGraphId +from ..keeper_dag.types import PamEndpoints, PamGraphId import importlib from typing import Any, Optional, List, Callable, Dict, TYPE_CHECKING @@ -22,7 +22,16 @@ class UserService: def __init__(self, record: Any, logger: Optional[Any] = None, history_level: int = 0, debug_level: int = 0, fail_on_corrupt: bool = True, log_prefix: str = "GS Services/Tasks", save_batch_count: int = 200, agent: Optional[str] = None, + use_per_graph_endpoints: bool = False, **kwargs): + """ + :param use_per_graph_endpoints: If True, use the per-graph URL transport + (`/api/user/graph-sync/service_links/...`) via `read_endpoint` / + `write_endpoint`. If False (default), use the legacy single-endpoint + transport via `graph_id=PamGraphId.SERVICE_LINKS`. The legacy + default is preserved for backward compatibility; it will be removed + in a future major version once all consumers have migrated. + """ # Keep these for other graphs self._params = kwargs.get("params") @@ -41,6 +50,7 @@ def __init__(self, record: Any, logger: Optional[Any] = None, history_level: int self.debug_level = debug_level self.fail_on_corrupt = fail_on_corrupt self.save_batch_count = save_batch_count + self.use_per_graph_endpoints = use_per_graph_endpoints self.agent = make_agent("user_service") if agent is not None: @@ -73,18 +83,33 @@ def debug(self, msg, level: int = 0, secret: bool = False): def dag(self) -> DAG: if self._dag is None: - self._dag = DAG(conn=self.conn, - record=self.record, - graph_id=PamGraphId.SERVICE_LINKS, - auto_save=False, - logger=self.logger, - history_level=self.history_level, - debug_level=self.debug_level, - name="Discovery Services", - fail_on_corrupt=self.fail_on_corrupt, - log_prefix=self.log_prefix, - save_batch_count=self.save_batch_count, - agent=self.agent) + if self.use_per_graph_endpoints: + self._dag = DAG(conn=self.conn, + record=self.record, + read_endpoint=PamEndpoints.SERVICE_LINKS, + write_endpoint=PamEndpoints.SERVICE_LINKS, + auto_save=False, + logger=self.logger, + history_level=self.history_level, + debug_level=self.debug_level, + name="Discovery Services", + fail_on_corrupt=self.fail_on_corrupt, + log_prefix=self.log_prefix, + save_batch_count=self.save_batch_count, + agent=self.agent) + else: + self._dag = DAG(conn=self.conn, + record=self.record, + graph_id=PamGraphId.SERVICE_LINKS, + auto_save=False, + logger=self.logger, + history_level=self.history_level, + debug_level=self.debug_level, + name="Discovery Services", + fail_on_corrupt=self.fail_on_corrupt, + log_prefix=self.log_prefix, + save_batch_count=self.save_batch_count, + agent=self.agent) self._dag.load(sync_point=0) diff --git a/keepercommander/discovery_common/utils.py b/keepercommander/discovery_common/utils.py index 5d8b06d0d..7bbe2b5da 100644 --- a/keepercommander/discovery_common/utils.py +++ b/keepercommander/discovery_common/utils.py @@ -43,8 +43,10 @@ def get_connection(**kwargs): from ..keeper_dag.connection.local import Connection conn = Connection(logger=logger) else: - use_read_protobuf = kwargs.get("use_read_protobuf") - use_write_protobuf = kwargs.get("use_write_protobuf") + # New per-graph endpoints are protobuf-only; default both flags to True so reads on + # /api/user/graph-sync// don't fall back to JSON (which the new routes refuse). + use_read_protobuf = kwargs.get("use_read_protobuf", True) + use_write_protobuf = kwargs.get("use_write_protobuf", True) if ksm is not None: from ..keeper_dag.connection.ksm import Connection diff --git a/keepercommander/email_service.py b/keepercommander/email_service.py index e45f99acc..a9f456641 100644 --- a/keepercommander/email_service.py +++ b/keepercommander/email_service.py @@ -19,6 +19,7 @@ """ from __future__ import annotations +import importlib import logging import os import smtplib @@ -88,7 +89,7 @@ def check_provider_dependencies(provider: str) -> tuple: # Check for required packages on pip/source installations if provider == 'ses': try: - import boto3 + importlib.import_module('boto3') return (True, '') except ImportError: return ( @@ -102,7 +103,7 @@ def check_provider_dependencies(provider: str) -> tuple: elif provider == 'sendgrid': try: - import sendgrid + importlib.import_module('sendgrid') return (True, '') except ImportError: return ( @@ -116,8 +117,8 @@ def check_provider_dependencies(provider: str) -> tuple: elif provider == 'gmail-oauth': try: - import google.auth - import googleapiclient + importlib.import_module('google.auth') + importlib.import_module('googleapiclient') return (True, '') except ImportError: return ( @@ -131,7 +132,7 @@ def check_provider_dependencies(provider: str) -> tuple: elif provider == 'microsoft-oauth': try: - import msal + importlib.import_module('msal') return (True, '') except ImportError: return ( @@ -509,7 +510,7 @@ def test_connection(self) -> bool: try: # SendGrid doesn't have a dedicated test endpoint # We can verify the API key format and try to initialize the client - sg = self.SendGridAPIClient(self.config.sendgrid_api_key) + self.SendGridAPIClient(self.config.sendgrid_api_key) # If we get here without exception, API key format is valid # Note: This doesn't guarantee the key is active, but it's the best we can do @@ -667,7 +668,7 @@ def __init__(self, config: EmailConfig): def _load_credentials(self): """Load OAuth credentials from config.""" - from datetime import datetime, timezone + from datetime import datetime if not self.config.oauth_access_token: raise ValueError("Gmail OAuth access token is required") @@ -1152,7 +1153,7 @@ def validate_email_provider_dependencies(provider: str) -> tuple[bool, Optional[ # SendGrid if provider == 'sendgrid': try: - import sendgrid # noqa: F401 + importlib.import_module('sendgrid') return True, None except ImportError: return False, ( @@ -1164,7 +1165,7 @@ def validate_email_provider_dependencies(provider: str) -> tuple[bool, Optional[ # AWS SES if provider == 'ses': try: - import boto3 # noqa: F401 + importlib.import_module('boto3') return True, None except ImportError: return False, ( @@ -1176,10 +1177,10 @@ def validate_email_provider_dependencies(provider: str) -> tuple[bool, Optional[ # Gmail OAuth if provider == 'gmail-oauth': try: - import google.auth # noqa: F401 - import google.auth.transport.requests # noqa: F401 - import google.oauth2.credentials # noqa: F401 - import googleapiclient.discovery # noqa: F401 + importlib.import_module('google.auth') + importlib.import_module('google.auth.transport.requests') + importlib.import_module('google.oauth2.credentials') + importlib.import_module('googleapiclient.discovery') return True, None except ImportError: return False, ( @@ -1191,7 +1192,7 @@ def validate_email_provider_dependencies(provider: str) -> tuple[bool, Optional[ # Microsoft OAuth if provider == 'microsoft-oauth': try: - import msal # noqa: F401 + importlib.import_module('msal') return True, None except ImportError: return False, ( diff --git a/keepercommander/keeper_dag/__version__.py b/keepercommander/keeper_dag/__version__.py index a98f9837e..874042f3b 100644 --- a/keepercommander/keeper_dag/__version__.py +++ b/keepercommander/keeper_dag/__version__.py @@ -1 +1 @@ -__version__ = '1.1.9' # pragma: no cover +__version__ = '1.1.10' # pragma: no cover diff --git a/keepercommander/keeper_dag/connection/__init__.py b/keepercommander/keeper_dag/connection/__init__.py index b58470296..30388fd58 100644 --- a/keepercommander/keeper_dag/connection/__init__.py +++ b/keepercommander/keeper_dag/connection/__init__.py @@ -8,10 +8,9 @@ from ..crypto import encrypt_aes, decrypt_aes import csv import os -import sys import time +import sys from enum import Enum -from ...constants import get_keeper_server_hostname from pydantic import BaseModel from typing import Optional, Union, Any, Dict, Tuple, TYPE_CHECKING if TYPE_CHECKING: # pragma: no cover @@ -32,6 +31,8 @@ class ConnectionBase: ADD_DATA = "/add_data" SYNC = "/sync" + MULTI_SYNC = "/multi_sync" + GET_LEAFS = "/get_leafs" TIMEOUT = 30 @@ -100,7 +101,10 @@ def get_encrypted_payload_data(encrypted_payload_data: bytes) -> bytes: @staticmethod def get_router_host(server_hostname: str): - server_hostname = get_keeper_server_hostname(server_hostname) + # Defensive: accept URL-formatted inputs (e.g. "https://keepersecurity.com") + # and extract the bare hostname before the GovCloud subdomain check. + if server_hostname and '://' in server_hostname: + server_hostname = server_hostname.split('://', 1)[1].split('/', 1)[0] # Only PROD GovCloud strips the subdomain (workaround for prod infrastructure). # DEV/QA GOV (govcloud.dev.keepersecurity.us, govcloud.qa.keepersecurity.us) keep govcloud. @@ -332,3 +336,135 @@ def add_data(self, error=str(err) ) raise DAGException(f"Could not create a new DAG structure: {err}") + + def multi_sync(self, + multi_query: Union[BaseModel, gs_pb2.GraphSyncMultiQuery], + graph_id: Optional[int] = None, + endpoint: Optional[str] = None, + agent: Optional[str] = None) -> bytes: + """POST a GraphSyncMultiQuery to /multi_sync. + + Used by per-graph reads: after `get_leafs` discovers the stream refs + rooted at the graph's origin, `multi_sync` fetches sync data for all + those streams in one round-trip. Mirrors `sync()` in transport shape + (encrypt/headers, decrypt-on-read, transaction log, error handling). + """ + if agent is None: + agent = f"keeper-dag/{__version__}" + + endpoint = self._endpoint(ConnectionBase.MULTI_SYNC, endpoint) + self.logger.debug(f"endpoint {endpoint}") + + try: + multi_query, headers = self.payload_and_headers(multi_query) + payload = self.rest_call_to_router(http_method="POST", + endpoint=endpoint, + agent=agent, + headers=headers, + payload=multi_query) + + if self.use_read_protobuf: + try: + self.logger.debug(f"decrypt payload with transmission key {kotlin_bytes(self.transmission_key)}") + payload = self.get_encrypted_payload_data(payload) + payload = decrypt_aes(payload, self.transmission_key) + except Exception as err: + self.logger.error(f"Could not decrypt protobuf graph multi-sync response: {type(err)}, {err}") + + self.write_transaction_log( + graph_id=graph_id, + request=multi_query, + response=payload, + agent=agent, + endpoint=endpoint, + error=None + ) + + return payload + + except DAGConnectionException as err: + self.write_transaction_log( + graph_id=graph_id, + request=multi_query, + response=None, + agent=agent, + endpoint=endpoint, + error=str(err) + ) + raise err + except Exception as err: + self.write_transaction_log( + graph_id=graph_id, + request=multi_query, + response=None, + agent=agent, + endpoint=endpoint, + error=str(err) + ) + raise DAGException(f"Could not load the DAG structure (multi_sync): {err}") + + def get_leafs(self, + leafs_query: Union[BaseModel, gs_pb2.GraphSyncLeafsQuery], + graph_id: Optional[int] = None, + endpoint: Optional[str] = None, + agent: Optional[str] = None) -> bytes: + """POST a GraphSyncLeafsQuery to /get_leafs. + + Returns the serialized GraphSyncRefsResult — the list of stream refs + rooted at the queried vertices. Used as the discovery step before a + `multi_sync` call (per the per-graph read pattern that Web Vault + already uses). + """ + if agent is None: + agent = f"keeper-dag/{__version__}" + + endpoint = self._endpoint(ConnectionBase.GET_LEAFS, endpoint) + self.logger.debug(f"endpoint {endpoint}") + + try: + leafs_query, headers = self.payload_and_headers(leafs_query) + payload = self.rest_call_to_router(http_method="POST", + endpoint=endpoint, + agent=agent, + headers=headers, + payload=leafs_query) + + if self.use_read_protobuf: + try: + self.logger.debug(f"decrypt payload with transmission key {kotlin_bytes(self.transmission_key)}") + payload = self.get_encrypted_payload_data(payload) + payload = decrypt_aes(payload, self.transmission_key) + except Exception as err: + self.logger.error(f"Could not decrypt protobuf get_leafs response: {type(err)}, {err}") + + self.write_transaction_log( + graph_id=graph_id, + request=leafs_query, + response=payload, + agent=agent, + endpoint=endpoint, + error=None + ) + + return payload + + except DAGConnectionException as err: + self.write_transaction_log( + graph_id=graph_id, + request=leafs_query, + response=None, + agent=agent, + endpoint=endpoint, + error=str(err) + ) + raise err + except Exception as err: + self.write_transaction_log( + graph_id=graph_id, + request=leafs_query, + response=None, + agent=agent, + endpoint=endpoint, + error=str(err) + ) + raise DAGException(f"Could not get leafs: {err}") diff --git a/keepercommander/keeper_dag/dag.py b/keepercommander/keeper_dag/dag.py index 81a75339c..2f928e7c7 100644 --- a/keepercommander/keeper_dag/dag.py +++ b/keepercommander/keeper_dag/dag.py @@ -15,7 +15,7 @@ import importlib import traceback import sys -from typing import Optional, Union, List, Any, Tuple, TYPE_CHECKING +from typing import Optional, Union, List, Any, Tuple, Dict, TYPE_CHECKING if TYPE_CHECKING: from .connection import ConnectionBase @@ -93,7 +93,12 @@ def __init__(self, if logger is None: logger = logging.getLogger() self.logger = logger - self.debug_level = int(os.environ.get("GS_DEBUG_LEVEL", os.environ.get("DAG_DEBUG_LEVEL", debug_level))) + self.debug_level = os.environ.get("GS_DEBUG_LEVEL", os.environ.get("DAG_DEBUG_LEVEL", debug_level)) + try: + self.debug_level = int(self.debug_level) + except (Exception,): + self.debug_level = 0 + # Prevent duplicate edges to be added. # The goal is to prevent unneeded edges. @@ -302,12 +307,12 @@ def __str__(self): ret += f" python instance id: {id(self)}\n" ret += f" name: {self.name}\n" ret += f" key: {self.key}\n" - ret += f" vertices:\n" + ret += " vertices:\n" for v in self.all_vertices: ret += f" * {v.uid}, Keys: {v.keychain}, Active: {v.active}\n" for e in v.edges: if e.edge_type == EdgeType.DATA: - ret += f" + has a DATA edge" + ret += " + has a DATA edge" if e.content is not None: ret += ", has content" else: @@ -479,6 +484,23 @@ def get_vertices_by_path_value(self, path: str, inc_deleted: bool = False) -> Li return results def _sync(self, sync_point: int = 0) -> Tuple[List[DAGData], int]: + """Dispatch to legacy single-stream sync or per-graph multi-stream sync. + + When `read_endpoint` is set, the server uses the per-graph URL pattern + (`/api/user/graph-sync//...`). That model splits the graph across + multiple streams, so a single-stream `sync` returns only a fragment. + Web Vault uses `get_leafs` -> `multi_sync` to read the full graph; + this client follows the same pattern. + + When only `graph_id` is set (legacy single-endpoint transport), the + single-stream sync remains correct. + """ + if self.read_endpoint is not None: + return self._sync_per_graph(sync_point) + return self._sync_legacy(sync_point) + + def _sync_legacy(self, sync_point: int = 0) -> Tuple[List[DAGData], int]: + """Single-stream sync against the legacy `/sync` endpoint.""" # The web service will send 500 items, if there is more the 'has_more' flag is set to True. has_more = True @@ -513,6 +535,61 @@ def _sync(self, sync_point: int = 0) -> Tuple[List[DAGData], int]: return all_data, sync_point + def _sync_per_graph(self, sync_point: int = 0) -> Tuple[List[DAGData], int]: + """Multi-stream read against the per-graph endpoints. + + The graph's data lives in a single stream keyed by the graph's origin + (e.g. the PAM Configuration record's UID for TunnelDAG). We multi_sync + that stream directly — no `get_leafs` discovery step needed for this + caller pattern. (`Connection.get_leafs` remains available for callers + that start from leaf vertices and need to discover stream roots.) + + Returns aggregated (data, max_sync_point) just like `_sync_legacy`. + """ + + origin_bytes = urlsafe_str_to_bytes(self.uid) + + # Stream keyed by the graph's origin (e.g. config_uid for PAM linking). + per_stream_sync_point: Dict[bytes, int] = {origin_bytes: sync_point} + all_data: List[DAGData] = [] + max_sync_point = sync_point + + while per_stream_sync_point: + stream_ids = list(per_stream_sync_point.keys()) + multi_query = self.read_struct_obj.multi_sync_query( + stream_ids=stream_ids, + origin=origin_bytes, + sync_point=sync_point, + ) + # Per-stream syncPoint adjustment so each stream advances + # independently across pagination rounds (proto variant only; + # JSON variant builds via SyncQuery which already carries syncPoint). + try: + for inner, sid in zip(multi_query.queries, stream_ids): + inner.syncPoint = per_stream_sync_point[sid] + except Exception: # pragma: no cover - JSON variant has no .queries + pass + + multi_response = self.conn.multi_sync( + multi_query=multi_query, + graph_id=self.graph_id, + endpoint=self.read_endpoint, + agent=self.agent, + ) + multi_results = self.read_struct_obj.get_multi_sync_result(multi_response) + + next_per_stream: Dict[bytes, int] = {} + for result in multi_results: + all_data += result.data + if result.syncPoint and result.syncPoint > max_sync_point: + max_sync_point = result.syncPoint + if result.hasMore and result.streamId is not None: + next_per_stream[bytes(result.streamId)] = result.syncPoint + + per_stream_sync_point = next_per_stream + + return all_data, max_sync_point + def _load(self, sync_point: int = 0): """ @@ -689,7 +766,7 @@ def _mark_deletion(self): # If the vertex belongs to no vertex, and it not the root, then flag it for deletion. if found_edge_to_another_vertex is False and vertex.uid != self.uid: - self.debug(f" * vertex is deleted", level=3) + self.debug(" * vertex is deleted", level=3) vertex.active = False self.debug("", level=1) @@ -746,7 +823,7 @@ def _get_keychain(v): found_key_edge = True break except (Exception,): - self.debug(f" !! this is not the key", level=3) + self.debug(" !! this is not the key", level=3) if not was_able_to_decrypt: @@ -820,7 +897,7 @@ def _decrypt_data(self): self.debug(f" * content {edge.content}", level=3) break except (Exception,): - self.debug(f" !! this is not the key", level=3) + self.debug(" !! this is not the key", level=3) if not able_to_decrypt: @@ -831,7 +908,7 @@ def _decrypt_data(self): edge.content = content edge.needs_encryption = False - self.debug(f" * edge is not encrypted or key is incorrect.", level=3) + self.debug(" * edge is not encrypted or key is incorrect.", level=3) # Change the flag indicating that the content is in decrypted state. edge.is_encrypted = False @@ -909,7 +986,7 @@ def _flag(v: DAGVertex): self.debug(f"check vertex {v.uid}", level=3) if v.uid == self.uid: - self.debug(f" FOUND ROOT", level=3) + self.debug(" FOUND ROOT", level=3) return True # Check if we have any of these edges in this order. @@ -929,7 +1006,7 @@ def _flag(v: DAGVertex): version, highest_edge = v.get_highest_edge_version(next_vertex.uid) is_deletion = highest_edge.edge_type == EdgeType.DELETION if is_deletion: - self.debug(f" highest deletion edge. will not mark any edges as modified", + self.debug(" highest deletion edge. will not mark any edges as modified", level=3) found_path = _flag(next_vertex) @@ -1020,7 +1097,7 @@ def _add_data(vertex): # If this edge is not modified, don't add to the data list to save. if not edge.modified: - self.debug(f" not modified, not saving.", level=3) + self.debug(" not modified, not saving.", level=3) continue content = edge.content @@ -1043,7 +1120,7 @@ def _add_data(vertex): self.debug(f" enc safe content {content}", level=3) elif edge.edge_type == EdgeType.KEY: - self.debug(f" edge is key or acl, encrypt key", level=3) + self.debug(" edge is key or acl, encrypt key", level=3) head_vertex = self.get_vertex(edge.head_uid) key = head_vertex.key if key is None: diff --git a/keepercommander/keeper_dag/struct/__init__.py b/keepercommander/keeper_dag/struct/__init__.py index 53aa771da..ac87baa7b 100644 --- a/keepercommander/keeper_dag/struct/__init__.py +++ b/keepercommander/keeper_dag/struct/__init__.py @@ -54,3 +54,34 @@ def payload(origin_ref: Union[Ref, gs_pb2.GraphSyncRef], graph_id: Optional[int] = None) -> Union[DataPayload, gs_pb2.GraphSyncAddDataRequest]: pass + + # --- Per-graph multi-stream read transport --------------------------- + # Used by DAG._sync_per_graph when read_endpoint is set. Two-step pattern: + # 1. leafs_query(...) -> get_leafs_result(...) discovers stream refs. + # 2. multi_sync_query(...) -> get_multi_sync_result(...) fetches data. + + def leafs_query(self, + vertices: List[str]) -> Union[BaseModel, gs_pb2.GraphSyncLeafsQuery]: + """Build a GraphSyncLeafsQuery from a list of vertex UIDs (URL-safe str).""" + pass + + @staticmethod + def get_leafs_result(results: bytes) -> List[Ref]: + """Parse GraphSyncRefsResult bytes into a list of Ref objects. + Each Ref's `value` is the stream UID rooted under the queried vertex. + """ + pass + + def multi_sync_query(self, + stream_ids: List[bytes], + origin: bytes, + sync_point: int = 0) -> Union[BaseModel, gs_pb2.GraphSyncMultiQuery]: + """Build a GraphSyncMultiQuery wrapping one GraphSyncQuery per stream.""" + pass + + @staticmethod + def get_multi_sync_result(results: bytes): # -> List[SyncData] + """Parse GraphSyncMultiResult bytes into a list of SyncData, one per + inner GraphSyncResult (each carrying its own streamId/syncPoint/hasMore). + """ + pass diff --git a/keepercommander/keeper_dag/struct/default.py b/keepercommander/keeper_dag/struct/default.py index a58558bff..dd23a6256 100644 --- a/keepercommander/keeper_dag/struct/default.py +++ b/keepercommander/keeper_dag/struct/default.py @@ -1,8 +1,10 @@ from __future__ import annotations +import json from . import DataStructBase from ..types import SyncQuery, Ref, RefType, DAGData, DataPayload, EdgeType, SyncData -from ..crypto import generate_random_bytes, generate_uid_str, bytes_to_str +from ..crypto import generate_random_bytes, generate_uid_str, bytes_to_str, bytes_to_urlsafe_str import base64 +from pydantic import BaseModel from typing import Optional, List @@ -79,3 +81,62 @@ def payload(origin_ref: Ref, dataList=data_list, graphId=graph_id ) + + # --- Per-graph multi-stream read transport --------------------------- + + class _LeafsQuery(BaseModel): + vertices: List[str] + + class _MultiSyncQuery(BaseModel): + queries: List[SyncQuery] + + def leafs_query(self, vertices: List[str]) -> 'DataStruct._LeafsQuery': + return DataStruct._LeafsQuery(vertices=list(vertices)) + + @staticmethod + def get_leafs_result(results: bytes) -> List[Ref]: + try: + obj = json.loads(results) + except Exception as err: + raise Exception(f"Could not parse the leafs JSON result: {err}") + refs_list = obj.get("refs", []) if isinstance(obj, dict) else obj + out: List[Ref] = [] + for r in refs_list: + # Server may return either {type, value, name} or just a value str. + if isinstance(r, dict): + value = r.get("value") + if isinstance(value, bytes): + value = bytes_to_urlsafe_str(value) + out.append(Ref( + type=RefType(r["type"]) if r.get("type") is not None else RefType.GENERAL, + value=value, + name=r.get("name") or None, + )) + return out + + def multi_sync_query(self, + stream_ids: List[bytes], + origin: bytes, + sync_point: int = 0) -> 'DataStruct._MultiSyncQuery': + queries = [ + SyncQuery( + streamId=bytes_to_urlsafe_str(sid), + deviceId=bytes_to_urlsafe_str(origin), + syncPoint=sync_point, + graphId=None, + ) + for sid in stream_ids + ] + return DataStruct._MultiSyncQuery(queries=queries) + + @staticmethod + def get_multi_sync_result(results: bytes) -> List[SyncData]: + try: + obj = json.loads(results) + except Exception as err: + raise Exception(f"Could not parse the multi_sync JSON result: {err}") + items = obj.get("results", []) if isinstance(obj, dict) else obj + out: List[SyncData] = [] + for item in items: + out.append(SyncData.model_validate(item)) + return out diff --git a/keepercommander/keeper_dag/struct/protobuf.py b/keepercommander/keeper_dag/struct/protobuf.py index fcd123d5d..7a449f918 100644 --- a/keepercommander/keeper_dag/struct/protobuf.py +++ b/keepercommander/keeper_dag/struct/protobuf.py @@ -58,23 +58,16 @@ def sync_query(self, ) @staticmethod - def get_sync_result(results: bytes) -> SyncData: - - try: - result = gs_pb2.GraphSyncResult() - result.ParseFromString(results) - except Exception as err: - raise Exception(f"Could not parse the GraphSyncResult message: {err}") - - message = gs_pb2.GraphSyncResult() - message.ParseFromString(results) - + def _sync_data_from_result(message: gs_pb2.GraphSyncResult) -> SyncData: + """Convert a single GraphSyncResult protobuf into a SyncData pydantic + model. Extracted so both single-`sync` and multi_sync code paths share + identical per-result decoding. + """ data_list: List[SyncDataItem] = [] for item in message.data: data_list.append( SyncDataItem( type=DataStruct.PB_TO_DATA_MAP.get(item.data.type), - # content=bytes_to_str(item.data.content), content=item.data.content, content_is_base64=False, ref=Ref( @@ -92,9 +85,21 @@ def get_sync_result(results: bytes) -> SyncData: return SyncData( syncPoint=message.syncPoint, data=data_list, - hasMore=message.hasMore + hasMore=message.hasMore, + streamId=bytes(message.streamId) if message.streamId else None, ) + @staticmethod + def get_sync_result(results: bytes) -> SyncData: + + try: + message = gs_pb2.GraphSyncResult() + message.ParseFromString(results) + except Exception as err: + raise Exception(f"Could not parse the GraphSyncResult message: {err}") + + return DataStruct._sync_data_from_result(message) + @staticmethod def origin_ref(origin_ref_value: bytes, name: str) -> gs_pb2.GraphSyncRef: @@ -149,3 +154,49 @@ def payload(origin_ref: gs_pb2.GraphSyncRef, return gs_pb2.GraphSyncAddDataRequest( origin=origin_ref, data=data_list) + + # --- Per-graph multi-stream read transport --------------------------- + + def leafs_query(self, vertices: List[str]) -> gs_pb2.GraphSyncLeafsQuery: + return gs_pb2.GraphSyncLeafsQuery( + vertices=[urlsafe_str_to_bytes(v) for v in vertices] + ) + + @staticmethod + def get_leafs_result(results: bytes) -> List[Ref]: + msg = gs_pb2.GraphSyncRefsResult() + try: + msg.ParseFromString(results) + except Exception as err: + raise Exception(f"Could not parse the GraphSyncRefsResult message: {err}") + return [ + Ref( + type=DataStruct.PB_TO_REF_MAP.get(r.type), + value=bytes_to_urlsafe_str(r.value), + name=r.name or None, + ) + for r in msg.refs + ] + + def multi_sync_query(self, + stream_ids: List[bytes], + origin: bytes, + sync_point: int = 0) -> gs_pb2.GraphSyncMultiQuery: + return gs_pb2.GraphSyncMultiQuery(queries=[ + gs_pb2.GraphSyncQuery( + streamId=sid, + origin=origin, + syncPoint=sync_point, + maxCount=0, # let krouter default (currently 500) + ) + for sid in stream_ids + ]) + + @staticmethod + def get_multi_sync_result(results: bytes) -> List[SyncData]: + msg = gs_pb2.GraphSyncMultiResult() + try: + msg.ParseFromString(results) + except Exception as err: + raise Exception(f"Could not parse the GraphSyncMultiResult message: {err}") + return [DataStruct._sync_data_from_result(r) for r in msg.results] diff --git a/keepercommander/keeper_dag/types.py b/keepercommander/keeper_dag/types.py index 26f372717..8ec784c47 100644 --- a/keepercommander/keeper_dag/types.py +++ b/keepercommander/keeper_dag/types.py @@ -115,6 +115,17 @@ class PamEndpoints(BaseEnum): } +# Inverse map for callers that have a graph_id int and need the PamEndpoints enum +# to address the new /api/user/graph-sync// routes. +GRAPH_ID_TO_ENDPOINT = { + PamGraphId.PAM.value: PamEndpoints.PAM, + PamGraphId.DISCOVERY_RULES.value: PamEndpoints.DISCOVERY_RULES, + PamGraphId.DISCOVERY_JOBS.value: PamEndpoints.DISCOVERY_JOBS, + PamGraphId.INFRASTRUCTURE.value: PamEndpoints.INFRASTRUCTURE, + PamGraphId.SERVICE_LINKS.value: PamEndpoints.SERVICE_LINKS, +} + + class SyncQuery(BaseModel): streamId: Optional[str] = None # base64 of a user's ID who is syncing. deviceId: Optional[str] = None @@ -125,7 +136,10 @@ class SyncQuery(BaseModel): class SyncDataItem(BaseModel): ref: Ref parentRef: Optional[Ref] = None - content: Optional[str] = None + # Either a base64-encoded string (JSON wire format) or raw bytes + # (protobuf wire format). `content_is_base64` distinguishes them so the + # consumer can decode appropriately. + content: Optional[Union[str, bytes]] = None content_is_base64: bool = True type: Optional[str] = None path: Optional[str] = None @@ -136,6 +150,9 @@ class SyncData(BaseModel): syncPoint: int data: List[SyncDataItem] hasMore: bool + # Per-graph multi_sync: identifies which stream this result came from. + # None for single-stream `sync` results (backward compatible). + streamId: Optional[bytes] = None class Ref(BaseModel): diff --git a/pytest.ini b/pytest.ini index b3353c33f..16d2950da 100644 --- a/pytest.ini +++ b/pytest.ini @@ -8,3 +8,5 @@ markers = keeper_imports: smoke test to make sure all packages and modules in keeper can be imported integration: tests using internal dev.keepersecurity.com test accounts stored in config.json cross_enterprise: created to test a specific issue with cross-enterprise and not normally run + unit: credential-provision unit tests (mocked, no live server) + e2e: end-to-end credential-provision tests (run manually with pytest -m e2e) diff --git a/tests/test_credential_provision_execute.py b/tests/test_credential_provision_execute.py index c1231ad71..83dfc69a5 100644 --- a/tests/test_credential_provision_execute.py +++ b/tests/test_credential_provision_execute.py @@ -13,10 +13,9 @@ - logging.info line when existing_password is used (no value in log) """ -import logging import pytest from unittest import TestCase -from unittest.mock import MagicMock, patch, call +from unittest.mock import MagicMock, patch from keepercommander.commands.credential_provision import ( CredentialProvisionCommand, @@ -356,7 +355,7 @@ def test_group_add_call_site_appears_after_create_pam_user(self): import os, re repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) src_path = os.path.join(repo_root, self.SOURCE_FILE) - with open(src_path, 'r') as f: + with open(src_path, 'r', encoding='utf-8') as f: source = f.read() # Find the line number of the execute()-level _create_pam_user call. @@ -392,7 +391,7 @@ def test_add_ad_user_to_groups_uses_local_gateway_uid(self): import os, re repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) src_path = os.path.join(repo_root, self.SOURCE_FILE) - with open(src_path, 'r') as f: + with open(src_path, 'r', encoding='utf-8') as f: source = f.read() # Find any call to _add_ad_user_to_groups_via_gateway pattern = re.compile( @@ -420,7 +419,7 @@ def test_rotation_configured_log_gated_on_real_success(self): import os repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) src_path = os.path.join(repo_root, self.SOURCE_FILE) - with open(src_path, 'r') as f: + with open(src_path, 'r', encoding='utf-8') as f: source = f.read() idx = source.find("'✅ Rotation configured'") self.assertGreater(idx, 0, "Could not locate the rotation-configured log line") @@ -443,7 +442,7 @@ def test_rotation_success_log_gated_on_rotation_success(self): import os repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) src_path = os.path.join(repo_root, self.SOURCE_FILE) - with open(src_path, 'r') as f: + with open(src_path, 'r', encoding='utf-8') as f: source = f.read() idx = source.find("'✅ Password rotation submitted'") self.assertGreater(idx, 0, "Could not locate the success-log line") @@ -460,7 +459,7 @@ def test_no_log_statement_references_existing_password_value(self): repo_root = os.path.abspath(os.path.join( os.path.dirname(__file__), '..')) src_path = os.path.join(repo_root, self.SOURCE_FILE) - with open(src_path, 'r') as f: + with open(src_path, 'r', encoding='utf-8') as f: source = f.read() # Look for patterns like logging.X(f"... {existing_password} ...") # where existing_password is the VALUE, not the field name. diff --git a/tests/test_dag_integration.py b/tests/test_dag_integration.py new file mode 100644 index 000000000..720eebb8a --- /dev/null +++ b/tests/test_dag_integration.py @@ -0,0 +1,175 @@ +""" +Layer-A / Layer-B integration tests (plan §13.8 T1 / task #30). + +These tests hit a real dev krouter and require a logged-in `KeeperParams` from +`tests/dag_integration.json` (same JSON shape used by `tests/data_config.py`). +Gated by `@pytest.mark.integration` so they don't run as part of the default +CI matrix (`pytest unit-tests/`). + +Manual run: + cd + -m pytest -m integration tests/test_dag_integration.py -v + +If `dag_integration.json` is missing, the whole class skips cleanly. + +Scope: +- Layer A: one sync round-trip per graph (5 graphs × 1 verb = 5 cases). Validates + URL routing + auth + protobuf serialization end-to-end against the real server. +- Layer A: one get_leafs smoke (consolidation case). +- Layer B: configure_resource shape validation (1 happy-path no-op, 1 permission-denied). +- Layer B: set_record_rotation validation paths (sub-1h rejected, invalid record rejected). + +Total: ~10 cases. +""" +import os +from unittest import TestCase, skipUnless + +import pytest + +from data_config import read_config_file +from keepercommander import api +from keepercommander.params import KeeperParams + + +_TESTS_DIR = os.path.dirname(os.path.abspath(__file__)) +_DAG_CONFIG = os.path.join(_TESTS_DIR, 'dag_integration.json') + + +@pytest.mark.integration +@skipUnless( + os.path.isfile(_DAG_CONFIG), + 'tests/dag_integration.json not found (integration credentials; create one with ' + 'server/user/private_key/device_token/clone_code/password fields to enable)', +) +class TestDagIntegration(TestCase): + """Real-server integration tests for the DAG / graph-sync + Layer-B endpoints.""" + + params: KeeperParams = None # type: ignore[assignment] + + @classmethod + def setUpClass(cls): + cls.params = KeeperParams() + read_config_file(cls.params, 'dag_integration.json') + api.login(cls.params) + + # ----------------------------------------------------------------------- # + # Layer A — one sync round-trip per graph # + # ----------------------------------------------------------------------- # + + def _do_sync_smoke(self, endpoint): + """Send an empty sync to the given PamEndpoints; expect a clean 200 (proto).""" + from keepercommander.keeper_dag.connection.commander import Connection + from keepercommander.keeper_dag.proto import GraphSync_pb2 as gs_pb2 + + conn = Connection(params=self.params) + query = gs_pb2.GraphSyncQuery( + streamId=b'\x00' * 16, # nonexistent stream — server returns empty + origin=b'\x00' * 16, + syncPoint=0, + ) + result = conn.sync(query, endpoint=endpoint) + # Should return bytes (encrypted protobuf) without raising. Content may be empty. + self.assertIsNotNone(result) + + def test_sync_pam_graph(self): + from keepercommander.keeper_dag.types import PamEndpoints + self._do_sync_smoke(PamEndpoints.PAM) + + def test_sync_discovery_rules_graph(self): + from keepercommander.keeper_dag.types import PamEndpoints + self._do_sync_smoke(PamEndpoints.DISCOVERY_RULES) + + def test_sync_discovery_jobs_graph(self): + from keepercommander.keeper_dag.types import PamEndpoints + self._do_sync_smoke(PamEndpoints.DISCOVERY_JOBS) + + def test_sync_infrastructure_graph(self): + from keepercommander.keeper_dag.types import PamEndpoints + self._do_sync_smoke(PamEndpoints.INFRASTRUCTURE) + + def test_sync_service_links_graph(self): + from keepercommander.keeper_dag.types import PamEndpoints + self._do_sync_smoke(PamEndpoints.SERVICE_LINKS) + + # ----------------------------------------------------------------------- # + # Layer A — get_leafs consolidation # + # ----------------------------------------------------------------------- # + + def test_get_leafs_empty_vertices_list(self): + """get_leafs with empty vertices list returns a clean empty result (consolidation case).""" + from keepercommander.keeper_dag.connection.commander import Connection + from keepercommander.keeper_dag.proto import GraphSync_pb2 as gs_pb2 + from keepercommander.keeper_dag.types import PamEndpoints + + conn = Connection(params=self.params) + query = gs_pb2.GraphSyncLeafsQuery(vertices=[]) + result = conn.get_leafs(query, endpoint=PamEndpoints.PAM) + self.assertIsNotNone(result) + + # ----------------------------------------------------------------------- # + # Layer B — configure_resource # + # ----------------------------------------------------------------------- # + + def test_configure_resource_unauthorized_record_returns_not_allowed(self): + """Sending configure_resource with a recordUid we don't own should fail with RRC_NOT_ALLOWED.""" + from keepercommander.commands.pam._layer_b import RouterResponseError + from keepercommander.commands.pam.router_helper import router_configure_resource + from keepercommander.proto import pam_pb2 + + rq = pam_pb2.PAMResourceConfig( + recordUid=b'\x00' * 16, # nonexistent / unauthorized + networkUid=b'\x00' * 16, + adminUid=b'\x00' * 16, + ) + with self.assertRaises(Exception) as cm: + router_configure_resource(self.params, rq) + # The exact code depends on krouter's validation order; we expect a permission + # or bad-request style failure, not a generic 500. + err = cm.exception + if isinstance(err, RouterResponseError): + self.assertIn( + err.response_code_name, + {'RRC_NOT_ALLOWED', 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', 'RRC_BAD_REQUEST'}, + f'unexpected response code: {err.response_code_name}', + ) + + # ----------------------------------------------------------------------- # + # Layer B — set_record_rotation # + # ----------------------------------------------------------------------- # + + def test_set_record_rotation_subhour_schedule_rejected(self): + """Krouter rejects schedule with frequency < 1h (UserRest.kt:654-658).""" + from keepercommander.commands.pam._layer_b import RouterResponseError + from keepercommander.commands.pam.router_helper import router_set_record_rotation_information + from keepercommander.proto import router_pb2 + + rq = router_pb2.RouterRecordRotationRequest( + recordUid=b'\x00' * 16, + schedule='*/5 * * * *', # every 5 minutes — sub-1h + ) + with self.assertRaises(Exception) as cm: + router_set_record_rotation_information(self.params, rq) + err = cm.exception + # Server validates schedule before record-existence, so this should always + # surface as RRC_GENERAL_ERROR with the cron-interval message. + if isinstance(err, RouterResponseError): + self.assertEqual(err.response_code_name, 'RRC_GENERAL_ERROR') + self.assertIn('hour', str(err).lower()) + + def test_set_record_rotation_unauthorized_record_rejected(self): + """Setting rotation on a nonexistent / unauthorized record returns an error.""" + from keepercommander.commands.pam._layer_b import RouterResponseError + from keepercommander.commands.pam.router_helper import router_set_record_rotation_information + from keepercommander.proto import router_pb2 + + rq = router_pb2.RouterRecordRotationRequest( + recordUid=b'\x00' * 16, + schedule='0 2 * * *', # valid daily schedule (well above 1h) + configurationUid=b'\x00' * 16, + ) + with self.assertRaises(Exception) as cm: + router_set_record_rotation_information(self.params, rq) + err = cm.exception + if isinstance(err, RouterResponseError): + # Some flavor of "not allowed" or "bad request" — never RRC_OK. + self.assertNotEqual(err.response_code_name, 'RRC_OK') diff --git a/tests/test_security_audit_refresh.py b/tests/test_security_audit_refresh.py index 372f0223c..74ad7b607 100644 --- a/tests/test_security_audit_refresh.py +++ b/tests/test_security_audit_refresh.py @@ -1,7 +1,7 @@ import os import json from collections import Counter -from unittest import TestCase +from unittest import TestCase, skipUnless import pytest @@ -14,7 +14,25 @@ from keepercommander import vault +_TESTS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +def _security_audit_config_path(): + name = os.environ.get('KEEPER_CONFIG', '../config.json') + if os.path.isabs(name): + return name + return os.path.normpath(os.path.join(_TESTS_DIR, name)) + + +_SECURITY_AUDIT_CONFIG = _security_audit_config_path() + + @pytest.mark.integration +@skipUnless( + os.path.isfile(_SECURITY_AUDIT_CONFIG), + 'Set KEEPER_CONFIG or add config.json to enable security-audit integration tests ' + f'(looked for {_SECURITY_AUDIT_CONFIG})', +) class TestSecurityAuditRefresh(TestCase): params = None # type: KeeperParams diff --git a/unit-tests/pam/_dag_fixtures.py b/unit-tests/pam/_dag_fixtures.py new file mode 100644 index 000000000..57bde1feb --- /dev/null +++ b/unit-tests/pam/_dag_fixtures.py @@ -0,0 +1,68 @@ +""" +Shared protobuf fixture builders for DAG / graph-sync tests. + +Builders use the actual proto schemas so a wire-format change breaks tests at the +right place. Imported by: +- test_dag_graph_sync_endpoints.py (Layer A) +- test_dag_layer_b.py (Layer B configure_resource / set_record_rotation, when added) +""" +from typing import Iterable, Optional + +from keepercommander.keeper_dag.proto import GraphSync_pb2 as gs_pb2 +from keepercommander.keeper_dag.types import PamEndpoints + + +# Graphs covered by the new per-graph routing. The five values match +# /api/user/graph-sync// exactly. +ALL_GRAPHS = [ + (PamEndpoints.PAM, 'pam'), + (PamEndpoints.DISCOVERY_RULES, 'discovery_rules'), + (PamEndpoints.DISCOVERY_JOBS, 'discovery_jobs'), + (PamEndpoints.INFRASTRUCTURE, 'infrastructure'), + (PamEndpoints.SERVICE_LINKS, 'service_links'), +] + +ALL_VERBS = ['add_data', 'sync', 'multi_sync', 'get_leafs'] + + +def make_sync_query(stream_id: bytes = b'\x00' * 16, + origin: bytes = b'\x00' * 16, + sync_point: int = 0, + max_count: int = 0) -> gs_pb2.GraphSyncQuery: + return gs_pb2.GraphSyncQuery( + streamId=stream_id, + origin=origin, + syncPoint=sync_point, + maxCount=max_count, + ) + + +def make_multi_sync_query(queries: Optional[Iterable[gs_pb2.GraphSyncQuery]] = None) -> gs_pb2.GraphSyncMultiQuery: + if queries is None: + queries = [make_sync_query()] + return gs_pb2.GraphSyncMultiQuery(queries=list(queries)) + + +def make_add_data_request(origin_ref: Optional[gs_pb2.GraphSyncRef] = None, + data: Optional[Iterable[gs_pb2.GraphSyncData]] = None) -> gs_pb2.GraphSyncAddDataRequest: + if origin_ref is None: + origin_ref = gs_pb2.GraphSyncRef(value=b'\x00' * 16) + if data is None: + data = [] + return gs_pb2.GraphSyncAddDataRequest(origin=origin_ref, data=list(data)) + + +def make_leafs_query(vertices: Optional[Iterable[bytes]] = None) -> gs_pb2.GraphSyncLeafsQuery: + if vertices is None: + vertices = [b'\x00' * 16] + return gs_pb2.GraphSyncLeafsQuery(vertices=list(vertices)) + + +def proto_for_verb(verb: str): + """Return a builder function for the proto type that verb expects.""" + return { + 'add_data': make_add_data_request, + 'sync': make_sync_query, + 'multi_sync': make_multi_sync_query, + 'get_leafs': make_leafs_query, + }[verb] diff --git a/unit-tests/pam/test_dag_cross_cutting.py b/unit-tests/pam/test_dag_cross_cutting.py new file mode 100644 index 000000000..0a24378ab --- /dev/null +++ b/unit-tests/pam/test_dag_cross_cutting.py @@ -0,0 +1,178 @@ +""" +Layer-A cross-cutting tests (plan §13.6). + +Concerns that touch every DAG/graph-sync request rather than any single endpoint: +- Auth header construction (Authorization, TransmissionKey, User-Agent). +- HTTP error surfacing (4xx/5xx -> DAGConnectionException after retries). +- 429 throttle handling (doesn't count against retry budget; backs off). +- Debug logging includes URL on each attempt. + +Mock seams (same as test_dag_graph_sync_endpoints.py): +- `keepercommander.keeper_dag.connection.commander.requests.request` for the HTTP call. +- `keepercommander.keeper_dag.connection.commander.time.sleep` so retry tests are fast. +""" +import logging +import os +import sys +from unittest.mock import MagicMock, patch + +import pytest +import requests as requests_lib + +sys.path.insert(0, os.path.dirname(__file__)) +import _dag_fixtures # noqa: E402 + +from keepercommander.keeper_dag.exceptions import DAGConnectionException # noqa: E402 +from keepercommander.keeper_dag.types import PamEndpoints # noqa: E402 + +REQUESTS_MODULE = 'keepercommander.keeper_dag.connection.commander.requests' +SLEEP_TARGET = 'keepercommander.keeper_dag.connection.commander.time.sleep' + + +def _build_connection_with_real_headers(): + """Build a Connection where header construction runs but no real crypto is needed. + + Uses the deprecated `encrypted_transmission_key`/`encrypted_session_token`/`transmission_key` + constructor kwargs which short-circuit the public-key encryption path in + payload_and_headers(), so tests can run without server keys. + """ + from keepercommander.keeper_dag.connection.commander import Connection + + params = MagicMock() + params.config = {'server': 'krouter.test'} + os.environ['KROUTER_URL'] = 'https://krouter.test' + os.environ['VERIFY_SSL'] = 'false' + + conn = Connection( + params=params, + verify_ssl=False, + encrypted_transmission_key=b'\x11' * 64, + encrypted_session_token=b'\x22' * 80, + transmission_key=b'\x33' * 32, + ) + return conn + + +def _ok_response(): + rsp = MagicMock() + rsp.status_code = 200 + rsp.content = b'' + rsp.raise_for_status.return_value = None + return rsp + + +def _http_error_response(status_code): + rsp = MagicMock() + rsp.status_code = status_code + rsp.content = b'simulated error' + rsp.reason = 'Simulated' + err = requests_lib.exceptions.HTTPError(response=rsp) + rsp.raise_for_status.side_effect = err + return rsp + + +# --------------------------------------------------------------------------- # +# Auth headers # +# --------------------------------------------------------------------------- # + + +def test_request_includes_keeperuser_authorization_header(): + """Authorization: KeeperUser must accompany every user request.""" + conn = _build_connection_with_real_headers() + query = _dag_fixtures.make_sync_query() + with patch(f'{REQUESTS_MODULE}.request', return_value=_ok_response()) as mock_req: + conn.sync(query, endpoint=PamEndpoints.PAM) + sent_headers = mock_req.call_args.kwargs['headers'] + assert 'Authorization' in sent_headers, f'no Authorization header; got {sorted(sent_headers)}' + assert sent_headers['Authorization'].startswith('KeeperUser '), ( + f'expected KeeperUser scheme, got {sent_headers["Authorization"]!r}' + ) + + +def test_request_includes_transmission_key_header(): + """TransmissionKey header carries the encrypted-with-server-public-key blob.""" + conn = _build_connection_with_real_headers() + query = _dag_fixtures.make_sync_query() + with patch(f'{REQUESTS_MODULE}.request', return_value=_ok_response()) as mock_req: + conn.sync(query, endpoint=PamEndpoints.PAM) + sent_headers = mock_req.call_args.kwargs['headers'] + assert 'TransmissionKey' in sent_headers + assert len(sent_headers['TransmissionKey']) > 0 + + +def test_request_includes_user_agent_header(): + """User-Agent must be set so server can identify caller version.""" + conn = _build_connection_with_real_headers() + query = _dag_fixtures.make_sync_query() + with patch(f'{REQUESTS_MODULE}.request', return_value=_ok_response()) as mock_req: + conn.sync(query, endpoint=PamEndpoints.PAM, agent='test-agent/1.0') + sent_headers = mock_req.call_args.kwargs['headers'] + assert sent_headers.get('User-Agent') == 'test-agent/1.0' + + +# --------------------------------------------------------------------------- # +# Error surfacing # +# --------------------------------------------------------------------------- # + + +@pytest.mark.parametrize('status_code', [400, 401, 403, 404, 500, 502, 503]) +def test_non_throttle_http_errors_raise_dag_exception(status_code): + """4xx (non-429) and 5xx exhaust retries then raise DAGConnectionException.""" + conn = _build_connection_with_real_headers() + query = _dag_fixtures.make_sync_query() + with patch(SLEEP_TARGET): # skip real sleep between retries + with patch(f'{REQUESTS_MODULE}.request', return_value=_http_error_response(status_code)) as mock_req: + with pytest.raises(DAGConnectionException) as exc_info: + conn.sync(query, endpoint=PamEndpoints.PAM) + # Default retry is 5 attempts in rest_call_to_router signature + assert mock_req.call_count == 5, f'expected 5 retry attempts, got {mock_req.call_count}' + assert str(status_code) in str(exc_info.value) + + +def test_429_does_not_consume_retry_budget_and_increases_wait(): + """429 throttle: retries indefinitely* against budget, retry_wait scaled by throttle_inc_factor. + + *In practice the loop still terminates because retry_wait grows exponentially and a real + deployment hits other failures; this test checks the documented behavior: 429 does + `attempt -= 1` and `retry_wait *= throttle_inc_factor` for some number of cycles before + we forcibly stop the mock. + """ + conn = _build_connection_with_real_headers() + query = _dag_fixtures.make_sync_query() + throttled = _http_error_response(429) + ok = _ok_response() + + # 3 throttles, then success — would normally exceed the default retry=5 budget if + # 429 counted against it, but 429s decrement attempt so we should reach success. + responses = [throttled, throttled, throttled, ok] + sleep_calls = [] + with patch(SLEEP_TARGET, side_effect=lambda s: sleep_calls.append(s)): + with patch(f'{REQUESTS_MODULE}.request', side_effect=responses): + conn.sync(query, endpoint=PamEndpoints.PAM) + # Three throttles -> three sleeps with strictly increasing wait (factor 1.5). + assert len(sleep_calls) == 3, f'expected 3 sleeps between throttles, got {sleep_calls}' + assert sleep_calls[0] < sleep_calls[1] < sleep_calls[2], ( + f'retry_wait did not grow: {sleep_calls}' + ) + + +# --------------------------------------------------------------------------- # +# Debug logging # +# --------------------------------------------------------------------------- # + + +def test_debug_logging_includes_full_url(caplog): + """Every attempt logs the full URL at DEBUG so operators can trace request paths.""" + conn = _build_connection_with_real_headers() + conn.logger = logging.getLogger('test_dag_cross_cutting') + query = _dag_fixtures.make_sync_query() + with caplog.at_level(logging.DEBUG, logger='test_dag_cross_cutting'): + with patch(f'{REQUESTS_MODULE}.request', return_value=_ok_response()): + conn.sync(query, endpoint=PamEndpoints.PAM) + url_log = next( + (rec.message for rec in caplog.records + if 'graph web service call to' in rec.message), + None, + ) + assert url_log is not None, f'expected URL debug log, captured: {[r.message for r in caplog.records]}' + assert '/api/user/graph-sync/pam/sync' in url_log diff --git a/unit-tests/pam/test_dag_graph_sync_endpoints.py b/unit-tests/pam/test_dag_graph_sync_endpoints.py new file mode 100644 index 000000000..a1f71b55f --- /dev/null +++ b/unit-tests/pam/test_dag_graph_sync_endpoints.py @@ -0,0 +1,203 @@ +""" +Layer-A graph-sync endpoint tests (plan §13.2). + +Covers the URL routing migration from the legacy `/api/user/` endpoint to +the per-graph `/api/user/graph-sync//` routes that landed in krouter's +DAGRest.kt. Five graphs (pam, discovery_rules, discovery_jobs, infrastructure, +service_links) crossed with four verbs (add_data, sync, multi_sync, get_leafs) += 20 routes; backward-compat and device-side variants verified separately. + +Mock seam: `keepercommander.keeper_dag.connection.commander.requests.request` for +end-to-end URL assertions, and direct `ConnectionBase._endpoint(...)` calls for +the URL-builder unit tests. +""" +import os +import sys +from unittest.mock import MagicMock, patch + +import pytest + +# Make _dag_fixtures importable when pytest discovers the test from the repo root. +sys.path.insert(0, os.path.dirname(__file__)) +import _dag_fixtures # noqa: E402 + +from keepercommander.keeper_dag.connection import ConnectionBase # noqa: E402 +from keepercommander.keeper_dag.types import ( # noqa: E402 + ENDPOINT_TO_GRAPH_ID_MAP, + GRAPH_ID_TO_ENDPOINT, + PamEndpoints, + PamGraphId, +) + +REQUESTS_MODULE = 'keepercommander.keeper_dag.connection.commander.requests' + + +# --------------------------------------------------------------------------- # +# URL builder tests — the direct verification that PamEndpoints values map to # +# the right URL path segment. # +# --------------------------------------------------------------------------- # + + +def _make_user_conn() -> ConnectionBase: + return ConnectionBase(is_device=False, logger=MagicMock()) + + +def _make_device_conn() -> ConnectionBase: + return ConnectionBase(is_device=True, logger=MagicMock()) + + +@pytest.mark.parametrize('endpoint_enum, graph_token', _dag_fixtures.ALL_GRAPHS) +@pytest.mark.parametrize('verb', _dag_fixtures.ALL_VERBS) +def test_user_endpoint_routes_per_graph(endpoint_enum, graph_token, verb): + """20 cases: every (graph, verb) hits /api/user/graph-sync//.""" + conn = _make_user_conn() + url = conn._endpoint(action='/' + verb, endpoint=endpoint_enum) + assert url == f'/api/user/graph-sync/{graph_token}/{verb}', ( + f'PamEndpoints.{endpoint_enum.name} + {verb} -> {url!r}' + ) + + +@pytest.mark.parametrize('endpoint_enum, graph_token', _dag_fixtures.ALL_GRAPHS) +@pytest.mark.parametrize('verb', _dag_fixtures.ALL_VERBS) +def test_device_endpoint_routes_per_graph(endpoint_enum, graph_token, verb): + """20 cases (Gateway perspective): /api/device/graph-sync//.""" + conn = _make_device_conn() + url = conn._endpoint(action='/' + verb, endpoint=endpoint_enum) + assert url == f'/api/device/graph-sync/{graph_token}/{verb}' + + +@pytest.mark.parametrize('verb', _dag_fixtures.ALL_VERBS) +def test_legacy_user_path_when_no_endpoint(verb): + """Backward compat: no endpoint -> /api/user/ (legacy generic route).""" + conn = _make_user_conn() + url = conn._endpoint(action='/' + verb, endpoint=None) + assert url == f'/api/user/{verb}' + + +@pytest.mark.parametrize('verb', _dag_fixtures.ALL_VERBS) +def test_legacy_device_path_when_no_endpoint(verb): + """Backward compat (Gateway): /api/device/ when endpoint=None.""" + conn = _make_device_conn() + url = conn._endpoint(action='/' + verb, endpoint=None) + assert url == f'/api/device/{verb}' + + +def test_endpoint_accepts_raw_string_form(): + """_endpoint() must tolerate the raw string form (not just the enum).""" + conn = _make_user_conn() + url = conn._endpoint(action='/sync', endpoint=PamEndpoints.PAM.value) + assert url == '/api/user/graph-sync/pam/sync' + + +def test_endpoint_normalizes_slashes(): + """_endpoint() should produce a single-slash URL regardless of input slash count.""" + conn = _make_user_conn() + url = conn._endpoint(action='sync', endpoint='/graph-sync/pam/') + assert url == '/api/user/graph-sync/pam/sync' + + +# --------------------------------------------------------------------------- # +# Inverse-map symmetry: GRAPH_ID_TO_ENDPOINT and ENDPOINT_TO_GRAPH_ID_MAP must # +# round-trip cleanly. Catches a future drift between the two maps. # +# --------------------------------------------------------------------------- # + + +def test_graph_id_endpoint_maps_are_inverses(): + """Every graph_id maps back to its PamEndpoints value and vice versa.""" + for endpoint_str, graph_id in ENDPOINT_TO_GRAPH_ID_MAP.items(): + assert GRAPH_ID_TO_ENDPOINT[graph_id].value == endpoint_str + for graph_id, endpoint_enum in GRAPH_ID_TO_ENDPOINT.items(): + assert ENDPOINT_TO_GRAPH_ID_MAP[endpoint_enum.value] == graph_id + + +def test_all_pam_graph_ids_are_routable(): + """Every PamGraphId value must have a GRAPH_ID_TO_ENDPOINT entry.""" + for graph in PamGraphId: + assert graph.value in GRAPH_ID_TO_ENDPOINT, ( + f'PamGraphId.{graph.name} has no endpoint mapping' + ) + + +def test_all_pam_endpoints_are_routable(): + """Every PamEndpoints value must have an ENDPOINT_TO_GRAPH_ID_MAP entry.""" + for endpoint in PamEndpoints: + assert endpoint.value in ENDPOINT_TO_GRAPH_ID_MAP + + +# --------------------------------------------------------------------------- # +# End-to-end smoke: through the Commander Connection class, verify that the # +# URL actually sent to `requests.request` matches the expected pattern. # +# Uses a fully stubbed Connection to skip crypto setup. # +# --------------------------------------------------------------------------- # + + +def _build_stubbed_commander_connection(): + """Build a Commander Connection with crypto bypassed for end-to-end URL assertions.""" + from keepercommander.keeper_dag.connection.commander import Connection + + params = MagicMock() + params.config = {'server': 'krouter.test'} + + # Bypass dag_server_url's hostname derivation; force a deterministic host. + os.environ['KROUTER_URL'] = 'https://krouter.test' + os.environ['VERIFY_SSL'] = 'false' + + conn = Connection(params=params, verify_ssl=False) + + # Skip the encryption pipeline — payload_and_headers normally encrypts the + # protobuf body with the transmission key. Tests only need to verify the URL, + # so return the raw serialized proto and a minimal header dict. + def _stub_payload_and_headers(payload): + body = payload.SerializeToString() if hasattr(payload, 'SerializeToString') else payload + return body, {'Content-Type': 'application/octet-stream'} + + conn.payload_and_headers = _stub_payload_and_headers + conn.transmission_key = b'\x00' * 32 + return conn + + +def _ok_response(): + rsp = MagicMock() + rsp.status_code = 200 + rsp.content = b'' + rsp.raise_for_status.return_value = None + return rsp + + +@pytest.mark.parametrize('endpoint_enum, graph_token', _dag_fixtures.ALL_GRAPHS) +def test_sync_end_to_end_url(endpoint_enum, graph_token): + """sync() with endpoint=PamEndpoints.X hits /api/user/graph-sync//sync.""" + conn = _build_stubbed_commander_connection() + query = _dag_fixtures.make_sync_query() + with patch(f'{REQUESTS_MODULE}.request', return_value=_ok_response()) as mock_req: + conn.sync(query, endpoint=endpoint_enum) + sent_url = mock_req.call_args.kwargs['url'] + assert sent_url == f'https://krouter.test/api/user/graph-sync/{graph_token}/sync' + + +@pytest.mark.parametrize('endpoint_enum, graph_token', _dag_fixtures.ALL_GRAPHS) +def test_add_data_end_to_end_url(endpoint_enum, graph_token): + """add_data() with endpoint=PamEndpoints.X hits .../add_data.""" + conn = _build_stubbed_commander_connection() + req = _dag_fixtures.make_add_data_request() + with patch(f'{REQUESTS_MODULE}.request', return_value=_ok_response()) as mock_req: + conn.add_data(req, endpoint=endpoint_enum) + sent_url = mock_req.call_args.kwargs['url'] + assert sent_url == f'https://krouter.test/api/user/graph-sync/{graph_token}/add_data' + + +def test_end_to_end_sends_protobuf_bytes_not_json(): + """Wire-format gate: the body sent is the protobuf serialization, not JSON.""" + conn = _build_stubbed_commander_connection() + query = _dag_fixtures.make_sync_query(stream_id=b'A' * 16, sync_point=42) + with patch(f'{REQUESTS_MODULE}.request', return_value=_ok_response()) as mock_req: + conn.sync(query, endpoint=PamEndpoints.PAM) + sent_body = mock_req.call_args.kwargs['data'] + # Body should be raw bytes (protobuf), not a JSON-encoded string. + assert isinstance(sent_body, (bytes, bytearray)) + # Round-trip parse to confirm it's a valid GraphSyncQuery proto with the right fields. + from keepercommander.keeper_dag.proto import GraphSync_pb2 as gs_pb2 + parsed = gs_pb2.GraphSyncQuery() + parsed.ParseFromString(bytes(sent_body)) + assert parsed.streamId == b'A' * 16 + assert parsed.syncPoint == 42 diff --git a/unit-tests/pam/test_dag_layer_b_configure_resource.py b/unit-tests/pam/test_dag_layer_b_configure_resource.py new file mode 100644 index 000000000..13f36247e --- /dev/null +++ b/unit-tests/pam/test_dag_layer_b_configure_resource.py @@ -0,0 +1,248 @@ +""" +Layer-B `configure_resource` tests (plan §13.3 / task #23). + +Verifies the `router_configure_resource` wrapper in `pam/router_helper.py`: +- Targets `/api/user/configure_resource`. +- Sends a `PAMResourceConfig` protobuf body, encrypted with the transmission key. +- Round-trips through `_post_request_to_router` (which is the shared protobuf + transport layer for all Layer-B endpoints). + +For each `PAMResourceConfig` field that the krouter handler at UserRest.kt:375-581 +processes, one happy-path test confirms Commander builds the right proto. + +For each permission-denial code that the handler can return (RRC_NOT_ALLOWED, +RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED for connections / rotation / +connections_and_rotation enforcement checks), one test confirms the +`RouterResponseError` raised by `_post_request_to_router` exposes the right +`response_code_name` so callers can decide on fallback. +""" +import importlib +import os +import sys +from unittest.mock import MagicMock, patch + +import pytest + +sys.path.insert(0, os.path.dirname(__file__)) + +# Pre-warm the import chain through pam_import.keeper_ai_settings to avoid the +# pre-existing circular import in commands.record/ksm.utils when router_helper +# is the first thing to drag in commands.utils. See test_dag_layer_b_migration.py +# for a test file that triggers the chain naturally; this one needs the explicit +# warm-up because it imports router_helper directly. +importlib.import_module('keepercommander.commands.pam_import.keeper_ai_settings') # side-effect: pre-warm + +from keepercommander.commands.pam._layer_b import RouterResponseError # noqa: E402 +from keepercommander.proto import pam_pb2, router_pb2 # noqa: E402 + + +# Mock seam: the requests.request inside _post_request_to_router. +REQUESTS_TARGET = 'keepercommander.commands.pam.router_helper.requests.request' + +RESOURCE_UID = b'\xAA' * 16 +NETWORK_UID = b'\xBB' * 16 +ADMIN_UID = b'\xCC' * 16 +DOMAIN_UID = b'\xDD' * 16 + + +def _mock_params(): + p = MagicMock() + p.config = {'server': 'krouter.test'} + p.session_token = 'BBBBBBBB' # base64 of 6 bytes; sufficient for url_safe_base64_decode + p.rest_context.server_key_id = 7 + os.environ['KROUTER_URL'] = 'https://krouter.test' + os.environ['VERIFY_SSL'] = 'false' + return p + + +def _ok_router_response(): + """Build a successful RouterResponse wire payload.""" + rs = router_pb2.RouterResponse() + rs.responseCode = router_pb2.RRC_OK + rsp = MagicMock() + rsp.status_code = 200 + rsp.headers = {} + rsp.content = rs.SerializeToString() + rsp.raise_for_status.return_value = None + return rsp + + +def _error_router_response(code_name: str, error_message: str = 'denied'): + """Build a RouterResponse with a non-OK responseCode wire payload.""" + rs = router_pb2.RouterResponse() + rs.responseCode = router_pb2.RouterResponseCode.Value(code_name) + rs.errorMessage = error_message + rsp = MagicMock() + rsp.status_code = 200 # HTTP 200; the error lives inside the protobuf body + rsp.headers = {} + rsp.content = rs.SerializeToString() + rsp.raise_for_status.return_value = None + return rsp + + +def _capture_call(mock_req): + """Return the (url, body_bytes) that requests.request was last called with.""" + kwargs = mock_req.call_args.kwargs + return kwargs.get('url') or mock_req.call_args.args[1], kwargs.get('data') + + +# --------------------------------------------------------------------------- # +# Happy path: URL + body shape # +# --------------------------------------------------------------------------- # + + +def test_configure_resource_hits_correct_url(): + """POSTs to /api/user/configure_resource.""" + from keepercommander.commands.pam.router_helper import router_configure_resource + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID, networkUid=NETWORK_UID) + with patch(REQUESTS_TARGET, return_value=_ok_router_response()) as mock_req: + router_configure_resource(_mock_params(), rq) + url, _ = _capture_call(mock_req) + assert url.endswith('/api/user/configure_resource') + + +def test_configure_resource_sends_protobuf_body(): + """Body is the encrypted PAMResourceConfig protobuf, not JSON.""" + from keepercommander.commands.pam.router_helper import router_configure_resource + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID, networkUid=NETWORK_UID, adminUid=ADMIN_UID) + with patch(REQUESTS_TARGET, return_value=_ok_router_response()) as mock_req: + router_configure_resource(_mock_params(), rq) + _, body = _capture_call(mock_req) + assert isinstance(body, (bytes, bytearray)), f'body must be bytes, got {type(body)}' + # Body is encrypted; check that it's NOT a JSON-encoded payload (sanity). + assert not body.startswith(b'{'), 'body should be encrypted protobuf, not JSON' + + +# --------------------------------------------------------------------------- # +# Per-field proto-shape tests — exercises every PAMResourceConfig field the # +# krouter handler reads. Catches accidental field-name typos or proto schema # +# drift. # +# --------------------------------------------------------------------------- # + + +def test_configure_resource_proto_recordUid_only(): + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID) + assert rq.recordUid == RESOURCE_UID + assert rq.networkUid == b'' + assert rq.adminUid == b'' + + +def test_configure_resource_proto_full_credential_link(): + """A typical 'configure resource with admin credential' shape.""" + rq = pam_pb2.PAMResourceConfig( + recordUid=RESOURCE_UID, + networkUid=NETWORK_UID, + adminUid=ADMIN_UID, + ) + assert rq.recordUid == RESOURCE_UID + assert rq.networkUid == NETWORK_UID + assert rq.adminUid == ADMIN_UID + + +def test_configure_resource_proto_connect_users_uid_list(): + rq = pam_pb2.PAMResourceConfig( + recordUid=RESOURCE_UID, + connectUsers=pam_pb2.UidList(uids=[b'\xEE' * 16, b'\xFF' * 16]), + ) + assert list(rq.connectUsers.uids) == [b'\xEE' * 16, b'\xFF' * 16] + + +def test_configure_resource_proto_domainUid(): + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID, domainUid=DOMAIN_UID) + assert rq.domainUid == DOMAIN_UID + + +def test_configure_resource_proto_meta_bytes(): + meta_json = b'{"allowedSettings":{"connections":true}}' + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID, meta=meta_json) + assert rq.meta == meta_json + + +def test_configure_resource_proto_jit_settings(): + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID, jitSettings=b'JIT_BYTES') + assert rq.jitSettings == b'JIT_BYTES' + + +def test_configure_resource_proto_keeper_ai_settings(): + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID, keeperAiSettings=b'AI_BYTES') + assert rq.keeperAiSettings == b'AI_BYTES' + + +def test_configure_resource_proto_all_fields_together(): + """All-fields construction — useful as a smoke for proto schema completeness.""" + rq = pam_pb2.PAMResourceConfig( + recordUid=RESOURCE_UID, + networkUid=NETWORK_UID, + adminUid=ADMIN_UID, + meta=b'{"allowedSettings":{}}', + connectionSettings=b'CONN', + connectUsers=pam_pb2.UidList(uids=[b'\x11' * 16]), + domainUid=DOMAIN_UID, + jitSettings=b'JIT', + keeperAiSettings=b'AI', + ) + # All fields round-trip through serialization (catches missing field-number gaps). + parsed = pam_pb2.PAMResourceConfig() + parsed.ParseFromString(rq.SerializeToString()) + assert parsed.recordUid == RESOURCE_UID + assert parsed.networkUid == NETWORK_UID + assert parsed.adminUid == ADMIN_UID + assert parsed.meta == b'{"allowedSettings":{}}' + assert parsed.connectionSettings == b'CONN' + assert list(parsed.connectUsers.uids) == [b'\x11' * 16] + assert parsed.domainUid == DOMAIN_UID + assert parsed.jitSettings == b'JIT' + assert parsed.keeperAiSettings == b'AI' + + +# --------------------------------------------------------------------------- # +# Permission denial paths — each enforcement check at UserRest.kt:540-571 # +# surfaces a specific RouterResponseCode; verify Commander parses it into the # +# right `response_code_name` so the fallback logic can branch correctly. # +# --------------------------------------------------------------------------- # + + +@pytest.mark.parametrize('code_name', [ + 'RRC_NOT_ALLOWED', # validatePamAccess failure (linking unauthorized record) + 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', # connections / rotation / both enforcement disabled +]) +def test_configure_resource_raises_router_response_error_on_permission_denied(code_name): + """Verify Commander parses the responseCode and surfaces it via RouterResponseError.""" + from keepercommander.commands.pam.router_helper import router_configure_resource + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID, networkUid=NETWORK_UID, adminUid=ADMIN_UID) + with patch(REQUESTS_TARGET, return_value=_error_router_response(code_name, 'denied')): + with pytest.raises(RouterResponseError) as exc_info: + router_configure_resource(_mock_params(), rq) + assert exc_info.value.response_code_name == code_name + assert 'denied' in str(exc_info.value) + + +@pytest.mark.parametrize('code_name', ['RRC_GENERAL_ERROR', 'RRC_BAD_REQUEST', 'RRC_TIMEOUT']) +def test_configure_resource_raises_router_response_error_on_non_permission_codes(code_name): + """Non-permission errors also surface as RouterResponseError; callers don't fall back on these.""" + from keepercommander.commands.pam.router_helper import router_configure_resource + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID) + with patch(REQUESTS_TARGET, return_value=_error_router_response(code_name, 'something broke')): + with pytest.raises(RouterResponseError) as exc_info: + router_configure_resource(_mock_params(), rq) + assert exc_info.value.response_code_name == code_name + + +# --------------------------------------------------------------------------- # +# Transport-level error surfacing (4xx/5xx HTTP errors) # +# --------------------------------------------------------------------------- # + + +def test_configure_resource_raises_keeper_api_error_on_http_4xx(): + """An HTTP 4xx from krouter surfaces as KeeperApiError (not RouterResponseError).""" + from keepercommander.error import KeeperApiError + from keepercommander.commands.pam.router_helper import router_configure_resource + rq = pam_pb2.PAMResourceConfig(recordUid=RESOURCE_UID) + rsp = MagicMock() + rsp.status_code = 401 + rsp.text = 'unauthorized' + rsp.headers = {} + with patch(REQUESTS_TARGET, return_value=rsp): + with pytest.raises(KeeperApiError) as exc_info: + router_configure_resource(_mock_params(), rq) + assert exc_info.value.result_code == 401 diff --git a/unit-tests/pam/test_dag_layer_b_fallback.py b/unit-tests/pam/test_dag_layer_b_fallback.py new file mode 100644 index 000000000..dc9515ea3 --- /dev/null +++ b/unit-tests/pam/test_dag_layer_b_fallback.py @@ -0,0 +1,359 @@ +""" +Layer-B fallback decision tests (plan §13.8 T2 / task #29). + +Covers the KEEPER_DAG_LB_FALLBACK env-var-controlled fallback policy: +- Default (env unset) → fallback enabled. +- Truthy values ('1', 'true', 'yes', 'on') → fallback enabled. +- Falsy values ('0', 'false', 'no', 'off', '') → strict mode (no fallback). +- Only RRC_NOT_ALLOWED / RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED warrant fallback. +- Non-permission errors (other RRC codes, generic exceptions) never trigger fallback. +- Backward compat: legacy generic Exception with the code string in its message is recognized. +""" +import os +from unittest.mock import patch + +import pytest + +from keepercommander.commands.pam._layer_b import ( + RouterResponseError, + _layer_b_fallback_enabled, + should_fallback_on_layer_b_error, +) + + +# --------------------------------------------------------------------------- # +# RouterResponseError # +# --------------------------------------------------------------------------- # + + +def test_router_response_error_is_exception_subclass(): + """Existing `except Exception:` handlers must still catch this.""" + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + assert isinstance(err, Exception) + + +def test_router_response_error_carries_code_and_message(): + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + assert err.response_code == 13 + assert err.response_code_name == 'RRC_NOT_ALLOWED' + assert 'denied' in str(err) + assert 'RRC_NOT_ALLOWED' in str(err) + + +# --------------------------------------------------------------------------- # +# Env var parsing # +# --------------------------------------------------------------------------- # + + +@pytest.mark.parametrize('value', ['1', 'true', 'TRUE', 'True', 'yes', 'YES', 'on', 'ON']) +def test_fallback_enabled_for_truthy_env(value): + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': value}): + assert _layer_b_fallback_enabled() is True + + +@pytest.mark.parametrize('value', ['0', 'false', 'FALSE', 'False', 'no', 'NO', 'off', 'OFF', '']) +def test_fallback_disabled_for_falsy_env(value): + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': value}): + assert _layer_b_fallback_enabled() is False + + +def test_fallback_default_when_env_unset(): + """Default OFF (strict mode). Mature endpoints (configure_resource, + set_record_rotation) are deployed everywhere — denials are real and should + propagate. Set KEEPER_DAG_LB_FALLBACK=1 only to opt INTO legacy fallback + during the rollout of a new endpoint not on the always-downgrade list.""" + env_without = {k: v for k, v in os.environ.items() if k != 'KEEPER_DAG_LB_FALLBACK'} + with patch.dict(os.environ, env_without, clear=True): + assert _layer_b_fallback_enabled() is False + + +# --------------------------------------------------------------------------- # +# Fallback decision logic # +# --------------------------------------------------------------------------- # + + +@pytest.mark.parametrize('code_name', [ + 'RRC_NOT_ALLOWED', + 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', + 'RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED', +]) +def test_fallback_on_permission_denied_when_enabled(code_name): + err = RouterResponseError(13, code_name, 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}): + assert should_fallback_on_layer_b_error(err) is True + + +@pytest.mark.parametrize('code_name', [ + 'RRC_NOT_ALLOWED', + 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', + 'RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED', +]) +def test_no_fallback_on_permission_denied_when_disabled(code_name): + err = RouterResponseError(13, code_name, 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '0'}): + assert should_fallback_on_layer_b_error(err) is False + + +@pytest.mark.parametrize('code_name', ['RRC_OK', 'RRC_BAD_REQUEST', 'RRC_GENERAL_ERROR', 'RRC_TIMEOUT']) +def test_no_fallback_on_non_permission_codes(code_name): + """Only permission-denial codes trigger fallback; other errors propagate.""" + err = RouterResponseError(99, code_name, 'something broke') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}): + assert should_fallback_on_layer_b_error(err) is False + + +def test_no_fallback_on_unrelated_exception(): + """Generic exceptions (network errors, value errors, etc.) never trigger fallback.""" + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}): + assert should_fallback_on_layer_b_error(ValueError('nope')) is False + assert should_fallback_on_layer_b_error(KeyError('missing')) is False + assert should_fallback_on_layer_b_error(RuntimeError('boom')) is False + + +def test_fallback_recognizes_legacy_exception_with_code_in_message(): + """Backward compat: pre-RouterResponseError code paths still surface as fallback-worthy.""" + legacy = Exception('something failed Response code: RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}): + assert should_fallback_on_layer_b_error(legacy) is True + + +def test_legacy_message_match_respects_disabled_flag(): + """Even with the legacy message pattern, disabled flag wins.""" + legacy = Exception('denied Response code: RRC_NOT_ALLOWED') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '0'}): + assert should_fallback_on_layer_b_error(legacy) is False + + +# --------------------------------------------------------------------------- # +# Feature-disabled cache: HTTP 404 + per-code marking behavior # +# --------------------------------------------------------------------------- # + + +from keepercommander.commands.pam._layer_b import ( + clear_layer_b_feature_cache, + is_layer_b_feature_disabled, + mark_layer_b_feature_disabled, + _feature_cache_ttl_sec, +) +from keepercommander.error import KeeperApiError + + +@pytest.fixture(autouse=True) +def _clear_cache_between_tests(): + clear_layer_b_feature_cache() + yield + clear_layer_b_feature_cache() + + +def test_cache_miss_when_unmarked(): + assert is_layer_b_feature_disabled('http://host', 'ep') is False + + +def test_cache_hit_after_marking(): + with patch.dict(os.environ, {'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}): + mark_layer_b_feature_disabled('http://host', 'configure_resource') + assert is_layer_b_feature_disabled('http://host', 'configure_resource') is True + + +def test_cache_isolated_per_host_endpoint(): + """Different host or endpoint pairs are independent cache keys.""" + with patch.dict(os.environ, {'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}): + mark_layer_b_feature_disabled('http://host-a', 'configure_resource') + assert is_layer_b_feature_disabled('http://host-a', 'configure_resource') is True + # Different host: not cached + assert is_layer_b_feature_disabled('http://host-b', 'configure_resource') is False + # Same host, different endpoint: not cached + assert is_layer_b_feature_disabled('http://host-a', 'set_record_rotation') is False + + +def test_cache_ttl_zero_disables_marking(): + with patch.dict(os.environ, {'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '0'}): + mark_layer_b_feature_disabled('http://host', 'ep') + # TTL=0 means caching disabled entirely + assert is_layer_b_feature_disabled('http://host', 'ep') is False + + +def test_cache_ttl_negative_treated_as_disabled(): + with patch.dict(os.environ, {'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '-5'}): + assert _feature_cache_ttl_sec() == 0 + + +def test_cache_ttl_garbage_treated_as_disabled(): + with patch.dict(os.environ, {'KEEPER_DAG_LB_FEATURE_CACHE_TTL': 'not-a-number'}): + assert _feature_cache_ttl_sec() == 0 + + +def test_cache_entry_expires(): + """A cache entry past its expiry is treated as a miss and evicted.""" + import time + with patch.dict(os.environ, {'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '1'}): + mark_layer_b_feature_disabled('http://host', 'ep') + assert is_layer_b_feature_disabled('http://host', 'ep') is True + # Sleep slightly longer than TTL + time.sleep(1.1) + assert is_layer_b_feature_disabled('http://host', 'ep') is False + + +def test_http_404_triggers_fallback_and_cache(): + """An older krouter without the new endpoint returns HTTP 404. The decision + function should fall back AND mark the cache so we don't keep round-tripping.""" + err = KeeperApiError(404, 'Not Found') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}): + assert should_fallback_on_layer_b_error(err, host='http://host', endpoint='configure_network_graph') is True + assert is_layer_b_feature_disabled('http://host', 'configure_network_graph') is True + + +def test_rrc_not_allowed_per_user_does_not_cache(): + """Per-user RRC_NOT_ALLOWED falls back per-call but must NOT poison the cache + (the answer depends on which user/record, not on the host).""" + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}): + assert should_fallback_on_layer_b_error(err, host='http://host', endpoint='configure_resource') is True + # Cache MUST NOT be poisoned by a per-user denial + assert is_layer_b_feature_disabled('http://host', 'configure_resource') is False + + +@pytest.mark.parametrize('code_name', [ + 'RRC_NOT_ALLOWED', + 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', + 'RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED', +]) +def test_rrc_codes_never_mark_cache(code_name): + """RRC codes (per-user OR tenant-level) fall back per-call but never mark + the (host, endpoint) cache. Only HTTP 404 marks the cache. Reasoning: + tenant admins can flip features at runtime, so a tenant-level denial isn't + a stable signal; per-user denials depend on caller context.""" + err = RouterResponseError(8, code_name, 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}): + assert should_fallback_on_layer_b_error(err, host='http://host', endpoint='configure_resource') is True + # Cache MUST NOT be marked — only 404 marks the cache. + assert is_layer_b_feature_disabled('http://host', 'configure_resource') is False + + +def test_marking_without_host_or_endpoint_is_noop(): + """When host or endpoint is missing, marking silently no-ops so callers that + don't have routing context (e.g. legacy paths) don't crash.""" + with patch.dict(os.environ, {'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}): + mark_layer_b_feature_disabled(None, 'ep') + mark_layer_b_feature_disabled('http://host', None) + mark_layer_b_feature_disabled('', '') + # No exceptions raised, and the cache has no entries + assert is_layer_b_feature_disabled('http://host', 'ep') is False + + +# --------------------------------------------------------------------------- # +# Per-endpoint always-downgrade policy (mechanism + strict default behavior) # +# --------------------------------------------------------------------------- # + + +def _env_without_fallback(): + return {k: v for k, v in os.environ.items() if k != 'KEEPER_DAG_LB_FALLBACK'} + + +def test_always_downgrade_set_currently_empty(): + """`_LB_ALWAYS_DOWNGRADE_ENDPOINTS` is intentionally empty — all three + Layer-B endpoints (configure_resource, set_record_rotation, + configure_network_graph) honor `KEEPER_DAG_LB_FALLBACK` uniformly. The + frozenset is preserved as scaffolding for future endpoints whose rollout + window needs an unconditional auto-downgrade safety net. If this test + fails (the set became non-empty), double-check the addition is intentional + and update the module docstring + this test.""" + from keepercommander.commands.pam._layer_b import _LB_ALWAYS_DOWNGRADE_ENDPOINTS + assert _LB_ALWAYS_DOWNGRADE_ENDPOINTS == frozenset(), ( + f'_LB_ALWAYS_DOWNGRADE_ENDPOINTS is no longer empty: ' + f'{set(_LB_ALWAYS_DOWNGRADE_ENDPOINTS)}' + ) + + +def test_always_downgrade_mechanism_works_when_endpoint_added(monkeypatch): + """Verify the always-downgrade mechanism via a hypothetical future endpoint. + When an endpoint IS on the list, 404 and RRC denials fall back even with + KEEPER_DAG_LB_FALLBACK unset (strict default).""" + import keepercommander.commands.pam._layer_b as lb + monkeypatch.setattr(lb, '_LB_ALWAYS_DOWNGRADE_ENDPOINTS', frozenset({'hypothetical_endpoint'})) + + with patch.dict(os.environ, _env_without_fallback(), clear=True): + # 404 → fall back + cache + assert should_fallback_on_layer_b_error( + KeeperApiError(404, 'Not Found'), + host='http://host', endpoint='hypothetical_endpoint' + ) is True + # RRC denial → fall back (no cache) + assert should_fallback_on_layer_b_error( + RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied'), + host='http://host', endpoint='hypothetical_endpoint' + ) is True + + +def test_always_downgrade_scoping(monkeypatch): + """Per-endpoint policy is scoped — an endpoint on the always-downgrade + list must NOT affect the decision for any other endpoint.""" + import keepercommander.commands.pam._layer_b as lb + monkeypatch.setattr(lb, '_LB_ALWAYS_DOWNGRADE_ENDPOINTS', frozenset({'hypothetical_endpoint'})) + + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, _env_without_fallback(), clear=True): + # hypothetical_endpoint: falls back (on the list) + assert should_fallback_on_layer_b_error(err, host='h', endpoint='hypothetical_endpoint') is True + # All real endpoints: propagate (none on the list, env unset) + for ep in ('configure_resource', 'set_record_rotation', 'configure_network_graph'): + assert should_fallback_on_layer_b_error(err, host='h', endpoint=ep) is False, ( + f'{ep} should propagate in strict mode' + ) + + +def test_configure_resource_strict_on_404_when_env_unset(): + """Mature endpoint `configure_resource`: 404 propagates when env var is + unset (strict default). Opt INTO fallback by setting KEEPER_DAG_LB_FALLBACK=1.""" + err = KeeperApiError(404, 'Not Found') + with patch.dict(os.environ, _env_without_fallback(), clear=True): + assert should_fallback_on_layer_b_error( + err, host='http://host', endpoint='configure_resource' + ) is False + + +def test_configure_resource_falls_back_on_404_when_env_on(): + """Opt-in path: KEEPER_DAG_LB_FALLBACK=1 makes 404 fall back for + `configure_resource` too.""" + err = KeeperApiError(404, 'Not Found') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}): + assert should_fallback_on_layer_b_error( + err, host='http://host', endpoint='configure_resource' + ) is True + # 404 marks the cache regardless of which endpoint + assert is_layer_b_feature_disabled('http://host', 'configure_resource') is True + + +def test_configure_resource_strict_on_rrc_when_env_unset(): + """Mature endpoint, RRC denial: propagates when env var is unset. This is + the security-relevant default — denials are real and should not silently + fall back to a legacy write that bypasses the check.""" + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, _env_without_fallback(), clear=True): + assert should_fallback_on_layer_b_error( + err, host='http://host', endpoint='configure_resource' + ) is False + + +def test_transient_errors_never_downgrade(monkeypatch): + """5xx, ConnectionError, generic exceptions never trigger downgrade for + any endpoint — including any future always-downgrade entries. Transient + failures propagate so an operator notices (and the existing requests-level + retry handles intermittent blips).""" + import keepercommander.commands.pam._layer_b as lb + monkeypatch.setattr(lb, '_LB_ALWAYS_DOWNGRADE_ENDPOINTS', frozenset({'hypothetical_endpoint'})) + + transient = [ + KeeperApiError(500, 'Internal Server Error'), + KeeperApiError(503, 'Service Unavailable'), + ConnectionError('connection refused'), + TimeoutError('read timeout'), + RuntimeError('something else'), + ] + with patch.dict(os.environ, _env_without_fallback(), clear=True): + for err in transient: + for ep in ('hypothetical_endpoint', 'configure_resource', + 'set_record_rotation', 'configure_network_graph'): + assert should_fallback_on_layer_b_error( + err, host='http://host', endpoint=ep + ) is False, f'{type(err).__name__} should not trigger downgrade on {ep}' diff --git a/unit-tests/pam/test_dag_layer_b_migration.py b/unit-tests/pam/test_dag_layer_b_migration.py new file mode 100644 index 000000000..d89681a13 --- /dev/null +++ b/unit-tests/pam/test_dag_layer_b_migration.py @@ -0,0 +1,1122 @@ +""" +Layer-B migration regression tests (plan §13.5 / task #25). + +Covers the migrated write functions in `pam_import/keeper_ai_settings.py`: +- `set_resource_keeper_ai_settings` (AI risk-level settings, ai_settings edge) +- `set_resource_jit_settings` (JIT elevation settings, jit_settings edge) + +Each function verifies: +- Primary path calls `router_configure_resource` with the correct `PAMResourceConfig` + proto fields (`recordUid`, `networkUid`, and either `keeperAiSettings` or `jitSettings`). +- On `RRC_NOT_ALLOWED*` + `KEEPER_DAG_LB_FALLBACK` ON, fallback to the legacy + DAG-write path fires. +- Strict mode (`KEEPER_DAG_LB_FALLBACK=0`) propagates the error as a `False` return + (consistent with the legacy semantics — log + return False). +- Input validation paths (bad settings, missing record key) return early without + calling configure_resource. +""" +import json +import os +import sys +from unittest.mock import MagicMock, patch + +import pytest + +sys.path.insert(0, os.path.dirname(__file__)) + +from keepercommander.commands.pam._layer_b import RouterResponseError, clear_layer_b_feature_cache +from keepercommander.commands.pam_import import keeper_ai_settings as ai_mod +from keepercommander.proto import pam_pb2, router_pb2 + + +RESOURCE_UID_STR = 'AAAAAAAAAAAAAAAAAAAAAA' # 16-byte base64-urlsafe (22 chars) +CONFIG_UID_STR = 'BBBBBBBBBBBBBBBBBBBBBB' +RECORD_KEY = b'\x01' * 32 + +TEST_ROUTER_URL = 'http://test-krouter' + + +@pytest.fixture(autouse=True) +def _stub_router_url_and_clear_cache(monkeypatch): + """Stub `get_router_url` so the Layer-B feature-disabled cache has a stable + host key without touching `params.rest_context`. Also clears the cache between + tests so a previous test's marking doesn't bleed into the next.""" + monkeypatch.setattr( + 'keepercommander.commands.pam.router_helper.get_router_url', + lambda params: TEST_ROUTER_URL, + ) + clear_layer_b_feature_cache() + yield + clear_layer_b_feature_cache() + + +def _patch_inputs(monkeypatch=None): + """Mock the shared input-resolution helper so each test starts from a known good state.""" + return patch.object( + ai_mod, + '_resolve_resource_settings_inputs', + return_value=(RECORD_KEY, CONFIG_UID_STR), + ) + + +def _mock_params(): + p = MagicMock() + p.session_token = 'token' + return p + + +# --------------------------------------------------------------------------- # +# AI settings migration # +# --------------------------------------------------------------------------- # + + +class TestSetResourceKeeperAiSettingsMigration: + + def test_happy_path_calls_configure_resource_with_correct_proto(self): + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + return None + + with _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'CIPHER_BYTES'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=_capture): + ok = ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'critical'}, config_uid=CONFIG_UID_STR + ) + assert ok is True + rq = captured['rq'] + assert isinstance(rq, pam_pb2.PAMResourceConfig) + # 22-char base64-urlsafe -> 16 bytes + assert len(rq.recordUid) == 16 + assert len(rq.networkUid) == 16 + assert rq.keeperAiSettings == b'CIPHER_BYTES' + # Critical: must NOT be set on jitSettings field + assert rq.jitSettings == b'' + + def test_permission_denied_with_fallback_enabled_calls_legacy(self): + legacy_called = {'count': 0} + + def _legacy(*args, **kwargs): + legacy_called['count'] += 1 + return True + + err = RouterResponseError(13, 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'CIPHER'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err), \ + patch.object(ai_mod, '_set_resource_keeper_ai_settings_legacy', side_effect=_legacy): + ok = ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'high'}, config_uid=CONFIG_UID_STR + ) + assert ok is True + assert legacy_called['count'] == 1, 'legacy fallback was not invoked' + + def test_permission_denied_with_fallback_disabled_returns_false(self): + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '0'}), \ + _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'CIPHER'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err), \ + patch.object(ai_mod, '_set_resource_keeper_ai_settings_legacy') as legacy_mock: + ok = ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'low'}, config_uid=CONFIG_UID_STR + ) + assert ok is False + legacy_mock.assert_not_called() + + def test_non_permission_error_returns_false_without_fallback(self): + """A generic server error (not a permission denial) doesn't fall back; returns False.""" + err = RouterResponseError(99, 'RRC_GENERAL_ERROR', 'something broke') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'CIPHER'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err), \ + patch.object(ai_mod, '_set_resource_keeper_ai_settings_legacy') as legacy_mock: + ok = ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'low'}, config_uid=CONFIG_UID_STR + ) + assert ok is False + legacy_mock.assert_not_called() + + def test_bad_input_returns_false_without_calling_configure_resource(self): + with patch.object(ai_mod, '_resolve_resource_settings_inputs', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource') as call_mock: + ok = ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {}, config_uid=CONFIG_UID_STR + ) + assert ok is False + call_mock.assert_not_called() + + +# --------------------------------------------------------------------------- # +# JIT settings migration # +# --------------------------------------------------------------------------- # + + +class TestSetResourceJitSettingsMigration: + + def test_happy_path_calls_configure_resource_with_jit_field(self): + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'JIT_CIPHER'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=_capture): + ok = ai_mod.set_resource_jit_settings( + _mock_params(), RESOURCE_UID_STR, {'elevate': True}, config_uid=CONFIG_UID_STR + ) + assert ok is True + rq = captured['rq'] + assert isinstance(rq, pam_pb2.PAMResourceConfig) + assert rq.jitSettings == b'JIT_CIPHER' + # JIT migration must NOT populate the AI-settings field + assert rq.keeperAiSettings == b'' + + def test_permission_denied_with_fallback_enabled_calls_legacy(self): + legacy_called = {'count': 0} + + def _legacy(*args, **kwargs): + legacy_called['count'] += 1 + return True + + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'JIT_CIPHER'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err), \ + patch.object(ai_mod, '_set_resource_jit_settings_legacy', side_effect=_legacy): + ok = ai_mod.set_resource_jit_settings( + _mock_params(), RESOURCE_UID_STR, {'elevate': True}, config_uid=CONFIG_UID_STR + ) + assert ok is True + assert legacy_called['count'] == 1 + + @pytest.mark.parametrize('bad_settings, allow_empty', [ + ('not a dict', False), + (None, False), + ({}, False), # empty dict + allow_empty=False short-circuits + ]) + def test_bad_input_short_circuits_before_configure_resource(self, bad_settings, allow_empty): + with patch('keepercommander.commands.pam.router_helper.router_configure_resource') as call_mock: + ok = ai_mod.set_resource_jit_settings( + _mock_params(), RESOURCE_UID_STR, bad_settings, + config_uid=CONFIG_UID_STR, allow_empty=allow_empty, + ) + assert ok is False + call_mock.assert_not_called() + + def test_empty_settings_allowed_when_flag_set(self): + """allow_empty=True lets empty settings reach configure_resource (used for resets).""" + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b''), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=_capture): + ok = ai_mod.set_resource_jit_settings( + _mock_params(), RESOURCE_UID_STR, {}, config_uid=CONFIG_UID_STR, allow_empty=True, + ) + assert ok is True + assert captured['rq'].jitSettings == b'' + + +# --------------------------------------------------------------------------- # +# Domain-dir migration # +# --------------------------------------------------------------------------- # + + +DIR_UID_STR = 'CCCCCCCCCCCCCCCCCCCCCC' # 22-char base64-urlsafe -> 16 bytes + + +class TestSetResourceDomainDirMigration: + + def test_happy_path_sends_domain_uid_in_proto(self): + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with _patch_inputs(), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=_capture): + ok = ai_mod.set_resource_domain_dir( + _mock_params(), RESOURCE_UID_STR, DIR_UID_STR, config_uid=CONFIG_UID_STR + ) + assert ok is True + rq = captured['rq'] + assert isinstance(rq, pam_pb2.PAMResourceConfig) + assert len(rq.recordUid) == 16 + assert len(rq.networkUid) == 16 + assert len(rq.domainUid) == 16 + # Must NOT leak into unrelated fields + assert rq.adminUid == b'' + assert rq.jitSettings == b'' + + def test_permission_denied_with_fallback_enabled_calls_legacy(self): + legacy_called = {'count': 0} + + def _legacy(*args, **kwargs): + legacy_called['count'] += 1 + return True + + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + _patch_inputs(), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err), \ + patch.object(ai_mod, '_set_resource_domain_dir_legacy', side_effect=_legacy): + ok = ai_mod.set_resource_domain_dir( + _mock_params(), RESOURCE_UID_STR, DIR_UID_STR, config_uid=CONFIG_UID_STR + ) + assert ok is True + assert legacy_called['count'] == 1 + + def test_permission_denied_with_fallback_disabled_returns_false(self): + err = RouterResponseError(13, 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '0'}), \ + _patch_inputs(), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err), \ + patch.object(ai_mod, '_set_resource_domain_dir_legacy') as legacy_mock: + ok = ai_mod.set_resource_domain_dir( + _mock_params(), RESOURCE_UID_STR, DIR_UID_STR, config_uid=CONFIG_UID_STR + ) + assert ok is False + legacy_mock.assert_not_called() + + +# --------------------------------------------------------------------------- # +# TunnelGraph.edit_tunneling_config migration # +# --------------------------------------------------------------------------- # + + +class TestTunnelGraphEditTunnelingConfigMigration: + """Verify the migration of TunnelGraph.edit_tunneling_config to + `configure_network_graph`. The configuration record's allowedSettings + belong on the network endpoint, not configure_resource — the latter + bypasses per-feature enforcement checks for RBI / rotation / connections + that the network endpoint enforces server-side. + + Policy: `configure_network_graph` honors `KEEPER_DAG_LB_FALLBACK` the + same way `configure_resource` does — strict by default, opt INTO legacy + fallback by setting the env var. + """ + + @staticmethod + def _build_tg(monkeypatch=None): + """Construct a TunnelDAG without going through __init__ (which needs network setup).""" + from keepercommander.commands.tunnel.port_forward.TunnelGraph import TunnelDAG + + tg = TunnelDAG.__new__(TunnelDAG) + tg.params = MagicMock() + tg.record = MagicMock() + tg.record.record_uid = RESOURCE_UID_STR + tg.linking_dag = MagicMock() + config_vertex = MagicMock() + config_vertex.vertex_type = 'PAM_NETWORK' + config_vertex.get_content = MagicMock(return_value={'allowedSettings': {}}) + tg.linking_dag.get_vertex.return_value = config_vertex + return tg, config_vertex + + def test_happy_path_sends_allowed_settings_via_network_graph(self): + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, config_vertex = self._build_tg() + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch.object(tg_mod, 'get_vertex_content', return_value={'allowedSettings': {}}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph', side_effect=_capture): + tg.edit_tunneling_config(connections='on', session_recording='off') + + rq = captured.get('rq') + assert rq is not None, 'router_configure_network_graph was not called' + assert isinstance(rq, router_pb2.PAMNetworkConfigurationRequest) + # recordUid is the config record UID (16 bytes from 22-char b64url) + assert len(rq.recordUid) == 16 + # networkSettings.allowedSettings carries the inner allowed-settings JSON + allowed_bytes = rq.networkSettings.allowedSettings + assert allowed_bytes, 'networkSettings.allowedSettings empty' + as_str = allowed_bytes.decode() + assert 'connections' in as_str + assert 'sessionRecording' in as_str + # No resource/rotation sub-requests on a settings-only call + assert len(rq.resources) == 0 + assert len(rq.rotations) == 0 + + def test_no_changes_skips_configure_network_graph(self): + """When nothing is dirty, the migration must NOT call configure_network_graph.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, _ = self._build_tg() + with patch.object(tg_mod, 'get_vertex_content', return_value={'allowedSettings': {}}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph') as cng_mock: + tg.edit_tunneling_config() # all args None -> nothing dirty + cng_mock.assert_not_called() + + def test_permission_denied_with_fallback_enabled_invokes_legacy_save(self): + """KEEPER_DAG_LB_FALLBACK=1 + RRC denial → legacy DAG-write fires.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, config_vertex = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value={'allowedSettings': {}}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph', side_effect=err): + tg.edit_tunneling_config(connections='on') + config_vertex.add_data.assert_called_once() + tg.linking_dag.save.assert_called_once() + + def test_permission_denied_with_strict_mode_propagates(self): + """KEEPER_DAG_LB_FALLBACK=0 (default) + RRC denial → propagate, no legacy.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, config_vertex = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '0'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value={'allowedSettings': {}}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph', side_effect=err): + with pytest.raises(RouterResponseError): + tg.edit_tunneling_config(connections='on') + config_vertex.add_data.assert_not_called() + tg.linking_dag.save.assert_not_called() + + def test_non_permission_error_propagates(self): + """Transient / generic server errors never trigger downgrade, + regardless of env value.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, config_vertex = self._build_tg() + err = RouterResponseError(99, 'RRC_GENERAL_ERROR', 'broken') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value={'allowedSettings': {}}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph', side_effect=err): + with pytest.raises(RouterResponseError): + tg.edit_tunneling_config(connections='on') + config_vertex.add_data.assert_not_called() + + def test_http_404_with_fallback_enabled_caches_and_short_circuits(self): + """KEEPER_DAG_LB_FALLBACK=1 + HTTP 404 → fall back + mark cache. + Subsequent calls within TTL skip the new API entirely.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + from keepercommander.commands.pam._layer_b import is_layer_b_feature_disabled + from keepercommander.error import KeeperApiError + + tg, config_vertex = self._build_tg() + err = KeeperApiError(404, 'Not Found') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value={'allowedSettings': {}}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph', side_effect=err) as cng_mock: + tg.edit_tunneling_config(connections='on') + assert cng_mock.call_count == 1 + assert is_layer_b_feature_disabled(TEST_ROUTER_URL, 'configure_network_graph') + + tg.edit_tunneling_config(tunneling='on') + assert cng_mock.call_count == 1, 'new API should NOT have been hit a second time' + assert config_vertex.add_data.call_count == 2 + + def test_http_404_with_strict_mode_propagates(self): + """KEEPER_DAG_LB_FALLBACK=0 (default) + HTTP 404 → propagate. No + legacy write, no cache marking.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + from keepercommander.commands.pam._layer_b import is_layer_b_feature_disabled + from keepercommander.error import KeeperApiError + + tg, config_vertex = self._build_tg() + err = KeeperApiError(404, 'Not Found') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '0', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value={'allowedSettings': {}}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph', side_effect=err): + with pytest.raises(Exception): # KeeperApiError + tg.edit_tunneling_config(connections='on') + config_vertex.add_data.assert_not_called() + assert is_layer_b_feature_disabled(TEST_ROUTER_URL, 'configure_network_graph') is False + + def test_rrc_code_does_not_mark_cache(self): + """RRC permission codes fall back per-call (when env=1) but do NOT + mark the cache. Tenant admins can flip features mid-session.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + from keepercommander.commands.pam._layer_b import is_layer_b_feature_disabled + + tg, config_vertex = self._build_tg() + err = RouterResponseError(9, 'RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED', 'feature off') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value={'allowedSettings': {}}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph', side_effect=err) as cng_mock: + tg.edit_tunneling_config(connections='on') + tg.edit_tunneling_config(tunneling='on') + assert cng_mock.call_count == 2 + assert is_layer_b_feature_disabled(TEST_ROUTER_URL, 'configure_network_graph') is False + assert config_vertex.add_data.call_count == 2 + + +# --------------------------------------------------------------------------- # +# TunnelGraph.link_user_to_resource(is_admin=True) migration # +# (The headline credential-linking security target.) # +# --------------------------------------------------------------------------- # + + +USER_UID_STR = 'DDDDDDDDDDDDDDDDDDDDDD' # 22-char base64-urlsafe -> 16 bytes +TARGET_RES_UID_STR = 'EEEEEEEEEEEEEEEEEEEEEE' + + +class TestTunnelGraphLinkUserToResourceMigration: + """Verify TunnelGraph.link_user_to_resource(is_admin=True) -> configure_resource. + + Other flag combinations (belongs_to alone, is_launch_credential without is_admin) + must stay on the legacy path because PAMResourceConfig doesn't carry those flags + independently from adminUid. + """ + + @staticmethod + def _build_tg(): + from keepercommander.commands.tunnel.port_forward.TunnelGraph import TunnelDAG + + tg = TunnelDAG.__new__(TunnelDAG) + tg.params = MagicMock() + tg.record = MagicMock() + tg.record.record_uid = CONFIG_UID_STR + tg.linking_dag = MagicMock() + # Resource exists in the graph and "belongs to" the config (precondition). + resource_vertex = MagicMock() + tg.linking_dag.get_vertex.return_value = resource_vertex + # Mark the resource as belonging to the config so the early-out doesn't fire. + tg.resource_belongs_to_config = MagicMock(return_value=True) + return tg, resource_vertex + + def test_is_admin_true_routes_to_configure_resource(self): + """is_admin=True triggers a configure_resource POST with the right adminUid.""" + tg, _ = self._build_tg() + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=_capture), \ + patch.object(tg, 'link_user') as link_user_mock: + result = tg.link_user_to_resource(USER_UID_STR, TARGET_RES_UID_STR, is_admin=True) + + assert result is None + rq = captured.get('rq') + assert rq is not None, 'router_configure_resource was not called' + assert isinstance(rq, pam_pb2.PAMResourceConfig) + # adminUid must be the user being linked + assert len(rq.adminUid) == 16 + # recordUid is the resource + assert len(rq.recordUid) == 16 + # networkUid is the config (the TunnelGraph's record) + assert len(rq.networkUid) == 16 + # Critical: legacy link_user must NOT be called when configure_resource succeeded + link_user_mock.assert_not_called() + + def test_is_admin_true_with_fallback_invokes_legacy_link_user_on_denial(self): + """RRC_NOT_ALLOWED with fallback ON -> legacy link_user runs.""" + tg, resource_vertex = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=err), \ + patch.object(tg, 'link_user') as link_user_mock: + tg.link_user_to_resource(USER_UID_STR, TARGET_RES_UID_STR, is_admin=True, belongs_to=True) + # Legacy link_user invoked with original args + link_user_mock.assert_called_once() + args, kwargs = link_user_mock.call_args + assert args[0] == USER_UID_STR + # source_vertex should be the resource_vertex (positional arg 1) + assert args[1] is resource_vertex + + def test_is_admin_true_strict_mode_raises_no_legacy(self): + """RRC_NOT_ALLOWED with fallback OFF -> RouterResponseError propagates.""" + tg, _ = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '0'}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=err), \ + patch.object(tg, 'link_user') as link_user_mock: + with pytest.raises(RouterResponseError): + tg.link_user_to_resource(USER_UID_STR, TARGET_RES_UID_STR, is_admin=True) + link_user_mock.assert_not_called() + + def test_is_admin_false_or_none_stays_on_legacy_path(self): + """When is_admin is not True (None/False), configure_resource must NOT be invoked.""" + tg, _ = self._build_tg() + with patch('keepercommander.commands.pam.router_helper.router_configure_resource') as cr_mock, \ + patch.object(tg, 'link_user') as link_user_mock: + tg.link_user_to_resource(USER_UID_STR, TARGET_RES_UID_STR, belongs_to=True) + cr_mock.assert_not_called() + link_user_mock.assert_called_once() + + def test_non_permission_error_raises_no_legacy(self): + """Generic RouterResponseError (not permission-denied) propagates; legacy doesn't run.""" + tg, _ = self._build_tg() + err = RouterResponseError(99, 'RRC_GENERAL_ERROR', 'broken') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=err), \ + patch.object(tg, 'link_user') as link_user_mock: + with pytest.raises(RouterResponseError): + tg.link_user_to_resource(USER_UID_STR, TARGET_RES_UID_STR, is_admin=True) + link_user_mock.assert_not_called() + + def test_resource_not_in_config_returns_false_early(self): + """When resource doesn't belong to config, return False without touching configure_resource.""" + tg, _ = self._build_tg() + tg.resource_belongs_to_config = MagicMock(return_value=False) + with patch('keepercommander.commands.pam.router_helper.router_configure_resource') as cr_mock, \ + patch.object(tg, 'link_user') as link_user_mock: + result = tg.link_user_to_resource(USER_UID_STR, TARGET_RES_UID_STR, is_admin=True) + assert result is False + cr_mock.assert_not_called() + link_user_mock.assert_not_called() + + +# --------------------------------------------------------------------------- # +# TunnelGraph is_iam_user migration via set_record_rotation (no fallback) # +# --------------------------------------------------------------------------- # + + +class TestTunnelGraphIamUserMigration: + """is_iam_user link is permission-checked by calling + set_record_rotation(recordUid=user_uid, noop=False) with NO resourceUid + and NO saasConfiguration. Server enforces edit-access on the pamUser + record. NO legacy fallback - errors must propagate so an unauthorized link + is never written. + """ + + @staticmethod + def _build_tg(): + from keepercommander.commands.tunnel.port_forward.TunnelGraph import TunnelDAG + + tg = TunnelDAG.__new__(TunnelDAG) + tg.params = MagicMock() + tg.record = MagicMock() + tg.record.record_uid = CONFIG_UID_STR + tg.linking_dag = MagicMock() + config_vertex = MagicMock() + tg.linking_dag.get_vertex.return_value = config_vertex + return tg, config_vertex + + def test_link_user_to_config_calls_set_record_rotation_with_noop(self): + """link_user_to_config() permission-checks via set_record_rotation BEFORE + the legacy DAG write.""" + tg, _ = self._build_tg() + captured = {} + + def _capture(params, rq, *args, **kwargs): + captured['rq'] = rq + + with patch('keepercommander.commands.pam.router_helper.router_set_record_rotation_information', + side_effect=_capture), \ + patch.object(tg, 'link_user') as link_user_mock: + tg.link_user_to_config(USER_UID_STR) + + rq = captured.get('rq') + assert rq is not None, 'router_set_record_rotation_information was not called' + assert isinstance(rq, router_pb2.RouterRecordRotationRequest) + # recordUid is the pamUser record (the target of the permission check) + assert len(rq.recordUid) == 16 + # noop=False is what triggers the is_iam_user write server-side + assert rq.noop is False + # NO resourceUid, NO saasConfiguration — these would change the semantics + assert rq.resourceUid == b'' + assert rq.saasConfiguration == b'' + # After permission check succeeds, the legacy link_user mutation still runs + link_user_mock.assert_called_once() + + def test_link_user_to_config_no_fallback_on_permission_denial(self): + """If set_record_rotation fails (permission denied), DO NOT fall back to + the legacy DAG write - propagate so the unauthorized link is never + written. Don't need legacy for iam_user.""" + tg, _ = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + # Even with KEEPER_DAG_LB_FALLBACK=1, the iam_user path does not consult it. + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + patch('keepercommander.commands.pam.router_helper.router_set_record_rotation_information', + side_effect=err), \ + patch.object(tg, 'link_user') as link_user_mock: + with pytest.raises(RouterResponseError): + tg.link_user_to_config(USER_UID_STR) + # Critical: legacy link_user did NOT run + link_user_mock.assert_not_called() + + def test_link_user_to_config_with_options_iam_user_true_permission_checks(self): + """link_user_to_config_with_options(is_iam_user=True) routes through + set_record_rotation BEFORE the local ACL mutation. is_admin and + belongs_to combinations don't disable the iam_user permission check.""" + tg, config_vertex = self._build_tg() + # User vertex stub + user_vertex = MagicMock() + user_vertex.vertex_type = None # forces the vertex_type set path + # First call: config_vertex (record_uid). Second call: user_vertex. + tg.linking_dag.get_vertex.side_effect = [config_vertex, user_vertex] + config_vertex.has.return_value = False # no existing ACL -> bare write + captured = {} + + def _capture(params, rq, *args, **kwargs): + captured['rq'] = rq + + with patch('keepercommander.commands.pam.router_helper.router_set_record_rotation_information', + side_effect=_capture): + tg.link_user_to_config_with_options(USER_UID_STR, is_iam_user=True, belongs_to=True) + + rq = captured.get('rq') + assert rq is not None + assert isinstance(rq, router_pb2.RouterRecordRotationRequest) + assert rq.noop is False + + def test_link_user_to_config_with_options_iam_user_not_true_skips_set_rotation(self): + """When is_iam_user is None/False, set_record_rotation must NOT be called. + Only is_iam_user=True triggers the permission check.""" + tg, config_vertex = self._build_tg() + user_vertex = MagicMock() + user_vertex.vertex_type = None + tg.linking_dag.get_vertex.side_effect = [config_vertex, user_vertex] + config_vertex.has.return_value = False + + with patch('keepercommander.commands.pam.router_helper.router_set_record_rotation_information') as srr_mock: + # belongs_to=True alone, no iam_user + tg.link_user_to_config_with_options(USER_UID_STR, belongs_to=True) + srr_mock.assert_not_called() + + +# --------------------------------------------------------------------------- # +# Layer-B feature-disabled cache: end-to-end behavior at the call sites # +# --------------------------------------------------------------------------- # + + +class TestLayerBFeatureDisabledCache: + """End-to-end behavior of the (host, endpoint) cache at Layer-B call sites. + + Policy: ONLY HTTP 404 marks the cache. RRC permission codes (per-user OR + tenant-level) fall back per-call but never mark the cache, because tenant + admins can flip features mid-session and per-user denials depend on caller + context. + """ + + def test_rrc_codes_never_mark_cache(self): + """Neither RRC_NOT_ALLOWED nor RRC_NOT_ALLOWED_*_NOT_ENABLED marks the + cache — they all fall back per-call when fallback is on, and propagate + when fallback is off, but never poison the cache.""" + from keepercommander.commands.pam._layer_b import is_layer_b_feature_disabled + + for code in ('RRC_NOT_ALLOWED', + 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', + 'RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED'): + err = RouterResponseError(13, code, 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}), \ + _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'CIPHER'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err), \ + patch.object(ai_mod, '_set_resource_keeper_ai_settings_legacy', return_value=True): + ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'high'}, config_uid=CONFIG_UID_STR + ) + assert not is_layer_b_feature_disabled(TEST_ROUTER_URL, 'configure_resource'), \ + f'{code} should NOT mark the cache' + clear_layer_b_feature_cache() + + def test_http_404_marks_cache_and_short_circuits(self): + """HTTP 404 (endpoint not deployed) marks the cache; subsequent calls + skip the new API entirely. Requires KEEPER_DAG_LB_FALLBACK=1 for + configure_resource (mature endpoint — strict by default).""" + from keepercommander.commands.pam._layer_b import is_layer_b_feature_disabled + from keepercommander.error import KeeperApiError + + err = KeeperApiError(404, 'Not Found') + legacy_calls = {'count': 0} + + def _legacy(*args, **kwargs): + legacy_calls['count'] += 1 + return True + + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}), \ + _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'CIPHER'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err) as cr_mock, \ + patch.object(ai_mod, '_set_resource_keeper_ai_settings_legacy', side_effect=_legacy): + # First call: hits new API, gets 404, falls back, marks cache. + ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'high'}, config_uid=CONFIG_UID_STR + ) + assert cr_mock.call_count == 1 + assert is_layer_b_feature_disabled(TEST_ROUTER_URL, 'configure_resource') + + # Second call within TTL: cache hit, skip new API entirely. + ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'critical'}, config_uid=CONFIG_UID_STR + ) + assert cr_mock.call_count == 1, 'new API should NOT have been hit a second time' + assert legacy_calls['count'] == 2, 'legacy path should run both times' + + def test_strict_mode_no_fallback_on_rrc(self): + """With KEEPER_DAG_LB_FALLBACK unset (strict default), RRC denials on + mature endpoints propagate (return False) — no legacy fallback. This + is the security-relevant default.""" + env_without = {k: v for k, v in os.environ.items() if k != 'KEEPER_DAG_LB_FALLBACK'} + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, env_without, clear=True), \ + _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'CIPHER'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err), \ + patch.object(ai_mod, '_set_resource_keeper_ai_settings_legacy') as legacy_mock: + ok = ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'high'}, config_uid=CONFIG_UID_STR + ) + assert ok is False + legacy_mock.assert_not_called() + + def test_cache_ttl_zero_disables_caching(self): + """KEEPER_DAG_LB_FEATURE_CACHE_TTL=0 disables the cache entirely; every + call hits the new API even after a tenant-level denial.""" + err = RouterResponseError(9, 'RRC_NOT_ALLOWED_PAM_CONFIG_FEATURES_NOT_ENABLED', 'pam off') + + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '0'}), \ + _patch_inputs(), \ + patch.object(ai_mod, 'encrypt_aes', return_value=b'CIPHER'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err) as cr_mock, \ + patch.object(ai_mod, '_set_resource_keeper_ai_settings_legacy', return_value=True): + ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'a'}, config_uid=CONFIG_UID_STR + ) + ai_mod.set_resource_keeper_ai_settings( + _mock_params(), RESOURCE_UID_STR, {'level': 'b'}, config_uid=CONFIG_UID_STR + ) + # With cache disabled, BOTH calls hit the new API. + assert cr_mock.call_count == 2 + + +# --------------------------------------------------------------------------- # +# TunnelGraph.set_resource_allowed migration (Phase 2d) # +# --------------------------------------------------------------------------- # + + +class TestTunnelGraphSetResourceAllowedMigration: + """`set_resource_allowed` writes a record's `meta` JSON (allowedSettings + + optional rotateOnTermination/version) for either a resource (is_config=False) + or the PAM Configuration record itself (is_config=True). Migrated to: + - is_config=False -> configure_resource(meta=...) + - is_config=True -> configure_network_graph(networkSettings.allowedSettings=...) + Same fallback policy as edit_tunneling_config: strict by default; env=1 + opts INTO legacy fallback. + """ + + @staticmethod + def _build_tg(): + from keepercommander.commands.tunnel.port_forward.TunnelGraph import TunnelDAG + + tg = TunnelDAG.__new__(TunnelDAG) + tg.params = MagicMock() + tg.record = MagicMock() + # self.record.record_uid is the CONFIG UID per TunnelDAG.__init__. + tg.record.record_uid = CONFIG_UID_STR + tg.linking_dag = MagicMock() + resource_vertex = MagicMock() + # Start with no existing content so `dirty` becomes True for any setting. + resource_vertex.vertex_type = 'PAM_MACHINE' + tg.linking_dag.get_vertex.return_value = resource_vertex + return tg, resource_vertex + + def test_resource_happy_path_uses_configure_resource(self): + """is_config=False with a setting flips -> configure_resource(meta=...) + carrying the full meta JSON. recordUid is the resource, networkUid is + the config.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, _ = self._build_tg() + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch.object(tg_mod, 'get_vertex_content', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=_capture): + tg.set_resource_allowed(RESOURCE_UID_STR, connections='on', rotation='on') + + rq = captured.get('rq') + assert rq is not None, 'router_configure_resource was not called' + assert isinstance(rq, pam_pb2.PAMResourceConfig) + # recordUid is the resource, networkUid is the config + assert len(rq.recordUid) == 16 + assert len(rq.networkUid) == 16 + assert rq.recordUid != rq.networkUid + # meta carries the full JSON shape that legacy would write + meta_str = rq.meta.decode() + assert 'allowedSettings' in meta_str + assert 'connections' in meta_str + assert 'rotation' in meta_str + + def test_config_happy_path_uses_configure_network_graph(self): + """is_config=True flips the call to configure_network_graph with the + inner allowedSettings dict (matches edit_tunneling_config's shape).""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, _ = self._build_tg() + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch.object(tg_mod, 'get_vertex_content', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph', side_effect=_capture): + tg.set_resource_allowed(CONFIG_UID_STR, is_config=True, + connections='on', rotation='on', remote_browser_isolation='on') + + rq = captured.get('rq') + assert rq is not None, 'router_configure_network_graph was not called' + assert isinstance(rq, router_pb2.PAMNetworkConfigurationRequest) + # recordUid is the config record + assert len(rq.recordUid) == 16 + # networkSettings.allowedSettings carries the INNER allowed-settings dict + as_str = rq.networkSettings.allowedSettings.decode() + assert 'connections' in as_str + assert 'rotation' in as_str + assert 'remoteBrowserIsolation' in as_str + # No resource/rotation sub-lists on a settings-only call + assert len(rq.resources) == 0 + assert len(rq.rotations) == 0 + + def test_no_changes_skips_both_endpoints(self): + """When nothing flips (all args None), neither endpoint is called.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, resource_vertex = self._build_tg() + existing = {'allowedSettings': {'connections': True}} + with patch.object(tg_mod, 'get_vertex_content', return_value=existing), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource') as cr_mock, \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph') as cng_mock: + tg.set_resource_allowed(RESOURCE_UID_STR) # all flags None + cr_mock.assert_not_called() + cng_mock.assert_not_called() + resource_vertex.add_data.assert_not_called() + + def test_resource_permission_denied_with_fallback_invokes_legacy(self): + """env=1 + RRC denial on configure_resource -> legacy add_data + save runs.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, resource_vertex = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err): + tg.set_resource_allowed(RESOURCE_UID_STR, connections='on') + resource_vertex.add_data.assert_called_once() + tg.linking_dag.save.assert_called_once() + + def test_resource_strict_mode_propagates(self): + """env unset (strict) + RRC denial -> propagate, no legacy write.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + env_without = {k: v for k, v in os.environ.items() if k != 'KEEPER_DAG_LB_FALLBACK'} + tg, resource_vertex = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, env_without, clear=True), \ + patch.object(tg_mod, 'get_vertex_content', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err): + with pytest.raises(RouterResponseError): + tg.set_resource_allowed(RESOURCE_UID_STR, connections='on') + resource_vertex.add_data.assert_not_called() + tg.linking_dag.save.assert_not_called() + + def test_config_permission_denied_with_fallback_invokes_legacy(self): + """is_config=True + env=1 + RRC denial on configure_network_graph + -> legacy add_data + save runs (same shape as edit_tunneling_config).""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, resource_vertex = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_network_graph', side_effect=err): + tg.set_resource_allowed(CONFIG_UID_STR, is_config=True, rotation='on') + resource_vertex.add_data.assert_called_once() + tg.linking_dag.save.assert_called_once() + + def test_meta_version_v1_payload_shape(self): + """meta_version=1 sends the versioned meta payload (version + allowedSettings + + rotateOnTermination) instead of the bare dict.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + from keepercommander.commands.tunnel.port_forward.TunnelGraph import RESOURCE_META_VERSION_V1 + + tg, _ = self._build_tg() + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch.object(tg_mod, 'get_vertex_content', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=_capture): + tg.set_resource_allowed(RESOURCE_UID_STR, connections='on', + meta_version=RESOURCE_META_VERSION_V1, + rotate_on_termination=True) + + rq = captured.get('rq') + assert rq is not None + meta = json.loads(rq.meta.decode()) + assert meta.get('version') == RESOURCE_META_VERSION_V1 + assert meta.get('rotateOnTermination') is True + assert meta.get('allowedSettings', {}).get('connections') is True + + def test_pam_remote_browser_settings_uses_resource_endpoint(self): + """When called with a non-standard allowed_settings_name (RBI case), + the migration still uses configure_resource(meta=...) — server's + mergeJson handles the custom meta key. The full meta JSON carries + the custom key, not 'allowedSettings'.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, _ = self._build_tg() + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch.object(tg_mod, 'get_vertex_content', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=_capture): + tg.set_resource_allowed(RESOURCE_UID_STR, + remote_browser_isolation='on', + allowed_settings_name='pamRemoteBrowserSettings') + + rq = captured.get('rq') + assert rq is not None + meta = json.loads(rq.meta.decode()) + # Custom key, NOT 'allowedSettings' + assert 'pamRemoteBrowserSettings' in meta + assert meta['pamRemoteBrowserSettings'].get('remoteBrowserIsolation') is True + assert 'allowedSettings' not in meta + + def test_http_404_falls_back_and_caches(self): + """env=1 + HTTP 404 on configure_resource -> fall back to legacy + mark + cache. Subsequent calls within TTL skip the new API.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + from keepercommander.commands.pam._layer_b import is_layer_b_feature_disabled + from keepercommander.error import KeeperApiError + + tg, resource_vertex = self._build_tg() + err = KeeperApiError(404, 'Not Found') + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1', 'KEEPER_DAG_LB_FEATURE_CACHE_TTL': '300'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err) as cr_mock: + tg.set_resource_allowed(RESOURCE_UID_STR, connections='on') + assert cr_mock.call_count == 1 + assert is_layer_b_feature_disabled(TEST_ROUTER_URL, 'configure_resource') + # Second call: cache short-circuits, new API NOT hit. + tg.set_resource_allowed(RESOURCE_UID_STR, tunneling='on') + assert cr_mock.call_count == 1 + assert resource_vertex.add_data.call_count == 2 + + +# --------------------------------------------------------------------------- # +# TunnelGraph.upgrade_resource_meta_to_v1 migration (Phase 2e) # +# --------------------------------------------------------------------------- # + + +class TestTunnelGraphUpgradeResourceMetaToV1Migration: + """`upgrade_resource_meta_to_v1` writes a versioned resource meta payload + {version: 1, allowedSettings: {...}, rotateOnTermination: bool} so the + Vault reads ACL launch credentials. Migrated to configure_resource(meta=...) + -- same shape as set_resource_allowed(meta_version=1). Server's mergeJson + honors the version field with `oldMetaVersion <= newMetaVersion` upgrade + check (UserRest.kt configureResourceGraph). + """ + + @staticmethod + def _build_tg(existing_content=None): + from keepercommander.commands.tunnel.port_forward.TunnelGraph import TunnelDAG + + tg = TunnelDAG.__new__(TunnelDAG) + tg.params = MagicMock() + tg.record = MagicMock() + tg.record.record_uid = CONFIG_UID_STR + tg.linking_dag = MagicMock() + resource_vertex = MagicMock() + tg.linking_dag.get_vertex.return_value = resource_vertex + return tg, resource_vertex + + def test_already_v1_skips_both_endpoints(self): + """If the existing meta already has version >= 1, the upgrade is a no-op.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + from keepercommander.commands.tunnel.port_forward.TunnelGraph import RESOURCE_META_VERSION_V1 + + tg, resource_vertex = self._build_tg() + existing = {'version': RESOURCE_META_VERSION_V1, 'allowedSettings': {}} + with patch.object(tg_mod, 'get_vertex_content', return_value=existing), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource') as cr_mock: + tg.upgrade_resource_meta_to_v1(RESOURCE_UID_STR) + cr_mock.assert_not_called() + resource_vertex.add_data.assert_not_called() + + def test_v0_upgrade_uses_configure_resource(self): + """No version or version=0 -> upgrade to v1 via configure_resource(meta=...). + The proto's meta carries the versioned JSON payload.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + from keepercommander.commands.tunnel.port_forward.TunnelGraph import RESOURCE_META_VERSION_V1 + + tg, _ = self._build_tg() + existing = {'allowedSettings': {'connections': True}} # no version + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch.object(tg_mod, 'get_vertex_content', return_value=existing), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=_capture): + tg.upgrade_resource_meta_to_v1(RESOURCE_UID_STR) + + rq = captured.get('rq') + assert rq is not None, 'router_configure_resource was not called' + assert isinstance(rq, pam_pb2.PAMResourceConfig) + # recordUid is the resource, networkUid is the config + assert len(rq.recordUid) == 16 + assert len(rq.networkUid) == 16 + assert rq.recordUid != rq.networkUid + # meta carries the versioned payload + meta = json.loads(rq.meta.decode()) + assert meta.get('version') == RESOURCE_META_VERSION_V1 + assert 'allowedSettings' in meta + assert meta['allowedSettings'].get('connections') is True + + def test_no_resource_vertex_returns_early(self): + """If get_vertex returns None, no write at all.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, _ = self._build_tg() + tg.linking_dag.get_vertex.return_value = None + with patch.object(tg_mod, 'get_vertex_content', return_value=None), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource') as cr_mock: + tg.upgrade_resource_meta_to_v1(RESOURCE_UID_STR) + cr_mock.assert_not_called() + + def test_permission_denied_with_fallback_invokes_legacy(self): + """env=1 + RRC denial -> legacy add_data + save runs.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + tg, resource_vertex = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED_ENFORCEMENT_NOT_ENABLED', 'denied') + existing = {'allowedSettings': {}} + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': '1'}), \ + patch.object(tg_mod, 'get_vertex_content', return_value=existing), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err): + tg.upgrade_resource_meta_to_v1(RESOURCE_UID_STR) + resource_vertex.add_data.assert_called_once() + tg.linking_dag.save.assert_called_once() + + def test_strict_mode_propagates(self): + """env unset (strict) + RRC denial -> propagate, no legacy.""" + from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod + + env_without = {k: v for k, v in os.environ.items() if k != 'KEEPER_DAG_LB_FALLBACK'} + tg, resource_vertex = self._build_tg() + err = RouterResponseError(13, 'RRC_NOT_ALLOWED', 'denied') + existing = {'allowedSettings': {}} + with patch.dict(os.environ, env_without, clear=True), \ + patch.object(tg_mod, 'get_vertex_content', return_value=existing), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', side_effect=err): + with pytest.raises(RouterResponseError): + tg.upgrade_resource_meta_to_v1(RESOURCE_UID_STR) + resource_vertex.add_data.assert_not_called() diff --git a/unit-tests/pam/test_dag_layer_b_set_record_rotation.py b/unit-tests/pam/test_dag_layer_b_set_record_rotation.py new file mode 100644 index 000000000..19495e8b0 --- /dev/null +++ b/unit-tests/pam/test_dag_layer_b_set_record_rotation.py @@ -0,0 +1,218 @@ +""" +Layer-B `set_record_rotation` tests (plan §13.4 / task #24). + +Covers the `router_set_record_rotation_information` wrapper in +`pam/router_helper.py`, which POSTs `Router.RouterRecordRotationRequest` to +krouter's `/api/user/set_record_rotation` endpoint (UserRest.kt:652-693). + +Krouter-side validation (mirrored in tests below): +- Schedule frequency < 1h -> RRC_GENERAL_ERROR with cron-interval message. +- KA backend failure -> error propagates. +- Scheduler `setup_rotation` HTTP non-OK -> RRC_BAD_REQUEST / RRC_GENERAL_ERROR. +- IAM noop variant + happy path. +""" +import importlib +import os +import sys +from unittest.mock import MagicMock, patch + +import pytest + +sys.path.insert(0, os.path.dirname(__file__)) + +# Pre-warm the import chain (see test_dag_layer_b_configure_resource.py for context). +importlib.import_module('keepercommander.commands.pam_import.keeper_ai_settings') # side-effect: pre-warm + +from keepercommander.commands.pam._layer_b import RouterResponseError # noqa: E402 +from keepercommander.proto import router_pb2 # noqa: E402 + + +REQUESTS_TARGET = 'keepercommander.commands.pam.router_helper.requests.request' + +RECORD_UID = b'\xAA' * 16 +CONFIG_UID = b'\xBB' * 16 +RESOURCE_UID = b'\xCC' * 16 + + +def _mock_params(): + p = MagicMock() + p.config = {'server': 'krouter.test'} + p.session_token = 'BBBBBBBB' + p.rest_context.server_key_id = 7 + os.environ['KROUTER_URL'] = 'https://krouter.test' + os.environ['VERIFY_SSL'] = 'false' + return p + + +def _ok_router_response(): + rs = router_pb2.RouterResponse() + rs.responseCode = router_pb2.RRC_OK + rsp = MagicMock() + rsp.status_code = 200 + rsp.headers = {} + rsp.content = rs.SerializeToString() + rsp.raise_for_status.return_value = None + return rsp + + +def _error_router_response(code_name: str, error_message: str = ''): + rs = router_pb2.RouterResponse() + rs.responseCode = router_pb2.RouterResponseCode.Value(code_name) + rs.errorMessage = error_message + rsp = MagicMock() + rsp.status_code = 200 + rsp.headers = {} + rsp.content = rs.SerializeToString() + rsp.raise_for_status.return_value = None + return rsp + + +# --------------------------------------------------------------------------- # +# URL + body # +# --------------------------------------------------------------------------- # + + +def test_set_record_rotation_hits_correct_url(): + from keepercommander.commands.pam.router_helper import router_set_record_rotation_information + rq = router_pb2.RouterRecordRotationRequest(recordUid=RECORD_UID, configurationUid=CONFIG_UID) + with patch(REQUESTS_TARGET, return_value=_ok_router_response()) as mock_req: + router_set_record_rotation_information(_mock_params(), rq) + url = mock_req.call_args.kwargs.get('url') or mock_req.call_args.args[1] + assert url.endswith('/api/user/set_record_rotation') + + +def test_set_record_rotation_sends_protobuf_body(): + from keepercommander.commands.pam.router_helper import router_set_record_rotation_information + rq = router_pb2.RouterRecordRotationRequest( + recordUid=RECORD_UID, + configurationUid=CONFIG_UID, + resourceUid=RESOURCE_UID, + schedule='0 0 * * *', + ) + with patch(REQUESTS_TARGET, return_value=_ok_router_response()) as mock_req: + router_set_record_rotation_information(_mock_params(), rq) + body = mock_req.call_args.kwargs.get('data') + assert isinstance(body, (bytes, bytearray)) + # Encrypted blob, not JSON + assert not body.startswith(b'{') + + +# --------------------------------------------------------------------------- # +# Proto shape — every field krouter consumes # +# --------------------------------------------------------------------------- # + + +def test_rotation_proto_minimal(): + rq = router_pb2.RouterRecordRotationRequest(recordUid=RECORD_UID) + assert rq.recordUid == RECORD_UID + assert rq.schedule == '' + assert rq.disabled is False + assert rq.noop is False + + +def test_rotation_proto_full_shape_roundtrips(): + rq = router_pb2.RouterRecordRotationRequest( + recordUid=RECORD_UID, + revision=42, + configurationUid=CONFIG_UID, + resourceUid=RESOURCE_UID, + schedule='0 2 * * *', # daily 2am + enterpriseUserId=12345, + pwdComplexity=b'\x14\x04\x04\x04\x04', # length=20, caps=4, digits=4, lower=4, special=4 + disabled=False, + noop=False, + saasConfiguration=b'', + ) + parsed = router_pb2.RouterRecordRotationRequest() + parsed.ParseFromString(rq.SerializeToString()) + assert parsed.recordUid == RECORD_UID + assert parsed.revision == 42 + assert parsed.configurationUid == CONFIG_UID + assert parsed.resourceUid == RESOURCE_UID + assert parsed.schedule == '0 2 * * *' + assert parsed.enterpriseUserId == 12345 + assert parsed.pwdComplexity == b'\x14\x04\x04\x04\x04' + assert parsed.disabled is False + assert parsed.noop is False + + +def test_rotation_proto_iam_noop_variant(): + """IAM users: resourceUid empty, noop=False (links to config directly).""" + rq = router_pb2.RouterRecordRotationRequest( + recordUid=RECORD_UID, + configurationUid=CONFIG_UID, + resourceUid=b'', + noop=False, + ) + assert rq.resourceUid == b'' + assert rq.noop is False + + +def test_rotation_proto_disabled_with_empty_schedule(): + """Clear-schedule case: disabled=True, schedule='[]' (or empty) — krouter clears scheduler.""" + rq = router_pb2.RouterRecordRotationRequest( + recordUid=RECORD_UID, + configurationUid=CONFIG_UID, + disabled=True, + schedule='', + ) + assert rq.disabled is True + assert rq.schedule == '' + + +# --------------------------------------------------------------------------- # +# Error surfacing # +# --------------------------------------------------------------------------- # + + +def test_subhour_schedule_surfaces_general_error(): + """Krouter rejects schedule with frequency < 1h as RRC_GENERAL_ERROR (UserRest.kt:654-658).""" + from keepercommander.commands.pam.router_helper import router_set_record_rotation_information + rq = router_pb2.RouterRecordRotationRequest(recordUid=RECORD_UID, schedule='*/5 * * * *') + with patch(REQUESTS_TARGET, return_value=_error_router_response( + 'RRC_GENERAL_ERROR', 'Cron setup error: interval set to less than one hour')): + with pytest.raises(RouterResponseError) as exc_info: + router_set_record_rotation_information(_mock_params(), rq) + assert exc_info.value.response_code_name == 'RRC_GENERAL_ERROR' + assert 'less than one hour' in str(exc_info.value) + + +def test_ka_backend_failure_propagates(): + """KA (KeeperApp) backend rejection during set_record_rotation propagates.""" + from keepercommander.commands.pam.router_helper import router_set_record_rotation_information + rq = router_pb2.RouterRecordRotationRequest(recordUid=RECORD_UID) + with patch(REQUESTS_TARGET, return_value=_error_router_response( + 'RRC_GENERAL_ERROR', 'KA returned error')): + with pytest.raises(RouterResponseError) as exc_info: + router_set_record_rotation_information(_mock_params(), rq) + assert exc_info.value.response_code_name == 'RRC_GENERAL_ERROR' + + +def test_scheduler_bad_request_surfaces_as_rrc_bad_request(): + """Scheduler `setup_rotation` returning 400 -> RRC_BAD_REQUEST per UserRest.kt:682-684.""" + from keepercommander.commands.pam.router_helper import router_set_record_rotation_information + rq = router_pb2.RouterRecordRotationRequest(recordUid=RECORD_UID, schedule='0 2 * * *') + with patch(REQUESTS_TARGET, return_value=_error_router_response( + 'RRC_BAD_REQUEST', 'scheduler rejected')): + with pytest.raises(RouterResponseError) as exc_info: + router_set_record_rotation_information(_mock_params(), rq) + assert exc_info.value.response_code_name == 'RRC_BAD_REQUEST' + + +# --------------------------------------------------------------------------- # +# Happy path # +# --------------------------------------------------------------------------- # + + +def test_happy_path_returns_without_error(): + from keepercommander.commands.pam.router_helper import router_set_record_rotation_information + rq = router_pb2.RouterRecordRotationRequest( + recordUid=RECORD_UID, + configurationUid=CONFIG_UID, + resourceUid=RESOURCE_UID, + schedule='0 2 * * *', # daily 2am — well over 1h frequency + revision=1, + ) + with patch(REQUESTS_TARGET, return_value=_ok_router_response()): + # Should not raise + router_set_record_rotation_information(_mock_params(), rq) diff --git a/unit-tests/pam/test_dag_multi_sync_load.py b/unit-tests/pam/test_dag_multi_sync_load.py new file mode 100644 index 000000000..38d51a08e --- /dev/null +++ b/unit-tests/pam/test_dag_multi_sync_load.py @@ -0,0 +1,232 @@ +""" +Tests for the per-graph read path in `keeper_dag.DAG._sync_per_graph`. + +When `read_endpoint` is set, the DAG reads via the per-graph endpoint +(`/api/user/graph-sync//multi_sync`). The stream is keyed by the graph's +origin (e.g. the PAM Configuration record UID for TunnelDAG), so +`_sync_per_graph` issues `multi_sync(streamId=self.uid_bytes, syncPoint=0)` +directly — no `get_leafs` discovery step is needed for this caller pattern. + +`Connection.get_leafs` remains available on the transport layer for callers +that *do* start from leaf vertices and want to discover stream roots; it just +isn't exercised by the TunnelDAG load path. + +These tests verify: + - Legacy `graph_id`-only path is untouched (dispatch routes correctly). + - When `read_endpoint` is set, the new path goes straight to multi_sync. + - Per-stream pagination via hasMore advances per-stream syncPoints. + - Wire shape of multi_sync_query. + - End-to-end aggregation of multi_sync into DAG._vertices. +""" +import importlib +import os +import sys +from unittest.mock import MagicMock + +sys.path.insert(0, os.path.dirname(__file__)) + +# Pre-warm the circular-import chain (same pattern as other Layer-B tests). +importlib.import_module('keepercommander.commands.pam_import.keeper_ai_settings') + +from keepercommander.keeper_dag.dag import DAG # noqa: E402 +from keepercommander.keeper_dag.proto import GraphSync_pb2 as gs_pb2 # noqa: E402 +from keepercommander.keeper_dag.types import PamEndpoints # noqa: E402 +from keepercommander.keeper_dag.crypto import bytes_to_urlsafe_str # noqa: E402 + + +ORIGIN_BYTES = b'\xAA' * 16 +ORIGIN_STR = bytes_to_urlsafe_str(ORIGIN_BYTES) + + +# --------------------------------------------------------------------------- # +# Fixtures # +# --------------------------------------------------------------------------- # + + +def _make_dag(read_endpoint=None, graph_id=None, use_read_protobuf=True): + """Build a DAG with a mocked connection so we can assert sync wiring. + + Skips real load — caller invokes _sync_legacy / _sync_per_graph directly. + """ + conn = MagicMock() + conn.use_read_protobuf = use_read_protobuf + conn.use_write_protobuf = use_read_protobuf + conn.get_record_uid.return_value = ORIGIN_STR + conn.get_key_bytes.return_value = ORIGIN_BYTES + + record = MagicMock() + record.record_uid = ORIGIN_STR + record.record_key = ORIGIN_BYTES + + dag = DAG( + conn=conn, + record=record, + read_endpoint=read_endpoint, + write_endpoint=read_endpoint, + graph_id=graph_id, + auto_save=False, + decrypt=False, + fail_on_corrupt=False, + ) + return dag, conn + + +def _multi_sync_result(per_stream) -> bytes: + """Build a serialized GraphSyncMultiResult. + + per_stream: list of (stream_id bytes, sync_point int, has_more bool, + data_items list[GraphSyncDataPlus]) + """ + results = [] + for stream_id, sync_point, has_more, data_items in per_stream: + results.append(gs_pb2.GraphSyncResult( + streamId=stream_id, + syncPoint=sync_point, + hasMore=has_more, + data=data_items or [], + )) + return gs_pb2.GraphSyncMultiResult(results=results).SerializeToString() + + +# --------------------------------------------------------------------------- # +# Dispatch: legacy vs per-graph # +# --------------------------------------------------------------------------- # + + +def test_sync_dispatches_to_legacy_when_read_endpoint_unset(): + """`graph_id=0` only -> dispatch goes to _sync_legacy; multi_sync untouched.""" + dag, conn = _make_dag(read_endpoint=None, graph_id=0) + # Stub sync() to return an empty serialized GraphSyncResult immediately. + conn.sync.return_value = gs_pb2.GraphSyncResult(syncPoint=0).SerializeToString() + + dag._sync(sync_point=0) + + conn.sync.assert_called() + conn.get_leafs.assert_not_called() + conn.multi_sync.assert_not_called() + + +def test_sync_dispatches_to_per_graph_when_read_endpoint_set(): + """`read_endpoint=PamEndpoints.PAM` -> dispatch goes straight to multi_sync.""" + dag, conn = _make_dag(read_endpoint=PamEndpoints.PAM) + conn.multi_sync.return_value = _multi_sync_result([ + (ORIGIN_BYTES, 0, False, []), + ]) + + dag._sync(sync_point=0) + + conn.multi_sync.assert_called_once() + conn.sync.assert_not_called() + # get_leafs is NOT used by this caller pattern. + conn.get_leafs.assert_not_called() + + +# --------------------------------------------------------------------------- # +# _sync_per_graph behavior # +# --------------------------------------------------------------------------- # + + +def test_per_graph_single_round_when_stream_has_no_more(): + """Stream reports hasMore=False -> multi_sync called exactly once.""" + dag, conn = _make_dag(read_endpoint=PamEndpoints.PAM) + conn.multi_sync.return_value = _multi_sync_result([ + (ORIGIN_BYTES, 7, False, []), + ]) + + data, max_sp = dag._sync_per_graph(sync_point=0) + + assert conn.multi_sync.call_count == 1 + assert max_sp == 7 + conn.get_leafs.assert_not_called() + + +def test_per_graph_loops_while_stream_has_more(): + """hasMore=True -> multi_sync invoked again with advanced syncPoint.""" + dag, conn = _make_dag(read_endpoint=PamEndpoints.PAM) + + call_responses = [ + _multi_sync_result([(ORIGIN_BYTES, 10, True, [])]), # still has more + _multi_sync_result([(ORIGIN_BYTES, 20, False, [])]), # done + ] + conn.multi_sync.side_effect = call_responses + + data, max_sp = dag._sync_per_graph(sync_point=0) + + assert conn.multi_sync.call_count == 2 + assert max_sp == 20 + + # Second call should advance syncPoint based on the first round's response. + second_call_query = conn.multi_sync.call_args_list[1].kwargs.get('multi_query') + if second_call_query is None: + second_call_query = conn.multi_sync.call_args_list[1].args[0] + assert second_call_query.queries[0].syncPoint == 10 + + +def test_per_graph_multi_sync_query_wire_shape(): + """multi_sync_query has one GraphSyncQuery for the graph's origin stream.""" + dag, conn = _make_dag(read_endpoint=PamEndpoints.PAM) + conn.multi_sync.return_value = _multi_sync_result([ + (ORIGIN_BYTES, 1, False, []), + ]) + + dag._sync_per_graph(sync_point=0) + + multi_query = conn.multi_sync.call_args.kwargs.get('multi_query') + if multi_query is None: + multi_query = conn.multi_sync.call_args.args[0] + + assert len(multi_query.queries) == 1 + q = multi_query.queries[0] + assert bytes(q.streamId) == ORIGIN_BYTES + assert bytes(q.origin) == ORIGIN_BYTES + assert q.syncPoint == 0 + + +def test_per_graph_aggregates_data_items(): + """All data items in the response land in the returned all_data list.""" + dag, conn = _make_dag(read_endpoint=PamEndpoints.PAM) + + def _item(uid: bytes): + return gs_pb2.GraphSyncDataPlus(data=gs_pb2.GraphSyncData( + type=gs_pb2.GraphSyncDataType.GSE_KEY, + content=b'', + ref=gs_pb2.GraphSyncRef(type=gs_pb2.RefType.RFT_GENERAL, value=uid), + parentRef=gs_pb2.GraphSyncRef(type=gs_pb2.RefType.RFT_GENERAL, value=ORIGIN_BYTES), + )) + + conn.multi_sync.return_value = _multi_sync_result([ + (ORIGIN_BYTES, 5, False, [_item(b'\x01' * 16), + _item(b'\x02' * 16), + _item(b'\x03' * 16)]), + ]) + + data, _ = dag._sync_per_graph(sync_point=0) + assert len(data) == 3 + + +def test_per_graph_passes_read_endpoint_url(): + """multi_sync receives endpoint=self.read_endpoint so the per-graph URL is hit.""" + dag, conn = _make_dag(read_endpoint=PamEndpoints.PAM) + conn.multi_sync.return_value = _multi_sync_result([ + (ORIGIN_BYTES, 0, False, []), + ]) + + dag._sync_per_graph(sync_point=0) + + endpoint = conn.multi_sync.call_args.kwargs.get('endpoint') + # The DAG normalizes Enum -> .value at construction. + assert endpoint == PamEndpoints.PAM.value + + +def test_per_graph_empty_response_returns_no_data(): + """When the server returns an empty stream, all_data is empty and sync_point=initial.""" + dag, conn = _make_dag(read_endpoint=PamEndpoints.PAM) + # Server has nothing for this stream. + conn.multi_sync.return_value = _multi_sync_result([ + (ORIGIN_BYTES, 0, False, []), + ]) + + data, sp = dag._sync_per_graph(sync_point=0) + + assert data == [] + assert sp == 0 diff --git a/unit-tests/pam/test_discovery_common_per_graph_flag.py b/unit-tests/pam/test_discovery_common_per_graph_flag.py new file mode 100644 index 000000000..5ce8dbb65 --- /dev/null +++ b/unit-tests/pam/test_discovery_common_per_graph_flag.py @@ -0,0 +1,232 @@ +""" +Tests for the additive `use_per_graph_endpoints` flag on vendored +`discovery_common` classes. + +Pattern 1 (additive) refactor: each class now accepts +`use_per_graph_endpoints: bool = False`. + +- Default (False) -> legacy single-endpoint transport via `graph_id=PamGraphId.*`. +- Explicit True -> per-graph URL transport via `read_endpoint=PamEndpoints.*` + and `write_endpoint=PamEndpoints.*`. + +Covered classes: Infrastructure, Jobs, Rules, UserService, RecordLink. + +RecordLink is the special case: its endpoint attrs are also forced on when the +underlying connection has the protobuf flags set (`use_read_protobuf` / +`use_write_protobuf`), to preserve existing behavior. +""" +import importlib +import os +import sys +from unittest.mock import MagicMock, patch + +import pytest + +sys.path.insert(0, os.path.dirname(__file__)) + +# Pre-warm the circular-import chain (same pattern as test_dag_layer_b_*). +importlib.import_module('keepercommander.commands.pam_import.keeper_ai_settings') + +from keepercommander.discovery_common.infrastructure import Infrastructure # noqa: E402 +from keepercommander.discovery_common.jobs import Jobs # noqa: E402 +from keepercommander.discovery_common.record_link import RecordLink # noqa: E402 +from keepercommander.discovery_common.rule import Rules # noqa: E402 +from keepercommander.discovery_common.user_service import UserService # noqa: E402 +from keepercommander.keeper_dag.types import PamEndpoints, PamGraphId # noqa: E402 + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + +def _mock_record(): + rec = MagicMock() + rec.title = "TestConfig" + rec.record_uid = b'\xAA' * 16 + return rec + + +def _mock_conn(use_read_protobuf=False, use_write_protobuf=False): + """Mock for the underlying Connection that discovery_common uses.""" + conn = MagicMock() + conn.use_read_protobuf = use_read_protobuf + conn.use_write_protobuf = use_write_protobuf + return conn + + +def _make_fake_dag(): + """Return a MagicMock that walks like a DAG for the lazy property body. + + `has_graph` is set so that RecordLink / Rules don't try to add bootstrap + vertices, and `load` returns 0 (a stable sync point). + """ + dag = MagicMock() + dag.has_graph = True + dag.load.return_value = 0 + dag.uid = b'\xBB' * 16 + return dag + + +def _instantiate_and_capture(cls, module_path, **init_kwargs): + """ + Instantiate `cls`, force the lazy `dag` property, return the kwargs passed + to the patched `DAG(...)` constructor. + """ + # The class imports `DAG` and `get_connection` from its own module + # namespace, so we patch them where they live. + fake_dag = _make_fake_dag() + conn = init_kwargs.pop('_conn', _mock_conn()) + + with patch(f'{module_path}.DAG', return_value=fake_dag) as dag_cls, \ + patch(f'{module_path}.get_connection', return_value=conn): + instance = cls(record=_mock_record(), **init_kwargs) + # Trigger the lazy property. + _ = instance.dag + + assert dag_cls.call_count == 1, "DAG should be constructed exactly once" + _, dag_kwargs = dag_cls.call_args + return instance, dag_kwargs + + +# --------------------------------------------------------------------------- +# Parametrized tests for the four "simple" classes +# (Infrastructure, Jobs, Rules, UserService — same default/opt-in branch shape) +# --------------------------------------------------------------------------- + +SIMPLE_CASES = [ + pytest.param( + Infrastructure, + 'keepercommander.discovery_common.infrastructure', + PamGraphId.INFRASTRUCTURE, + PamEndpoints.INFRASTRUCTURE, + id='Infrastructure', + ), + pytest.param( + Jobs, + 'keepercommander.discovery_common.jobs', + PamGraphId.DISCOVERY_JOBS, + PamEndpoints.DISCOVERY_JOBS, + id='Jobs', + ), + pytest.param( + Rules, + 'keepercommander.discovery_common.rule', + PamGraphId.DISCOVERY_RULES, + PamEndpoints.DISCOVERY_RULES, + id='Rules', + ), + pytest.param( + UserService, + 'keepercommander.discovery_common.user_service', + PamGraphId.SERVICE_LINKS, + PamEndpoints.SERVICE_LINKS, + id='UserService', + ), +] + + +@pytest.mark.parametrize('cls,module_path,expected_graph_id,expected_endpoint', SIMPLE_CASES) +def test_default_uses_legacy_graph_id(cls, module_path, expected_graph_id, expected_endpoint): + """Default (use_per_graph_endpoints=False) passes graph_id, no endpoints.""" + _, dag_kwargs = _instantiate_and_capture(cls, module_path) + + assert dag_kwargs.get('graph_id') is expected_graph_id, \ + f"{cls.__name__} default should pass graph_id={expected_graph_id!r}" + assert 'read_endpoint' not in dag_kwargs, \ + f"{cls.__name__} default must not pass read_endpoint" + assert 'write_endpoint' not in dag_kwargs, \ + f"{cls.__name__} default must not pass write_endpoint" + + +@pytest.mark.parametrize('cls,module_path,expected_graph_id,expected_endpoint', SIMPLE_CASES) +def test_explicit_true_uses_per_graph_endpoints(cls, module_path, expected_graph_id, expected_endpoint): + """Explicit True passes read/write_endpoint, no graph_id.""" + _, dag_kwargs = _instantiate_and_capture( + cls, module_path, use_per_graph_endpoints=True + ) + + assert dag_kwargs.get('read_endpoint') is expected_endpoint, \ + f"{cls.__name__}(use_per_graph_endpoints=True) should pass read_endpoint={expected_endpoint!r}" + assert dag_kwargs.get('write_endpoint') is expected_endpoint, \ + f"{cls.__name__}(use_per_graph_endpoints=True) should pass write_endpoint={expected_endpoint!r}" + assert 'graph_id' not in dag_kwargs, \ + f"{cls.__name__}(use_per_graph_endpoints=True) must not pass graph_id" + + +@pytest.mark.parametrize('cls,module_path,expected_graph_id,expected_endpoint', SIMPLE_CASES) +def test_flag_is_persisted_on_instance(cls, module_path, expected_graph_id, expected_endpoint): + """The flag is stored on the instance so callers / tests can introspect it.""" + instance, _ = _instantiate_and_capture(cls, module_path, use_per_graph_endpoints=True) + assert instance.use_per_graph_endpoints is True + + instance2, _ = _instantiate_and_capture(cls, module_path) + assert instance2.use_per_graph_endpoints is False + + +# --------------------------------------------------------------------------- +# RecordLink — special case: always passes graph_id=PAM, *and* the endpoints +# are forced on when either (a) the flag is True or (b) the underlying +# connection has protobuf enabled. +# --------------------------------------------------------------------------- + +RECORD_LINK_MODULE = 'keepercommander.discovery_common.record_link' + + +def test_record_link_default_no_endpoints(): + """Plain default: no protobuf, no opt-in -> both endpoint attrs are None.""" + instance, dag_kwargs = _instantiate_and_capture(RecordLink, RECORD_LINK_MODULE) + + assert instance.use_per_graph_endpoints is False + assert instance.write_endpoint is None + assert instance.read_endpoint is None + # RecordLink always passes graph_id=PamGraphId.PAM (unique to this class). + assert dag_kwargs.get('graph_id') is PamGraphId.PAM + assert dag_kwargs.get('write_endpoint') is None + assert dag_kwargs.get('read_endpoint') is None + + +def test_record_link_explicit_true_sets_pam_endpoints(): + """Opt-in: both endpoints become PamEndpoints.PAM.""" + instance, dag_kwargs = _instantiate_and_capture( + RecordLink, RECORD_LINK_MODULE, use_per_graph_endpoints=True, + ) + + assert instance.use_per_graph_endpoints is True + assert instance.write_endpoint is PamEndpoints.PAM + assert instance.read_endpoint is PamEndpoints.PAM + assert dag_kwargs.get('write_endpoint') is PamEndpoints.PAM + assert dag_kwargs.get('read_endpoint') is PamEndpoints.PAM + # graph_id is still passed (RecordLink's existing behavior — protobuf + # transport keys off the endpoints, legacy keys off graph_id). + assert dag_kwargs.get('graph_id') is PamGraphId.PAM + + +def test_record_link_write_protobuf_alone_sets_write_endpoint(): + """`conn.use_write_protobuf=True` alone -> write_endpoint=PAM, read=None.""" + conn = _mock_conn(use_read_protobuf=False, use_write_protobuf=True) + instance, _ = _instantiate_and_capture(RecordLink, RECORD_LINK_MODULE, _conn=conn) + + assert instance.use_per_graph_endpoints is False + assert instance.write_endpoint is PamEndpoints.PAM + assert instance.read_endpoint is None + + +def test_record_link_read_protobuf_alone_sets_read_endpoint(): + """`conn.use_read_protobuf=True` alone -> read_endpoint=PAM, write=None.""" + conn = _mock_conn(use_read_protobuf=True, use_write_protobuf=False) + instance, _ = _instantiate_and_capture(RecordLink, RECORD_LINK_MODULE, _conn=conn) + + assert instance.use_per_graph_endpoints is False + assert instance.write_endpoint is None + assert instance.read_endpoint is PamEndpoints.PAM + + +def test_record_link_flag_takes_precedence_over_no_protobuf(): + """Opt-in True even with no protobuf on conn -> both endpoints set.""" + conn = _mock_conn(use_read_protobuf=False, use_write_protobuf=False) + instance, _ = _instantiate_and_capture( + RecordLink, RECORD_LINK_MODULE, _conn=conn, use_per_graph_endpoints=True, + ) + + assert instance.write_endpoint is PamEndpoints.PAM + assert instance.read_endpoint is PamEndpoints.PAM diff --git a/unit-tests/pam/test_get_config_uid_local_vault.py b/unit-tests/pam/test_get_config_uid_local_vault.py new file mode 100644 index 000000000..f7780d3fb --- /dev/null +++ b/unit-tests/pam/test_get_config_uid_local_vault.py @@ -0,0 +1,175 @@ +""" +Tests for ``get_config_uid_from_local_vault`` and the ``get_config_uid`` flow +that now tries the local scan first. + +The local scan resolves resource_uid -> config_uid by enumerating PAM +Configuration vault records (record_version=6) and finding the one whose +`pamResources.resourceRef` list contains the resource_uid. This is Web Vault +parity — zero network, no gateway required. The legacy `get_dag_leafs` path +remains as a fallback for cold-vault scenarios. +""" +import importlib +import os +import sys +from unittest.mock import MagicMock, patch + +sys.path.insert(0, os.path.dirname(__file__)) + +# Pre-warm the circular-import chain. +importlib.import_module('keepercommander.commands.pam_import.keeper_ai_settings') + +from keepercommander.commands.tunnel.port_forward import tunnel_helpers # noqa: E402 +from keepercommander import vault # noqa: E402 + + +RESOURCE_UID = 'RESOURCE_UID_1111111111' +OTHER_RESOURCE_UID = 'RESOURCE_UID_2222222222' +CONFIG_UID = 'CONFIG_UID_AAAAAAAAAAAAA' +OTHER_CONFIG_UID = 'CONFIG_UID_BBBBBBBBBBBBB' + + +# --------------------------------------------------------------------------- # +# Fixtures # +# --------------------------------------------------------------------------- # + + +def _make_config_record(record_uid, resource_refs): + """ + Build a stand-in for a PAM Configuration TypedRecord whose `pamResources` + typed field carries the given resourceRef list. + """ + rec = MagicMock(spec=vault.TypedRecord) + rec.record_uid = record_uid + rec.record_type = 'pamNetworkConfiguration' + + field = MagicMock() + field.get_default_value.return_value = { + 'controllerUid': 'GATEWAY_UID', + 'folderUid': '', + 'resourceRef': list(resource_refs), + } + rec.get_typed_field.return_value = field + return rec + + +# --------------------------------------------------------------------------- # +# get_config_uid_from_local_vault — happy paths # +# --------------------------------------------------------------------------- # + + +def test_local_vault_finds_config_for_resource(): + """Returns config_uid when a local PAM config's resourceRef contains the resource_uid.""" + records = [ + _make_config_record(OTHER_CONFIG_UID, resource_refs=[OTHER_RESOURCE_UID]), + _make_config_record(CONFIG_UID, resource_refs=[RESOURCE_UID, 'unrelated_uid']), + ] + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter(records)): + result = tunnel_helpers.get_config_uid_from_local_vault(params, RESOURCE_UID) + assert result == CONFIG_UID + + +def test_local_vault_returns_first_match(): + """If multiple configs (shouldn't happen in practice) list the resource, returns first encountered.""" + records = [ + _make_config_record(CONFIG_UID, resource_refs=[RESOURCE_UID]), + _make_config_record(OTHER_CONFIG_UID, resource_refs=[RESOURCE_UID]), + ] + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter(records)): + result = tunnel_helpers.get_config_uid_from_local_vault(params, RESOURCE_UID) + assert result == CONFIG_UID + + +# --------------------------------------------------------------------------- # +# get_config_uid_from_local_vault — no-match / edge cases # +# --------------------------------------------------------------------------- # + + +def test_local_vault_returns_none_when_no_config_owns_resource(): + """No config has the resource in its resourceRef -> None (caller falls back to get_dag_leafs).""" + records = [ + _make_config_record(OTHER_CONFIG_UID, resource_refs=[OTHER_RESOURCE_UID]), + ] + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter(records)): + result = tunnel_helpers.get_config_uid_from_local_vault(params, RESOURCE_UID) + assert result is None + + +def test_local_vault_returns_none_when_empty_vault(): + records = [] + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter(records)): + result = tunnel_helpers.get_config_uid_from_local_vault(params, RESOURCE_UID) + assert result is None + + +def test_local_vault_returns_none_for_blank_record_uid(): + """Defensive: empty/None resource_uid short-circuits without iterating the vault.""" + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records') as mock_find: + assert tunnel_helpers.get_config_uid_from_local_vault(params, None) is None + assert tunnel_helpers.get_config_uid_from_local_vault(params, '') is None + mock_find.assert_not_called() + + +def test_local_vault_skips_records_without_pam_resources_field(): + """Records missing the pamResources field are skipped, not fatal.""" + bad_rec = MagicMock(spec=vault.TypedRecord) + bad_rec.record_uid = 'BAD_REC' + bad_rec.get_typed_field.return_value = None # field missing + + good_rec = _make_config_record(CONFIG_UID, resource_refs=[RESOURCE_UID]) + + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter([bad_rec, good_rec])): + result = tunnel_helpers.get_config_uid_from_local_vault(params, RESOURCE_UID) + assert result == CONFIG_UID + + +def test_local_vault_skips_non_typed_records(): + """find_records may return non-TypedRecord items (e.g. PasswordRecord); skip them.""" + non_typed = MagicMock(spec=[]) # no TypedRecord interface + good_rec = _make_config_record(CONFIG_UID, resource_refs=[RESOURCE_UID]) + + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter([non_typed, good_rec])): + result = tunnel_helpers.get_config_uid_from_local_vault(params, RESOURCE_UID) + assert result == CONFIG_UID + + +# --------------------------------------------------------------------------- # +# get_config_uid — wires local scan in front of get_dag_leafs # +# --------------------------------------------------------------------------- # + + +def test_get_config_uid_uses_local_match_and_skips_get_dag_leafs(): + """When local scan finds a config, get_dag_leafs is never called.""" + records = [_make_config_record(CONFIG_UID, resource_refs=[RESOURCE_UID])] + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter(records)), \ + patch.object(tunnel_helpers, 'get_dag_leafs') as mock_dl: + result = tunnel_helpers.get_config_uid(params, b'tok', b'tk', RESOURCE_UID) + assert result == CONFIG_UID + mock_dl.assert_not_called() + + +def test_get_config_uid_falls_back_to_get_dag_leafs_when_no_local_match(): + """No local match -> legacy get_dag_leafs path; whatever it returns flows through.""" + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter([])), \ + patch.object(tunnel_helpers, 'get_dag_leafs', + return_value=[{'type': 'rec', 'value': CONFIG_UID, 'name': None}]) as mock_dl: + result = tunnel_helpers.get_config_uid(params, b'tok', b'tk', RESOURCE_UID) + assert result == CONFIG_UID + mock_dl.assert_called_once() + + +def test_get_config_uid_returns_none_when_neither_path_resolves(): + """No local match and get_dag_leafs returns None -> None propagates.""" + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter([])), \ + patch.object(tunnel_helpers, 'get_dag_leafs', return_value=None): + result = tunnel_helpers.get_config_uid(params, b'tok', b'tk', RESOURCE_UID) + assert result is None diff --git a/unit-tests/pam/test_kcm_import.py b/unit-tests/pam/test_kcm_import.py index e8b5db5ad..59f0f980b 100644 --- a/unit-tests/pam/test_kcm_import.py +++ b/unit-tests/pam/test_kcm_import.py @@ -11,6 +11,7 @@ import json import os +import platform import sys import tempfile import unittest @@ -1281,7 +1282,17 @@ class TestOutputFilePermissions(unittest.TestCase): @patch('keepercommander.commands.pam_import.kcm_import.getpass.getpass', return_value='pass') def test_output_file_owner_only(self, mock_getpass, MockConnector): - """--output file must have 0o600 permissions (owner read/write only).""" + """--output file must be locked to owner-only access. + + POSIX: verifies `os.stat().st_mode & 0o777 == 0o600` (the kernel honors + the mode passed to `os.open`). + + Windows: Python's `os.stat().st_mode` reflects DOS attributes, not the + NTFS DACL, so a POSIX-style mode comparison can't see the icacls + hardening. Instead, verify via the `icacls` CLI that the principals + `set_file_permissions` strips (NT AUTHORITY\\SYSTEM, BUILTIN\\Administrators) + are absent — the inheritance-r + remove + owner-only-grant chain ran. + """ mock_conn = MockConnector.return_value mock_conn.extract_groups.return_value = [] mock_conn.extract_connections.return_value = ([ @@ -1290,7 +1301,6 @@ def test_output_file_owner_only(self, mock_getpass, MockConnector): cmd = PAMProjectKCMImportCommand() params = MagicMock() - import stat tmp_dir = tempfile.mkdtemp() output_path = os.path.join(tmp_dir, 'test_output.json') try: @@ -1298,9 +1308,24 @@ def test_output_file_owner_only(self, mock_getpass, MockConnector): db_host='127.0.0.1', output=output_path) - file_mode = os.stat(output_path).st_mode & 0o777 - self.assertEqual(file_mode, 0o600, - f'Expected 0o600, got {oct(file_mode)}') + if platform.system() == 'Windows': + import subprocess + acl_dump = subprocess.run( + ['icacls', output_path], + capture_output=True, text=True, check=True, + ).stdout + self.assertNotIn( + 'NT AUTHORITY\\SYSTEM', acl_dump, + f'SYSTEM should have been removed but is still in the ACL:\n{acl_dump}', + ) + self.assertNotIn( + 'BUILTIN\\Administrators', acl_dump, + f'Administrators should have been removed but is still in the ACL:\n{acl_dump}', + ) + else: + file_mode = os.stat(output_path).st_mode & 0o777 + self.assertEqual(file_mode, 0o600, + f'Expected 0o600, got {oct(file_mode)}') finally: if os.path.exists(output_path): os.unlink(output_path) @@ -2291,7 +2316,6 @@ def test_output_file_real_db(self): cmd = PAMProjectKCMImportCommand() params = MagicMock() - import stat tmp_dir = tempfile.mkdtemp() output_path = os.path.join(tmp_dir, 'kcm_export.json') try: diff --git a/unit-tests/pam/test_set_launch_credentials.py b/unit-tests/pam/test_set_launch_credentials.py new file mode 100644 index 000000000..af16ad03f --- /dev/null +++ b/unit-tests/pam/test_set_launch_credentials.py @@ -0,0 +1,275 @@ +""" +Tests for ``TunnelDAG.set_launch_credentials`` — the unified Layer-B path that +collapses (clear + link + meta-upgrade) into a single permission-checked +configure_resource call. + +Behavior matrix: + +| Case | configure_resource sent? | connectUsers.uids | meta has version=1? | local link_user(belongs_to=True) follow-up? | +| --------------------------------- | ------------------------ | ----------------------- | ------------------- | ------------------------------------------- | +| set, Layer-B enabled, success | yes | [launch_uid bytes] | yes | yes (preserve legacy parity) | +| clear, Layer-B enabled, success | yes | [] | yes | no (no launch user to mark) | +| set, Layer-B feature-disabled | no | n/a | n/a | legacy fallback (link+clear+meta) | +| set, Layer-B RRC_NOT_ALLOWED + FB | yes (then fall back) | n/a | n/a | legacy fallback | +| set, RRC_NOT_ALLOWED + FB off | yes (raises) | n/a | n/a | none — exception propagates | +""" +import importlib +import json +import os +import sys +from unittest.mock import MagicMock, patch + +import pytest + +sys.path.insert(0, os.path.dirname(__file__)) + +# Pre-warm the circular-import chain (same pattern as other Layer-B tests). +importlib.import_module('keepercommander.commands.pam_import.keeper_ai_settings') + +from keepercommander.commands.tunnel.port_forward.TunnelGraph import ( # noqa: E402 + TunnelDAG, RESOURCE_META_VERSION_V1, +) +from keepercommander.commands.pam._layer_b import RouterResponseError # noqa: E402 + + +RESOURCE_UID = 'AAAAAAAAAAAAAAAAAAAAAA' # 22-char base64url, decodes to 16 bytes +NETWORK_UID = 'BBBBBBBBBBBBBBBBBBBBBB' +LAUNCH_UID = 'CCCCCCCCCCCCCCCCCCCCCC' + + +# --------------------------------------------------------------------------- # +# Test fixture: build a minimal TunnelDAG-shaped object without going through # +# the full constructor (which requires a live KeeperParams + DAG connection). # +# --------------------------------------------------------------------------- # + + +def _make_tdag(initial_meta=None): + """ + Construct a TunnelDAG-shaped object directly (bypassing __init__) and + populate just the attributes that `set_launch_credentials` touches. + + `initial_meta` is the current `meta` content the resource vertex returns + via `get_vertex_content`. None means a brand-new (version-0) resource. + """ + tdag = TunnelDAG.__new__(TunnelDAG) + tdag.params = MagicMock() + tdag.params.config = {'server': 'krouter.test'} + tdag.record = MagicMock() + tdag.record.record_uid = NETWORK_UID + + # Resource vertex mock: get_vertex_content returns initial_meta. + resource_vertex = MagicMock() + resource_vertex.content_as_dict = initial_meta + + # linking_dag.get_vertex(uid) -> resource_vertex + tdag.linking_dag = MagicMock() + tdag.linking_dag.get_vertex.return_value = resource_vertex + + # Spy/track link_user follow-up calls so the SET success path is observable. + tdag.link_user = MagicMock() + # Spy/track legacy fallback methods. + tdag.clear_launch_credential_for_resource = MagicMock() + tdag.link_user_to_resource = MagicMock() + tdag.upgrade_resource_meta_to_v1 = MagicMock() + return tdag, resource_vertex + + +# --------------------------------------------------------------------------- # +# Layer-B happy paths # +# --------------------------------------------------------------------------- # + + +def test_set_path_builds_proto_with_launch_uid_and_meta_v1(): + """SET path: connectUsers.uids has the launch_uid; meta has version=1.""" + tdag, _ = _make_tdag(initial_meta=None) + + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + return None + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=False), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=_capture): + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=LAUNCH_UID) + + rq = captured['rq'] + assert len(rq.recordUid) == 16 + assert len(rq.networkUid) == 16 + assert list(rq.connectUsers.uids) == [bytes(rq.connectUsers.uids[0])] + assert len(rq.connectUsers.uids) == 1 + assert len(rq.connectUsers.uids[0]) == 16 # decoded launch_uid + meta = json.loads(rq.meta.decode()) + assert meta['version'] == RESOURCE_META_VERSION_V1 + # belongs_to=True follow-up fires only for SET path. + tdag.link_user.assert_called_once() + args, kwargs = tdag.link_user.call_args + assert args[0] == LAUNCH_UID + assert kwargs.get('belongs_to') is True + # No legacy fallback paths invoked. + tdag.clear_launch_credential_for_resource.assert_not_called() + tdag.link_user_to_resource.assert_not_called() + tdag.upgrade_resource_meta_to_v1.assert_not_called() + + +def test_clear_path_builds_proto_with_empty_uids(): + """CLEAR path: connectUsers wrapper is set but uids is empty; meta upgraded.""" + tdag, _ = _make_tdag(initial_meta={'allowedSettings': {'connections': True}}) + + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + return None + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=False), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=_capture): + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=None) + + rq = captured['rq'] + # Wrapper present (hasConnectUsers()==true server-side), uids list empty. + assert len(rq.connectUsers.uids) == 0 + meta = json.loads(rq.meta.decode()) + assert meta['version'] == RESOURCE_META_VERSION_V1 + assert meta['allowedSettings'] == {'connections': True} + # CLEAR path doesn't do a belongs_to follow-up. + tdag.link_user.assert_not_called() + + +def test_set_path_preserves_existing_meta_fields(): + """The meta-upgrade carries existing allowedSettings / rotateOnTermination.""" + initial = { + 'version': 0, + 'allowedSettings': {'connections': True, 'rotation': False}, + 'rotateOnTermination': True, + } + tdag, _ = _make_tdag(initial_meta=initial) + + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=False), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=_capture): + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=LAUNCH_UID) + + meta = json.loads(captured['rq'].meta.decode()) + assert meta['version'] == RESOURCE_META_VERSION_V1 + assert meta['allowedSettings'] == {'connections': True, 'rotation': False} + assert meta['rotateOnTermination'] is True + + +# --------------------------------------------------------------------------- # +# Feature-disabled fallback (no Layer-B attempt at all) # +# --------------------------------------------------------------------------- # + + +def test_set_path_feature_disabled_uses_legacy_sequence(): + """When Layer-B is feature-disabled, fall straight through to legacy 3-op path.""" + tdag, _ = _make_tdag(initial_meta=None) + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=True), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource') as mock_cr: + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=LAUNCH_UID) + mock_cr.assert_not_called() + + tdag.clear_launch_credential_for_resource.assert_called_once_with( + RESOURCE_UID, exclude_user_uid=LAUNCH_UID, + ) + tdag.link_user_to_resource.assert_called_once() + args, kwargs = tdag.link_user_to_resource.call_args + assert args[0] == LAUNCH_UID + assert kwargs.get('is_launch_credential') is True + assert kwargs.get('belongs_to') is True + tdag.upgrade_resource_meta_to_v1.assert_called_once_with(RESOURCE_UID) + + +def test_clear_path_feature_disabled_uses_legacy_sequence(): + """CLEAR path under feature-disabled: clear (no exclusion) + meta upgrade, no link.""" + tdag, _ = _make_tdag(initial_meta=None) + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=True), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource') as mock_cr: + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=None) + mock_cr.assert_not_called() + + tdag.clear_launch_credential_for_resource.assert_called_once_with( + RESOURCE_UID, exclude_user_uid=None, + ) + tdag.link_user_to_resource.assert_not_called() + tdag.upgrade_resource_meta_to_v1.assert_called_once_with(RESOURCE_UID) + + +# --------------------------------------------------------------------------- # +# RRC_NOT_ALLOWED fallback behavior # +# --------------------------------------------------------------------------- # + + +def test_set_path_rrc_not_allowed_falls_back_when_env_enabled(): + """On RRC_NOT_ALLOWED with KEEPER_DAG_LB_FALLBACK on, fall through to legacy.""" + tdag, _ = _make_tdag(initial_meta=None) + + err = RouterResponseError(403, 'RRC_NOT_ALLOWED', 'denied') + + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': 'true'}), \ + patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=False), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=err): + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=LAUNCH_UID) + + tdag.clear_launch_credential_for_resource.assert_called_once() + tdag.link_user_to_resource.assert_called_once() + tdag.upgrade_resource_meta_to_v1.assert_called_once() + # Success path's belongs_to=True follow-up was NOT taken (we never reached it). + tdag.link_user.assert_not_called() + + +def test_set_path_rrc_not_allowed_raises_when_env_disabled(): + """On RRC_NOT_ALLOWED with fallback OFF, the exception propagates (strict mode).""" + tdag, _ = _make_tdag(initial_meta=None) + + err = RouterResponseError(403, 'RRC_NOT_ALLOWED', 'denied') + + with patch.dict(os.environ, {'KEEPER_DAG_LB_FALLBACK': 'false'}), \ + patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=False), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=err): + with pytest.raises(RouterResponseError): + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=LAUNCH_UID) + + # No fallback or follow-up writes. + tdag.clear_launch_credential_for_resource.assert_not_called() + tdag.link_user_to_resource.assert_not_called() + tdag.upgrade_resource_meta_to_v1.assert_not_called() + tdag.link_user.assert_not_called() + + +# --------------------------------------------------------------------------- # +# Edge cases # +# --------------------------------------------------------------------------- # + + +def test_missing_resource_vertex_is_no_op(): + """If the resource vertex doesn't exist locally, set_launch_credentials returns silently.""" + tdag, _ = _make_tdag(initial_meta=None) + tdag.linking_dag.get_vertex.return_value = None + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=False), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource') as mock_cr: + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=LAUNCH_UID) + mock_cr.assert_not_called() + + tdag.link_user.assert_not_called() + tdag.clear_launch_credential_for_resource.assert_not_called() + tdag.upgrade_resource_meta_to_v1.assert_not_called() diff --git a/unit-tests/test_email_service.py b/unit-tests/test_email_service.py index 498ebbf28..3c8b9e0b8 100644 --- a/unit-tests/test_email_service.py +++ b/unit-tests/test_email_service.py @@ -1,5 +1,3 @@ -import os -import sys import unittest from unittest.mock import patch, MagicMock, mock_open from keepercommander.email_service import ( @@ -308,7 +306,7 @@ def test_sendgrid_missing_library_raises_error(self): """Test SendGrid raises ImportError if library not installed""" # This will fail if sendgrid is not installed (expected in test environment) try: - provider = SendGridEmailProvider(self.config) + SendGridEmailProvider(self.config) except ImportError as e: self.assertIn("SendGrid requires additional dependencies", str(e)) @@ -333,7 +331,7 @@ def test_ses_missing_library_raises_error(self): """Test SES raises ImportError if boto3 not installed""" # This will fail if boto3 is not installed (expected in test environment) try: - provider = SESEmailProvider(self.config) + SESEmailProvider(self.config) except ImportError as e: self.assertIn("AWS SES requires additional dependencies", str(e)) @@ -464,7 +462,7 @@ def test_smtp_validation_case_insensitive(self): self.assertTrue(is_valid) self.assertIsNone(error_message) - @patch('builtins.__import__') + @patch('keepercommander.email_service.importlib.import_module') def test_sendgrid_validation_success(self, mock_import): """Test SendGrid validation succeeds when library is installed""" # Mock successful import of sendgrid @@ -474,7 +472,7 @@ def test_sendgrid_validation_success(self, mock_import): self.assertTrue(is_valid) self.assertIsNone(error_message) - @patch('builtins.__import__', side_effect=ImportError("No module named 'sendgrid'")) + @patch('keepercommander.email_service.importlib.import_module', side_effect=ImportError("No module named 'sendgrid'")) def test_sendgrid_validation_failure(self, mock_import): """Test SendGrid validation fails when library is missing""" is_valid, error_message = validate_email_provider_dependencies('sendgrid') @@ -483,7 +481,7 @@ def test_sendgrid_validation_failure(self, mock_import): self.assertIn('sendgrid', error_message.lower()) self.assertIn('pip install keepercommander[email-sendgrid]', error_message) - @patch('builtins.__import__') + @patch('keepercommander.email_service.importlib.import_module') def test_ses_validation_success(self, mock_import): """Test SES validation succeeds when boto3 is installed""" mock_import.return_value = MagicMock() @@ -492,7 +490,7 @@ def test_ses_validation_success(self, mock_import): self.assertTrue(is_valid) self.assertIsNone(error_message) - @patch('builtins.__import__', side_effect=ImportError("No module named 'boto3'")) + @patch('keepercommander.email_service.importlib.import_module', side_effect=ImportError("No module named 'boto3'")) def test_ses_validation_failure(self, mock_import): """Test SES validation fails when boto3 is missing""" is_valid, error_message = validate_email_provider_dependencies('ses') @@ -501,7 +499,7 @@ def test_ses_validation_failure(self, mock_import): self.assertIn('boto3', error_message.lower()) self.assertIn('pip install keepercommander[email-ses]', error_message) - @patch('builtins.__import__') + @patch('keepercommander.email_service.importlib.import_module') def test_gmail_oauth_validation_success(self, mock_import): """Test Gmail OAuth validation succeeds when Google libraries are installed""" mock_import.return_value = MagicMock() @@ -510,7 +508,7 @@ def test_gmail_oauth_validation_success(self, mock_import): self.assertTrue(is_valid) self.assertIsNone(error_message) - @patch('builtins.__import__', side_effect=ImportError("No module named 'google.auth'")) + @patch('keepercommander.email_service.importlib.import_module', side_effect=ImportError("No module named 'google.auth'")) def test_gmail_oauth_validation_failure(self, mock_import): """Test Gmail OAuth validation fails when Google libraries are missing""" is_valid, error_message = validate_email_provider_dependencies('gmail-oauth') @@ -520,7 +518,7 @@ def test_gmail_oauth_validation_failure(self, mock_import): self.assertIn('pip install keepercommander[email-gmail-oauth]', error_message) self.assertIn('google-api-python-client', error_message) - @patch('builtins.__import__') + @patch('keepercommander.email_service.importlib.import_module') def test_microsoft_oauth_validation_success(self, mock_import): """Test Microsoft OAuth validation succeeds when msal is installed""" mock_import.return_value = MagicMock() @@ -529,7 +527,7 @@ def test_microsoft_oauth_validation_success(self, mock_import): self.assertTrue(is_valid) self.assertIsNone(error_message) - @patch('builtins.__import__', side_effect=ImportError("No module named 'msal'")) + @patch('keepercommander.email_service.importlib.import_module', side_effect=ImportError("No module named 'msal'")) def test_microsoft_oauth_validation_failure(self, mock_import): """Test Microsoft OAuth validation fails when msal is missing""" is_valid, error_message = validate_email_provider_dependencies('microsoft-oauth') From d6c128330f60f74dbb42d9d76f0234239e1edea5 Mon Sep 17 00:00:00 2001 From: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> Date: Wed, 3 Jun 2026 17:07:55 -0500 Subject: [PATCH 16/23] Merge DAG overlap onto release: keeper_dag parity+multi_sync, local-vault config_uid, set_launch_credentials, USE_LOCAL_DAG warning --- keepercommander/cli.py | 13 ++++ .../tunnel/port_forward/tunnel_helpers.py | 48 ++++++++++++- .../commands/tunnel_and_connections.py | 9 ++- .../keeper_dag/connection/local.py | 71 +++++++++++++++---- keepercommander/keeper_dag/utils.py | 19 +++-- unit-tests/test_keeper_dag_local_perms.py | 35 +++++++-- 6 files changed, 161 insertions(+), 34 deletions(-) diff --git a/keepercommander/cli.py b/keepercommander/cli.py index 6edd16969..09abef0a9 100644 --- a/keepercommander/cli.py +++ b/keepercommander/cli.py @@ -796,6 +796,19 @@ def _(event): # Mark that we're in the shell loop (used by supershell to know if it should start a shell on exit) params._in_shell_loop = True + # NB! USE_LOCAL_DAG=TRUE should be used only for testing. Warn here — after the + # login/decrypt output, right before the first prompt — so the notice isn't buried: + # the local test DAG engine routes PAM/DAG/discovery commands to a local SQLite + # graph instead of the gateway/router, so they may fail or behave incorrectly + # until it is disabled or removed. + if not params.batch_mode and not skip_init: + from .utils import value_to_boolean + if value_to_boolean(os.environ.get('USE_LOCAL_DAG', False)): + logging.warning( + f"{Fore.YELLOW}USE_LOCAL_DAG=TRUE environment variable is enabled (local test DAG engine); " + f"some PAM/DAG commands may fail. Unset it or set it to false to use the live gateway.{Fore.RESET}" + ) + while True: if params.session_token: ttk.TTK.update(params) diff --git a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py index d597cd8c4..2e1401b03 100644 --- a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py +++ b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py @@ -746,8 +746,54 @@ def tunnel_decrypt(symmetric_key: AESGCM, encrypted_data: str): return None +def get_config_uid_from_local_vault(params, record_uid): + """ + Resolve resource_uid -> config_uid by scanning the user's local PAM + Configuration records (record_version=6) for one whose `pamResources.resourceRef` + list contains `record_uid`. Returns the config record_uid (str) on match, or + None if no local config owns this resource. + + Web Vault parity: zero network, no gateway required. Used as the primary + lookup in `get_config_uid`; the legacy `get_dag_leafs` path remains as a + fallback (e.g. vault not yet synced). + """ + if not record_uid: + return None + try: + from .... import vault, vault_extensions + except Exception as e: + logging.debug('local-vault config scan: imports failed: %s', e) + return None + try: + for kr in vault_extensions.find_records(params, record_version=6): + if not isinstance(kr, vault.TypedRecord): + continue + try: + field = kr.get_typed_field('pamResources') + if not field: + continue + value = field.get_default_value(dict) + if not value: + continue + refs = value.get('resourceRef') or [] + if record_uid in refs: + return kr.record_uid + except Exception as e: + logging.debug('local-vault config scan: skipping record %s: %s', + getattr(kr, 'record_uid', '?'), e) + continue + except Exception as e: + logging.debug('local-vault config scan failed: %s', e) + return None + + def get_config_uid(params, encrypted_session_token, encrypted_transmission_key, record_uid): - # try to get config from dag + # Tier 1: local vault scan (fastest, no network) — Web Vault parity. + local_config_uid = get_config_uid_from_local_vault(params, record_uid) + if local_config_uid: + return local_config_uid + + # Tier 2: legacy gateway-mediated lookup via the old `/api/user/get_leafs`. try: rs = get_dag_leafs(params, encrypted_session_token, encrypted_transmission_key, record_uid) # response: "[{\"type\":\"rec\",\"value\":\"Jagbt2dxrft_91FovB5dwg\",\"name\":null}]" diff --git a/keepercommander/commands/tunnel_and_connections.py b/keepercommander/commands/tunnel_and_connections.py index c5f165c35..67706a564 100644 --- a/keepercommander/commands/tunnel_and_connections.py +++ b/keepercommander/commands/tunnel_and_connections.py @@ -418,6 +418,8 @@ def execute(self, params, **kwargs): dirty = False existing_config_uid = get_config_uid(params, encrypted_session_token, encrypted_transmission_key, record_uid) + if not config_uid: + config_uid = existing_config_uid tmp_dag = TunnelDAG(params, encrypted_session_token, encrypted_transmission_key, config_uid, transmission_key=transmission_key) @@ -2953,8 +2955,7 @@ def execute(self, params, **kwargs): f'{bcolors.FAIL}--clear-launch-user is only supported for pamMachine, pamDatabase, and ' f'pamDirectory records. Record "{record_uid}" is of type "{record_type}" and does not ' f'support launch credentials.{bcolors.ENDC}') - tdag.clear_launch_credential_for_resource(record_uid) - tdag.upgrade_resource_meta_to_v1(record_uid) + tdag.set_launch_credentials(record_uid) elif launch_user_name: launch_rec = RecordMixin.resolve_single_record(params, launch_user_name) if not launch_rec: @@ -2965,9 +2966,7 @@ def execute(self, params, **kwargs): f'{bcolors.FAIL}Launch user record must be a pamUser record type.{bcolors.ENDC}') launch_uid = launch_rec.record_uid if record_type in launch_credential_record_types: - tdag.clear_launch_credential_for_resource(record_uid, exclude_user_uid=launch_uid) - tdag.link_user_to_resource(launch_uid, record_uid, is_launch_credential=True, belongs_to=True) - tdag.upgrade_resource_meta_to_v1(record_uid) + tdag.set_launch_credentials(record_uid, launch_uid=launch_uid) # Print out PAM Settings if not kwargs.get("silent", False): tdag.print_tunneling_config(record_uid, record.get_typed_field('pamSettings'), config_uid) diff --git a/keepercommander/keeper_dag/connection/local.py b/keepercommander/keeper_dag/connection/local.py index 73aecd8ea..0567860d5 100644 --- a/keepercommander/keeper_dag/connection/local.py +++ b/keepercommander/keeper_dag/connection/local.py @@ -7,6 +7,7 @@ import json import os import logging +import uuid from enum import Enum from tabulate import tabulate @@ -33,6 +34,20 @@ class Connection(ConnectionBase): DB_FILE = "local_dag.db" + @staticmethod + def filename(): + current = os.environ.get("PYTEST_XDIST_WORKER") + if current is None: + current = os.environ.get("PYTEST_CURRENT_TEST") + if current is None: + current = str(os.getpid()) + if current is None: + raise Exception("Cannot get a unique identifier for the test") + + current = uuid.uuid5(uuid.NAMESPACE_DNS, current).hex + + return current + "_" + Connection.DB_FILE + def __init__(self, limit: int = 100, db_file: Optional[str] = None, @@ -51,9 +66,10 @@ def __init__(self, use_write_protobuf=use_write_protobuf) if db_file is None: - db_file = os.environ.get("LOCAL_DAG_DB_FILE", Connection.DB_FILE) + db_file = os.environ.get("LOCAL_DAG_DB_FILE", Connection.filename()) if db_dir is None: - db_dir = os.environ.get("LOCAL_DAG_DIR", os.environ.get("HOME", os.environ.get("USERPROFILE", "./"))) + db_dir = os.environ.get("LOCAL_DAG_DIR", os.environ.get("HOME", + os.environ.get("USERPROFILE", "./"))) self.allow_debug = value_to_boolean(os.environ.get("GS_CONN_DEBUG", False)) if self.allow_debug is True: @@ -62,6 +78,8 @@ def __init__(self, self.db_file = os.path.join(db_dir, db_file) self.limit = limit + self.debug(f"local db files = {self.db_file}") + self.create_database() def debug(self, msg): @@ -220,7 +238,7 @@ def _find_stream_id(self, payload: DataPayload): res = cursor.execute(sql, (current_stream_id, graph_id, EdgeType.DATA.value)) row = res.fetchone() if row is None: - self.debug(f" no edge found") + self.debug(" no edge found") if current_stream_id == item_stream_id: current_stream_id = None break @@ -241,7 +259,7 @@ def _find_stream_id(self, payload: DataPayload): res = cursor.execute(sql, (current_stream_id, graph_id, EdgeType.DATA.value)) row = res.fetchone() if row is None: - self.debug(f" no edge found") + self.debug(" no edge found") if current_stream_id == item_stream_id: current_stream_id = None break @@ -443,16 +461,6 @@ def sync(self, is_protobuf = True sync_query = self._sync_pb_to_pydantic(sync_query) - edge_type_map = { - EdgeType.DATA.value: "data", - EdgeType.KEY.value: "key", - EdgeType.LINK.value: "link", - EdgeType.ACL.value: "acl", - EdgeType.DELETION.value: "deletion", - EdgeType.DENIAL.value: "denial", - EdgeType.UNDENIAL.value: "undenial", - } - stream_id = sync_query.streamId graph_id = sync_query.graphId sync_point = sync_query.syncPoint @@ -574,6 +582,41 @@ def sync(self, hasMore=has_more ).model_dump_json().encode() + def multi_sync(self, + multi_query: Union[gs_pb2.GraphSyncMultiQuery, Any], + graph_id: Optional[int] = None, + endpoint: Optional[str] = None, + agent: Optional[str] = None) -> bytes: + """Local mirror of the network per-graph ``multi_sync``. + + The local SQLite store has no per-graph URL routing, so each sub-query + in the ``GraphSyncMultiQuery`` is run through this connection's own + ``sync()`` — identical stream / sync-point / graph-id semantics as the + single-stream read and the save path — and the per-stream results are + assembled into the same multi-stream envelope the network endpoint + returns: ``GraphSyncMultiResult`` (protobuf) or ``{"results": [...]}`` + (JSON). + """ + is_protobuf = isinstance(multi_query, gs_pb2.GraphSyncMultiQuery) + queries = list(multi_query.queries) + + if is_protobuf: + multi = gs_pb2.GraphSyncMultiResult() + for sub_query in queries: + single = self.sync(sub_query, graph_id=graph_id, + endpoint=endpoint, agent=agent) + result = gs_pb2.GraphSyncResult() + result.ParseFromString(single) + multi.results.add().CopyFrom(result) + return multi.SerializeToString() + + results = [] + for sub_query in queries: + single = self.sync(sub_query, graph_id=graph_id, + endpoint=endpoint, agent=agent) + results.append(json.loads(single)) + return json.dumps({"results": results}).encode() + def debug_dump(self) -> str: ret = "" diff --git a/keepercommander/keeper_dag/utils.py b/keepercommander/keeper_dag/utils.py index eca48cc56..43ad5a76e 100644 --- a/keepercommander/keeper_dag/utils.py +++ b/keepercommander/keeper_dag/utils.py @@ -21,12 +21,17 @@ def kotlin_bytes(data: bytes): def set_file_permissions(file_path): # type: (str) -> None """ - Set secure file permissions (0o600 on POSIX, owner-RW-only via icacls on Windows) - for files containing sensitive local state. + Set secure file permissions (0o600 on POSIX, owner-Modify-only via icacls on + Windows) for files containing sensitive local state -- currently the local + DAG SQLite database. - Mirrors keepercommander.utils.set_file_permissions intentionally — keeper_dag is - vendored as a self-contained sub-package and must not import upward from its - parent. Keep these two functions in sync. + POSIX: `chmod 0o600` so only the owning user can read/write/delete. + + Windows: NTFS DACL hardening via icacls -- strip inheritance, remove + `NT AUTHORITY\\SYSTEM` and `BUILTIN\\Administrators`, then grant the current + user `:M` (Modify, which is Read + Write + Delete). `:M` is used rather + than `:RW` because POSIX 0o600 lets the owner delete their own file, and a + bare `:RW` grant on Windows would NOT include delete. """ file_path = os.path.abspath(file_path) @@ -48,7 +53,7 @@ def set_file_permissions(file_path): # type: (str) -> None subprocess.run(["icacls", file_path, "/inheritance:r"], check=True, capture_output=True) subprocess.run(["icacls", file_path, "/remove", "NT AUTHORITY\\SYSTEM", "BUILTIN\\Administrators"], check=False, capture_output=True) - subprocess.run(["icacls", file_path, "/grant", f"{username}:RW"], check=True, capture_output=True) - logging.debug(f'Set secure permissions (owner RW only) for Windows file: {file_path}') + subprocess.run(["icacls", file_path, "/grant", f"{username}:M"], check=True, capture_output=True) + logging.debug(f'Set secure permissions (owner Modify only) for Windows file: {file_path}') except Exception: logging.warning(f'Failed to set file permissions for {file_path}') diff --git a/unit-tests/test_keeper_dag_local_perms.py b/unit-tests/test_keeper_dag_local_perms.py index 857963da3..f1e6d8890 100644 --- a/unit-tests/test_keeper_dag_local_perms.py +++ b/unit-tests/test_keeper_dag_local_perms.py @@ -1,20 +1,41 @@ import os import platform import stat +import subprocess import tempfile -from unittest import TestCase, skipIf +from unittest import TestCase from keepercommander.keeper_dag.connection.local import Connection as LocalConnection -@skipIf(platform.system() == 'Windows', - 'POSIX mode bits are not meaningful on Windows; the secure-perms helper uses icacls there') class TestLocalDagPermissions(TestCase): - def test_create_database_sets_mode_600(self): + def test_create_database_sets_owner_only_access(self): + """Local DAG SQLite db must be locked to owner-only access. + + POSIX: verifies mode == 0o600 via os.stat. + Windows: verifies the icacls-applied ACL has the principals stripped + by `set_file_permissions` (NT AUTHORITY\\SYSTEM, BUILTIN\\Administrators) + — Python's os.stat().st_mode reads DOS attributes, not the NTFS DACL. + """ with tempfile.TemporaryDirectory() as tmp_dir: LocalConnection(db_file='test_dag.db', db_dir=tmp_dir) db_path = os.path.join(tmp_dir, 'test_dag.db') self.assertTrue(os.path.isfile(db_path)) - mode = stat.S_IMODE(os.stat(db_path).st_mode) - self.assertEqual(mode, 0o600, - f'Expected 0o600 on freshly created local DAG db, got {oct(mode)}') + + if platform.system() == 'Windows': + acl_dump = subprocess.run( + ['icacls', db_path], + capture_output=True, text=True, check=True, + ).stdout + self.assertNotIn( + 'NT AUTHORITY\\SYSTEM', acl_dump, + f'SYSTEM should have been removed but is still in the ACL:\n{acl_dump}', + ) + self.assertNotIn( + 'BUILTIN\\Administrators', acl_dump, + f'Administrators should have been removed but is still in the ACL:\n{acl_dump}', + ) + else: + mode = stat.S_IMODE(os.stat(db_path).st_mode) + self.assertEqual(mode, 0o600, + f'Expected 0o600 on freshly created local DAG db, got {oct(mode)}') From 3820c7154da460f0554620ed1ebe334f51016940 Mon Sep 17 00:00:00 2001 From: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> Date: Wed, 3 Jun 2026 17:30:32 -0500 Subject: [PATCH 17/23] sync: take release kcm_import + test (non-DAG; resolves Windows os.unlink perms mismatch) --- .../commands/pam_import/kcm_import.py | 25 +++++++------ unit-tests/pam/test_kcm_import.py | 36 ++++--------------- 2 files changed, 20 insertions(+), 41 deletions(-) diff --git a/keepercommander/commands/pam_import/kcm_import.py b/keepercommander/commands/pam_import/kcm_import.py index c5e02f167..5bc4e5f6e 100644 --- a/keepercommander/commands/pam_import/kcm_import.py +++ b/keepercommander/commands/pam_import/kcm_import.py @@ -27,6 +27,8 @@ import threading import time +from typing import Dict, List, Tuple + from ..base import Command from ...display import bcolors from ...error import CommandError @@ -1365,10 +1367,6 @@ def execute(self, params, **kwargs): fd = os.open(output_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) with os.fdopen(fd, 'w') as f: json.dump(out_data, f, indent=2) - # On Windows os.open() ignores POSIX mode bits; apply the cross-platform - # owner-only helper (POSIX chmod 0o600 / Windows icacls grant owner:RW). - from ... import utils as _ku - _ku.set_file_permissions(output_file) redact_note = '' if include_creds else ' (credentials redacted)' logging.warning('JSON written to %s (%d resources, %d users)%s', output_file, num_resources, num_users, redact_note) @@ -1425,9 +1423,9 @@ def execute(self, params, **kwargs): gw_label, existing_project) else: print(f'\n{gw_label} belongs to project "{existing_project}".') - print(' [1] Create a NEW project with its own folders and gateway (recommended)') + print(f' [1] Create a NEW project with its own folders and gateway (recommended)') print(f' [2] Add records into "{existing_project}" existing folders') - print(' [3] Cancel import') + print(f' [3] Cancel import') choice = input('\n Select [1]: ').strip() if choice == '2': config_uid = resolved_config @@ -1766,6 +1764,8 @@ def _run_extend_batch(params, config_uid, tmp_path, batch_items, """ from .extend import PAMProjectExtendCommand + pre_cache = set(params.record_cache.keys()) + stdout_trap = io.StringIO() with _STDOUT_LOCK: original_stdout = sys.stdout @@ -1787,6 +1787,9 @@ def _run_extend_batch(params, config_uid, tmp_path, batch_items, except (BrokenPipeError, OSError): pass + post_cache = set(params.record_cache.keys()) + new_uids = post_cache - pre_cache + # Parse output for per-record info # Common patterns from extend.py output: # "PAM User is missing required field `password`" @@ -2011,7 +2014,7 @@ def _interactive_group_picker(groups, resolver, resources, users): total_res = len(resources) total_usr = len(users) print(f'\n Total: {total_res} resources, {total_usr} users') - print('\n [A] Import ALL groups') + print(f'\n [A] Import ALL groups') print() try: @@ -2527,8 +2530,8 @@ def _resolve_ksm_tokens(params, resources, users): f'matching vault record was found (the KSM shared folder may ' f'not be accessible to your account):\n' + '\n'.join( f' - {line}' for line in unresolved_lines) + - '\n Action: add passwords to these records after import, ' - 'or share the KSM app folder with your vault and re-import.') + f'\n Action: add passwords to these records after import, ' + f'or share the KSM app folder with your vault and re-import.') return warnings, resolved_details, unresolved_details @@ -3437,7 +3440,7 @@ def _resolve_gateway(params, gateway_arg): for i, g in enumerate(online, 1): uid_str = utils.base64_url_encode(g.controllerUid) print(f' [{i}] {g.controllerName} ({uid_str})') - print('\n [N] Create a new gateway') + print(f'\n [N] Create a new gateway') print() choice = input(' Select gateway [N]: ').strip() if choice and choice.upper() != 'N': @@ -3669,7 +3672,7 @@ def _resolve_db_password(self, params, kwargs): for i, (rec, title) in enumerate(candidates, 1): uid = rec.record_uid if hasattr(rec, 'record_uid') else '?' print(f' [{i}] {title} ({uid})') - print('\n [M] Enter password manually') + print(f'\n [M] Enter password manually') print() choice = input(' Select [M]: ').strip() if choice and choice.upper() != 'M': diff --git a/unit-tests/pam/test_kcm_import.py b/unit-tests/pam/test_kcm_import.py index 59f0f980b..e8b5db5ad 100644 --- a/unit-tests/pam/test_kcm_import.py +++ b/unit-tests/pam/test_kcm_import.py @@ -11,7 +11,6 @@ import json import os -import platform import sys import tempfile import unittest @@ -1282,17 +1281,7 @@ class TestOutputFilePermissions(unittest.TestCase): @patch('keepercommander.commands.pam_import.kcm_import.getpass.getpass', return_value='pass') def test_output_file_owner_only(self, mock_getpass, MockConnector): - """--output file must be locked to owner-only access. - - POSIX: verifies `os.stat().st_mode & 0o777 == 0o600` (the kernel honors - the mode passed to `os.open`). - - Windows: Python's `os.stat().st_mode` reflects DOS attributes, not the - NTFS DACL, so a POSIX-style mode comparison can't see the icacls - hardening. Instead, verify via the `icacls` CLI that the principals - `set_file_permissions` strips (NT AUTHORITY\\SYSTEM, BUILTIN\\Administrators) - are absent — the inheritance-r + remove + owner-only-grant chain ran. - """ + """--output file must have 0o600 permissions (owner read/write only).""" mock_conn = MockConnector.return_value mock_conn.extract_groups.return_value = [] mock_conn.extract_connections.return_value = ([ @@ -1301,6 +1290,7 @@ def test_output_file_owner_only(self, mock_getpass, MockConnector): cmd = PAMProjectKCMImportCommand() params = MagicMock() + import stat tmp_dir = tempfile.mkdtemp() output_path = os.path.join(tmp_dir, 'test_output.json') try: @@ -1308,24 +1298,9 @@ def test_output_file_owner_only(self, mock_getpass, MockConnector): db_host='127.0.0.1', output=output_path) - if platform.system() == 'Windows': - import subprocess - acl_dump = subprocess.run( - ['icacls', output_path], - capture_output=True, text=True, check=True, - ).stdout - self.assertNotIn( - 'NT AUTHORITY\\SYSTEM', acl_dump, - f'SYSTEM should have been removed but is still in the ACL:\n{acl_dump}', - ) - self.assertNotIn( - 'BUILTIN\\Administrators', acl_dump, - f'Administrators should have been removed but is still in the ACL:\n{acl_dump}', - ) - else: - file_mode = os.stat(output_path).st_mode & 0o777 - self.assertEqual(file_mode, 0o600, - f'Expected 0o600, got {oct(file_mode)}') + file_mode = os.stat(output_path).st_mode & 0o777 + self.assertEqual(file_mode, 0o600, + f'Expected 0o600, got {oct(file_mode)}') finally: if os.path.exists(output_path): os.unlink(output_path) @@ -2316,6 +2291,7 @@ def test_output_file_real_db(self): cmd = PAMProjectKCMImportCommand() params = MagicMock() + import stat tmp_dir = tempfile.mkdtemp() output_path = os.path.join(tmp_dir, 'kcm_export.json') try: From ce6f2ee427eca87ccd4d4bac6ffb8c7224ff27b4 Mon Sep 17 00:00:00 2001 From: pvagare-ks Date: Thu, 4 Jun 2026 18:44:05 +0530 Subject: [PATCH 18/23] NSF folder data support for share-report command (#2116) (#2117) --- .../nested_share_folder/folder_commands.py | 2 +- keepercommander/commands/register.py | 130 +++++++++++++++++- .../nested_share_folder/folder_api.py | 9 +- keepercommander/shared_record.py | 54 +++++++- unit-tests/test_command_register.py | 117 ++++++++++++++++ unit-tests/test_nested_share_folder.py | 46 +++++++ 6 files changed, 351 insertions(+), 7 deletions(-) diff --git a/keepercommander/commands/nested_share_folder/folder_commands.py b/keepercommander/commands/nested_share_folder/folder_commands.py index 57c04a7af..d19e7c148 100644 --- a/keepercommander/commands/nested_share_folder/folder_commands.py +++ b/keepercommander/commands/nested_share_folder/folder_commands.py @@ -443,7 +443,7 @@ def _apply(cls, params, action, folder_uid, recipient, role, expiration, else: logging.warning("%s share '%s' failed", kind, recipient) except ValueError as e: - logging.warning("%s '%s': %s", kind, recipient, e) + logging.warning("nsf-share-folder: %s", e) except Exception as e: raise CommandError('nsf-share-folder', str(e)) diff --git a/keepercommander/commands/register.py b/keepercommander/commands/register.py index 63f1e9fd7..858f3e6de 100644 --- a/keepercommander/commands/register.py +++ b/keepercommander/commands/register.py @@ -1087,6 +1087,128 @@ class ShareReportCommand(Command): def get_parser(self): return share_report_parser + @staticmethod + def _resolve_nested_folder_name(params, folder_uid): + folder = getattr(params, 'nested_share_folders', {}).get(folder_uid) or {} + return folder.get('name') or folder_uid + + @staticmethod + def _resolve_team_name(params, team_uid): + if params.enterprise: + teams = params.enterprise.get('teams') or [] + team_name = next((t.get('name') for t in teams if t.get('team_uid') == team_uid), None) + if team_name: + return team_name + return team_uid + + @staticmethod + def _get_team_users(params, team_uid): + if not params.enterprise: + return [] + users = {u.get('enterprise_user_id'): u.get('username') + for u in (params.enterprise.get('users') or [])} + team_users = [] + for team_user in params.enterprise.get('team_users') or []: + if team_user.get('team_uid') != team_uid: + continue + username = users.get(team_user.get('enterprise_user_id')) + if username: + team_users.append(username) + return team_users + + @staticmethod + def _is_nested_folder_owner(folder_info, accessor): + owner_username = folder_info.get('owner_username') + owner_account_uid = folder_info.get('owner_account_uid') + username = accessor.get('username') + if owner_username and username and owner_username.lower() == username.lower(): + return True + accessor_uid = accessor.get('accessor_uid') + return bool(owner_account_uid and accessor_uid and owner_account_uid == accessor_uid) + + @staticmethod + def _get_nested_folder_permission_label(accessor): + def format_permission_label(role): + labels = { + 'viewer': 'Viewer', + 'share-manager': 'Share Manager', + 'content-manager': 'Content Manager', + 'content-share-manager': 'Content and Share Manager', + 'full-manager': 'Full Manager', + 'contributor': 'Contributor', + 'requestor': 'Requestor', + 'navigator': 'Navigator', + 'unresolved': 'Unresolved', + } + return labels.get(role, role.replace('-', ' ').title() if role else '') + + if not isinstance(accessor, dict): + return '' + role_name = accessor.get('role') + if role_name: + from .nested_share_folder.helpers import format_role_display + return format_permission_label(format_role_display(role_name)) + perms = accessor.get('permissions') or {} + if not perms: + return '' + from .nested_share_folder.helpers import get_access_role_label + return format_permission_label(get_access_role_label({ + 'can_change_ownership': perms.get('can_change_ownership', False), + 'can_delete': perms.get('can_delete', False), + 'can_update_access': perms.get('can_update_access', False), + 'can_approve_access': perms.get('can_approve_access', False), + 'can_edit': perms.get('can_edit_records', False), + 'can_view': perms.get('can_view_records', False), + 'can_list_access': perms.get('can_list_access', False), + })) + + @staticmethod + def _get_nested_folder_report_rows(params, show_team_users=False): + rows = [] + nsf_folders = getattr(params, 'nested_share_folders', {}) or {} + if not nsf_folders: + return rows + + from .. import nested_share_folder as _nsf + + sharing_states = getattr(params, 'nested_share_folder_sharing_states', {}) or {} + for folder_uid, folder_info in nsf_folders.items(): + sharing_state = sharing_states.get(folder_uid) + if sharing_state and not sharing_state.get('shared') and sharing_state.get('count', 0) <= 0: + continue + try: + result = _nsf.get_folder_access_v3(params, [folder_uid], resolve_usernames=True) + except Exception as e: + logging.debug('Could not retrieve Nested Share Folder access for %s: %s', folder_uid, e) + continue + + folder_name = folder_info.get('name') or folder_uid + folder_path = get_folder_path(params, folder_uid) + for folder_result in result.get('results', []): + if not folder_result.get('success'): + continue + for accessor in folder_result.get('accessors', []): + if ShareReportCommand._is_nested_folder_owner(folder_info, accessor): + continue + permissions = ShareReportCommand._get_nested_folder_permission_label(accessor) + access_type = accessor.get('access_type') + if access_type == 'AT_TEAM': + team_uid = accessor.get('accessor_uid') + team_name = ShareReportCommand._resolve_team_name(params, team_uid) + rows.append([folder_uid, folder_name, 'Nested Share Folder', + f'(Team) {team_name}', permissions, folder_path]) + if show_team_users: + for username in ShareReportCommand._get_team_users(params, team_uid): + rows.append([folder_uid, folder_name, 'Nested Share Folder', + f'(Team User) {username}', permissions, folder_path]) + else: + username = accessor.get('username') or accessor.get('accessor_uid') + if username: + rows.append([folder_uid, folder_name, 'Nested Share Folder', + username, permissions, folder_path]) + + return rows + @staticmethod def sf_report(params, out=None, fmt=None, show_team_users=False): def get_share_info(share_target, name_key): # type: (Dict[str, Any], str) -> Dict[str, str] @@ -1134,13 +1256,13 @@ def get_team_user_shares(team_share, users, team_users): return team_user_shares title = 'Shared folders' - headers = ['Folder UID', 'Folder Name', 'Shared To', 'Permissions', 'Folder Path'] + headers = ['Folder UID', 'Folder Name', 'Type', 'Shared To', 'Permissions', 'Folder Path'] shared_folders = {**params.shared_folder_cache} table = [] for uid, props in shared_folders.items(): path = get_folder_path(params, uid) name = props['name_unencrypted'] - row = [uid, name] + row = [uid, name, 'Shared Folder'] users = props.get('users') or [] teams = props.get('teams') or [] user_shares = [get_share_info(u, 'username') for u in users] @@ -1150,6 +1272,7 @@ def get_team_user_shares(team_share, users, team_users): shared_to.extend(get_team_shares(ts)) rows = [(*row, target.get('name'), target.get('permissions'), path) for target in shared_to] table += rows + table += ShareReportCommand._get_nested_folder_report_rows(params, show_team_users=show_team_users) return dump_report_data(table, headers, title=title, fmt=fmt, filename=out) def execute(self, params, **kwargs): @@ -1255,7 +1378,8 @@ def get_sf_shares(): for user in sf_shares: for shared_folder_uid in sf_shares[user]: sf = api.get_shared_folder(params, shared_folder_uid) - row = [user, shared_folder_uid, sf.name if sf else ''] + folder_name = sf.name if sf else self._resolve_nested_folder_name(params, shared_folder_uid) + row = [user, shared_folder_uid, folder_name] table.append(row) if output_format == 'table': diff --git a/keepercommander/nested_share_folder/folder_api.py b/keepercommander/nested_share_folder/folder_api.py index e6d8f2307..9c40fe00e 100644 --- a/keepercommander/nested_share_folder/folder_api.py +++ b/keepercommander/nested_share_folder/folder_api.py @@ -21,6 +21,7 @@ resolve_uid_email, encrypt_for_recipient, load_user_public_key, parse_folder_access_result, resolve_team_identifier, get_team_keys, encrypt_for_team, + handle_share_invite, ) from .permissions import ( FolderUsageType, SetBooleanValue, resolve_role_name, ROLE_NAME_MAP, @@ -339,9 +340,15 @@ def grant_folder_access_v3(params, folder_uid, user_uid, role='viewer', user_email = user_uid if is_email else None if is_email: try: - user_public_key, use_ecc, actual_uid_bytes, _inv = get_user_public_key(params, user_email) + user_public_key, use_ecc, actual_uid_bytes, needs_invite = \ + get_user_public_key(params, user_email) except Exception as e: raise ValueError(f"User '{user_email}' not found or has no public key. {e}") + if not user_public_key: + handle_share_invite(params, user_email, needs_invite) + raise ValueError(f"User '{user_email}' has no public key") + if not actual_uid_bytes: + raise ValueError(f"User '{user_email}' not found") else: actual_uid_bytes, user_email = resolve_uid_email(params, user_uid) if not actual_uid_bytes: diff --git a/keepercommander/shared_record.py b/keepercommander/shared_record.py index ae88bf91d..1a480b1ba 100644 --- a/keepercommander/shared_record.py +++ b/keepercommander/shared_record.py @@ -1,3 +1,4 @@ +import logging from enum import Enum from typing import Dict, List @@ -176,9 +177,10 @@ def get_ordered_permissions(self): ordered = list(self.permissions.values()) for user_perms in self.user_permissions.values(): if user_perms.to_uid: - ordered.remove(user_perms) team_perms = self.team_permissions.get(user_perms.to_uid) - ordered.insert(ordered.index(team_perms) + 1, user_perms) + if team_perms in ordered and user_perms in ordered: + ordered.remove(user_perms) + ordered.insert(ordered.index(team_perms) + 1, user_perms) return ordered def merge_permissions(self, share_target, perms_to_merge, sp_type): @@ -274,6 +276,54 @@ def load_team_permissions(t_perms, sf_uid): load_user_permissions(user_perms) sf_perms = shares.get('shared_folder_permissions', []) + is_nested_share_record = self.uid in getattr(params, 'nested_share_records', {}) + if is_nested_share_record and not user_perms and not sf_perms: + try: + from . import nested_share_folder as _nsf + nested_accesses = _nsf.get_record_accesses_v3(params, [self.uid]).get('record_accesses', []) + for access in nested_accesses: + if access.get('record_uid') != self.uid: + continue + + share_target = access.get('accessor_name') or access.get('access_type_uid') or '' + if not share_target: + continue + + if access.get('owner'): + self.owner = share_target + + share_info = { + 'editable': access.get('can_edit', False), + 'shareable': access.get('can_update_access', False) or access.get('can_approve_access', False), + 'view': access.get('can_view', True), + } + inherited = access.get('inherited', False) + access_type = access.get('access_type') + if access_type == 'AT_TEAM': + team_uid = access.get('access_type_uid') + if inherited: + team_usernames = team_members.get(team_uid, set()) + for folder_uid in self.folder_uids: + update_sf_shares(share_target, folder_uid) + for username in team_usernames: + update_sf_shares(username, folder_uid) + share_info['team_uid'] = access.get('access_type_uid') + share_info['name'] = share_target + load_team_permissions([share_info], None) + else: + if inherited: + for folder_uid in self.folder_uids: + update_sf_shares(share_target, folder_uid) + share_info['username'] = share_target + share_type = SharePermissions.SharePermissionsType.SF_USER if inherited \ + else SharePermissions.SharePermissionsType.USER + load_user_permissions([share_info], None, share_type) + + apply_role_restrictions() + return + except Exception as e: + logging.debug('Could not load Nested Share Folder permissions for %s: %s', self.uid, e) + SF_UID = 'shared_folder_uid' sf_cache = params.shared_folder_cache shared_folders = {sfp.get(SF_UID): sf_cache.get(sfp.get(SF_UID)) for sfp in sf_perms} diff --git a/unit-tests/test_command_register.py b/unit-tests/test_command_register.py index 58d4998c3..2da3c9b00 100644 --- a/unit-tests/test_command_register.py +++ b/unit-tests/test_command_register.py @@ -1,4 +1,5 @@ import datetime +import json from contextlib import contextmanager from unittest import TestCase, mock @@ -7,6 +8,7 @@ from keepercommander.error import CommandError from keepercommander.proto import APIRequest_pb2, record_pb2 from keepercommander import utils +from keepercommander.subfolder import NestedShareFolderNode vault_env = VaultEnvironment() @@ -348,6 +350,121 @@ def test_share_folder_rotate_on_expiration_rejects_folder_without_pam_user(self) folder=shared_folder_uid, expire_in='1d', rotate_on_expiration=True) self.assertIn('pamUser', str(ctx.exception)) + @staticmethod + def _attach_nested_share_folder(params, record_uid, folder_name='Drive'): + folder_uid = utils.generate_uid() + folder_node = NestedShareFolderNode() + folder_node.uid = folder_uid + folder_node.name = folder_name + folder_node.parent_uid = None + params.folder_cache[folder_uid] = folder_node + params.root_folder.subfolders.append(folder_uid) + + params.nested_share_folders[folder_uid] = { + 'folder_uid': folder_uid, + 'name': folder_name, + 'owner_username': params.user, + } + params.nested_share_folder_records[folder_uid] = {record_uid} + params.nested_share_records[record_uid] = { + 'record_uid': record_uid, + 'shared': True, + } + params.subfolder_cache[folder_uid] = { + 'folder_uid': folder_uid, + 'type': 'user_folder', + 'name': folder_name, + 'source': 'nested_share_folder', + } + for record_uids in params.subfolder_record_cache.values(): + record_uids.discard(record_uid) + params.subfolder_record_cache[folder_uid] = {record_uid} + params.record_cache[record_uid]['shared'] = True + params.record_cache[record_uid].pop('shares', None) + return folder_uid + + def test_share_report_owner_supports_nested_share_records(self): + params = get_synced_params() + record_uid = next(uid for uid, rec in params.record_cache.items() if not rec.get('shared')) + folder_uid = self._attach_nested_share_folder(params, record_uid) + + access_result = { + 'record_accesses': [ + { + 'record_uid': record_uid, + 'accessor_name': params.user, + 'access_type': 'AT_USER', + 'access_type_uid': utils.generate_uid(), + 'owner': True, + 'inherited': False, + 'can_view': True, + 'can_edit': True, + 'can_update_access': True, + 'can_approve_access': True, + }, + { + 'record_uid': record_uid, + 'accessor_name': 'user2@keepersecurity.com', + 'access_type': 'AT_USER', + 'access_type_uid': utils.generate_uid(), + 'owner': False, + 'inherited': True, + 'can_view': True, + 'can_edit': True, + 'can_update_access': True, + 'can_approve_access': True, + } + ] + } + + with mock.patch('keepercommander.api.get_record_shares', return_value=None), \ + mock.patch('keepercommander.nested_share_folder.record_api.get_record_accesses_v3', + return_value=access_result): + cmd = register.ShareReportCommand() + report = cmd.execute(params, format='json', owner=True, verbose=True, container=[folder_uid]) + + data = json.loads(report) + self.assertEqual(len(data), 1) + self.assertEqual(data[0]['record_uid'], record_uid) + self.assertEqual(data[0]['record_owner'], params.user) + self.assertEqual(data[0]['folder_path'], 'Drive') + self.assertIn('user2@keepersecurity.com', data[0]['shared_with']) + + def test_share_report_folders_supports_nested_share_folders(self): + params = get_synced_params() + record_uid = next(uid for uid in params.record_cache) + folder_uid = self._attach_nested_share_folder(params, record_uid) + params.nested_share_folder_sharing_states[folder_uid] = { + 'shared': True, + 'count': 1, + } + access_result = { + 'results': [{ + 'folder_uid': folder_uid, + 'success': True, + 'accessors': [{ + 'username': 'user2@keepersecurity.com', + 'accessor_uid': utils.generate_uid(), + 'access_type': 'AT_USER', + 'role': 'CONTENT_SHARE_MANAGER', + }] + }] + } + + with mock.patch('keepercommander.nested_share_folder.folder_api.get_folder_access_v3', + return_value=access_result): + cmd = register.ShareReportCommand() + report = cmd.execute(params, format='json', folders=True) + + data = json.loads(report) + row = next((x for x in data if x['Folder UID'] == folder_uid), None) + self.assertIsNotNone(row) + self.assertEqual(row['Folder Name'], 'Drive') + self.assertEqual(row['Type'], 'Nested Share Folder') + self.assertEqual(row['Shared To'], 'user2@keepersecurity.com') + self.assertEqual(row['Folder Path'], 'Drive') + self.assertEqual(row['Permissions'], 'Content and Share Manager') + @staticmethod def record_share_rq_rs(rq): status = record_pb2.SharedRecordStatus() diff --git a/unit-tests/test_nested_share_folder.py b/unit-tests/test_nested_share_folder.py index 02d2817ec..94ca1b549 100644 --- a/unit-tests/test_nested_share_folder.py +++ b/unit-tests/test_nested_share_folder.py @@ -582,6 +582,52 @@ def test_share_record_revoke(self, mock_unshare): record=ruid, email='user@example.com', action='revoke') + @patch('keepercommander.nested_share_folder.folder_api.grant_folder_access_v3') + def test_share_folder_invite_message_uses_command_prefix(self, mock_grant): + from keepercommander.commands.nested_share_folder import NestedShareFolderShareCommand + + fuid, fobj = _make_folder() + email = 'user@example.com' + mock_grant.side_effect = ValueError( + f"Share invitation has been sent to '{email}'. " + "Please repeat this command once the invitation is accepted.") + + cmd = NestedShareFolderShareCommand() + with self.assertLogs(level='WARNING') as logs: + cmd.execute(_make_params(nested_share_folders={fuid: fobj}), + folder=[fuid], user=[email], action='grant', role='viewer') + + output = '\n'.join(logs.output) + self.assertIn('nsf-share-folder: Share invitation has been sent', output) + self.assertNotIn("User '", output) + + +class TestNestedShareFolderFolderApi(TestCase): + + @patch('keepercommander.nested_share_folder.folder_api.folder_access_update_v3') + @patch('keepercommander.nested_share_folder.folder_api.handle_share_invite') + @patch('keepercommander.nested_share_folder.folder_api.get_user_public_key') + def test_grant_folder_access_sends_invite_when_no_active_share( + self, mock_get_public_key, mock_handle_invite, mock_access_update): + from keepercommander.nested_share_folder.folder_api import grant_folder_access_v3 + + fuid, fobj = _make_folder() + params = _make_params(nested_share_folders={fuid: fobj}) + email = 'user@example.com' + + mock_get_public_key.return_value = (None, False, None, True) + mock_handle_invite.side_effect = ValueError( + f"Share invitation has been sent to '{email}'. " + "Please repeat this command once the invitation is accepted.") + + with self.assertRaises(ValueError) as ctx: + grant_folder_access_v3(params, fuid, email, role='viewer') + + self.assertIn('Share invitation has been sent', str(ctx.exception)) + mock_get_public_key.assert_called_once_with(params, email) + mock_handle_invite.assert_called_once_with(params, email, True) + mock_access_update.assert_not_called() + class TestNestedShareFolderDisplayCommands(TestCase): From 1a96f47399ee49a5f4ddf2c605e9d26cf50c676e Mon Sep 17 00:00:00 2001 From: Sergey Kolupaev Date: Thu, 4 Jun 2026 12:20:12 -0700 Subject: [PATCH 19/23] Release 18.0.5 --- keepercommander/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keepercommander/__init__.py b/keepercommander/__init__.py index 38f11ce99..3ad1bb2ec 100644 --- a/keepercommander/__init__.py +++ b/keepercommander/__init__.py @@ -10,4 +10,4 @@ # Contact: commander@keepersecurity.com # -__version__ = '18.0.4' +__version__ = '18.0.5' From 2319a90c2bb63a9c4ffb875858ce8f7dabc09506 Mon Sep 17 00:00:00 2001 From: lthievenaz-keeper Date: Wed, 3 Jun 2026 11:13:04 +0100 Subject: [PATCH 20/23] Improve manual for epm scim ad Improvements for `epm scim ad` help menu: - Document support for hostname and IP address for `--ad-url` - Add `\` escape character for `--ad-user` - Clarify that `--ad-password` is optional and will be prompted if unset --- keepercommander/commands/pedm/pedm_admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keepercommander/commands/pedm/pedm_admin.py b/keepercommander/commands/pedm/pedm_admin.py index 33f7afe15..4e7f75c67 100644 --- a/keepercommander/commands/pedm/pedm_admin.py +++ b/keepercommander/commands/pedm/pedm_admin.py @@ -228,9 +228,9 @@ def __init__(self): help='Azure cloud (AzureCloud, AzureChinaCloud, etc.)') ad_parser = subparsers.add_parser('ad', help='Connect via Active Directory') - ad_parser.add_argument('--ad-url', dest='ad_url', required=True, help='AD LDAP URL (e.g., ldap(s)://)') - ad_parser.add_argument('--ad-user', dest='ad_user', help='AD bind user (userPrincipalName or DOMAIN\\username)') - ad_parser.add_argument('--ad-password', dest='ad_password', help='AD password') + ad_parser.add_argument('--ad-url', dest='ad_url', required=True, help='DC hostname, IP or LDAP URL (, ldap(s)://)') + ad_parser.add_argument('--ad-user', dest='ad_user', help='AD bind user (userPrincipalName or DOMAIN\\\\username)') + ad_parser.add_argument('--ad-password', dest='ad_password', help='AD password. Will be prompted if not set.') ad_parser.add_argument('--group', dest='groups', action='append', help='AD group name or DN (repeatable)') ad_parser.add_argument('--ad-domain', dest='ad_domain', action='store', choices=['netbios', 'dns'], help='Use NetBIOS domain names (e.g., TEST) or DNS names (e.g., test.local)') From 90337fc3c4f848ae37d3519221851e73159fadf6 Mon Sep 17 00:00:00 2001 From: Max Ustinov Date: Tue, 2 Jun 2026 12:56:55 -0700 Subject: [PATCH 21/23] Fix: exempt /health from service-mode API rate limiting Service mode applies a global flask-limiter default limit to every route. The /health endpoint had no explicit limiter configuration, so it inherited that default and counted against the shared per-client rate-limit budget. Liveness/readiness probes and load balancer health checks poll /health frequently, so the budget is eventually exhausted. Once that happens /health starts returning HTTP 429, probes fail, and the orchestrator replaces the pod -- which resets the in-memory counter and repeats the cycle on a fixed interval. Decorating the endpoint with @limiter.exempt removes it from all rate-limit calculations. /health performs no backend calls and returns a static status, so exempting it has no security impact. --- keepercommander/service/api/routes.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/keepercommander/service/api/routes.py b/keepercommander/service/api/routes.py index 33bb4ebdd..2d2488067 100644 --- a/keepercommander/service/api/routes.py +++ b/keepercommander/service/api/routes.py @@ -14,6 +14,7 @@ from .command import create_command_blueprint, create_legacy_command_blueprint from .onboarding import create_onboarding_blueprint from ..decorators.logging import logger, debug_decorator +from ..decorators.security import limiter def _setup_queue_mode(app: Flask) -> None: """Setup queue mode with native v2 and synchronous v1 compatibility endpoints.""" @@ -45,7 +46,11 @@ def init_routes(app: Optional[Flask] = None) -> None: raise ValueError("App instance is required") # Add health check endpoint (no authentication required for Docker/orchestrators) + # Exempt from rate limiting: liveness/readiness probes and load balancer health + # checks poll this frequently and must never consume the API rate-limit budget, + # otherwise the limit is eventually exhausted and probes start receiving HTTP 429. @app.route("/health", methods=["GET"]) + @limiter.exempt def health_check(): """Health check endpoint for Docker and orchestrators.""" return jsonify({"status": "ok"}), 200 From 781c424963e87e62ac53ecd1ebf7aba2f50e9421 Mon Sep 17 00:00:00 2001 From: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:36:22 -0500 Subject: [PATCH 22/23] Fix pam connection edit: connection settings + launch/admin credentials --- keepercommander/commands/pam_debug/dump.py | 4 +- .../tunnel/port_forward/TunnelGraph.py | 86 +++++++++++-- .../tunnel/port_forward/tunnel_helpers.py | 13 +- .../commands/tunnel_and_connections.py | 78 ++++++++++-- .../pam/test_coerce_settings_subdicts.py | 84 +++++++++++++ unit-tests/pam/test_dag_layer_b_migration.py | 80 +++++++++++- .../pam/test_get_config_uid_local_vault.py | 25 +++- unit-tests/pam/test_set_launch_credentials.py | 119 +++++++++++++++++- 8 files changed, 454 insertions(+), 35 deletions(-) create mode 100644 unit-tests/pam/test_coerce_settings_subdicts.py diff --git a/keepercommander/commands/pam_debug/dump.py b/keepercommander/commands/pam_debug/dump.py index 1f9f6c2bc..5231a8049 100644 --- a/keepercommander/commands/pam_debug/dump.py +++ b/keepercommander/commands/pam_debug/dump.py @@ -135,11 +135,11 @@ def _on_folder(f): if not config_uid: from ..tunnel.port_forward.tunnel_helpers import ( get_config_uid_from_local_vault, - get_config_uid_from_krouter, + get_config_uid_via_pam_link, ) config_uid = ( get_config_uid_from_local_vault(params, rec_uid) - or get_config_uid_from_krouter(params, rec_uid) + or get_config_uid_via_pam_link(params, rec_uid) ) if config_uid: diff --git a/keepercommander/commands/tunnel/port_forward/TunnelGraph.py b/keepercommander/commands/tunnel/port_forward/TunnelGraph.py index 150b22916..b5a149760 100644 --- a/keepercommander/commands/tunnel/port_forward/TunnelGraph.py +++ b/keepercommander/commands/tunnel/port_forward/TunnelGraph.py @@ -73,6 +73,27 @@ def ensure_resource_meta_v1(content): return out +def build_meta_version_upgrade(): + """Meta payload that bumps a resource's meta to v1 WITHOUT re-asserting any + allowedSettings flags. + + Used by the launch-credential / version-upgrade Layer-B calls. krouter + deep-merges the ``meta`` JSON edge (krouter Serialization.kt ``mergeJson``) + and never deletes keys absent from the new payload, so an EMPTY + ``allowedSettings`` preserves the server's current connections / portForwards / + recording flags while still bumping ``version``. ``allowedSettings`` must be + present (even if empty) because krouter strictly decodes the incoming meta + into its non-nullable ``Meta.allowedSettings`` DTO before merging. + + This avoids the revert bug: ``set_resource_allowed``'s Layer-B path enables + ``connections`` on the server but does not refresh the in-memory vertex, so a + follow-up ``ensure_resource_meta_v1(get_vertex_content(...))`` would re-send a + STALE ``connections=false`` that deep-merges and flips connections back off — + making the vault hide the (still-present) connection port/protocol. + """ + return {"version": int(RESOURCE_META_VERSION_V1), "allowedSettings": {}} + + class TunnelDAG: def __init__(self, params, encrypted_session_token, encrypted_transmission_key, record_uid: str, is_config=False, transmission_key=None): @@ -469,10 +490,19 @@ def link_user_to_resource(self, user_uid, resource_uid, is_admin=None, belongs_t endpoint = 'configure_resource' if not is_layer_b_feature_disabled(host, endpoint): try: + # adminUid must ride ALONGSIDE connectUsers: krouter only flips + # is_admin on an already-existing ACL edge when connectUsers is + # present (UserRest.kt:295-318); a standalone adminUid no-ops on + # an existing edge (UserRest.kt:331-341). Send the resource's + # CURRENT launch credentials as connectUsers so the reconciliation + # sets is_admin without clearing any existing launch credential. + launch_uids = self.get_launch_credentials(resource_uid) rq = pam_pb2.PAMResourceConfig( recordUid=url_safe_str_to_bytes(resource_uid), networkUid=url_safe_str_to_bytes(self.record.record_uid), adminUid=url_safe_str_to_bytes(user_uid), + connectUsers=pam_pb2.UidList( + uids=[url_safe_str_to_bytes(u) for u in launch_uids]), ) router_configure_resource(self.params, rq) logging.debug( @@ -568,6 +598,20 @@ def check_if_resource_has_launch_credential(self, resource_uid): return user_vertex.uid return False + def get_launch_credentials(self, resource_uid): + """Return the list of user UIDs currently flagged is_launch_credential on the resource.""" + result = [] + resource_vertex = self.linking_dag.get_vertex(resource_uid) + if resource_vertex is None: + return result + for user_vertex in resource_vertex.has_vertices(EdgeType.ACL): + acl_edge = user_vertex.get_edge(resource_vertex, EdgeType.ACL) + if acl_edge: + content = acl_edge.content_as_dict + if content and content.get('is_launch_credential'): + result.append(user_vertex.uid) + return result + def clear_launch_credential_for_resource(self, resource_uid, exclude_user_uid=None): """Remove is_launch_credential from all users on a resource except exclude_user_uid.""" resource_vertex = self.linking_dag.get_vertex(resource_uid) @@ -589,9 +633,10 @@ def clear_launch_credential_for_resource(self, resource_uid, exclude_user_uid=No if dirty: self.linking_dag.save() - def set_launch_credentials(self, resource_uid, launch_uid=None): + def set_launch_credentials(self, resource_uid, launch_uid=None, admin_uid=None): """ - Set or clear the launch credential for a resource via Layer-B configure_resource. + Set or clear the launch credential (and optionally the admin) for a resource + via a single Layer-B configure_resource round-trip. krouter's connectUsers field has replacement semantics (UserRest.kt:generateResourceConnectionEdges): sending [launch_uid] sets @@ -600,19 +645,31 @@ def set_launch_credentials(self, resource_uid, launch_uid=None): the v1 upgrade in the same call. This replaces the legacy 2-3 op sequence (clear + link + meta-upgrade) with one permission-checked round-trip. + admin_uid (optional): when given, adminUid is sent in the SAME request as + connectUsers. This is required for the admin to actually take effect on an + ALREADY-EXISTING ACL edge: a standalone configure_resource(adminUid) with no + connectUsers no-ops on an existing edge (UserRest.kt:331-341 only touches + is_launch_credential), whereas adminUid alongside connectUsers flips is_admin + on that edge (UserRest.kt:295-318). Sent on a user NOT in connectUsers so the + admin does not also become a launch credential. + For a fresh launch_uid (no existing edge), krouter creates the new edge with belongs_to=null; a follow-up local DAG-write of belongs_to=True preserves legacy parity. For existing edges, krouter preserves belongs_to already so the follow-up is a no-op. Fallback on RRC_NOT_ALLOWED* (or feature-disabled) with KEEPER_DAG_LB_FALLBACK - enabled: legacy clear + link (if set) + meta-upgrade. + enabled: legacy clear + link (if set) + admin link (if set) + meta-upgrade. """ resource_vertex = self.linking_dag.get_vertex(resource_uid) if resource_vertex is None: return - upgraded_meta = ensure_resource_meta_v1(get_vertex_content(resource_vertex)) + # Version-only meta: bump to v1 (so the vault reads ACL launch credentials) + # WITHOUT re-asserting a possibly-stale allowedSettings snapshot. See + # build_meta_version_upgrade() — re-sending the in-memory allowedSettings + # here would clobber connections that set_resource_allowed just enabled. + upgraded_meta = build_meta_version_upgrade() uids = [url_safe_str_to_bytes(launch_uid)] if launch_uid is not None else [] from ...pam.router_helper import router_configure_resource, get_router_url @@ -627,10 +684,12 @@ def set_launch_credentials(self, resource_uid, launch_uid=None): connectUsers=pam_pb2.UidList(uids=uids), meta=json.dumps(upgraded_meta).encode(), ) + if admin_uid is not None: + rq.adminUid = url_safe_str_to_bytes(admin_uid) router_configure_resource(self.params, rq) logging.debug( f"set_launch_credentials: resource={resource_uid} " - f"launch_uid={launch_uid} via configure_resource" + f"launch_uid={launch_uid} admin_uid={admin_uid} via configure_resource" ) if launch_uid is not None: self.link_user(launch_uid, resource_vertex, belongs_to=True) @@ -645,6 +704,9 @@ def set_launch_credentials(self, resource_uid, launch_uid=None): ) self.clear_launch_credential_for_resource(resource_uid, exclude_user_uid=launch_uid) + if admin_uid is not None: + # Legacy fallback: write is_admin directly on the resource ACL edge. + self.link_user(admin_uid, resource_vertex, is_admin=True, belongs_to=True) if launch_uid is not None: self.link_user_to_resource(launch_uid, resource_uid, is_launch_credential=True, belongs_to=True) @@ -660,11 +722,13 @@ def upgrade_resource_meta_to_v1(self, resource_uid): return upgraded = ensure_resource_meta_v1(content) - # Primary: Layer-B configure_resource (permission-checked). Same meta - # shape we use in set_resource_allowed(meta_version=1): the versioned - # payload {version, allowedSettings, rotateOnTermination} is sent as - # the proto's `meta` field. krouter's mergeJson honors version with the - # `oldMetaVersion <= newMetaVersion` upgrade check. + # Primary: Layer-B configure_resource (permission-checked). Send a + # version-only meta — krouter deep-merges the meta edge, so an empty + # allowedSettings bumps `version` (the `oldMetaVersion <= newMetaVersion` + # upgrade check) while preserving the server's current flags. Re-sending + # the in-memory `upgraded` here would clobber flags set earlier in the + # same command (the Layer-B set_resource_allowed does not refresh the + # in-memory vertex). See build_meta_version_upgrade(). from ...pam.router_helper import router_configure_resource, get_router_url from ...pam._layer_b import is_layer_b_feature_disabled host = get_router_url(self.params) @@ -674,7 +738,7 @@ def upgrade_resource_meta_to_v1(self, resource_uid): rq = pam_pb2.PAMResourceConfig( recordUid=url_safe_str_to_bytes(resource_uid), networkUid=url_safe_str_to_bytes(self.record.record_uid), - meta=json.dumps(upgraded).encode(), + meta=json.dumps(build_meta_version_upgrade()).encode(), ) router_configure_resource(self.params, rq) logging.debug( diff --git a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py index 2e1401b03..10655dbda 100644 --- a/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py +++ b/keepercommander/commands/tunnel/port_forward/tunnel_helpers.py @@ -793,7 +793,18 @@ def get_config_uid(params, encrypted_session_token, encrypted_transmission_key, if local_config_uid: return local_config_uid - # Tier 2: legacy gateway-mediated lookup via the old `/api/user/get_leafs`. + # Tier 2: krouter per-graph PAM_LINK get_leafs (server-side, no gateway). + # Precise single-owner resolution — the Web Vault uses the same call. The + # legacy graphId=0 lookup below can return a stale/duplicate config when a + # resource still carries link edges under more than one PAM config; loading + # that graph then raises DAGPathException ("Found multiple vertex that use + # the path") on the next get_vertex. Resolving the owner precisely here + # avoids it. Mirrors the dag-api-migration resolution order. + link_config_uid = get_config_uid_via_pam_link(params, record_uid) + if link_config_uid: + return link_config_uid + + # Tier 3: legacy gateway-mediated lookup via the old `/api/user/get_leafs`. try: rs = get_dag_leafs(params, encrypted_session_token, encrypted_transmission_key, record_uid) # response: "[{\"type\":\"rec\",\"value\":\"Jagbt2dxrft_91FovB5dwg\",\"name\":null}]" diff --git a/keepercommander/commands/tunnel_and_connections.py b/keepercommander/commands/tunnel_and_connections.py index 67706a564..d30fb6f73 100644 --- a/keepercommander/commands/tunnel_and_connections.py +++ b/keepercommander/commands/tunnel_and_connections.py @@ -65,6 +65,28 @@ _LEASE_SHUTDOWN_EVENTS_BY_RECORD: Dict[str, threading.Event] = {} +def _coerce_settings_subdicts(entry, *keys): + """Coerce the named sub-fields of a pamSettings / pamRemoteBrowserSettings + entry to dicts, in place. + + Discovery and the Web Vault may publish ``connection`` / ``portForward`` as an + empty string (``""``) or omit them entirely (e.g. ``{"portForward": ""}`` with + no ``connection``). Code that then indexes ``entry["connection"][...]`` / + ``entry["portForward"][...]`` raises ``KeyError`` (missing) or ``TypeError`` + (string). Call this first so those writes always land on a real dict. + + Returns True if anything was coerced (callers may use it to flag the record dirty). + """ + changed = False + if not isinstance(entry, dict): + return changed + for key in keys: + if not isinstance(entry.get(key), dict): + entry[key] = {} + changed = True + return changed + + # Group Commands class PAMTunnelCommand(GroupCommand): @@ -466,6 +488,12 @@ def execute(self, params, **kwargs): tmp_dag.link_resource_to_config(record_uid) if not pam_settings.value: pam_settings.value.append({"connection": {}, "portForward": {}}) + # Discovery may publish value[0] with portForward as "" (empty + # string) or absent; coerce to a dict before indexing, else the + # writes below raise TypeError (string) / KeyError (missing). + if not isinstance(pam_settings.value[0], dict): + pam_settings.value[0] = {"connection": {}, "portForward": {}} + _coerce_settings_subdicts(pam_settings.value[0], "connection", "portForward") if _tunneling and tunneling_override_port: pam_settings.value[0]['portForward']['port'] = tunneling_override_port dirty = True @@ -2763,8 +2791,14 @@ def execute(self, params, **kwargs): else: if not pam_settings.value: pam_settings.value.append({"connection": {}, "portForward": {}}) - if not pam_settings.value[0]: + if not isinstance(pam_settings.value[0], dict): pam_settings.value[0] = {"connection": {}, "portForward": {}} + # An existing pamSettings may carry only one of the two sub-dicts, + # or publish them as "" (empty string) instead of a dict — e.g. + # discovery publishes {"portForward": ""} with no usable "connection". + # Coerce both to dicts before indexing, otherwise -cn on -p rdp -cop + # raises KeyError (missing key) or TypeError (string) below. + _coerce_settings_subdicts(pam_settings.value[0], "connection", "portForward") if _connections: if connection_override_port: pam_settings.value[0]["connection"]["port"] = connection_override_port @@ -2934,13 +2968,18 @@ def execute(self, params, **kwargs): typescript_recording=kwargs.get('typescriptrecording', None), rotate_on_termination=rot_bool) - # admin parameter is optional yet if not set connections may fail + # admin (-a) and launch (-lu / --clear-launch-user) credentials are + # applied together in a SINGLE configure_resource round-trip. krouter + # only flips is_admin on an already-existing ACL edge when adminUid is + # sent alongside connectUsers (UserRest.kt:295-318); a standalone + # adminUid call no-ops on an existing edge. So fold admin into the + # launch call instead of issuing two separate requests. admin_name = kwargs.get('admin') adm_rec = RecordMixin.resolve_single_record(params, admin_name) admin_uid = adm_rec.record_uid if adm_rec else None - if admin_uid and record_type in launch_credential_record_types: - tdag.link_user_to_resource(admin_uid, record_uid, is_admin=True, belongs_to=True) - # tdag.link_user_to_config(admin_uid) # is_iam_user=True + # admin credential is only meaningful on launch-capable resources + if admin_uid and record_type not in launch_credential_record_types: + admin_uid = None # launch-user parameter sets the launch credential; --clear-launch-user removes it clear_launch_user = bool(kwargs.get('clear_launch_user')) @@ -2949,13 +2988,16 @@ def execute(self, params, **kwargs): if clear_launch_user and launch_user_name: raise CommandError('pam connection edit', f'{bcolors.FAIL}Use either --clear-launch-user or --launch-user, not both.{bcolors.ENDC}') + + launch_uid = None + apply_launch = False if clear_launch_user: if record_type not in launch_credential_record_types: raise CommandError('pam connection edit', f'{bcolors.FAIL}--clear-launch-user is only supported for pamMachine, pamDatabase, and ' f'pamDirectory records. Record "{record_uid}" is of type "{record_type}" and does not ' f'support launch credentials.{bcolors.ENDC}') - tdag.set_launch_credentials(record_uid) + apply_launch = True # launch_uid stays None -> clears the launch credential elif launch_user_name: launch_rec = RecordMixin.resolve_single_record(params, launch_user_name) if not launch_rec: @@ -2964,9 +3006,21 @@ def execute(self, params, **kwargs): if not isinstance(launch_rec, vault.TypedRecord) or launch_rec.record_type != 'pamUser': raise CommandError('', f'{bcolors.FAIL}Launch user record must be a pamUser record type.{bcolors.ENDC}') - launch_uid = launch_rec.record_uid if record_type in launch_credential_record_types: - tdag.set_launch_credentials(record_uid, launch_uid=launch_uid) + apply_launch = True + launch_uid = launch_rec.record_uid + + if apply_launch: + # set/clear launch credential and (optionally) the admin in one call + tdag.set_launch_credentials(record_uid, launch_uid=launch_uid, admin_uid=admin_uid) + elif admin_uid: + # admin-only: preserve the current launch credential in the same + # request so krouter sets is_admin on the (possibly pre-existing) + # edge without clearing an existing launch credential. + current_launch = tdag.check_if_resource_has_launch_credential(record_uid) + tdag.set_launch_credentials(record_uid, + launch_uid=(current_launch or None), + admin_uid=admin_uid) # Print out PAM Settings if not kwargs.get("silent", False): tdag.print_tunneling_config(record_uid, record.get_typed_field('pamSettings'), config_uid) @@ -3364,6 +3418,14 @@ def execute(self, params, **kwargs): rbs_fld.value.append({'connection': {'protocol': 'http'}}) # type: ignore dirty = True + # A pre-existing pamRemoteBrowserSettings may lack a usable "connection" + # dict (absent, or published as ""); coerce it once so the writes below + # (rbs_fld.value[0]["connection"][...]) don't KeyError/TypeError. + if (isinstance(rbs_fld, vault.TypedField) and rbs_fld.value + and isinstance(rbs_fld.value[0], dict)): + if _coerce_settings_subdicts(rbs_fld.value[0], 'connection'): + dirty = True + if autofill: af_rec = RecordMixin.resolve_single_record(params, autofill) if not af_rec: diff --git a/unit-tests/pam/test_coerce_settings_subdicts.py b/unit-tests/pam/test_coerce_settings_subdicts.py new file mode 100644 index 000000000..fbe7268b9 --- /dev/null +++ b/unit-tests/pam/test_coerce_settings_subdicts.py @@ -0,0 +1,84 @@ +""" +Unit tests for ``_coerce_settings_subdicts`` — the guard that protects pamSettings / +pamRemoteBrowserSettings sub-field writes from KeyError/TypeError. + +Discovery and the Web Vault may publish ``connection`` / ``portForward`` as an empty +string (``""``) or omit them entirely (e.g. ``{"portForward": ""}`` with no +``connection``). The command code then indexes ``entry["connection"][...]`` / +``entry["portForward"][...]``, which would raise without this coercion. Covers the +"pam connection edit" (bug #1), "pam tunnel edit" portForward, and "pam rbi edit" +connection guards. +""" +import importlib +import os +import sys +import unittest + +sys.path.insert(0, os.path.dirname(__file__)) + +skip_tests = False +skip_reason = "" +try: + # Pre-warm the circular-import chain (same pattern as the other pam tests). + importlib.import_module('keepercommander.commands.pam_import.keeper_ai_settings') + from keepercommander.commands.tunnel_and_connections import _coerce_settings_subdicts +except ImportError as e: # pragma: no cover + skip_tests = True + skip_reason = f"Cannot import tunnel_and_connections: {e}" + + +@unittest.skipIf(skip_tests, skip_reason) +class TestCoerceSettingsSubdicts(unittest.TestCase): + + def test_missing_key_is_created_as_dict(self): + entry = {"portForward": {}} # discovery published no "connection" + changed = _coerce_settings_subdicts(entry, "connection", "portForward") + self.assertEqual(entry["connection"], {}) + self.assertEqual(entry["portForward"], {}) + self.assertTrue(changed) + + def test_empty_string_value_is_coerced_to_dict(self): + entry = {"connection": {"port": 3389}, "portForward": ""} # real dump shape + changed = _coerce_settings_subdicts(entry, "connection", "portForward") + self.assertEqual(entry["portForward"], {}) + # existing connection dict is preserved untouched + self.assertEqual(entry["connection"], {"port": 3389}) + self.assertTrue(changed) + + def test_none_value_is_coerced_to_dict(self): + entry = {"connection": None} + changed = _coerce_settings_subdicts(entry, "connection") + self.assertEqual(entry["connection"], {}) + self.assertTrue(changed) + + def test_existing_dicts_are_left_unchanged_and_returns_false(self): + entry = {"connection": {"protocol": "rdp"}, "portForward": {"port": "10389"}} + changed = _coerce_settings_subdicts(entry, "connection", "portForward") + self.assertEqual(entry["connection"], {"protocol": "rdp"}) + self.assertEqual(entry["portForward"], {"port": "10389"}) + self.assertFalse(changed) + + def test_single_key_only_touches_that_key(self): + entry = {"connection": "", "portForward": ""} + changed = _coerce_settings_subdicts(entry, "connection") # RBI: connection only + self.assertEqual(entry["connection"], {}) + self.assertEqual(entry["portForward"], "") # untouched + self.assertTrue(changed) + + def test_non_dict_entry_is_a_noop(self): + # Caller is responsible for ensuring entry is a dict; helper must not raise. + self.assertFalse(_coerce_settings_subdicts("", "connection")) + self.assertFalse(_coerce_settings_subdicts(None, "connection")) + + def test_indexing_after_coercion_does_not_raise(self): + # Reproduces the bug-#1 / tunnel-edit crash shape, then confirms the write lands. + entry = {"portForward": ""} + _coerce_settings_subdicts(entry, "connection", "portForward") + entry["connection"]["port"] = 3389 # would KeyError pre-fix + entry["portForward"]["port"] = "10389" # would TypeError pre-fix (str) + self.assertEqual(entry["connection"]["port"], 3389) + self.assertEqual(entry["portForward"]["port"], "10389") + + +if __name__ == '__main__': + unittest.main() diff --git a/unit-tests/pam/test_dag_layer_b_migration.py b/unit-tests/pam/test_dag_layer_b_migration.py index d89681a13..c332841c0 100644 --- a/unit-tests/pam/test_dag_layer_b_migration.py +++ b/unit-tests/pam/test_dag_layer_b_migration.py @@ -488,10 +488,17 @@ def _build_tg(): tg.linking_dag.get_vertex.return_value = resource_vertex # Mark the resource as belonging to the config so the early-out doesn't fire. tg.resource_belongs_to_config = MagicMock(return_value=True) + # No existing launch credentials by default (admin-only case). + tg.get_launch_credentials = MagicMock(return_value=[]) return tg, resource_vertex def test_is_admin_true_routes_to_configure_resource(self): - """is_admin=True triggers a configure_resource POST with the right adminUid.""" + """is_admin=True triggers a configure_resource POST with the right adminUid. + + With no existing launch credentials (a valid pamMachine state — admin only), + connectUsers is sent as an EMPTY-but-present wrapper so krouter still flips + is_admin on the existing edge (UserRest.kt:295-318) rather than no-opping + (UserRest.kt:331-341).""" tg, _ = self._build_tg() captured = {} @@ -513,9 +520,35 @@ def _capture(params, rq): assert len(rq.recordUid) == 16 # networkUid is the config (the TunnelGraph's record) assert len(rq.networkUid) == 16 + # connectUsers wrapper must be PRESENT (so krouter takes the flip-on-existing + # branch) but empty here (no launch creds to preserve). + assert rq.HasField('connectUsers') + assert len(rq.connectUsers.uids) == 0 # Critical: legacy link_user must NOT be called when configure_resource succeeded link_user_mock.assert_not_called() + def test_is_admin_true_preserves_existing_launch_creds_in_connect_users(self): + """When the resource already has launch credentials, they ride in connectUsers + so krouter sets is_admin WITHOUT clearing them (replacement semantics).""" + tg, _ = self._build_tg() + tg.get_launch_credentials = MagicMock(return_value=[USER_UID_STR.replace('D', 'F')]) + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=_capture), \ + patch.object(tg, 'link_user'): + tg.link_user_to_resource(USER_UID_STR, TARGET_RES_UID_STR, is_admin=True) + + rq = captured['rq'] + assert len(rq.adminUid) == 16 + assert len(rq.connectUsers.uids) == 1 # existing launch cred preserved + assert len(rq.connectUsers.uids[0]) == 16 + # admin is NOT the launch cred (distinct users) + assert bytes(rq.adminUid) != bytes(rq.connectUsers.uids[0]) + def test_is_admin_true_with_fallback_invokes_legacy_link_user_on_denial(self): """RRC_NOT_ALLOWED with fallback ON -> legacy link_user runs.""" tg, resource_vertex = self._build_tg() @@ -577,6 +610,42 @@ def test_resource_not_in_config_returns_false_early(self): link_user_mock.assert_not_called() +class TestTunnelGraphGetLaunchCredentials: + """get_launch_credentials enumerates is_launch_credential ACL edges on a resource.""" + + @staticmethod + def _make_user_vertex(uid, acl_content): + uv = MagicMock() + uv.uid = uid + edge = MagicMock() + edge.content_as_dict = acl_content + uv.get_edge.return_value = edge + return uv + + def _build_tg(self, user_vertices): + from keepercommander.commands.tunnel.port_forward.TunnelGraph import TunnelDAG + tg = TunnelDAG.__new__(TunnelDAG) + tg.linking_dag = MagicMock() + resource_vertex = MagicMock() + resource_vertex.has_vertices.return_value = user_vertices + tg.linking_dag.get_vertex.return_value = resource_vertex + return tg + + def test_returns_only_launch_credential_users(self): + uv_launch = self._make_user_vertex('LAUNCH_UID', {'belongs_to': True, 'is_launch_credential': True}) + uv_admin = self._make_user_vertex('ADMIN_UID', {'is_admin': True}) + uv_plain = self._make_user_vertex('PLAIN_UID', {'belongs_to': True}) + tg = self._build_tg([uv_launch, uv_admin, uv_plain]) + assert tg.get_launch_credentials('RES') == ['LAUNCH_UID'] + + def test_returns_empty_when_no_resource_vertex(self): + from keepercommander.commands.tunnel.port_forward.TunnelGraph import TunnelDAG + tg = TunnelDAG.__new__(TunnelDAG) + tg.linking_dag = MagicMock() + tg.linking_dag.get_vertex.return_value = None + assert tg.get_launch_credentials('RES') == [] + + # --------------------------------------------------------------------------- # # TunnelGraph is_iam_user migration via set_record_rotation (no fallback) # # --------------------------------------------------------------------------- # @@ -1053,7 +1122,7 @@ def test_already_v1_skips_both_endpoints(self): def test_v0_upgrade_uses_configure_resource(self): """No version or version=0 -> upgrade to v1 via configure_resource(meta=...). - The proto's meta carries the versioned JSON payload.""" + The proto's meta carries a VERSION-ONLY payload (empty allowedSettings).""" from keepercommander.commands.tunnel.port_forward import TunnelGraph as tg_mod from keepercommander.commands.tunnel.port_forward.TunnelGraph import RESOURCE_META_VERSION_V1 @@ -1075,11 +1144,12 @@ def _capture(params, rq): assert len(rq.recordUid) == 16 assert len(rq.networkUid) == 16 assert rq.recordUid != rq.networkUid - # meta carries the versioned payload + # meta bumps version only; allowedSettings is sent EMPTY so krouter's + # deep-merge preserves the server's existing flags (connections stays True) + # rather than re-asserting a possibly-stale in-memory snapshot. meta = json.loads(rq.meta.decode()) assert meta.get('version') == RESOURCE_META_VERSION_V1 - assert 'allowedSettings' in meta - assert meta['allowedSettings'].get('connections') is True + assert meta['allowedSettings'] == {} def test_no_resource_vertex_returns_early(self): """If get_vertex returns None, no write at all.""" diff --git a/unit-tests/pam/test_get_config_uid_local_vault.py b/unit-tests/pam/test_get_config_uid_local_vault.py index f7780d3fb..b743c81c5 100644 --- a/unit-tests/pam/test_get_config_uid_local_vault.py +++ b/unit-tests/pam/test_get_config_uid_local_vault.py @@ -155,10 +155,30 @@ def test_get_config_uid_uses_local_match_and_skips_get_dag_leafs(): mock_dl.assert_not_called() +def test_get_config_uid_uses_pam_link_before_get_dag_leafs(): + """No local match -> precise PAM_LINK resolution wins and legacy get_dag_leafs is skipped. + + Regression guard for the "Found multiple vertex that use the path" bug: the + legacy graphId=0 get_dag_leafs can return a stale/duplicate config when a + resource still has link edges under more than one PAM config. Resolving the + owner precisely via the per-graph PAM_LINK get_leafs avoids loading that + ambiguous graph, so it must run before the legacy path. + """ + params = MagicMock() + with patch('keepercommander.vault_extensions.find_records', return_value=iter([])), \ + patch.object(tunnel_helpers, 'get_config_uid_via_pam_link', return_value=CONFIG_UID) as mock_link, \ + patch.object(tunnel_helpers, 'get_dag_leafs') as mock_dl: + result = tunnel_helpers.get_config_uid(params, b'tok', b'tk', RESOURCE_UID) + assert result == CONFIG_UID + mock_link.assert_called_once() + mock_dl.assert_not_called() + + def test_get_config_uid_falls_back_to_get_dag_leafs_when_no_local_match(): - """No local match -> legacy get_dag_leafs path; whatever it returns flows through.""" + """No local match and no PAM_LINK owner -> legacy get_dag_leafs path; its result flows through.""" params = MagicMock() with patch('keepercommander.vault_extensions.find_records', return_value=iter([])), \ + patch.object(tunnel_helpers, 'get_config_uid_via_pam_link', return_value=''), \ patch.object(tunnel_helpers, 'get_dag_leafs', return_value=[{'type': 'rec', 'value': CONFIG_UID, 'name': None}]) as mock_dl: result = tunnel_helpers.get_config_uid(params, b'tok', b'tk', RESOURCE_UID) @@ -167,9 +187,10 @@ def test_get_config_uid_falls_back_to_get_dag_leafs_when_no_local_match(): def test_get_config_uid_returns_none_when_neither_path_resolves(): - """No local match and get_dag_leafs returns None -> None propagates.""" + """No local match, no PAM_LINK owner, and get_dag_leafs returns None -> None propagates.""" params = MagicMock() with patch('keepercommander.vault_extensions.find_records', return_value=iter([])), \ + patch.object(tunnel_helpers, 'get_config_uid_via_pam_link', return_value=''), \ patch.object(tunnel_helpers, 'get_dag_leafs', return_value=None): result = tunnel_helpers.get_config_uid(params, b'tok', b'tk', RESOURCE_UID) assert result is None diff --git a/unit-tests/pam/test_set_launch_credentials.py b/unit-tests/pam/test_set_launch_credentials.py index af16ad03f..76c832ba8 100644 --- a/unit-tests/pam/test_set_launch_credentials.py +++ b/unit-tests/pam/test_set_launch_credentials.py @@ -35,6 +35,7 @@ RESOURCE_UID = 'AAAAAAAAAAAAAAAAAAAAAA' # 22-char base64url, decodes to 16 bytes NETWORK_UID = 'BBBBBBBBBBBBBBBBBBBBBB' LAUNCH_UID = 'CCCCCCCCCCCCCCCCCCCCCC' +ADMIN_UID = 'DDDDDDDDDDDDDDDDDDDDDD' # --------------------------------------------------------------------------- # @@ -135,16 +136,29 @@ def _capture(params, rq): assert len(rq.connectUsers.uids) == 0 meta = json.loads(rq.meta.decode()) assert meta['version'] == RESOURCE_META_VERSION_V1 - assert meta['allowedSettings'] == {'connections': True} + # Version-only meta: empty allowedSettings so krouter's deep-merge preserves + # the server's current flags (connections stays True) instead of re-asserting + # a stale in-memory snapshot. + assert meta['allowedSettings'] == {} # CLEAR path doesn't do a belongs_to follow-up. tdag.link_user.assert_not_called() -def test_set_path_preserves_existing_meta_fields(): - """The meta-upgrade carries existing allowedSettings / rotateOnTermination.""" +def test_set_path_sends_version_only_meta_and_does_not_clobber_allowed_settings(): + """Regression: the launch-cred meta must NOT re-assert a (possibly stale) + in-memory allowedSettings snapshot. + + set_resource_allowed's Layer-B path enables connections on the SERVER but does + not refresh the in-memory vertex, so get_vertex_content here still reports the + pre-command flags (e.g. connections=False). If we sent those, krouter's + deep-merge would flip connections back off and the vault would hide the + connection port/protocol. The fix sends version=1 with an EMPTY allowedSettings + so the server's just-enabled connections survives the merge untouched. + """ + # Simulate the stale in-memory snapshot (connections still off / rotation set). initial = { 'version': 0, - 'allowedSettings': {'connections': True, 'rotation': False}, + 'allowedSettings': {'connections': False, 'rotation': False}, 'rotateOnTermination': True, } tdag, _ = _make_tdag(initial_meta=initial) @@ -162,8 +176,101 @@ def _capture(params, rq): meta = json.loads(captured['rq'].meta.decode()) assert meta['version'] == RESOURCE_META_VERSION_V1 - assert meta['allowedSettings'] == {'connections': True, 'rotation': False} - assert meta['rotateOnTermination'] is True + # No stale flags re-asserted — empty allowedSettings, no rotateOnTermination. + assert meta['allowedSettings'] == {} + assert 'connections' not in meta['allowedSettings'] + assert 'rotateOnTermination' not in meta + + +# --------------------------------------------------------------------------- # +# Admin credential — sent ALONGSIDE connectUsers in the same request # +# --------------------------------------------------------------------------- # + + +def test_set_path_with_admin_sends_admin_uid_alongside_connect_users(): + """Admin + launch in one configure_resource: connectUsers carries the launch + uid AND adminUid is set (so krouter flips is_admin even on an existing edge), + with admin NOT in connectUsers (so it does not also become a launch cred).""" + tdag, _ = _make_tdag(initial_meta={'allowedSettings': {'connections': True}}) + + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=False), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=_capture): + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=LAUNCH_UID, admin_uid=ADMIN_UID) + + rq = captured['rq'] + assert len(rq.connectUsers.uids) == 1 + assert len(rq.connectUsers.uids[0]) == 16 # launch uid present + assert len(rq.adminUid) == 16 # admin uid present + assert bytes(rq.adminUid) != bytes(rq.connectUsers.uids[0]) # admin not the launch cred + meta = json.loads(rq.meta.decode()) + assert meta['version'] == RESOURCE_META_VERSION_V1 + assert meta['allowedSettings'] == {} + # belongs_to follow-up fires for the launch user only. + tdag.link_user.assert_called_once() + + +def test_admin_only_sends_admin_uid_with_empty_connect_users(): + """Admin-only (no launch): empty connectUsers wrapper + adminUid, so krouter + sets is_admin on the existing edge and there is no launch follow-up.""" + tdag, _ = _make_tdag(initial_meta=None) + + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=False), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=_capture): + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=None, admin_uid=ADMIN_UID) + + rq = captured['rq'] + assert len(rq.connectUsers.uids) == 0 + assert len(rq.adminUid) == 16 + tdag.link_user.assert_not_called() + + +def test_no_admin_uid_leaves_admin_field_unset(): + """Without admin_uid the adminUid field stays empty (admin untouched).""" + tdag, _ = _make_tdag(initial_meta=None) + + captured = {} + + def _capture(params, rq): + captured['rq'] = rq + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=False), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource', + side_effect=_capture): + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=LAUNCH_UID) + + assert len(captured['rq'].adminUid) == 0 + + +def test_admin_fallback_links_admin_via_legacy_when_feature_disabled(): + """Under feature-disabled fallback, admin is written via legacy link_user(is_admin=True).""" + tdag, resource_vertex = _make_tdag(initial_meta=None) + + with patch('keepercommander.commands.pam._layer_b.is_layer_b_feature_disabled', return_value=True), \ + patch('keepercommander.commands.pam.router_helper.get_router_url', return_value='krouter.test'), \ + patch('keepercommander.commands.pam.router_helper.router_configure_resource') as mock_cr: + tdag.set_launch_credentials(RESOURCE_UID, launch_uid=LAUNCH_UID, admin_uid=ADMIN_UID) + mock_cr.assert_not_called() + + admin_calls = [c for c in tdag.link_user.call_args_list if c.args and c.args[0] == ADMIN_UID] + assert len(admin_calls) == 1 + assert admin_calls[0].kwargs.get('is_admin') is True + # launch still goes through the legacy link_user_to_resource path. + tdag.link_user_to_resource.assert_called_once() # --------------------------------------------------------------------------- # From 49afb766ad26bc805a55660b3775f3a2b2eb976c Mon Sep 17 00:00:00 2001 From: m-afanasiuk Date: Thu, 4 Jun 2026 15:04:18 -0500 Subject: [PATCH 23/23] Universal Secret Sync commands --- keepercommander/commands/discoveryrotation.py | 6 + keepercommander/commands/pam/pam_dto.py | 20 + keepercommander/commands/pam/router_helper.py | 9 + .../commands/universalsecretsync.py | 718 ++++++++++++++++++ keepercommander/constants.py | 1 + 5 files changed, 754 insertions(+) create mode 100644 keepercommander/commands/universalsecretsync.py diff --git a/keepercommander/commands/discoveryrotation.py b/keepercommander/commands/discoveryrotation.py index 78edeb618..7671ef65d 100644 --- a/keepercommander/commands/discoveryrotation.py +++ b/keepercommander/commands/discoveryrotation.py @@ -89,6 +89,10 @@ from .pam_saas.config import PAMActionSaasConfigCommand from .pam_saas.update import PAMActionSaasUpdateCommand from .tunnel_and_connections import PAMTunnelCommand, PAMConnectionCommand, PAMRbiCommand, PAMSplitCommand +from .universalsecretsync import ( + PAMUniversalSyncConfigCommand, + PAMUniversalSyncRunCommand +) # These characters are based on the Vault PAM_DEFAULT_SPECIAL_CHAR = '''!@#$%^?();',.=+[]<>{}-_/\\*&:"`~|''' @@ -196,6 +200,8 @@ def __init__(self): self.register_command('workflow', PAMWorkflowCommand(), 'Manage PAM Workflows', 'w') self.register_command('access', PAMPrivilegedAccessCommand(), 'Manage privileged cloud access operations', 'ac') + self.register_command('universal-sync-config', PAMUniversalSyncConfigCommand(), 'Manage Universal Sync Configurations', 'usc') + self.register_command('universal-sync-run', PAMUniversalSyncRunCommand(), 'Run Universal Sync', 'usr') class PAMGatewayCommand(GroupCommand): diff --git a/keepercommander/commands/pam/pam_dto.py b/keepercommander/commands/pam/pam_dto.py index e5d353671..b1e056b49 100644 --- a/keepercommander/commands/pam/pam_dto.py +++ b/keepercommander/commands/pam/pam_dto.py @@ -382,3 +382,23 @@ def __init__(self, inputs, conversation_id=None, gateway_destination=None): def toJSON(self): return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) + + +class GatewayActionUniversalSyncRunInputs: + + def __init__(self, network_uid, dry_run=False): + self.configurationUid = network_uid + self.dryRun = dry_run + + def toJSON(self): + return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) + + +class GatewayActionUniversalSyncRun(GatewayAction): + + def __init__(self, inputs: GatewayActionUniversalSyncRunInputs, conversation_id=None, gateway_destination=None): + super().__init__('universal-secrets-sync', inputs=inputs, conversation_id=conversation_id, + gateway_destination=gateway_destination, is_scheduled=True) + + def toJSON(self): + return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) diff --git a/keepercommander/commands/pam/router_helper.py b/keepercommander/commands/pam/router_helper.py index 1ccc11d49..e74dfaa05 100644 --- a/keepercommander/commands/pam/router_helper.py +++ b/keepercommander/commands/pam/router_helper.py @@ -111,6 +111,15 @@ def router_configure_network_graph(params, proto_request, transmission_key=None, return rs +def router_configure_universal_sync(params, proto_request, transmission_key=None, + encrypted_transmission_key=None, encrypted_session_token=None): + rs = _post_request_to_router(params, 'configure_universal_sync', proto_request, transmission_key=transmission_key, + encrypted_transmission_key=encrypted_transmission_key, + encrypted_session_token=encrypted_session_token) + + return rs + + def router_get_rotation_schedules(params, proto_request): return _post_request_to_router(params, 'get_rotation_schedules', rq_proto=proto_request, rs_type=pam_pb2.PAMRotationSchedulesResponse) diff --git a/keepercommander/commands/universalsecretsync.py b/keepercommander/commands/universalsecretsync.py new file mode 100644 index 000000000..3dddfacfb --- /dev/null +++ b/keepercommander/commands/universalsecretsync.py @@ -0,0 +1,718 @@ +# _ __ +# | |/ /___ ___ _ __ ___ _ _ ® +# | ' 0 else "None" + + row = [ + config['record_uid'], + config['record_title'], + config['record_type'], + enabled_str, + dry_run_str, + folders_str, + config['vault_name'], + config['sync_identity'] + ] + table.append(row) + + dump_report_data(table, headers, fmt='table', filename="", row_number=False, column_width=None) + + @staticmethod + def print_uss_configuration_details(params, network_uid, format_type='table'): + """Display detailed view of a single USS configuration with folder sync status""" + from ..display import Spinner + from ..keeper_dag import DAG + from ..keeper_dag.connection.commander import Connection + from datetime import datetime + + # Load the network record + network = vault.KeeperRecord.load(params, network_uid) + if not network: + if format_type == 'json': + return json.dumps({"error": f'Network "{network_uid}" not found'}) + else: + raise CommandError('', f'{bcolors.FAIL}Network "{network_uid}" not found{bcolors.ENDC}') + + # Check if it's a supported USS configuration type + uss_supported_types = ('pamGcpConfiguration', 'pamAzureConfiguration', 'pamAwsConfiguration') + if not isinstance(network, vault.TypedRecord) or network.record_type not in uss_supported_types: + if format_type == 'json': + return json.dumps({"error": f'Record "{network_uid}" is not a USS configuration'}) + else: + raise CommandError('', f'{bcolors.FAIL}Record "{network_uid}" is not a USS configuration{bcolors.ENDC}') + + spinner = Spinner('Loading USS configuration details...') + spinner.start() + try: + # Create DAG connection and load graph + conn = Connection(params=params) + dag = DAG(conn=conn, record=network, graph_id=0, logger=logging, history_level=1) + dag.load(sync_point=0) + + # Get root vertex and check for universal_sync loop edge + root = dag.get_root + + universal_sync_edge = None + for edge in root.edges: + if edge.path == 'universal_sync' and edge.head_uid == root.uid: + universal_sync_edge = edge + break + + if not universal_sync_edge: + spinner.stop() + if format_type == 'json': + return json.dumps({"error": "No USS configuration found for this network"}) + else: + raise CommandError('', f'{bcolors.FAIL}No USS configuration found for this network{bcolors.ENDC}') + + # Get the configuration data + config_data = universal_sync_edge.content_as_dict or {} + + # Decrypt vault_name if present. The router stores it under the + # 'vaultName' key as a base64-url string of the encrypted bytes. + vault_name = 'N/A' + vault_name_encrypted = config_data.get('vaultName') + if vault_name_encrypted: + try: + vault_name_bytes = crypto.decrypt_aes_v2( + utils.base64_url_decode(vault_name_encrypted), network.record_key) + vault_name = vault_name_bytes.decode('utf-8') + except Exception as e: + logging.debug(f"Failed to decrypt vault_name for network {network.record_uid}: {e}") + vault_name = 'N/A' + + # Decrypt sync_identity if present. The router stores it under the + # 'syncIdentity' key as a base64-url string of the encrypted bytes; the + # decrypted value is the UID of the Identity record used for syncing. + sync_identity = 'N/A' + sync_identity_encrypted = config_data.get('syncIdentity') + if sync_identity_encrypted: + try: + sync_identity_bytes = crypto.decrypt_aes_v2( + utils.base64_url_decode(sync_identity_encrypted), network.record_key) + sync_identity_uid = sync_identity_bytes.decode('utf-8') + identity_record = vault.KeeperRecord.load(params, sync_identity_uid) + if identity_record and getattr(identity_record, 'title', None): + sync_identity = f'{identity_record.title} ({sync_identity_uid})' + else: + sync_identity = sync_identity_uid + except Exception as e: + logging.debug(f"Failed to decrypt sync_identity for network {network.record_uid}: {e}") + sync_identity = 'N/A' + + # Get folder vertices with sync status + folder_vertices = [] + for vertex in root.has_vertices(): + for edge in vertex.edges: + if edge.path == 'universal_sync_folder' and edge.head_uid == root.uid: + folder_vertices.append(vertex) + break + + # Collect folder information with sync status + folder_details = [] + for folder_vertex in folder_vertices: + folder_uid = folder_vertex.uid + folder_name = None + + # Try to get folder from folder_cache + if folder_uid in params.folder_cache: + folder = params.folder_cache[folder_uid] + folder_name = folder.name if hasattr(folder, 'name') else None + # If not found, try subfolder_cache + if not folder_name and folder_uid in params.subfolder_cache: + sf = params.subfolder_cache[folder_uid] + if 'data_unencrypted' in sf: + try: + data = json.loads(sf['data_unencrypted'].decode()) + folder_name = data.get('name') + except Exception: + pass + + # Get universal_sync_complete loop edge for this folder + sync_complete_data = None + for edge in folder_vertex.edges: + if edge.path == 'universal_sync_complete' and edge.head_uid == folder_vertex.uid: + sync_complete_data = edge.content_as_dict + break + + folder_details.append({ + 'uid': folder_uid, + 'name': folder_name or folder_uid, + 'sync_complete_data': sync_complete_data + }) + + finally: + spinner.stop() + + # Display results + if format_type == 'json': + result = { + 'record_uid': network.record_uid, + 'record_title': network.title if hasattr(network, 'title') else 'N/A', + 'record_type': network.record_type if hasattr(network, 'record_type') else 'N/A', + 'enabled': config_data.get('enabled', False), + 'dry_run_enabled': config_data.get('dryRunEnabled', False), + 'vault_name': vault_name, + 'sync_identity': sync_identity, + 'folders': [] + } + + for folder in folder_details: + sync_data = folder.get('sync_complete_data') + folder_result = { + 'uid': folder['uid'], + 'name': folder['name'], + 'last_synced': None, + 'success': None + } + if sync_data: + folder_result['last_synced'] = sync_data.get('lastSynced') + folder_result['success'] = sync_data.get('success') + folder_result['not_accessible'] = sync_data.get('notAccessible', False) + folder_result['error_message'] = sync_data.get('errorMessage') + result['folders'].append(folder_result) + + return json.dumps(result, indent=2) + else: + # Display as name-value pairs (like PAM config detail view) + table = [] + header = ['name', 'value'] + + table.append(['UID', network.record_uid]) + table.append(['Name', network.title if hasattr(network, 'title') else 'N/A']) + table.append(['Config Type', network.record_type]) + table.append(['Enabled', 'Yes' if config_data.get('enabled', False) else 'No']) + table.append(['Dry Run', 'Yes' if config_data.get('dryRunEnabled', False) else 'No']) + table.append(['Vault Name', vault_name]) + table.append(['Sync Identity', sync_identity]) + table.append(['', '']) # Blank row separator + + # Display folder sync details + if folder_details: + table.append([f'{bcolors.BOLD}Folders ({len(folder_details)}){bcolors.ENDC}', '']) + for folder in folder_details: + sync_data = folder.get('sync_complete_data') + + if sync_data: + # Get last synced timestamp + last_synced_ms = sync_data.get('lastSynced') + if last_synced_ms: + try: + dt = datetime.fromtimestamp(last_synced_ms / 1000) + last_synced_str = dt.strftime('%Y-%m-%d %H:%M:%S') + except Exception: + last_synced_str = str(last_synced_ms) + else: + last_synced_str = 'Never' + + # Get success status + success = sync_data.get('success', False) + if success: + status_str = f"{bcolors.OKGREEN}Success{bcolors.ENDC}" + else: + status_str = f"{bcolors.FAIL}Failed{bcolors.ENDC}" + else: + last_synced_str = 'Never' + status_str = 'N/A' + + table.append([f' {folder["name"]}', '']) + table.append([f' Last Synced', last_synced_str]) + table.append([f' Status', status_str]) + + # On failure, surface the reason the gateway reported. + if sync_data and not sync_data.get('success', False): + if sync_data.get('notAccessible'): + table.append([' Error', 'Folder not accessible to the gateway']) + elif sync_data.get('errorMessage'): + table.append([' Error', sync_data.get('errorMessage')]) + else: + table.append(['Folders', 'None']) + + dump_report_data(table, header, no_header=True, right_align=(0,)) + + +class PAMUniversalSyncConfigAddCommand(Command): + parser = argparse.ArgumentParser(prog='pam universal-sync-config add') + parser.add_argument('--network', '-n', required=True, dest='network', action='store', + help='Network UID or name to configure universal sync') + parser.add_argument('--enabled', '-e', dest='enabled', action='store', + choices=['true', 'false'], default='true', help='Enable or disable universal sync (default: true)') + parser.add_argument('--dry-run', '-dr', dest='dry_run', action='store', + choices=['true', 'false'], default='false', help='Enable or disable dry run mode (default: false)') + parser.add_argument('--folder', '-f', dest='folder', action='append', + help='Folder UID where synced records will be created (can be specified multiple times)') + parser.add_argument('--sync-identity', '-si', dest='sync_identity', action='store', + help='Identity record UID to use for syncing') + parser.add_argument('--vault-name', '-vn', dest='vault_name', action='store', + help='Vault name for universal sync') + + def get_parser(self): + return PAMUniversalSyncConfigAddCommand.parser + + def execute(self, params, **kwargs): + network_name = kwargs.get('network') + if not network_name: + raise CommandError('', f'{bcolors.FAIL}Network is required{bcolors.ENDC}') + + network = vault.KeeperRecord.load(params, network_name) + if not network: + raise CommandError('', f'{bcolors.FAIL}Network "{network_name}" not found{bcolors.ENDC}') + + rq = pam_pb2.PAMUniversalSyncConfig() + rq.networkUid = url_safe_str_to_bytes(network.record_uid) + + enabled = kwargs.get('enabled') + if enabled is not None: + rq.enabled = enabled.lower() == 'true' + + dry_run = kwargs.get('dry_run') + if dry_run is not None: + rq.dryRunEnabled = dry_run.lower() == 'true' + + folders = kwargs.get('folder') + if folders: + for folder in folders: + folder_uid = folder + # Try to resolve folder by name if not a UID + if len(folder_uid) != 22: + matching_folders = [f for f in params.folder_cache if params.folder_cache[f].name == folder] + if matching_folders: + folder_uid = matching_folders[0] + + folder_obj = pam_pb2.PAMUniversalSyncFolder() + folder_obj.uid = url_safe_str_to_bytes(folder_uid) + rq.folders.append(folder_obj) + + sync_identity = kwargs.get('sync_identity') + if sync_identity: + sync_identity_bytes = string_to_bytes(sync_identity) + encrypted_sync_identity = crypto.encrypt_aes_v2(sync_identity_bytes, network.record_key) + rq.syncIdentity = encrypted_sync_identity + + vault_name = kwargs.get('vault_name') + if vault_name: + vault_name_bytes = string_to_bytes(vault_name) + encrypted_vault_name = crypto.encrypt_aes_v2(vault_name_bytes, network.record_key) + rq.vaultName = encrypted_vault_name + + encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) + + try: + router_helper.router_configure_universal_sync(params, rq, transmission_key, + encrypted_transmission_key, + encrypted_session_token) + print(f'{bcolors.OKGREEN}Universal sync configuration added for network: {network.title}{bcolors.ENDC}') + except Exception as e: + raise CommandError('', f'{bcolors.FAIL}Error adding universal sync configuration: {e}{bcolors.ENDC}') + + +class PAMUniversalSyncConfigEditCommand(Command): + parser = argparse.ArgumentParser(prog='pam universal-sync-config edit') + parser.add_argument('--network', '-n', required=True, dest='network', action='store', + help='Network UID or name to configure universal sync') + parser.add_argument('--enabled', '-e', dest='enabled', action='store', + choices=['true', 'false'], help='Enable or disable universal sync') + parser.add_argument('--dry-run', '-dr', dest='dry_run', action='store', + choices=['true', 'false'], help='Enable or disable dry run mode') + parser.add_argument('--folder', '-f', dest='folder', action='append', + help='Folder UID where synced records will be created (can be specified multiple times)') + parser.add_argument('--sync-identity', '-si', dest='sync_identity', action='store', + help='Identity record UID to use for syncing') + parser.add_argument('--vault-name', '-vn', dest='vault_name', action='store', + help='Vault name for universal sync') + + def get_parser(self): + return PAMUniversalSyncConfigEditCommand.parser + + def execute(self, params, **kwargs): + from ..keeper_dag import DAG + from ..keeper_dag.connection.commander import Connection + + network_name = kwargs.get('network') + if not network_name: + raise CommandError('', f'{bcolors.FAIL}Network is required{bcolors.ENDC}') + + network = vault.KeeperRecord.load(params, network_name) + if not network: + raise CommandError('', f'{bcolors.FAIL}Network "{network_name}" not found{bcolors.ENDC}') + + # Load existing config from DAG + # history_level=1 means only load latest/active edges, not full history + try: + conn = Connection(params=params) + dag = DAG(conn=conn, record=network, graph_id=0, logger=logging, history_level=1) + dag.load(sync_point=0) + root = dag.get_root + + # Look for universal_sync loop edge + universal_sync_edge = None + for edge in root.edges: + if edge.path == 'universal_sync' and edge.head_uid == root.uid: + universal_sync_edge = edge + break + + existing_config = universal_sync_edge.content_as_dict if universal_sync_edge else {} + except Exception: + existing_config = {} + + rq = pam_pb2.PAMUniversalSyncConfig() + rq.networkUid = url_safe_str_to_bytes(network.record_uid) + + # Use existing values if new values not provided + enabled = kwargs.get('enabled') + if enabled is not None: + rq.enabled = enabled.lower() == 'true' + elif 'enabled' in existing_config: + rq.enabled = existing_config['enabled'] + + dry_run = kwargs.get('dry_run') + if dry_run is not None: + rq.dryRunEnabled = dry_run.lower() == 'true' + elif 'dryRunEnabled' in existing_config: + rq.dryRunEnabled = existing_config['dryRunEnabled'] + + # Handle folders - if provided, replace all; if not, keep existing + folders = kwargs.get('folder') + if folders: + for folder in folders: + folder_uid = folder + # Try to resolve folder by name if not a UID + if len(folder_uid) != 22: + matching_folders = [f for f in params.folder_cache if params.folder_cache[f].name == folder] + if matching_folders: + folder_uid = matching_folders[0] + + folder_obj = pam_pb2.PAMUniversalSyncFolder() + folder_obj.uid = url_safe_str_to_bytes(folder_uid) + rq.folders.append(folder_obj) + else: + # Keep existing folders by loading them from DAG + try: + for vertex in root.has_vertices(None, True): + for edge in vertex.edges: + if edge.path == 'universal_sync_folder' and edge.head_uid == root.uid: + folder_obj = pam_pb2.PAMUniversalSyncFolder() + folder_obj.uid = url_safe_str_to_bytes(vertex.uid) + rq.folders.append(folder_obj) + break + except Exception: + pass + + # The router rebuilds the whole config object from the request, so any field + # not re-sent is dropped. Preserve existing encrypted values when not provided. + sync_identity = kwargs.get('sync_identity') + if sync_identity: + sync_identity_bytes = string_to_bytes(sync_identity) + encrypted_sync_identity = crypto.encrypt_aes_v2(sync_identity_bytes, network.record_key) + rq.syncIdentity = encrypted_sync_identity + elif existing_config.get('syncIdentity'): + rq.syncIdentity = utils.base64_url_decode(existing_config['syncIdentity']) + + vault_name = kwargs.get('vault_name') + if vault_name: + vault_name_bytes = string_to_bytes(vault_name) + encrypted_vault_name = crypto.encrypt_aes_v2(vault_name_bytes, network.record_key) + rq.vaultName = encrypted_vault_name + elif existing_config.get('vaultName'): + rq.vaultName = utils.base64_url_decode(existing_config['vaultName']) + + encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) + + try: + router_helper.router_configure_universal_sync(params, rq, transmission_key, + encrypted_transmission_key, + encrypted_session_token) + print(f'{bcolors.OKGREEN}Universal sync configuration updated for network: {network.title}{bcolors.ENDC}') + except Exception as e: + raise CommandError('', f'{bcolors.FAIL}Error updating universal sync configuration: {e}{bcolors.ENDC}') + + +class PAMUniversalSyncConfigRemoveCommand(Command): + parser = argparse.ArgumentParser(prog='pam universal-sync-config remove') + parser.add_argument('--network', '-n', required=True, dest='network', action='store', + help='Network UID or name to remove universal sync configuration') + + def get_parser(self): + return PAMUniversalSyncConfigRemoveCommand.parser + + def execute(self, params, **kwargs): + network_name = kwargs.get('network') + if not network_name: + raise CommandError('', f'{bcolors.FAIL}Network is required{bcolors.ENDC}') + + network = vault.KeeperRecord.load(params, network_name) + if not network: + raise CommandError('', f'{bcolors.FAIL}Network "{network_name}" not found{bcolors.ENDC}') + + # Create empty request to remove config + rq = pam_pb2.PAMUniversalSyncConfig() + rq.networkUid = url_safe_str_to_bytes(network.record_uid) + # All other fields are left empty, which signals removal + + encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) + + try: + router_helper.router_configure_universal_sync(params, rq, transmission_key, + encrypted_transmission_key, + encrypted_session_token) + print(f'{bcolors.OKGREEN}Universal sync configuration removed for network: {network.title}{bcolors.ENDC}') + except Exception as e: + raise CommandError('', f'{bcolors.FAIL}Error removing universal sync configuration: {e}{bcolors.ENDC}') + + +class PAMUniversalSyncRunCommand(Command): + parser = argparse.ArgumentParser(prog='pam universal-sync-run') + parser.add_argument('--network', '-n', required=True, dest='network', action='store', + help='Network UID or name to run universal sync') + + def get_parser(self): + return PAMUniversalSyncRunCommand.parser + + def execute(self, params, **kwargs): + from keeper_secrets_manager_core.utils import url_safe_str_to_bytes + from .pam.config_helper import configuration_controller_get + from .pam.router_helper import router_get_connected_gateways, print_router_response + + network_name = kwargs.get('network') + if not network_name: + raise CommandError('', f'{bcolors.FAIL}Network is required{bcolors.ENDC}') + + network = vault.KeeperRecord.load(params, network_name) + if not network: + raise CommandError('', f'{bcolors.FAIL}Network "{network_name}" not found{bcolors.ENDC}') + + # Get the controller/gateway UID associated with this network configuration + controller = configuration_controller_get(params, url_safe_str_to_bytes(network.record_uid)) + if not controller.controllerUid: + raise CommandError('', f'{bcolors.FAIL}Gateway UID not found for network configuration ' + f'{network.record_uid}.{bcolors.ENDC}') + + # Find connected controllers + enterprise_controllers_connected = router_get_connected_gateways(params) + + controller_from_config_bytes = controller.controllerUid + gateway_uid = utils.base64_url_encode(controller.controllerUid) + if enterprise_controllers_connected: + router_controllers = {controller.controllerUid: controller for controller in + list(enterprise_controllers_connected.controllers)} + connected_controller = router_controllers.get(controller_from_config_bytes) + + if not connected_controller: + print(f'{bcolors.WARNING}The Gateway "{gateway_uid}" is down.{bcolors.ENDC}') + return + else: + print(f'{bcolors.WARNING}There are no connected gateways.{bcolors.ENDC}') + return + + action_inputs = GatewayActionUniversalSyncRunInputs(network_uid=network.record_uid) + + encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(params) + + conversation_id = GatewayAction.generate_conversation_id() + + try: + router_response = router_send_action_to_gateway( + params=params, + gateway_action=GatewayActionUniversalSyncRun(inputs=action_inputs, conversation_id=conversation_id, + gateway_destination=gateway_uid), + message_type=pam_pb2.CMT_USS, + is_streaming=False, + transmission_key=transmission_key, + encrypted_transmission_key=encrypted_transmission_key, + encrypted_session_token=encrypted_session_token + ) + except Exception as e: + raise CommandError('', f'{bcolors.FAIL}Error running universal sync: {e}{bcolors.ENDC}') + + if router_response is None: + print(f'{bcolors.FAIL}The router returned a failure.{bcolors.ENDC}') + return + + print_router_response(router_response, 'job_info', conversation_id, gateway_uid=gateway_uid) + print(f"\nAfter action is finished, use: '{bcolors.OKGREEN}pam usc list -n {network.record_uid}{bcolors.ENDC}, to view sync results'") + diff --git a/keepercommander/constants.py b/keepercommander/constants.py index 89ced2729..4df4e5e3b 100644 --- a/keepercommander/constants.py +++ b/keepercommander/constants.py @@ -252,6 +252,7 @@ class PrivilegeScope(enum.IntEnum): ("RESTRICT_SF_FOLDER_DELETION", 253, "BOOLEAN", "SHARING_ENFORCEMENTS"), ("RESTRICT_PLATFORM_PASSKEY_LOGIN", 254, "BOOLEAN", "ACCOUNT_ENFORCEMENTS"), ("RESTRICT_CROSS_PLATFORM_PASSKEY_LOGIN", 255, "BOOLEAN", "ACCOUNT_ENFORCEMENTS"), + ("ALLOW_CONFIGURE_USS_SETTINGS", 265, "BOOLEAN", "VAULT_FEATURES"), ("IP_MAX_DISTANCE_SESSION_WEB", 256, "LONG", "ACCOUNT_ENFORCEMENTS"), ("IP_MAX_DISTANCE_SESSION_MOBILE", 257, "LONG", "ACCOUNT_ENFORCEMENTS"), ("IP_MAX_DISTANCE_SESSION_DESKTOP", 258, "LONG", "ACCOUNT_ENFORCEMENTS"),