diff --git a/.github/workflows/publish-sdk.yml b/.github/workflows/publish-sdk.yml index 3ecaaddd..b9dea09d 100644 --- a/.github/workflows/publish-sdk.yml +++ b/.github/workflows/publish-sdk.yml @@ -20,10 +20,10 @@ jobs: with: ref: ${{ github.event.inputs.version }} - - name: Set up Python 3.13 - uses: actions/setup-python@v5 + - name: Set up Python 3.14 + uses: actions/setup-python@v7 with: - python-version: '3.13' + python-version: '3.14' - name: Install dependencies run: | @@ -38,7 +38,7 @@ jobs: python3 -m build --wheel keepersdk-package - name: Archive the package - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: KeeperSdkWheel retention-days: 1 @@ -52,15 +52,15 @@ jobs: environment: test steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: KeeperSdkWheel path: keepersdk-package/dist - - name: Set up Python 3.13 - uses: actions/setup-python@v5 + - name: Set up Python 3.14 + uses: actions/setup-python@v7 with: - python-version: '3.13' + python-version: '3.14' - name: Publish to Test PyPI env: @@ -77,15 +77,15 @@ jobs: environment: prod steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: KeeperSdkWheel path: keepersdk-package/dist - - name: Set up Python 3.13 - uses: actions/setup-python@v5 + - name: Set up Python 3.14 + uses: actions/setup-python@v7 with: - python-version: '3.13' + python-version: '3.14' - name: Publish to PyPI env: diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index 1c6460c2..a6c1200c 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -13,7 +13,7 @@ jobs: test-with-unittest: strategy: matrix: - python-version: ['3.8', '3.14'] + python-version: ['3.10', '3.14'] runs-on: ubuntu-latest permissions: diff --git a/examples/sdk_examples/device_management/admin_account_lock_device.py b/examples/sdk_examples/device_management/admin_account_lock_device.py new file mode 100644 index 00000000..acf106a1 --- /dev/null +++ b/examples/sdk_examples/device_management/admin_account_lock_device.py @@ -0,0 +1,549 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + device_management, + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.vault import sqlite_storage, vault_online, ksm_management + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def print_admin_devices_table(devices): + if not devices: + print('\nNo devices found.') + return + print(f'\nAdmin Device List ({len(devices)} devices found)') + print('=' * 120) + print( + f"{'ID':<4} {'Enterprise User ID':<20} {'Device Name':<22} " + f"{'UI Category':<18} {'Device Status':<16} {'Login Status':<14} {'Last Accessed':<20}" + ) + print('-' * 120) + for d in devices: + last = d.last_accessed.strftime('%Y-%m-%d %H:%M:%S') if d.last_accessed else 'N/A' + print( + f"{d.list_index:<4} {d.enterprise_user_id:<20} {d.name[:21]:<22} " + f"{d.ui_category[:17]:<18} {d.device_status[:15]:<16} " + f"{d.login_status[:13]:<14} {last:<20}" + ) + print('-' * 120) + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + return + + # Fill in your values here (enterprise admin required). + enterprise_user_id = 0 + device_identifiers = [''] + + try: + print(f'Account-locking {len(device_identifiers)} device(s) for user {enterprise_user_id}...') + for name in device_management.account_lock_admin_user_devices( + keeper_auth_context, enterprise_user_id, device_identifiers + ): + print( + f"Device action successfully completed: '{name}' account locked " + f'for user {enterprise_user_id}' + ) + print(f'\nUpdated device list for user {enterprise_user_id}:') + print_admin_devices_table( + device_management.list_admin_devices(keeper_auth_context, [enterprise_user_id]) + ) + except Exception as e: + print(f'Error account-locking admin devices: {e}') + finally: + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/device_management/admin_account_unlock_device.py b/examples/sdk_examples/device_management/admin_account_unlock_device.py new file mode 100644 index 00000000..dc1eadb2 --- /dev/null +++ b/examples/sdk_examples/device_management/admin_account_unlock_device.py @@ -0,0 +1,549 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + device_management, + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.vault import sqlite_storage, vault_online, ksm_management + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def print_admin_devices_table(devices): + if not devices: + print('\nNo devices found.') + return + print(f'\nAdmin Device List ({len(devices)} devices found)') + print('=' * 120) + print( + f"{'ID':<4} {'Enterprise User ID':<20} {'Device Name':<22} " + f"{'UI Category':<18} {'Device Status':<16} {'Login Status':<14} {'Last Accessed':<20}" + ) + print('-' * 120) + for d in devices: + last = d.last_accessed.strftime('%Y-%m-%d %H:%M:%S') if d.last_accessed else 'N/A' + print( + f"{d.list_index:<4} {d.enterprise_user_id:<20} {d.name[:21]:<22} " + f"{d.ui_category[:17]:<18} {d.device_status[:15]:<16} " + f"{d.login_status[:13]:<14} {last:<20}" + ) + print('-' * 120) + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + return + + # Fill in your values here (enterprise admin required). + enterprise_user_id = 0 + device_identifiers = [''] + + try: + print(f'Account-unlocking {len(device_identifiers)} device(s) for user {enterprise_user_id}...') + for name in device_management.account_unlock_admin_user_devices( + keeper_auth_context, enterprise_user_id, device_identifiers + ): + print( + f"Device action successfully completed: '{name}' account unlocked " + f'for user {enterprise_user_id}' + ) + print(f'\nUpdated device list for user {enterprise_user_id}:') + print_admin_devices_table( + device_management.list_admin_devices(keeper_auth_context, [enterprise_user_id]) + ) + except Exception as e: + print(f'Error account-unlocking admin devices: {e}') + finally: + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/device_management/admin_lock_device.py b/examples/sdk_examples/device_management/admin_lock_device.py new file mode 100644 index 00000000..7c16c649 --- /dev/null +++ b/examples/sdk_examples/device_management/admin_lock_device.py @@ -0,0 +1,549 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + device_management, + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.vault import sqlite_storage, vault_online, ksm_management + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def print_admin_devices_table(devices): + if not devices: + print('\nNo devices found.') + return + print(f'\nAdmin Device List ({len(devices)} devices found)') + print('=' * 120) + print( + f"{'ID':<4} {'Enterprise User ID':<20} {'Device Name':<22} " + f"{'UI Category':<18} {'Device Status':<16} {'Login Status':<14} {'Last Accessed':<20}" + ) + print('-' * 120) + for d in devices: + last = d.last_accessed.strftime('%Y-%m-%d %H:%M:%S') if d.last_accessed else 'N/A' + print( + f"{d.list_index:<4} {d.enterprise_user_id:<20} {d.name[:21]:<22} " + f"{d.ui_category[:17]:<18} {d.device_status[:15]:<16} " + f"{d.login_status[:13]:<14} {last:<20}" + ) + print('-' * 120) + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + return + + # Fill in your values here (enterprise admin required). + enterprise_user_id = 0 + device_identifiers = [''] + + try: + print(f'Locking {len(device_identifiers)} device(s) for user {enterprise_user_id}...') + for name in device_management.lock_admin_user_devices( + keeper_auth_context, enterprise_user_id, device_identifiers + ): + print( + f"Device action successfully completed: '{name}' locked " + f'for user {enterprise_user_id}' + ) + print(f'\nUpdated device list for user {enterprise_user_id}:') + print_admin_devices_table( + device_management.list_admin_devices(keeper_auth_context, [enterprise_user_id]) + ) + except Exception as e: + print(f'Error locking admin devices: {e}') + finally: + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/device_management/admin_unlock_device.py b/examples/sdk_examples/device_management/admin_unlock_device.py new file mode 100644 index 00000000..04fa79ed --- /dev/null +++ b/examples/sdk_examples/device_management/admin_unlock_device.py @@ -0,0 +1,549 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + device_management, + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.vault import sqlite_storage, vault_online, ksm_management + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def print_admin_devices_table(devices): + if not devices: + print('\nNo devices found.') + return + print(f'\nAdmin Device List ({len(devices)} devices found)') + print('=' * 120) + print( + f"{'ID':<4} {'Enterprise User ID':<20} {'Device Name':<22} " + f"{'UI Category':<18} {'Device Status':<16} {'Login Status':<14} {'Last Accessed':<20}" + ) + print('-' * 120) + for d in devices: + last = d.last_accessed.strftime('%Y-%m-%d %H:%M:%S') if d.last_accessed else 'N/A' + print( + f"{d.list_index:<4} {d.enterprise_user_id:<20} {d.name[:21]:<22} " + f"{d.ui_category[:17]:<18} {d.device_status[:15]:<16} " + f"{d.login_status[:13]:<14} {last:<20}" + ) + print('-' * 120) + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + return + + # Fill in your values here (enterprise admin required). + enterprise_user_id = 0 + device_identifiers = [''] + + try: + print(f'Unlocking {len(device_identifiers)} device(s) for user {enterprise_user_id}...') + for name in device_management.unlock_admin_user_devices( + keeper_auth_context, enterprise_user_id, device_identifiers + ): + print( + f"Device action successfully completed: '{name}' unlocked " + f'for user {enterprise_user_id}' + ) + print(f'\nUpdated device list for user {enterprise_user_id}:') + print_admin_devices_table( + device_management.list_admin_devices(keeper_auth_context, [enterprise_user_id]) + ) + except Exception as e: + print(f'Error unlocking admin devices: {e}') + finally: + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/enterprise_team/enterprise_team_get.py b/examples/sdk_examples/enterprise_team/enterprise_team_get.py new file mode 100644 index 00000000..5239e1c7 --- /dev/null +++ b/examples/sdk_examples/enterprise_team/enterprise_team_get.py @@ -0,0 +1,589 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.enterprise import enterprise_loader, enterprise_team_management, sqlite_enterprise_storage +from keepersdk.vault import sqlite_storage, vault_online + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def _load_enterprise(keeper_auth_context: keeper_auth.KeeperAuth) -> enterprise_loader.EnterpriseLoader: + conn = sqlite3.Connection('file::memory:', uri=True) + enterprise_id = keeper_auth_context.auth_context.enterprise_id or 0 + enterprise_storage = sqlite_enterprise_storage.SqliteEnterpriseStorage(lambda: conn, enterprise_id) + return enterprise_loader.EnterpriseLoader(keeper_auth_context, enterprise_storage) + + +def _load_vault(keeper_auth_context: keeper_auth.KeeperAuth) -> vault_online.VaultOnline: + conn = sqlite3.Connection('file::memory:', uri=True) + storage = sqlite_storage.SqliteVaultStorage( + lambda: conn, keeper_auth_context.auth_context.account_uid + ) + vault = vault_online.VaultOnline(keeper_auth_context, storage) + vault.sync_down() + return vault + + +def print_team_info(team_info: enterprise_team_management.EnterpriseTeamInfo) -> None: + print(f"\nTeam UID: {team_info.team_uid}") + print(f"Team Name: {team_info.team_name}") + if team_info.node_name: + print(f"Node: {team_info.node_name} [{team_info.node_id}]") + print(f"Access Level: {team_info.access_level}") + print(f"Restrict Edit: {team_info.restrict_edit}") + print(f"Restrict Share: {team_info.restrict_share}") + print(f"Restrict View: {team_info.restrict_view}") + + if team_info.team_roles: + print(f"Role(s): {', '.join(x.role_name for x in team_info.team_roles)}") + + if team_info.team_users: + print(f"User(s): {', '.join(x.username for x in team_info.team_users)}") + + if team_info.queued_team_users: + print(f"Queued User(s): {', '.join(x.username for x in team_info.queued_team_users)}") + + if team_info.members: + print(f"\n{'Enterprise User ID':<20} {'Email':<40} {'Share Admin':<12}") + print('-' * 74) + for member in team_info.members: + print( + f"{member.enterprise_user_id:<20} " + f"{member.email[:39]:<40} " + f"{'Yes' if member.is_share_admin else 'No':<12}" + ) + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + print('Login failed.') + return + + # Fill in your values here. + team_name_or_uid = '' + fetch_live_members = True + + is_admin = bool(keeper_auth_context.auth_context.is_enterprise_admin) + enterprise = None + vault = None + + try: + if is_admin: + enterprise = _load_enterprise(keeper_auth_context) + vault = _load_vault(keeper_auth_context) + + team_info = enterprise_team_management.get_team( + team_name_or_uid, + enterprise_data=enterprise.enterprise_data if enterprise else None, + vault_data_obj=vault.vault_data, + auth=keeper_auth_context, + vault=vault, + is_enterprise_admin=is_admin, + include_share_objects=True, + fetch_live_members=fetch_live_members, + ) + print_team_info(team_info) + except enterprise_team_management.EnterpriseTeamManagementError as exc: + print(f'Error getting team: {exc}') + except Exception as exc: + print(f'Error getting team: {exc}') + finally: + if enterprise is not None: + enterprise.close() + if vault is not None: + vault.close() + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/enterprise_team/enterprise_team_list.py b/examples/sdk_examples/enterprise_team/enterprise_team_list.py new file mode 100644 index 00000000..12a09058 --- /dev/null +++ b/examples/sdk_examples/enterprise_team/enterprise_team_list.py @@ -0,0 +1,561 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.enterprise import enterprise_loader, enterprise_team_management, sqlite_enterprise_storage + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def _load_enterprise(keeper_auth_context: keeper_auth.KeeperAuth) -> enterprise_loader.EnterpriseLoader: + conn = sqlite3.Connection('file::memory:', uri=True) + enterprise_id = keeper_auth_context.auth_context.enterprise_id or 0 + enterprise_storage = sqlite_enterprise_storage.SqliteEnterpriseStorage(lambda: conn, enterprise_id) + return enterprise_loader.EnterpriseLoader(keeper_auth_context, enterprise_storage) + + +def print_teams_table(summaries) -> None: + if not summaries: + print('\nNo teams found.') + return + + print(f'\nEnterprise Teams ({len(summaries)} found)') + print('=' * 120) + print( + f"{'Team Name':<30} {'Team UID':<28} {'Node':<25} " + f"{'Users':<8} {'Roles':<8} {'Restricts':<12}" + ) + print('-' * 120) + for team in summaries: + restricts = ( + f"{'R' if team.restrict_view else '-'}" + f"{'W' if team.restrict_edit else '-'}" + f"{'S' if team.restrict_share else '-'}" + ) + print( + f"{team.team_name[:29]:<30} {team.team_uid[:27]:<28} " + f"{team.node_name[:24]:<25} {team.user_count:<8} {team.role_count:<8} {restricts:<12}" + ) + print('-' * 120) + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + print('Login failed.') + return + + if not keeper_auth_context.auth_context.is_enterprise_admin: + print('ERROR: This operation requires enterprise admin privileges.') + keeper_auth_context.close() + return + + # Fill in your values here. + pattern = '' # Optional filter, e.g. 'Testing Team' + + enterprise = None + try: + enterprise = _load_enterprise(keeper_auth_context) + summaries = enterprise_team_management.list_teams( + enterprise.enterprise_data, + pattern=pattern or None, + ) + print_teams_table(summaries) + except Exception as exc: + print(f'Error listing teams: {exc}') + finally: + if enterprise is not None: + enterprise.close() + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/enterprise_team/enterprise_team_membership.py b/examples/sdk_examples/enterprise_team/enterprise_team_membership.py index 3f715a0c..0d7e4162 100644 --- a/examples/sdk_examples/enterprise_team/enterprise_team_membership.py +++ b/examples/sdk_examples/enterprise_team/enterprise_team_membership.py @@ -516,60 +516,57 @@ def view_team_membership(keeper_auth_context: keeper_auth.KeeperAuth): enterprise = enterprise_loader.EnterpriseLoader(keeper_auth_context, enterprise_storage) - team_search = input('Enter team name or UID: ').strip() + team_uid_or_name = "" - if not team_search: - print('No team specified') - else: - team_found = None + team_found = None + + for team in enterprise.enterprise_data.teams.get_all_entities(): + team_name = team.name if hasattr(team, 'name') and team.name else '' + team_uid = team.team_uid if hasattr(team, 'team_uid') else '' - for team in enterprise.enterprise_data.teams.get_all_entities(): - team_name = team.name if hasattr(team, 'name') and team.name else '' - team_uid = team.team_uid if hasattr(team, 'team_uid') else '' - - if (team_search.lower() in team_name.lower() or - team_search == team_uid): - team_found = team - break + if (team_uid_or_name.lower() in team_name.lower() or + team_uid_or_name == team_uid): + team_found = team + break + + if team_found: + team_name = team_found.name if hasattr(team_found, 'name') and team_found.name else 'N/A' + team_uid = team_found.team_uid if hasattr(team_found, 'team_uid') else 'N/A' - if team_found: - team_name = team_found.name if hasattr(team_found, 'name') and team_found.name else 'N/A' - team_uid = team_found.team_uid if hasattr(team_found, 'team_uid') else 'N/A' - - print(f"\nTeam Membership for: {team_name}") - print(f"Team UID: {team_uid}") - print("=" * 100) - - team_users = list(enterprise.enterprise_data.team_users.get_links_by_subject(team_uid)) - - if team_users: - print(f"\nUsers ({len(team_users)}):") - print("-" * 100) - print(f"{'Username':<40} {'Email':<40} {'Status':<20}") - print("-" * 100) - - for team_user in team_users: - user = enterprise.enterprise_data.users.get_entity(team_user.enterprise_user_id) - if user: - user_name = user.full_name if hasattr(user, 'full_name') and user.full_name else user.username - user_email = user.username - user_status = user.status if hasattr(user, 'status') else 'unknown' - print(f"{user_name[:39]:<40} {user_email[:39]:<40} {user_status:<20}") - else: - print("\nNo users in this team") - - queued_users = list(enterprise.enterprise_data.queued_team_users.get_links_by_subject(team_uid)) - if queued_users: - print(f"\nQueued Users ({len(queued_users)}):") - print("-" * 100) - for queued_user in queued_users: - user = enterprise.enterprise_data.users.get_entity(queued_user.enterprise_user_id) - if user: - print(f" - {user.username}") + print(f"\nTeam Membership for: {team_name}") + print(f"Team UID: {team_uid}") + print("=" * 100) + + team_users = list(enterprise.enterprise_data.team_users.get_links_by_subject(team_uid)) + + if team_users: + print(f"\nUsers ({len(team_users)}):") + print("-" * 100) + print(f"{'Username':<40} {'Email':<40} {'Status':<20}") + print("-" * 100) - print("=" * 100) + for team_user in team_users: + user = enterprise.enterprise_data.users.get_entity(team_user.enterprise_user_id) + if user: + user_name = user.full_name if hasattr(user, 'full_name') and user.full_name else user.username + user_email = user.username + user_status = user.status if hasattr(user, 'status') else 'unknown' + print(f"{user_name[:39]:<40} {user_email[:39]:<40} {user_status:<20}") else: - print(f'\nNo team found matching: "{team_search}"') + print("\nNo users in this team") + + queued_users = list(enterprise.enterprise_data.queued_team_users.get_links_by_subject(team_uid)) + if queued_users: + print(f"\nQueued Users ({len(queued_users)}):") + print("-" * 100) + for queued_user in queued_users: + user = enterprise.enterprise_data.users.get_entity(queued_user.enterprise_user_id) + if user: + print(f" - {user.username}") + + print("=" * 100) + else: + print(f'\nNo team found matching: "{team_uid_or_name}"') enterprise.close() keeper_auth_context.close() diff --git a/examples/sdk_examples/enterprise_team/enterprise_team_view.py b/examples/sdk_examples/enterprise_team/enterprise_team_view.py index 63ba9126..d0ba02e4 100644 --- a/examples/sdk_examples/enterprise_team/enterprise_team_view.py +++ b/examples/sdk_examples/enterprise_team/enterprise_team_view.py @@ -516,21 +516,21 @@ def view_enterprise_teams(keeper_auth_context: keeper_auth.KeeperAuth): enterprise = enterprise_loader.EnterpriseLoader(keeper_auth_context, enterprise_storage) - team_search = input('Enter team name or UID (or leave empty for all teams): ').strip() + team_uid_or_name = "" # team name or UID to search for or leave empty to display all teams teams_to_display = [] - if team_search: + if team_uid_or_name: for team in enterprise.enterprise_data.teams.get_all_entities(): team_name = team.name if hasattr(team, 'name') and team.name else '' team_uid = team.team_uid if hasattr(team, 'team_uid') else '' - if (team_search.lower() in team_name.lower() or - team_search == team_uid): + if (team_uid_or_name.lower() in team_name.lower() or + team_uid_or_name == team_uid): teams_to_display.append(team) if not teams_to_display: - print(f'\nNo teams found matching: "{team_search}"') + print(f'\nNo teams found matching: "{team_uid_or_name}"') else: teams_to_display = list(enterprise.enterprise_data.teams.get_all_entities()) diff --git a/examples/sdk_examples/enterprise_team/team_add_role.py b/examples/sdk_examples/enterprise_team/team_add_role.py new file mode 100644 index 00000000..af24a805 --- /dev/null +++ b/examples/sdk_examples/enterprise_team/team_add_role.py @@ -0,0 +1,579 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.enterprise import ( + batch_management, + enterprise_loader, + enterprise_management, + enterprise_user_management, + sqlite_enterprise_storage, +) + + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def find_team(enterprise_data, team_name_or_uid: str): + search = team_name_or_uid.strip() + for team in enterprise_data.teams.get_all_entities(): + team_name = team.name if team.name else '' + if search.lower() in team_name.lower() or search == team.team_uid: + return team + return None + + +def find_user_by_email(enterprise_data, email: str): + email_lower = email.strip().lower() + for user in enterprise_data.users.get_all_entities(): + if user.username.lower() == email_lower: + return user + return None + + +def load_enterprise(keeper_auth_context): + if not keeper_auth_context.auth_context.is_enterprise_admin: + raise RuntimeError('This operation requires enterprise admin privileges.') + enterprise_id = keeper_auth_context.auth_context.enterprise_id or 0 + conn = sqlite3.Connection('file::memory:', uri=True) + enterprise_storage = sqlite_enterprise_storage.SqliteEnterpriseStorage( + lambda: conn, enterprise_id + ) + loader = enterprise_loader.EnterpriseLoader(keeper_auth_context, enterprise_storage) + loader.load() + return loader + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + return + + # Fill in your values here (enterprise admin required). + team_name_or_uid = '' # team name or UID + role_name_or_id = '' # role name or ID + + try: + loader = load_enterprise(keeper_auth_context) + enterprise_data = loader.enterprise_data + + team = find_team(enterprise_data, team_name_or_uid) + if not team: + print(f'Team not found: {team_name_or_uid}') + return + role = enterprise_user_management.resolve_role(enterprise_data, role_name_or_id) + + if any(enterprise_data.managed_nodes.get_links_by_subject(role.role_id)): + print('Teams cannot be assigned to roles with administrative permissions.') + return + + existing_roles = { + x.role_id + for x in enterprise_data.role_teams.get_links_by_object(team.team_uid) + } + if role.role_id in existing_roles: + print(f"Role '{role.name}' is already assigned to team '{team.name}'") + return + + batch = batch_management.BatchManagement(loader=loader) + batch.modify_role_teams(to_add=[ + enterprise_management.RoleTeamEdit(role_id=role.role_id, team_uid=team.team_uid) + ]) + batch.apply() + print(f"Added role '{role.name}' to team '{team.name}'") + except Exception as e: + print(f'Error adding role to team: {e}') + finally: + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/enterprise_team/team_add_user.py b/examples/sdk_examples/enterprise_team/team_add_user.py new file mode 100644 index 00000000..567caf78 --- /dev/null +++ b/examples/sdk_examples/enterprise_team/team_add_user.py @@ -0,0 +1,570 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.enterprise import ( + enterprise_loader, + enterprise_user_management, + sqlite_enterprise_storage, +) + + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def find_team(enterprise_data, team_name_or_uid: str): + search = team_name_or_uid.strip() + for team in enterprise_data.teams.get_all_entities(): + team_name = team.name if team.name else '' + if search.lower() in team_name.lower() or search == team.team_uid: + return team + return None + + +def find_user_by_email(enterprise_data, email: str): + email_lower = email.strip().lower() + for user in enterprise_data.users.get_all_entities(): + if user.username.lower() == email_lower: + return user + return None + + +def load_enterprise(keeper_auth_context): + if not keeper_auth_context.auth_context.is_enterprise_admin: + raise RuntimeError('This operation requires enterprise admin privileges.') + enterprise_id = keeper_auth_context.auth_context.enterprise_id or 0 + conn = sqlite3.Connection('file::memory:', uri=True) + enterprise_storage = sqlite_enterprise_storage.SqliteEnterpriseStorage( + lambda: conn, enterprise_id + ) + loader = enterprise_loader.EnterpriseLoader(keeper_auth_context, enterprise_storage) + loader.load() + return loader + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + return + + # Fill in your values here (enterprise admin required). + team_name_or_uid = '' # team name or UID + user_email = '' # user email + + try: + loader = load_enterprise(keeper_auth_context) + enterprise_data = loader.enterprise_data + + team = find_team(enterprise_data, team_name_or_uid) + if not team: + print(f'Team not found: {team_name_or_uid}') + return + user = find_user_by_email(enterprise_data, user_email) + if not user: + print(f'User not found: {user_email}') + return + + result = enterprise_user_management.add_users_to_teams( + loader, + user_ids=[user.enterprise_user_id], + team_uids={team.team_uid}, + ) + print(result.message or 'Done') + if result.added_count: + print(f"Added user '{user.username}' to team '{team.name}'") + except Exception as e: + print(f'Error adding user to team: {e}') + finally: + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/enterprise_team/team_hide_shared_folders.py b/examples/sdk_examples/enterprise_team/team_hide_shared_folders.py new file mode 100644 index 00000000..c7bc5700 --- /dev/null +++ b/examples/sdk_examples/enterprise_team/team_hide_shared_folders.py @@ -0,0 +1,605 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.enterprise import ( + batch_management, + enterprise_loader, + enterprise_management, + enterprise_user_management, + sqlite_enterprise_storage, +) + + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def find_team(enterprise_data, team_name_or_uid: str): + search = team_name_or_uid.strip() + for team in enterprise_data.teams.get_all_entities(): + team_name = team.name if team.name else '' + if search.lower() in team_name.lower() or search == team.team_uid: + return team + return None + + +def find_user_by_email(enterprise_data, email: str): + email_lower = email.strip().lower() + for user in enterprise_data.users.get_all_entities(): + if user.username.lower() == email_lower: + return user + return None + + +def load_enterprise(keeper_auth_context): + if not keeper_auth_context.auth_context.is_enterprise_admin: + raise RuntimeError('This operation requires enterprise admin privileges.') + enterprise_id = keeper_auth_context.auth_context.enterprise_id or 0 + conn = sqlite3.Connection('file::memory:', uri=True) + enterprise_storage = sqlite_enterprise_storage.SqliteEnterpriseStorage( + lambda: conn, enterprise_id + ) + loader = enterprise_loader.EnterpriseLoader(keeper_auth_context, enterprise_storage) + loader.load() + return loader + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + return + + # Fill in your values here (enterprise admin required). + team_name_or_uid = '' + user_email = '' + # 'on' hides shared folders from the user on this team; 'off' shows them. + hide_shared_folders = 'on' + + try: + loader = load_enterprise(keeper_auth_context) + enterprise_data = loader.enterprise_data + + team = find_team(enterprise_data, team_name_or_uid) + if not team: + print(f'Team not found: {team_name_or_uid}') + return + user = find_user_by_email(enterprise_data, user_email) + if not user: + print(f'User not found: {user_email}') + return + + user_type = enterprise_management.team_user_type_from_hsf_flag(hide_shared_folders) + if user_type is None: + print("hide_shared_folders must be 'on' or 'off'") + return + + existing_users = { + x.enterprise_user_id + for x in enterprise_data.team_users.get_links_by_subject(team.team_uid) + } + if user.enterprise_user_id not in existing_users: + result = enterprise_user_management.add_users_to_teams( + loader, + user_ids=[user.enterprise_user_id], + team_uids={team.team_uid}, + hide_shared_folders=hide_shared_folders == 'on', + ) + print(result.message or 'Done') + if result.added_count: + hsf_label = 'hidden' if hide_shared_folders == 'on' else 'visible' + print( + f"Added user '{user.username}' to team '{team.name}' " + f"with shared folders {hsf_label}" + ) + return + + batch = batch_management.BatchManagement(loader=loader) + batch.modify_team_users(to_add=[ + enterprise_management.TeamUserEdit( + team_uid=team.team_uid, + enterprise_user_id=user.enterprise_user_id, + user_type=user_type, + ) + ]) + batch.apply() + hsf_label = 'hidden' if hide_shared_folders == 'on' else 'visible' + print( + f"Updated team '{team.name}' member '{user.username}': " + f"shared folders are now {hsf_label}" + ) + except Exception as e: + print(f'Error updating hide shared folders setting: {e}') + finally: + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/enterprise_team/team_remove_role.py b/examples/sdk_examples/enterprise_team/team_remove_role.py new file mode 100644 index 00000000..9eebea51 --- /dev/null +++ b/examples/sdk_examples/enterprise_team/team_remove_role.py @@ -0,0 +1,575 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.enterprise import ( + batch_management, + enterprise_loader, + enterprise_management, + enterprise_user_management, + sqlite_enterprise_storage, +) + + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def find_team(enterprise_data, team_name_or_uid: str): + search = team_name_or_uid.strip() + for team in enterprise_data.teams.get_all_entities(): + team_name = team.name if team.name else '' + if search.lower() in team_name.lower() or search == team.team_uid: + return team + return None + + +def find_user_by_email(enterprise_data, email: str): + email_lower = email.strip().lower() + for user in enterprise_data.users.get_all_entities(): + if user.username.lower() == email_lower: + return user + return None + + +def load_enterprise(keeper_auth_context): + if not keeper_auth_context.auth_context.is_enterprise_admin: + raise RuntimeError('This operation requires enterprise admin privileges.') + enterprise_id = keeper_auth_context.auth_context.enterprise_id or 0 + conn = sqlite3.Connection('file::memory:', uri=True) + enterprise_storage = sqlite_enterprise_storage.SqliteEnterpriseStorage( + lambda: conn, enterprise_id + ) + loader = enterprise_loader.EnterpriseLoader(keeper_auth_context, enterprise_storage) + loader.load() + return loader + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + return + + # Fill in your values here (enterprise admin required). + team_name_or_uid = '' # team name or UID + role_name_or_id = '' # role name or ID + + try: + loader = load_enterprise(keeper_auth_context) + enterprise_data = loader.enterprise_data + + team = find_team(enterprise_data, team_name_or_uid) + if not team: + print(f'Team not found: {team_name_or_uid}') + return + role = enterprise_user_management.resolve_role(enterprise_data, role_name_or_id) + + existing_roles = { + x.role_id + for x in enterprise_data.role_teams.get_links_by_object(team.team_uid) + } + if role.role_id not in existing_roles: + print(f"Role '{role.name}' is not assigned to team '{team.name}'") + return + + batch = batch_management.BatchManagement(loader=loader) + batch.modify_role_teams(to_remove=[ + enterprise_management.RoleTeamEdit(role_id=role.role_id, team_uid=team.team_uid) + ]) + batch.apply() + print(f"Removed role '{role.name}' from team '{team.name}'") + except Exception as e: + print(f'Error removing role from team: {e}') + finally: + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/enterprise_team/team_remove_user.py b/examples/sdk_examples/enterprise_team/team_remove_user.py new file mode 100644 index 00000000..6c919e95 --- /dev/null +++ b/examples/sdk_examples/enterprise_team/team_remove_user.py @@ -0,0 +1,570 @@ +import getpass +import sqlite3 +import json +import logging +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.enterprise import ( + enterprise_loader, + enterprise_user_management, + sqlite_enterprise_storage, +) + + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def find_team(enterprise_data, team_name_or_uid: str): + search = team_name_or_uid.strip() + for team in enterprise_data.teams.get_all_entities(): + team_name = team.name if team.name else '' + if search.lower() in team_name.lower() or search == team.team_uid: + return team + return None + + +def find_user_by_email(enterprise_data, email: str): + email_lower = email.strip().lower() + for user in enterprise_data.users.get_all_entities(): + if user.username.lower() == email_lower: + return user + return None + + +def load_enterprise(keeper_auth_context): + if not keeper_auth_context.auth_context.is_enterprise_admin: + raise RuntimeError('This operation requires enterprise admin privileges.') + enterprise_id = keeper_auth_context.auth_context.enterprise_id or 0 + conn = sqlite3.Connection('file::memory:', uri=True) + enterprise_storage = sqlite_enterprise_storage.SqliteEnterpriseStorage( + lambda: conn, enterprise_id + ) + loader = enterprise_loader.EnterpriseLoader(keeper_auth_context, enterprise_storage) + loader.load() + return loader + + +def main(): + keeper_auth_context, _ = login() + if not keeper_auth_context: + return + + # Fill in your values here (enterprise admin required). + team_name_or_uid = '' # team name or UID + user_email = '' # user email + + try: + loader = load_enterprise(keeper_auth_context) + enterprise_data = loader.enterprise_data + + team = find_team(enterprise_data, team_name_or_uid) + if not team: + print(f'Team not found: {team_name_or_uid}') + return + user = find_user_by_email(enterprise_data, user_email) + if not user: + print(f'User not found: {user_email}') + return + + result = enterprise_user_management.remove_users_from_teams( + loader, + user_ids=[user.enterprise_user_id], + team_uids={team.team_uid}, + ) + print(result.message or 'Done') + if result.removed_count: + print(f"Removed user '{user.username}' from team '{team.name}'") + except Exception as e: + print(f'Error removing user from team: {e}') + finally: + keeper_auth_context.close() + + +if __name__ == '__main__': + main() diff --git a/examples/sdk_examples/nested_shared_folders/nsf_record_add.py b/examples/sdk_examples/nested_shared_folders/nsf_record_add.py index 2eaac9e1..9eea775b 100644 --- a/examples/sdk_examples/nested_shared_folders/nsf_record_add.py +++ b/examples/sdk_examples/nested_shared_folders/nsf_record_add.py @@ -511,22 +511,20 @@ def close_vault(vault: vault_online.VaultOnline, keeper_auth_context: keeper_aut keeper_auth_context.close() def nsf_record_add(vault: vault_online.VaultOnline) -> None: - """Add a record to NSF (nsf-record-add).""" - TITLE = "My NSF Login" - RECORD_TYPE = "login" # e.g. login, password, general - FOLDER_UID_OR_NAME = "Projects" # NSF folder name or UID; None for root - NOTES = "Created via SDK example" + """Add a single NSF record (nsf-record-add / create_nsf_record).""" + TITLE = 'My NSF Login' + RECORD_TYPE = 'login' + FOLDER = 'Projects' # NSF folder name or UID; set None for root + NOTES = 'Created via SDK example' FIELDS = { - "login": "user@example.com", - "password": "changeme", - "url": "https://example.com", + 'login': 'user@example.com', + 'password': 'changeme', + 'url': 'https://example.com', } folder_uid = None - if FOLDER_UID_OR_NAME: - folder_uid = nsf_management.resolve_nsf_folder_uid(vault, FOLDER_UID_OR_NAME) - if not folder_uid: - raise ValueError(f"NSF folder not found: {FOLDER_UID_OR_NAME}") + if FOLDER: + folder_uid = nsf_management.resolve_nsf_folder_uid(vault, FOLDER) or FOLDER result = nsf_management.create_nsf_record( vault, diff --git a/examples/sdk_examples/nested_shared_folders/nsf_record_add_batch.py b/examples/sdk_examples/nested_shared_folders/nsf_record_add_batch.py new file mode 100644 index 00000000..d7e9f6d7 --- /dev/null +++ b/examples/sdk_examples/nested_shared_folders/nsf_record_add_batch.py @@ -0,0 +1,565 @@ +import getpass +import json +import logging +import sqlite3 +from typing import Dict, Optional + +import fido2 +import webbrowser + +from keepersdk import errors, utils +from keepersdk.authentication import ( + configuration, + endpoint, + keeper_auth, + login_auth, +) +from keepersdk.authentication.yubikey import ( + IKeeperUserInteraction, + yubikey_authenticate, +) +from keepersdk.constants import KEEPER_PUBLIC_HOSTS +from keepersdk.vault import nsf_management, sqlite_storage, vault_online + +try: + import pyperclip +except ImportError: + pyperclip = None + +logger = utils.get_logger() +logger.setLevel(logging.INFO) +if not logger.handlers: + _handler = logging.StreamHandler() + _handler.setLevel(logging.INFO) + _handler.setFormatter( + logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s") + ) + logger.addHandler(_handler) + + +class FidoCliInteraction(fido2.client.UserInteraction, IKeeperUserInteraction): + def output_text(self, text: str) -> None: + print(text) + + def prompt_up(self) -> None: + print( + "\nTouch the flashing Security key to authenticate or " + "press Ctrl-C to resume with the primary two factor authentication..." + ) + + def request_pin(self, permissions, rd_id): + return getpass.getpass("Enter Security Key PIN: ") + + def request_uv(self, permissions, rd_id): + print("User Verification required.") + return True + + +# Two-factor duration codes (used by LoginFlow) +_TWO_FACTOR_DURATION_CODES: Dict[login_auth.TwoFactorDuration, str] = { + login_auth.TwoFactorDuration.EveryLogin: "login", + login_auth.TwoFactorDuration.Every12Hours: "12_hours", + login_auth.TwoFactorDuration.EveryDay: "24_hours", + login_auth.TwoFactorDuration.Every30Days: "30_days", + login_auth.TwoFactorDuration.Forever: "forever", +} + + +class LoginFlow: + """ + Handles the full login process: server selection, username, password, + device approval, 2FA, SSO data key, and SSO token. + """ + + def __init__(self) -> None: + self._config = configuration.JsonConfigurationStorage() + self._logged_in_with_persistent = True + self._endpoint: Optional[endpoint.KeeperEndpoint] = None + + @property + def endpoint(self) -> Optional[endpoint.KeeperEndpoint]: + return self._endpoint + + @property + def logged_in_with_persistent(self) -> bool: + """True if login succeeded by resuming an existing persistent session (no step loop).""" + return self._logged_in_with_persistent + + def run(self) -> Optional[keeper_auth.KeeperAuth]: + """ + Run the login flow. + + Returns: + Authenticated Keeper context, or None if login fails. + """ + server = self._ensure_server() + keeper_endpoint = endpoint.KeeperEndpoint(self._config, server) + self._endpoint = keeper_endpoint + login_auth_context = login_auth.LoginAuth(keeper_endpoint) + + username = self._config.get().last_login or input("Enter username: ") + login_auth_context.resume_session = True + login_auth_context.login(username) + + while not login_auth_context.login_step.is_final(): + step = login_auth_context.login_step + if isinstance(step, login_auth.LoginStepDeviceApproval): + self._handle_device_approval(step) + elif isinstance(step, login_auth.LoginStepTwoFactor): + self._handle_two_factor(step) + elif isinstance(step, login_auth.LoginStepPassword): + self._handle_password(step) + elif isinstance(step, login_auth.LoginStepSsoToken): + self._handle_sso_token(step) + elif isinstance(step, login_auth.LoginStepSsoDataKey): + self._handle_sso_data_key(step) + elif isinstance(step, login_auth.LoginStepError): + print(f"Login error: ({step.code}) {step.message}") + return None + else: + raise NotImplementedError( + f"Unsupported login step type: {type(step).__name__}" + ) + self._logged_in_with_persistent = False + + if self._logged_in_with_persistent: + print("Successfully logged in with persistent login") + + if isinstance(login_auth_context.login_step, login_auth.LoginStepConnected): + return login_auth_context.login_step.take_keeper_auth() + + return None + + def _ensure_server(self) -> str: + if not self._config.get().last_server: + print("Available server options:") + for region, host in KEEPER_PUBLIC_HOSTS.items(): + print(f" {region}: {host}") + server = ( + input("Enter server (default: keepersecurity.com): ").strip() + or "keepersecurity.com" + ) + self._config.get().last_server = server + else: + server = self._config.get().last_server + return server + + def _handle_device_approval( + self, step: login_auth.LoginStepDeviceApproval + ) -> None: + """Device approval: same options as keepercli verify_device (email, keeper push, 2FA, resume).""" + menu = [ + ("email_send", "to send email"), + ("email_code=", "to validate verification code sent via email"), + ("keeper_push", "to send Keeper Push notification"), + ("2fa_send", "to send 2FA code"), + ("2fa_code=", "to validate a code provided by 2FA application"), + ("", "to resume"), + ] + lines = ["Approve by selecting a method below"] + lines.extend(f" {cmd} {desc}" for cmd, desc in menu) + print("\n".join(lines)) + + selection = input("Type your selection or to resume: ").strip() + if selection is None: + return + if selection in ("email_send", "es"): + step.send_push(channel=login_auth.DeviceApprovalChannel.Email) + print("An email with instructions has been sent. Press when approved.") + elif selection.startswith("email_code="): + code = selection[len("email_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.Email, code=code) + print("Successfully verified email code.") + elif selection in ("keeper_push", "kp"): + step.send_push(channel=login_auth.DeviceApprovalChannel.KeeperPush) + print( + "Successfully made a push notification to the approved device. " + "Press when approved." + ) + elif selection in ("2fa_send", "2fs"): + step.send_push(channel=login_auth.DeviceApprovalChannel.TwoFactor) + print("2FA code was sent.") + elif selection.startswith("2fa_code="): + code = selection[len("2fa_code=") :] + step.send_code(channel=login_auth.DeviceApprovalChannel.TwoFactor, code=code) + print("Successfully verified 2FA code.") + else: + step.resume() + + def _handle_password(self, step: login_auth.LoginStepPassword) -> None: + """Password step: prompt for password and retry on auth_failed (aligned with keepercli handle_verify_password).""" + print(f"\nEnter password for {step.username}") + while True: + password = getpass.getpass("Password: ") + if not password: + raise KeyboardInterrupt() + try: + step.verify_password(password) + break + except errors.KeeperApiError as kae: + print( + "Invalid email or password combination, please re-enter." + if kae.result_code == "auth_failed" + else kae.message + ) + + def _handle_two_factor(self, step: login_auth.LoginStepTwoFactor) -> None: + channels = [ + x + for x in step.get_channels() + if x.channel_type != login_auth.TwoFactorChannel.Other + ] + menu = [] + for i, channel in enumerate(channels): + desc = self._two_factor_channel_desc(channel.channel_type) + menu.append( + ( + str(i + 1), + f"{desc} {channel.channel_name} {channel.phone}", + ) + ) + menu.append(("q", "Quit authentication attempt and return to Commander prompt.")) + + lines = ["", "This account requires 2FA Authentication"] + lines.extend(f" {a}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + selection = input("Selection: ") + if selection is None: + return + if selection in ("q", "Q"): + raise KeyboardInterrupt() + try: + assert selection.isnumeric() + idx = 1 if not selection else int(selection) + assert 1 <= idx <= len(channels) + channel = channels[idx - 1] + desc = self._two_factor_channel_desc(channel.channel_type) + print(f"Selected {idx}. {desc}") + except AssertionError: + print( + "Invalid entry, additional factors of authentication shown " + "may be configured if not currently enabled." + ) + continue + + if channel.channel_type in ( + login_auth.TwoFactorChannel.TextMessage, + login_auth.TwoFactorChannel.KeeperDNA, + login_auth.TwoFactorChannel.DuoSecurity, + ): + action = next( + ( + x + for x in step.get_channel_push_actions(channel.channel_uid) + if x + in ( + login_auth.TwoFactorPushAction.TextMessage, + login_auth.TwoFactorPushAction.KeeperDna, + ) + ), + None, + ) + if action: + step.send_push(channel.channel_uid, action) + + if channel.channel_type == login_auth.TwoFactorChannel.SecurityKey: + try: + challenge = json.loads(channel.challenge) + signature = yubikey_authenticate(challenge, FidoCliInteraction()) + if signature: + print("Verified Security Key.") + step.send_code(channel.channel_uid, signature) + return + except Exception as e: + logger.error(e) + continue + + # 2FA code path + step.duration = min(step.duration, channel.max_expiration) + available_dura = sorted( + x for x in _TWO_FACTOR_DURATION_CODES if x <= channel.max_expiration + ) + available_codes = [ + _TWO_FACTOR_DURATION_CODES.get(x) or "login" for x in available_dura + ] + + while True: + mfa_desc = self._two_factor_duration_desc(step.duration) + prompt_exp = ( + f"\n2FA Code Duration: {mfa_desc}.\n" + f"To change duration: 2fa_duration={'|'.join(available_codes)}" + ) + print(prompt_exp) + + selection = input("\nEnter 2FA Code or Duration: ") + if not selection: + return + if selection in available_codes: + step.duration = self._two_factor_code_to_duration(selection) + elif selection.startswith("2fa_duration="): + code = selection[len("2fa_duration=") :] + if code in available_codes: + step.duration = self._two_factor_code_to_duration(code) + else: + print(f"Invalid 2FA duration: {code}") + else: + try: + step.send_code(channel.channel_uid, selection) + print("Successfully verified 2FA Code.") + return + except errors.KeeperApiError as kae: + print(f"Invalid 2FA code: ({kae.result_code}) {kae.message}") + + def _handle_sso_data_key( + self, step: login_auth.LoginStepSsoDataKey + ) -> None: + menu = [ + ("1", "Keeper Push. Send a push notification to your device."), + ("2", "Admin Approval. Request your admin to approve this device."), + ("r", "Resume SSO authentication after device is approved."), + ("q", "Quit SSO authentication attempt and return to Commander prompt."), + ] + lines = ["Approve this device by selecting a method below:"] + lines.extend(f" {cmd:>3}. {text}" for cmd, text in menu) + print("\n".join(lines)) + + while True: + answer = input("Selection: ") + if answer is None: + return + if answer == "q": + raise KeyboardInterrupt() + if answer == "r": + step.resume() + break + if answer in ("1", "2"): + step.request_data_key( + login_auth.DataKeyShareChannel.KeeperPush + if answer == "1" + else login_auth.DataKeyShareChannel.AdminApproval + ) + else: + print(f'Action "{answer}" is not supported.') + + def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None: + menu = [ + ("a", "SSO User with a Master Password."), + ] + if pyperclip: + menu.append(("c", "Copy SSO Login URL to clipboard.")) + else: + menu.append(("u", "Show SSO Login URL.")) + try: + wb = webbrowser.get() + menu.append(("o", "Navigate to SSO Login URL with the default web browser.")) + except Exception: + wb = None + if pyperclip: + menu.append(("p", "Paste SSO Token from clipboard.")) + menu.append(("t", "Enter SSO Token manually.")) + menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt.")) + + lines = [ + "", + "SSO Login URL:", + step.sso_login_url, + "Navigate to SSO Login URL with your browser and complete authentication.", + "Copy a returned SSO Token into clipboard." + + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."), + 'NOTE: To copy SSO Token please click "Copy authentication token" ' + 'button on "SSO Connect" page.', + "", + ] + lines.extend(f" {a:>3}. {t}" for a, t in menu) + print("\n".join(lines)) + + while True: + token = input("Selection: ") + if token == "q": + raise KeyboardInterrupt() + if token == "a": + step.login_with_password() + return + if token == "c": + token = None + if pyperclip: + try: + pyperclip.copy(step.sso_login_url) + print("SSO Login URL is copied to clipboard.") + except Exception: + print("Failed to copy SSO Login URL to clipboard.") + else: + print("Clipboard not available (install pyperclip).") + elif token == "u": + token = None + if not pyperclip: + print("\nSSO Login URL:", step.sso_login_url, "\n") + else: + print("Unsupported menu option (use 'c' to copy URL).") + elif token == "o": + token = None + if wb: + try: + wb.open_new_tab(step.sso_login_url) + except Exception: + print("Failed to open web browser.") + elif token == "p": + if pyperclip: + try: + token = pyperclip.paste() + except Exception: + token = "" + print("Failed to paste from clipboard") + else: + token = None + print("Clipboard not available (use 't' to enter token manually).") + elif token == "t": + token = getpass.getpass("Enter SSO Token: ").strip() + else: + if len(token) < 10: + print(f"Unsupported menu option: {token}") + continue + + if token: + try: + step.set_sso_token(token) + break + except errors.KeeperApiError as kae: + print(f"SSO Login error: ({kae.result_code}) {kae.message}") + + @staticmethod + def _two_factor_channel_desc( + channel_type: login_auth.TwoFactorChannel, + ) -> str: + return { + login_auth.TwoFactorChannel.Authenticator: "TOTP (Google and Microsoft Authenticator)", + login_auth.TwoFactorChannel.TextMessage: "Send SMS Code", + login_auth.TwoFactorChannel.DuoSecurity: "DUO", + login_auth.TwoFactorChannel.RSASecurID: "RSA SecurID", + login_auth.TwoFactorChannel.SecurityKey: "WebAuthN (FIDO2 Security Key)", + login_auth.TwoFactorChannel.KeeperDNA: "Keeper DNA (Watch)", + login_auth.TwoFactorChannel.Backup: "Backup Code", + }.get(channel_type, "Not Supported") + + @staticmethod + def _two_factor_duration_desc( + duration: login_auth.TwoFactorDuration, + ) -> str: + return { + login_auth.TwoFactorDuration.EveryLogin: "Require Every Login", + login_auth.TwoFactorDuration.Forever: "Save on this Device Forever", + login_auth.TwoFactorDuration.Every12Hours: "Ask Every 12 hours", + login_auth.TwoFactorDuration.EveryDay: "Ask Every 24 hours", + login_auth.TwoFactorDuration.Every30Days: "Ask Every 30 days", + }.get(duration, "Require Every Login") + + @staticmethod + def _two_factor_code_to_duration( + text: str, + ) -> login_auth.TwoFactorDuration: + for dura, code in _TWO_FACTOR_DURATION_CODES.items(): + if code == text: + return dura + return login_auth.TwoFactorDuration.EveryLogin + + +def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + """ + Enable persistent login and register data key for device. + Sets persistent_login to on and logout_timer to 30 days. + """ + keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1') + keeper_auth.register_data_key_for_device(keeper_auth_context) + mins_per_day = 60 * 24 + timeout_in_minutes = mins_per_day * 30 # 30 days + keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes)) + print("Persistent login turned on successfully and device registered") + + +def login(): + """ + Handle the login process including server selection, authentication, + and multi-factor authentication steps (device approval, password, 2FA + with channel selection and Security Key, SSO data key, SSO token). + + Returns: + tuple: (keeper_auth_context, keeper_endpoint) on success, or (None, None) if login fails. + """ + flow = LoginFlow() + keeper_auth_context = flow.run() + if keeper_auth_context and not flow.logged_in_with_persistent: + enable_persistent_login(keeper_auth_context) + keeper_endpoint = flow.endpoint if keeper_auth_context else None + return keeper_auth_context, keeper_endpoint + + +def open_vault(keeper_auth_context: keeper_auth.KeeperAuth) -> vault_online.VaultOnline: + conn = sqlite3.Connection("file::memory:", uri=True) + vault_storage = sqlite_storage.SqliteVaultStorage( + lambda: conn, + vault_owner=bytes(keeper_auth_context.auth_context.username, "utf-8"), + ) + vault = vault_online.VaultOnline(keeper_auth_context, vault_storage) + vault.sync_down() + return vault + + +def close_vault(vault: vault_online.VaultOnline, keeper_auth_context: keeper_auth.KeeperAuth) -> None: + vault.close() + keeper_auth_context.close() + +def nsf_record_add_batch(vault: vault_online.VaultOnline) -> None: + """Batch record add execution (nsf-record-add --batch / create_nsf_records).""" + RECORDS = [ + { + 'title': 'My NSF Login', + 'record_type': 'login', + 'folder': 'Projects', # NSF folder name or UID; omit for root + 'notes': 'Created via SDK batch example', + 'fields': { + 'login': 'user@example.com', + 'password': 'changeme', + 'url': 'https://example.com', + }, + }, + { + 'title': 'My NSF Login 2', + 'record_type': 'login', + 'folder': 'Projects', + 'fields': { + 'login': 'user2@example.com', + 'password': 'changeme2', + }, + }, + ] + + results = nsf_management.create_nsf_records(vault, RECORDS) + for result in results: + if result.success: + print(f"NSF record created: {result.record_uid} (status: {result.status})") + else: + print(f"NSF record failed: {result.record_uid} ({result.message or result.status})") + + +def nsf_record_add_batch_run(keeper_auth_context: keeper_auth.KeeperAuth) -> None: + vault = open_vault(keeper_auth_context) + try: + nsf_record_add_batch(vault) + except Exception as e: + print(f"Error: {e}") + finally: + close_vault(vault, keeper_auth_context) + + +def main() -> None: + keeper_auth_context, _ = login() + if keeper_auth_context: + nsf_record_add_batch_run(keeper_auth_context) + else: + print("Login failed.") + + +if __name__ == "__main__": + main() diff --git a/examples/sdk_examples/secrets_manager/app_clients.py b/examples/sdk_examples/secrets_manager/app_clients.py index e3df4095..0a7acdef 100644 --- a/examples/sdk_examples/secrets_manager/app_clients.py +++ b/examples/sdk_examples/secrets_manager/app_clients.py @@ -528,6 +528,7 @@ def add_client_to_ksm_app( unlock_ip: bool = False, first_access_expires_in_minutes: int = DEFAULT_FIRST_ACCESS_EXPIRES_MINUTES, access_expire_in_minutes: Optional[int] = None, + client_type: int = ksm_management.GENERAL, ) -> None: """ Add a client device to a KSM app using @@ -542,6 +543,7 @@ def add_client_to_ksm_app( unlock_ip: If True, do not lock the client to the current IP. first_access_expires_in_minutes: Minutes until the one-time token expires (default 60). access_expire_in_minutes: Optional minutes until app access expires (None = never). + client_type: Type of client to add (default GENERAL). """ vault, app_uid = _vault_and_app_uid(keeper_auth_context, app_uid_or_name) try: @@ -571,6 +573,7 @@ def add_client_to_ksm_app( access_expire_in_ms=access_expire_in_ms, master_key=master_key, server=server, + client_type=client_type, ) print(result["output_string"]) if result.get("token_info"): @@ -641,6 +644,7 @@ def main() -> None: unlock_ip = False # Set True to allow config from any IP first_access_expires_in_minutes = 60 # Token validity (max 1440 = 24h) access_expire_in_minutes = None # None = never expire app access + client_type = ksm_management.GENERAL add_client_to_ksm_app( keeper_auth_context, app_uid_or_name, @@ -649,6 +653,7 @@ def main() -> None: unlock_ip=unlock_ip, first_access_expires_in_minutes=first_access_expires_in_minutes, access_expire_in_minutes=access_expire_in_minutes, + client_type=client_type, ) elif action == "remove": client_names_or_ids = ["", ""] # Client ID(s) diff --git a/keepercli-package/setup.cfg b/keepercli-package/setup.cfg index 877a1315..26ff3856 100644 --- a/keepercli-package/setup.cfg +++ b/keepercli-package/setup.cfg @@ -15,12 +15,16 @@ classifiers = Operating System :: OS Independent Natural Language :: English Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 + Programming Language :: Python :: 3.14 Topic :: Security keywords = security, password [options] -python_requires = >=3.8 +python_requires = >=3.10 package_dir = = src include_package_data = True diff --git a/keepercli-package/src/keepercli/__init__.py b/keepercli-package/src/keepercli/__init__.py index fa3bfd75..ba3f4bde 100644 --- a/keepercli-package/src/keepercli/__init__.py +++ b/keepercli-package/src/keepercli/__init__.py @@ -9,5 +9,5 @@ # Contact: commander@keepersecurity.com # -__version__ = '1.2.2' +__version__ = '1.2.4' diff --git a/keepercli-package/src/keepercli/cli.py b/keepercli-package/src/keepercli/cli.py index 2b7e24f6..2c4cc910 100644 --- a/keepercli-package/src/keepercli/cli.py +++ b/keepercli-package/src/keepercli/cli.py @@ -1,12 +1,12 @@ import logging import sys -from typing import Optional, Any, Iterable, List +from typing import Optional, Any, Iterable, List, Callable from prompt_toolkit import PromptSession from prompt_toolkit.history import History from . import prompt_utils, api, autocomplete -from .commands import command_completer, base, command_history +from .commands import command_completer, base, command_history, command_visibility from .helpers import report_utils from .params import KeeperParams, KeeperConfig from keepersdk import constants @@ -30,6 +30,27 @@ def store_string(self, string: str) -> None: command_history.append(string) +_SCOPE_DISPLAY_NAMES = { + base.CommandScope.Account: 'Account Commands', + base.CommandScope.Vault: 'Vault Commands', + base.CommandScope.DeviceManagement: 'Device Management Commands', + base.CommandScope.Enterprise: 'Enterprise Commands', + base.CommandScope.MSP: 'MSP Commands', + base.CommandScope.Distributor: 'Distributor Commands', + base.CommandScope.Common: 'Miscellaneous Commands', +} + +_SCOPE_DISPLAY_ORDER = ( + base.CommandScope.Account, + base.CommandScope.Vault, + base.CommandScope.DeviceManagement, + base.CommandScope.Enterprise, + base.CommandScope.MSP, + base.CommandScope.Distributor, + base.CommandScope.Common, +) + + def do_command(command_line: str, context: KeeperParams, commands: base.CliCommands) -> Any: cmd, sep, args = command_line.partition(' ') orig_cmd = cmd @@ -44,7 +65,7 @@ def do_command(command_line: str, context: KeeperParams, commands: base.CliComma command, _ = commands.commands[cmd] return command.execute_args(context, args.strip(), command=orig_cmd) else: - display_command_help(commands) + display_command_help(commands, context) return None @@ -81,7 +102,8 @@ def get_prompt() -> str: if sys.stdin.isatty() and sys.stdout.isatty(): from prompt_toolkit.enums import EditingMode from prompt_toolkit.shortcuts import CompleteStyle - completer = command_completer.CommandCompleter(commands, autocomplete.standard_completer(context)) + completer = command_completer.CommandCompleter( + commands, autocomplete.standard_completer(context), context_getter=lambda: context) prompt_session = PromptSession( multiline=False, editing_mode=EditingMode.EMACS, complete_style=CompleteStyle.MULTI_COLUMN, complete_while_typing=False, completer=completer, auto_suggest=None, key_bindings=prompt_utils.kb, @@ -174,18 +196,31 @@ def get_prompt() -> str: context.clear_session() return 0 -def display_command_help(commands: base.CliCommands): +def display_command_help(commands: base.CliCommands, context: Optional[KeeperParams] = None): alias_lookup = {x[1]: x[0] for x in commands.aliases.items()} - all_scopes = {x[1]: x[1].name for x in commands.commands.values()} - scopes = sorted(all_scopes.keys()) + available_scopes = {value[1] for value in commands.commands.values()} headers = ['', 'Command', 'Alias', '', 'Description'] table = [] - for scope in scopes: - scope_commands = [key for key, value in commands.commands.items() if value[1] == scope] + for scope in _SCOPE_DISPLAY_ORDER: + if scope not in available_scopes: + continue + scope_commands = [ + key for key, value in commands.commands.items() + if value[1] == scope and command_visibility.is_command_visible(key, context) + ] + if not scope_commands: + continue + scope_name = _SCOPE_DISPLAY_NAMES.get(scope, scope.name) idx = 0 for cmd in sorted(scope_commands): c = commands.commands[cmd][0] - table.append([all_scopes[scope] if idx == 0 else '', cmd, alias_lookup.get(cmd) or '', '...', c.description()]) + table.append([ + scope_name if idx == 0 else '', + cmd, + alias_lookup.get(cmd) or '', + '...', + c.description(), + ]) idx += 1 prompt_utils.output_text('\nCommands:') diff --git a/keepercli-package/src/keepercli/commands/base.py b/keepercli-package/src/keepercli/commands/base.py index 624b060f..925ae48c 100644 --- a/keepercli-package/src/keepercli/commands/base.py +++ b/keepercli-package/src/keepercli/commands/base.py @@ -63,6 +63,7 @@ def description(self): class CommandScope(enum.IntFlag): Account = enum.auto() Vault = enum.auto() + DeviceManagement = enum.auto() Enterprise = enum.auto() MSP = enum.auto() Distributor = enum.auto() diff --git a/keepercli-package/src/keepercli/commands/command_completer.py b/keepercli-package/src/keepercli/commands/command_completer.py index db7fb32c..31105366 100644 --- a/keepercli-package/src/keepercli/commands/command_completer.py +++ b/keepercli-package/src/keepercli/commands/command_completer.py @@ -4,14 +4,17 @@ from . import base from .. import autocomplete +from .command_visibility import is_command_visible class CommandCompleter(completion.Completer): def __init__(self, command_collection: base.CommandCollection, - on_complete: Optional[Callable[[str, str], Iterable[str]]] = None) -> None: + on_complete: Optional[Callable[[str, str], Iterable[str]]] = None, + context_getter: Optional[Callable[[], object]] = None) -> None: self.commands = command_collection self.on_complete = on_complete + self.context_getter = context_getter def get_completions(self, document, complete_event): if not document.is_cursor_at_the_end: @@ -33,7 +36,11 @@ def get_completions(self, document, complete_event): command = self.commands.get_command_by_name(cmd) if command is None: if len(tokens) == 0 and document.char_before_cursor != ' ': - cmds = [x for x in self.commands.query_commands(cmd)] + context = self.context_getter() if self.context_getter else None + cmds = [ + x for x in self.commands.query_commands(cmd) + if is_command_visible(x, context) + ] cmds.sort() for c in cmds: yield completion.Completion(c, start_position=-len(document.text)) diff --git a/keepercli-package/src/keepercli/commands/command_visibility.py b/keepercli-package/src/keepercli/commands/command_visibility.py new file mode 100644 index 00000000..597159e9 --- /dev/null +++ b/keepercli-package/src/keepercli/commands/command_visibility.py @@ -0,0 +1,20 @@ +from typing import Optional, TYPE_CHECKING + +if TYPE_CHECKING: + from ..params import KeeperParams + +DEVICE_ADMIN_COMMANDS = frozenset({'device-admin-list', 'device-admin-action'}) + + +def is_enterprise_admin(context: Optional['KeeperParams']) -> bool: + return bool( + context + and context.auth + and context.auth.auth_context.is_enterprise_admin + ) + + +def is_command_visible(command: str, context: Optional['KeeperParams']) -> bool: + if command in DEVICE_ADMIN_COMMANDS: + return is_enterprise_admin(context) + return True diff --git a/keepercli-package/src/keepercli/commands/device_management.py b/keepercli-package/src/keepercli/commands/device_management.py index 319d4322..5be6acab 100644 --- a/keepercli-package/src/keepercli/commands/device_management.py +++ b/keepercli-package/src/keepercli/commands/device_management.py @@ -4,6 +4,7 @@ from datetime import datetime from typing import Callable, Dict, List, Optional +from keepersdk import errors from keepersdk.authentication import device_management from . import base @@ -34,9 +35,35 @@ def _format_timestamp(dt: Optional[datetime]) -> str: def _sdk_error(exc: Exception) -> base.CommandError: + if isinstance(exc, errors.KeeperApiError): + if device_management.is_device_api_unavailable(exc): + return base.CommandError(device_management.DEVICE_FEATURE_UNAVAILABLE_MESSAGE) return base.CommandError(str(exc)) +def _validate_admin_enterprise_user_ids( + context: KeeperParams, + enterprise_user_ids: List[int], +) -> List[int]: + """Return enterprise user IDs known to enterprise data; warn when IDs are not found.""" + base.require_enterprise_admin(context) + resolved: List[int] = [] + seen: set[int] = set() + for user_id in enterprise_user_ids: + if user_id in seen: + continue + seen.add(user_id) + if context.enterprise_data.users.get_entity(user_id) is None: + logger.warning( + "Warning: No enterprise_user_id found matching '%s'", user_id + ) + else: + resolved.append(user_id) + if not resolved and enterprise_user_ids: + logger.info('No matching enterprise_user_id found') + return resolved + + def _run_device_action_command( context: KeeperParams, device_identifiers: List[str], @@ -47,7 +74,7 @@ def _run_device_action_command( try: for name in action_fn(context.auth, device_identifiers): logger.info(success_message, name) - except ValueError as e: + except (ValueError, errors.KeeperApiError) as e: raise _sdk_error(e) from e @@ -79,7 +106,7 @@ def _display_admin_devices( """Fetch and print the admin device list table for the given enterprise user IDs.""" try: devices = device_management.list_admin_devices(context.auth, enterprise_user_ids) - except ValueError as e: + except (ValueError, errors.KeeperApiError) as e: raise _sdk_error(e) from e if not devices: @@ -186,6 +213,31 @@ def _display_admin_devices( 'handler': device_management.remove_admin_user_devices, 'action_verb': 'removed', }, + 'lock': { + 'description': ( + 'Lock the device for all users on the devices and the associated auto linked devices. ' + 'Logout all users from the device' + ), + 'handler': device_management.lock_admin_user_devices, + 'action_verb': 'locked', + }, + 'unlock': { + 'description': ( + 'Unlock the devices and the associated auto linked devices for the calling user' + ), + 'handler': device_management.unlock_admin_user_devices, + 'action_verb': 'unlocked', + }, + 'account-lock': { + 'description': 'Lock the device for the user only. If user is logged in, logout', + 'handler': device_management.account_lock_admin_user_devices, + 'action_verb': 'account locked', + }, + 'account-unlock': { + 'description': 'Unlock the device for the user', + 'handler': device_management.account_unlock_admin_user_devices, + 'action_verb': 'account unlocked', + }, } DEVICE_ADMIN_ACTION_CHOICES = list(DEVICE_ADMIN_ACTION_DEFINITIONS.keys()) @@ -231,7 +283,7 @@ def execute(self, context: KeeperParams, **kwargs): base.require_login(context) try: devices = device_management.list_user_devices(context.auth) - except ValueError as e: + except (ValueError, errors.KeeperApiError) as e: raise _sdk_error(e) from e if not devices: @@ -277,7 +329,7 @@ class DeviceRenameCommand(base.ArgparseCommand): def __init__(self): parser = argparse.ArgumentParser( prog='device-rename', - description='Rename a device for the current user', + description='Rename user devices', ) DeviceRenameCommand.add_arguments_to_parser(parser) super().__init__(parser) @@ -302,7 +354,7 @@ def execute(self, context: KeeperParams, **kwargs): logger.info("Device name updated from '%s' to '%s'", old_name, updated_name) logger.info('') _display_user_devices(context, title_prefix='Updated ') - except ValueError as e: + except (ValueError, errors.KeeperApiError) as e: raise _sdk_error(e) from e @@ -391,7 +443,7 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser): 'enterprise_user_ids', nargs='+', type=int, - help='List of Enterprise User IDs (required). You can get enterprise user IDs by running "ei --users" command', + help='List of Enterprise User IDs (required). You can get enterprise user IDs by running "enterprise-info user"', ) parser.error = base.ArgparseCommand.raise_parse_exception parser.exit = base.ArgparseCommand.suppress_exit @@ -399,11 +451,15 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser): def execute(self, context: KeeperParams, **kwargs): """Display admin device list in table or JSON format for the given enterprise user IDs.""" base.require_enterprise_admin(context) - enterprise_user_ids = kwargs.get('enterprise_user_ids') or [] + enterprise_user_ids = _validate_admin_enterprise_user_ids( + context, kwargs.get('enterprise_user_ids') or [] + ) + if not enterprise_user_ids: + return try: devices = device_management.list_admin_devices(context.auth, enterprise_user_ids) - except ValueError as e: + except (ValueError, errors.KeeperApiError) as e: raise _sdk_error(e) from e if not devices: @@ -437,7 +493,7 @@ class DeviceAdminActionCommand(base.ArgparseCommand): def __init__(self): parser = argparse.ArgumentParser( prog='device-admin-action', - description='Perform various action on one or more devices that the Admin has control of.', + description='Perform actions on devices across enterprise users', ) DeviceAdminActionCommand.add_arguments_to_parser(parser) super().__init__(parser) @@ -488,7 +544,12 @@ def execute(self, context: KeeperParams, **kwargs): """Run the requested admin device action and refresh the device list.""" base.require_enterprise_admin(context) action = kwargs.get('action') - enterprise_user_id = kwargs.get('enterprise_user_id') + validated_user_ids = _validate_admin_enterprise_user_ids( + context, [kwargs.get('enterprise_user_id')] + ) + if not validated_user_ids: + return + enterprise_user_id = validated_user_ids[0] devices = kwargs.get('devices') or [] config = DEVICE_ADMIN_ACTION_DEFINITIONS.get(action or '') if not config: @@ -506,7 +567,7 @@ def execute(self, context: KeeperParams, **kwargs): "Device action successfully completed: '%s' %s for user %s", name, action_verb, enterprise_user_id, ) - except ValueError as e: + except (ValueError, errors.KeeperApiError) as e: raise _sdk_error(e) from e logger.info('Updated device list for user %s:', enterprise_user_id) diff --git a/keepercli-package/src/keepercli/commands/enterprise_team.py b/keepercli-package/src/keepercli/commands/enterprise_team.py index f450d802..76fec1ed 100644 --- a/keepercli-package/src/keepercli/commands/enterprise_team.py +++ b/keepercli-package/src/keepercli/commands/enterprise_team.py @@ -3,7 +3,7 @@ from typing import Dict, List, Optional, Any, Tuple, Set from keepersdk import utils, crypto -from keepersdk.enterprise import enterprise_types, batch_management, enterprise_management +from keepersdk.enterprise import enterprise_types, batch_management, enterprise_management, enterprise_team_management from . import base, enterprise_utils from .. import api, prompt_utils from ..helpers import report_utils @@ -13,6 +13,182 @@ logger = api.get_logger() +def _add_team_membership_arguments(parser: argparse.ArgumentParser) -> None: + parser.add_argument('-au', '--add-user', action='append', help='add user to team') + parser.add_argument('-ru', '--remove-user', action='append', help='remove user from team. @all') + parser.add_argument('-ar', '--add-role', action='append', help='add role to team') + parser.add_argument('-rr', '--remove-role', action='append', help='remove role from team. @all') + parser.add_argument( + '-hsf', '--hide-shared-folders', dest='hide_shared_folders', action='store', + choices=['on', 'off'], help='User does not see shared folders. --add-user only', + ) + + +def _validate_add_edit_membership(kwargs: Dict[str, Any], *, has_queued_teams: bool = False) -> None: + remove_users = kwargs.get('remove_user') + if isinstance(remove_users, list) and any(x == '@all' for x in remove_users): + raise base.CommandError( + '@all is not supported on enterprise-team add/edit. ' + 'Use enterprise-team membership with -ru @all.') + remove_roles = kwargs.get('remove_role') + if isinstance(remove_roles, list) and any(x == '@all' for x in remove_roles): + raise base.CommandError( + '@all is not supported on enterprise-team add/edit. ' + 'Use enterprise-team membership with -rr @all.') + if has_queued_teams and (kwargs.get('add_user') or kwargs.get('remove_user')): + raise base.CommandError( + 'User membership changes are not supported when adding queued teams. ' + 'Use enterprise-team membership.') + + +def _has_membership_changes(kwargs: Dict[str, Any]) -> bool: + return any(( + kwargs.get('add_user'), + kwargs.get('remove_user'), + kwargs.get('add_role'), + kwargs.get('remove_role'), + )) + + +class _TeamMembershipTarget: + __slots__ = ('team_uid', 'name', 'queued') + + def __init__(self, team_uid: str, name: str, *, queued: bool = False) -> None: + self.team_uid = team_uid + self.name = name + self.queued = queued + + @classmethod + def from_team(cls, team: enterprise_types.Team) -> '_TeamMembershipTarget': + return cls(team.team_uid, team.name or '') + + @classmethod + def from_queued_team(cls, team: enterprise_types.QueuedTeam) -> '_TeamMembershipTarget': + return cls(team.team_uid, team.name or '', queued=True) + + @classmethod + def from_team_edit(cls, team: enterprise_management.TeamEdit) -> '_TeamMembershipTarget': + return cls(team.team_uid, team.name or '') + + +def _queue_team_membership_changes( + batch: batch_management.BatchManagement, + context: KeeperParams, + logger: enterprise_management.IEnterpriseManagementLogger, + kwargs: Dict[str, Any], + active_teams: List[_TeamMembershipTarget], + queued_teams: Optional[List[_TeamMembershipTarget]] = None, + *, + users_only: bool = False, +) -> None: + users_to_add: Optional[List[enterprise_types.User]] = None + roles_to_add: Optional[List[enterprise_types.Role]] = None + users_to_remove: Optional[List[enterprise_types.User]] = None + roles_to_remove: Optional[List[enterprise_types.Role]] = None + has_remove_all_users = False + has_remove_all_roles = False + + add_users = kwargs.get('add_user') + if isinstance(add_users, list): + users_to_add = enterprise_utils.UserUtils.resolve_existing_users( + context.enterprise_data, add_users) + add_roles = kwargs.get('add_role') + if not users_only and isinstance(add_roles, list): + resolved_roles = enterprise_utils.RoleUtils.resolve_existing_roles( + context.enterprise_data, add_roles) + roles_to_add = [] + for role in resolved_roles: + if enterprise_utils.RoleUtils.is_admin_role(context.enterprise_data, role.role_id): + logger.warning( + 'Teams cannot be assigned to roles with administrative permissions.') + else: + roles_to_add.append(role) + if not roles_to_add: + roles_to_add = None + remove_users = kwargs.get('remove_user') + if isinstance(remove_users, list): + has_remove_all_users = not users_only and any(x == '@all' for x in remove_users) + if not has_remove_all_users: + users_to_remove = enterprise_utils.UserUtils.resolve_existing_users( + context.enterprise_data, remove_users) + remove_roles = kwargs.get('remove_role') + if not users_only and isinstance(remove_roles, list): + has_remove_all_roles = any(x == '@all' for x in remove_roles) + if not has_remove_all_roles: + roles_to_remove = enterprise_utils.RoleUtils.resolve_existing_roles( + context.enterprise_data, remove_roles) + + user_type = enterprise_management.team_user_type_from_hsf_flag(kwargs.get('hide_shared_folders')) + + for team in active_teams: + existing_users = { + x.enterprise_user_id + for x in context.enterprise_data.team_users.get_links_by_subject(team.team_uid) + } + existing_roles = { + x.role_id + for x in context.enterprise_data.role_teams.get_links_by_object(team.team_uid) + } + if users_to_add: + for user in users_to_add: + if user.enterprise_user_id in existing_users: + if user_type is None: + logger.warning( + 'User \"%s\" is already a member of team \"%s\"', + user.username, team.name) + continue + batch.modify_team_users(to_add=[enterprise_management.TeamUserEdit( + team_uid=team.team_uid, + enterprise_user_id=user.enterprise_user_id, + user_type=user_type)]) + if roles_to_add: + team_roles_to_add = [x for x in roles_to_add if x.role_id not in existing_roles] + if team_roles_to_add: + batch.modify_role_teams(to_add=[enterprise_management.RoleTeamEdit( + role_id=x.role_id, team_uid=team.team_uid) for x in team_roles_to_add]) + if has_remove_all_users: + batch.modify_team_users(to_remove=[enterprise_management.TeamUserEdit( + team_uid=team.team_uid, enterprise_user_id=x) for x in existing_users]) + elif users_to_remove: + batch.modify_team_users(to_remove=[enterprise_management.TeamUserEdit( + team_uid=team.team_uid, enterprise_user_id=x.enterprise_user_id) + for x in users_to_remove]) + if has_remove_all_roles: + batch.modify_role_teams(to_remove=[enterprise_management.RoleTeamEdit( + role_id=x, team_uid=team.team_uid) for x in existing_roles]) + elif roles_to_remove: + batch.modify_role_teams(to_remove=[enterprise_management.RoleTeamEdit( + role_id=x.role_id, team_uid=team.team_uid) for x in roles_to_remove]) + + if users_only: + return + + for team in queued_teams or []: + existing_users = { + x.enterprise_user_id + for x in context.enterprise_data.queued_team_users.get_links_by_subject(team.team_uid) + } + if users_to_add: + for user in users_to_add: + if user.enterprise_user_id in existing_users: + if user_type is None: + logger.warning( + 'User \"%s\" is already queued for team \"%s\"', + user.username, team.name) + continue + batch.modify_team_users(to_add=[enterprise_management.TeamUserEdit( + team_uid=team.team_uid, + enterprise_user_id=user.enterprise_user_id, + user_type=user_type)]) + if has_remove_all_users: + batch.modify_team_users(to_remove=[enterprise_management.TeamUserEdit( + team_uid=team.team_uid, enterprise_user_id=x) for x in existing_users]) + elif users_to_remove: + batch.modify_team_users(to_remove=[enterprise_management.TeamUserEdit( + team_uid=team.team_uid, enterprise_user_id=x.enterprise_user_id) + for x in users_to_remove]) + + class EnterpriseTeamCommand(base.GroupCommand): def __init__(self): super().__init__('Manage an enterprise team(s)') @@ -24,61 +200,43 @@ def __init__(self): class EnterpriseTeamViewCommand(base.ArgparseCommand): + command_prog = 'enterprise-team view' + def __init__(self): - parser = argparse.ArgumentParser(prog='enterprise-team view', parents=[base.json_output_parser], description='View enterprise team.') + parser = argparse.ArgumentParser( + prog=self.command_prog, + parents=[base.json_output_parser], + description='View enterprise team.', + ) parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='print verbose information') parser.add_argument('team', help='Team Name or UID') super().__init__(parser) def execute(self, context: KeeperParams, **kwargs) -> Any: + return self._execute_team_view(context, **kwargs) + + def _execute_team_view(self, context: KeeperParams, **kwargs) -> Any: base.require_enterprise_admin(context) if context.vault is None: raise base.CommandError('Vault is not initialized. Login to initialize the vault.') verbose = kwargs.get('verbose') is True + team_name = kwargs.get('team') - enterprise_data = context.enterprise_data - team_name = kwargs.get('team') - team = enterprise_utils.TeamUtils.resolve_single_team(enterprise_data, team_name) - if team is None: - raise base.CommandError(f'Team name \"{team_name}\" does not exist') - node_name = enterprise_utils.NodeUtils.get_node_path(enterprise_data, team.node_id, omit_root=False) - team_obj = { - 'team_uid': team.team_uid, - 'team_name': team.name, - 'node_id': team.node_id, - 'node_name': node_name, - 'restrict_edit': team.restrict_edit, - 'restrict_share': team.restrict_share, - 'restrict_view': team.restrict_view, - } - role_ids = {x.role_id for x in enterprise_data.role_teams.get_links_by_object(team.team_uid)} - if role_ids: - roles = [r for r in (enterprise_data.roles.get_entity(x) for x in role_ids) if r] - if len(roles) > 0: - team_obj['team_roles'] = [{ - 'role_id': x.role_id, - 'role_name': x.name, - } for x in roles] - - user_ids = {x.enterprise_user_id for x in enterprise_data.team_users.get_links_by_subject(team.team_uid)} - if len(user_ids) > 0: - users = [u for u in (enterprise_data.users.get_entity(x) for x in user_ids) if u is not None] - if len(users) > 0: - team_obj['team_users'] = [{ - 'enterprise_user_id': x.enterprise_user_id, - 'username': x.username, - } for x in users] - - user_ids = {x.enterprise_user_id for x in enterprise_data.queued_team_users.get_links_by_subject(team.team_uid)} - if len(user_ids) > 0: - users = [u for u in (enterprise_data.users.get_entity(x) for x in user_ids) if u] - if len(users) > 0: - team_obj['queued_team_users'] = [{ - 'enterprise_user_id': x.enterprise_user_id, - 'username': x.username, - } for x in users] + try: + team_info = enterprise_team_management.get_team( + team_name, + enterprise_data=context.enterprise_data, + vault_data_obj=context.vault.vault_data if context.vault else None, + auth=context.auth, + vault=context.vault, + is_enterprise_admin=True, + fetch_live_members=verbose, + ) + except enterprise_team_management.EnterpriseTeamManagementError as exc: + raise base.CommandError(str(exc)) from exc + team_obj = team_info.to_dict() if kwargs.get('format') == 'json': json_text = json.dumps(team_obj, indent=4) @@ -97,10 +255,13 @@ def execute(self, context: KeeperParams, **kwargs) -> Any: if field_value is not None: row = [field_title, field_value] if verbose: - if field == 'node': + if field == 'node_name': row.append(team_obj.get('node_id')) table.append(row) + if team_obj.get('access_level'): + table.append(['Access Level', team_obj['access_level']]) + trs = team_obj.get('team_roles') if isinstance(trs, list) and len(trs) > 0: row = ['Role(s)'] @@ -125,6 +286,14 @@ def execute(self, context: KeeperParams, **kwargs) -> Any: row.append([x['enterprise_user_id'] for x in qtus]) table.append(row) + members = team_obj.get('members') + if isinstance(members, list) and len(members) > 0: + row = ['Member Email(s)'] + row.append([x['email'] for x in members]) + if verbose: + row.append([x['enterprise_user_id'] for x in members]) + table.append(row) + headers = ['', ''] if verbose: headers.append('') @@ -143,6 +312,7 @@ def __init__(self): action='store', help='disable record re-shares') parser.add_argument('--restrict-view', dest='restrict_view', choices=['on', 'off'], action='store', help='disable view/copy passwords') + _add_team_membership_arguments(parser) parser.add_argument('team', type=str, nargs='+', help='Team Name or Queued Team UID. Can be repeated.') super().__init__(parser) self.logger = api.get_logger() @@ -203,11 +373,13 @@ def execute(self, context: KeeperParams, **kwargs) -> None: restrict_view = r_view == 'on' batch = batch_management.BatchManagement(loader=context.enterprise_loader, logger=self) + new_team_edits: List[enterprise_management.TeamEdit] = [] if team_names: teams_to_add = [enterprise_management.TeamEdit( team_uid=utils.generate_uid(), name=x, node_id=parent_id, restrict_edit=restrict_edit, restrict_share=restrict_share, restrict_view=restrict_view) for x in team_names.values()] + new_team_edits = teams_to_add batch.modify_teams(to_add=teams_to_add) if queued_teams: @@ -217,6 +389,21 @@ def execute(self, context: KeeperParams, **kwargs) -> None: for x in queued_teams] batch.modify_teams(to_add=teams_to_add) + if _has_membership_changes(kwargs): + _validate_add_edit_membership(kwargs, has_queued_teams=bool(queued_teams)) + membership_targets = [ + _TeamMembershipTarget.from_team_edit(x) for x in new_team_edits + ] + if queued_teams: + membership_targets.extend( + _TeamMembershipTarget.from_team_edit(enterprise_management.TeamEdit( + team_uid=x.team_uid, name=x.name)) + for x in queued_teams + ) + _queue_team_membership_changes( + batch, context, self, kwargs, membership_targets, + ) + batch.apply() class EnterpriseTeamEditCommand(base.ArgparseCommand, enterprise_management.IEnterpriseManagementLogger): @@ -232,6 +419,7 @@ def __init__(self): action='store', help='disable record re-shares') parser.add_argument('--restrict-view', dest='restrict_view', choices=['on', 'off'], action='store', help='disable view/copy passwords') + _add_team_membership_arguments(parser) parser.add_argument('team', type=str, nargs='+', help='Team Name or UID. Can be repeated.') super().__init__(parser) self.logger = api.get_logger() @@ -281,6 +469,12 @@ def execute(self, context: KeeperParams, **kwargs) -> None: batch = batch_management.BatchManagement(loader=context.enterprise_loader, logger=self) batch.modify_teams(to_update=teams_to_edit) + if _has_membership_changes(kwargs): + _validate_add_edit_membership(kwargs) + _queue_team_membership_changes( + batch, context, self, kwargs, + [_TeamMembershipTarget.from_team(x) for x in team_list], + ) batch.apply() @@ -309,10 +503,7 @@ def execute(self, context: KeeperParams, **kwargs) -> None: class EnterpriseTeamMembershipCommand(base.ArgparseCommand, enterprise_management.IEnterpriseManagementLogger): def __init__(self): parser = argparse.ArgumentParser(prog='enterprise-team membership', description='Manage enterprise team membership.') - parser.add_argument('-au', '--add-user', action='append', help='add user to team') - parser.add_argument('-ru', '--remove-user', action='append', help='remove user from team. @all') - parser.add_argument('-ar', '--add-role', action='append', help='add user to team') - parser.add_argument('-rr', '--remove-role', action='append', help='remove user from team, @all') + _add_team_membership_arguments(parser) parser.add_argument('team', type=str, nargs='+', help='Team Name or UID. Can be repeated.') super().__init__(parser) self.logger = api.get_logger() @@ -323,81 +514,29 @@ def warning(self, message: str) -> None: def execute(self, context: KeeperParams, **kwargs) -> None: base.require_enterprise_admin(context) - team_list, missing_names = enterprise_utils.TeamUtils.resolve_existing_teams(context.enterprise_data, kwargs.get('team')) + if not _has_membership_changes(kwargs): + raise base.CommandError( + 'No membership changes specified. Use -au/--add-user, -ru/--remove-user, ' + '-ar/--add-role, or -rr/--remove-role.') + + team_list, missing_names = enterprise_utils.TeamUtils.resolve_existing_teams( + context.enterprise_data, kwargs.get('team')) queued_team_list: List[enterprise_types.QueuedTeam] if missing_names: - queued_team_list, missing_names = enterprise_utils.TeamUtils.resolve_queued_teams(context.enterprise_data, missing_names) + queued_team_list, missing_names = enterprise_utils.TeamUtils.resolve_queued_teams( + context.enterprise_data, missing_names) else: queued_team_list = [] if isinstance(missing_names, list) and len(missing_names) > 0: mn = ', '.join((str(x) for x in missing_names)) raise base.CommandError(f'Team name(s) \"{mn}\" could not be resolved') - users_to_add: Optional[List[enterprise_types.User]] = None - roles_to_add: Optional[List[enterprise_types.Role]] = None - users_to_remove: Optional[List[enterprise_types.User]] = None - roles_to_remove: Optional[List[enterprise_types.Role]] = None - has_remove_all_users: bool = False - has_remove_all_roles: bool = False - - add_users = kwargs.get('add_user') - if isinstance(add_users, list): - users_to_add = enterprise_utils.UserUtils.resolve_existing_users(context.enterprise_data, add_users) - add_roles = kwargs.get('add_role') - if isinstance(add_roles, list): - roles_to_add = enterprise_utils.RoleUtils.resolve_existing_roles(context.enterprise_data, add_roles) - remove_users = kwargs.get('remove_user') - if isinstance(remove_users, list): - has_remove_all_users = any((True for x in remove_users if x == '@all')) - if not has_remove_all_users: - users_to_remove = enterprise_utils.UserUtils.resolve_existing_users(context.enterprise_data, remove_users) - remove_roles = kwargs.get('remove_role') - if isinstance(remove_roles, list): - has_remove_all_roles = any((True for x in remove_roles if x == '@all')) - if not has_remove_all_roles: - roles_to_remove = enterprise_utils.RoleUtils.resolve_existing_roles(context.enterprise_data, remove_roles) - batch = batch_management.BatchManagement(loader=context.enterprise_loader, logger=self) - for team in team_list: - existing_users = {x.enterprise_user_id for x in context.enterprise_data.team_users.get_links_by_subject(team.team_uid)} - existing_roles = {x.role_id for x in context.enterprise_data.role_teams.get_links_by_object(team.team_uid)} - if users_to_add: - users_to_add = [x for x in users_to_add if x.enterprise_user_id not in existing_users] - if users_to_add: - batch.modify_team_users(to_add=[enterprise_management.TeamUserEdit( - team_uid=team.team_uid, enterprise_user_id=x.enterprise_user_id) for x in users_to_add]) - if roles_to_add: - roles_to_add = [x for x in roles_to_add if x.role_id not in existing_roles] - if roles_to_add: - batch.modify_role_teams(to_add=[enterprise_management.RoleTeamEdit( - role_id=x.role_id, team_uid=team.team_uid) for x in roles_to_add]) - if has_remove_all_users: - batch.modify_team_users(to_remove=[enterprise_management.TeamUserEdit( - team_uid=team.team_uid, enterprise_user_id=x) for x in existing_users]) - elif users_to_remove: - batch.modify_team_users(to_remove=[enterprise_management.TeamUserEdit( - team_uid=team.team_uid, enterprise_user_id=x.enterprise_user_id) for x in users_to_remove]) - if has_remove_all_roles: - batch.modify_role_teams(to_remove=[enterprise_management.RoleTeamEdit( - role_id=x, team_uid=team.team_uid) for x in existing_roles]) - elif roles_to_remove: - batch.modify_role_teams(to_remove=[enterprise_management.RoleTeamEdit( - role_id=x.role_id, team_uid=team.team_uid) for x in roles_to_remove]) - - for queued_team in queued_team_list: - existing_users = {x.enterprise_user_id for x in context.enterprise_data.queued_team_users.get_links_by_subject(queued_team.team_uid)} - if users_to_add: - users_to_add = [x for x in users_to_add if x.enterprise_user_id not in existing_users] - if users_to_add: - batch.modify_team_users(to_add=[enterprise_management.TeamUserEdit( - team_uid=queued_team.team_uid, enterprise_user_id=x.enterprise_user_id) for x in users_to_add]) - if has_remove_all_users: - batch.modify_team_users(to_remove=[enterprise_management.TeamUserEdit( - team_uid=queued_team.team_uid, enterprise_user_id=x) for x in existing_users]) - elif users_to_remove: - batch.modify_team_users(to_remove=[enterprise_management.TeamUserEdit( - team_uid=queued_team.team_uid, enterprise_user_id=x.enterprise_user_id) for x in users_to_remove]) - + _queue_team_membership_changes( + batch, context, self, kwargs, + [_TeamMembershipTarget.from_team(x) for x in team_list], + [_TeamMembershipTarget.from_queued_team(x) for x in queued_team_list], + ) batch.apply() diff --git a/keepercli-package/src/keepercli/commands/enterprise_user.py b/keepercli-package/src/keepercli/commands/enterprise_user.py index 1c77a374..16fbd01b 100644 --- a/keepercli-package/src/keepercli/commands/enterprise_user.py +++ b/keepercli-package/src/keepercli/commands/enterprise_user.py @@ -624,10 +624,8 @@ def execute(self, context: KeeperParams, **kwargs) -> None: hide_shared_folders: Optional[bool] = None hsf = kwargs.get('hide_shared_folders') if isinstance(hsf, str) and len(hsf) > 0: - hide_shared_folders = True if hsf == 'on' else False - user_type: Optional[int] = None - if isinstance(hide_shared_folders, bool): - user_type = 0 if hide_shared_folders else 2 + hide_shared_folders = hsf == 'on' + user_type = enterprise_management.team_user_type_from_hide_shared_folders(hide_shared_folders) for user in users: for team_uid in teams_to_add: team_membership_to_add.append(enterprise_management.TeamUserEdit( diff --git a/keepercli-package/src/keepercli/commands/enterprise_utils.py b/keepercli-package/src/keepercli/commands/enterprise_utils.py index 5050fe6a..6226038d 100644 --- a/keepercli-package/src/keepercli/commands/enterprise_utils.py +++ b/keepercli-package/src/keepercli/commands/enterprise_utils.py @@ -189,6 +189,10 @@ def resolve_single_role(e_data: enterprise_types.IEnterpriseData, role_name: Any raise base.CommandError(f'Role name \"{role_name}\" does not exist') return role + @staticmethod + def is_admin_role(e_data: enterprise_types.IEnterpriseData, role_id: int) -> bool: + return any(e_data.managed_nodes.get_links_by_subject(role_id)) + @staticmethod def enforcement_value_from_file(filepath: str) -> str: diff --git a/keepercli-package/src/keepercli/commands/nsf_commands.py b/keepercli-package/src/keepercli/commands/nsf_commands.py index fbcc0fc4..4444c0cd 100644 --- a/keepercli-package/src/keepercli/commands/nsf_commands.py +++ b/keepercli-package/src/keepercli/commands/nsf_commands.py @@ -1,12 +1,13 @@ import argparse import json -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Mapping, Optional, Union from keepersdk.vault import nsf_folder_records, nsf_management, nsf_sharing, vault_record, nsf_common from keepersdk.vault.share_management_utils import parse_nsf_share_expiration from keepersdk.vault.nsf_management import ( NsfError, NsfListRow, + NsfRecordAddSpec, NsfRemovePreviewItem, NsfRemoveResult, ) @@ -156,6 +157,77 @@ def build_nsf_record_data( return _typed_record_to_data(record, title, notes) +def _build_batch_record_data( + mixin: _NsfRecordDataMixin, + context: KeeperParams, + record_type: str, + title: str, + notes: Optional[str], + raw_fields: Union[Mapping[str, Any], List[str], None], +) -> Optional[Dict[str, Any]]: + if raw_fields is None: + return None + if isinstance(raw_fields, Mapping): + return None + record_fields: List[ParsedFieldValue] = [] + for field in raw_fields: + if not isinstance(field, str): + raise base.CommandError('Batch record fields must be strings or a field object') + parsed = RecordEditMixin.parse_field(field) + if parsed.type == 'file': + raise base.CommandError( + 'File attachments are not supported in nsf-record-add batch mode') + record_fields.append(parsed) + return mixin.build_nsf_record_data(context, record_type, title, notes, record_fields) + + +def _load_nsf_record_add_batch_specs( + mixin: _NsfRecordDataMixin, + context: KeeperParams, + batch_path: str, +) -> List[NsfRecordAddSpec]: + try: + with open(batch_path, 'r', encoding='utf-8') as handle: + payload = json.load(handle) + except OSError as exc: + raise base.CommandError(f'Unable to read batch file: {exc}') from exc + except json.JSONDecodeError as exc: + raise base.CommandError(f'Invalid JSON in batch file: {exc}') from exc + + if not isinstance(payload, list): + raise base.CommandError('Batch file must contain a JSON array of record definitions') + if not payload: + raise base.CommandError('Batch file must contain at least one record definition') + + specs: List[NsfRecordAddSpec] = [] + for index, entry in enumerate(payload, start=1): + if not isinstance(entry, dict): + raise base.CommandError(f'Batch record #{index} must be a JSON object') + title = entry.get('title') + record_type = entry.get('record_type') or entry.get('type') + if not title: + raise base.CommandError(f'Batch record #{index} is missing title') + if not record_type: + raise base.CommandError(f'Batch record #{index} is missing record_type') + + notes = entry.get('notes') + raw_fields = entry.get('fields') + record_data = entry.get('record_data') + field_map = raw_fields if isinstance(raw_fields, Mapping) else None + if record_data is None and isinstance(raw_fields, list): + record_data = _build_batch_record_data( + mixin, context, record_type, title, notes, raw_fields) + specs.append(NsfRecordAddSpec( + title=title, + record_type=record_type, + folder_uid=entry.get('folder_uid') or entry.get('folder'), + fields=field_map, + notes=notes, + record_data=record_data, + )) + return specs + + class NsfListCommand(base.ArgparseCommand): def __init__(self): @@ -485,6 +557,10 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser) -> None: parser.add_argument('-n', '--notes', dest='notes', type=str, help='record notes') parser.add_argument('--folder', dest='folder_uid', metavar='FOLDER', type=str, help='folder name or UID to store record') + parser.add_argument( + '--batch-file', dest='batch_file', metavar='FILE', type=str, + help='JSON file containing up to 1000 NSF records per API batch', + ) parser.add_argument('fields', nargs='*', type=str, help='load record type data from strings with dot notation') @@ -494,6 +570,10 @@ def execute(self, context: KeeperParams, **kwargs): prompt_utils.output_text(record_fields_description) return + batch_file = kwargs.get('batch_file') + if batch_file: + return self._execute_batch(context, vault, batch_file, kwargs.get('force') is True) + title = kwargs.get('title') if not title: raise base.CommandError('Title parameter is required.') @@ -547,6 +627,46 @@ def _run(): logger.info('NSF record created: %s', result.record_uid) return result.record_uid + def _execute_batch( + self, + context: KeeperParams, + vault, + batch_file: str, + force: bool) -> List[str]: + specs = _load_nsf_record_add_batch_specs(self, context, batch_file) + if self.warnings: + for w in self.warnings: + logger.warning(w) + if not force: + return [] + + def _run(): + return nsf_management.create_nsf_records(vault, specs) + + results = _wrap_nsf('nsf-record-add', _run) + created: List[str] = [] + failed = 0 + for result in results: + if result.success: + created.append(result.record_uid) + logger.info('NSF record created: %s (%s)', result.record_uid, result.status) + else: + failed += 1 + logger.warning( + 'NSF record add failed: %s (%s)', + result.record_uid, + result.message or result.status, + ) + logger.info( + 'NSF batch add complete: %d created, %d failed, %d total', + len(created), + failed, + len(results), + ) + if failed and not created: + raise base.CommandError('All NSF records in the batch failed to create') + return created + class NsfRecordUpdateCommand(base.ArgparseCommand, _NsfRecordDataMixin): @@ -851,6 +971,8 @@ def execute(self, context: KeeperParams, **kwargs): raise base.CommandError('Folder name cannot be empty') if new_name is None and color is None: raise base.CommandError('New folder name and/or color parameters are required.') + + display = _folder_name_from_vault(vault, folder_arg) def _run(): return nsf_management.update_nsf_folder( @@ -858,7 +980,6 @@ def _run(): result = _wrap_nsf('nsf-rndir', _run) if not kwargs.get('quiet'): - display = _folder_name_from_vault(vault, result.folder_uid) if new_name: logger.info('Folder "%s" has been renamed to "%s"', display, new_name) elif color: diff --git a/keepercli-package/src/keepercli/commands/pam/pam_config.py b/keepercli-package/src/keepercli/commands/pam/pam_config.py index 4bacc2e4..1e60cfd8 100644 --- a/keepercli-package/src/keepercli/commands/pam/pam_config.py +++ b/keepercli-package/src/keepercli/commands/pam/pam_config.py @@ -12,17 +12,31 @@ from keepersdk import utils from keepersdk.proto import pam_pb2, record_pb2 from keepersdk.helpers import config_utils -from keepersdk.vault import vault_online, vault_utils, vault_record, record_management +from keepersdk.vault import ( + vault_online, vault_utils, vault_record, record_management, nsf_management, vault_extensions, +) from keepersdk.helpers.pam_config_facade import PamConfigurationRecordFacade from keepersdk.helpers.tunnel.tunnel_graph import TunnelDAG, TriStateSetting, tunnel_utils from keepersdk.helpers.keeper_dag import dag_utils from keepersdk.helpers.keeper_dag.constants import PamConfigurationRecordType, PAM_CONFIGURATIONS from .. import record_edit +from . import pam_utils logger = api.get_logger() +class _PamFolderRef: + """Minimal folder info for list display (classic Folder/SharedFolder or NSF).""" + + __slots__ = ('name', 'folder_uid', 'shared_folder_uid') + + def __init__(self, name: str, uid: str): + self.name = name or uid + self.folder_uid = uid + self.shared_folder_uid = uid + + class PAMConfigListCommand(base.ArgparseCommand): def __init__(self): @@ -78,19 +92,20 @@ def _list_single_configuration(self, vault: vault_online.VaultOnline, config_uid if format_type == 'json' and isinstance(configuration, str): return configuration facade = self._create_facade(configuration) - shared_folder = self._load_shared_folder(vault, facade.folder_uid) - + shared_folder = self._load_folder_for_configuration(vault, configuration, facade) + if format_type == 'json': return self._format_single_config_json(configuration, facade, shared_folder) else: self._format_single_config_table(configuration, facade, shared_folder) def _list_all_configurations(self, vault: vault_online.VaultOnline, is_verbose: bool, format_type: str): - """Lists all PAM configurations.""" + """Lists all PAM configurations (classic shared folders and NSF).""" configs_data = [] table = [] headers = self._build_list_headers(is_verbose, format_type) - + seen_uids = set() + for config_record in self._find_pam_configurations(vault): full_record = vault.vault_data.load_record(config_record.record_uid) if not full_record or not isinstance(full_record, vault_record.TypedRecord): @@ -100,35 +115,53 @@ def _list_all_configurations(self, vault: vault_online.VaultOnline, is_verbose: continue facade = self._create_facade(full_record) - shared_folder_parents = vault_utils.get_folders_for_record(vault.vault_data, config_record.record_uid) - - if not shared_folder_parents: - logger.warning(f'Following configuration is not in the shared folder: UID: %s, Title: %s', - config_record.record_uid, config_record.title) + shared_folder = self._load_folder_for_configuration(vault, full_record, facade) + if not shared_folder: + logger.warning( + 'Following configuration is not in a shared folder or NSF folder: UID: %s, Title: %s', + config_record.record_uid, config_record.title) continue - shared_folder = shared_folder_parents[0] - + seen_uids.add(config_record.record_uid) if format_type == 'json': - config_data = self._build_config_json_data(config_record, facade, shared_folder, full_record, is_verbose) - configs_data.append(config_data) + configs_data.append( + self._build_config_json_data(config_record, facade, shared_folder, full_record, is_verbose)) else: - row = self._build_config_table_row(config_record, facade, shared_folder, full_record, is_verbose) - table.append(row) + table.append( + self._build_config_table_row(config_record, facade, shared_folder, full_record, is_verbose)) + + for full_record in self._find_nsf_pam_configurations(vault): + if full_record.record_uid in seen_uids: + continue + facade = self._create_facade(full_record) + shared_folder = self._load_folder_for_configuration(vault, full_record, facade) + if not shared_folder: + logger.warning( + 'Following NSF configuration is not in an NSF folder: UID: %s, Title: %s', + full_record.record_uid, full_record.title) + continue + if format_type == 'json': + configs_data.append( + self._build_config_json_data(full_record, facade, shared_folder, full_record, is_verbose)) + else: + table.append( + self._build_config_table_row(full_record, facade, shared_folder, full_record, is_verbose)) return self._format_output(configs_data, table, headers, format_type) def _load_and_validate_configuration(self, vault: vault_online.VaultOnline, config_uid: str, format_type: str): - """Loads and validates a PAM configuration record.""" + """Loads and validates a PAM configuration record (classic v6 or NSF).""" info = vault.vault_data.get_record(config_uid) - if not info or info.version != 6 or info.record_type not in PAM_CONFIGURATIONS: - return self._handle_error(format_type, f'Configuration {config_uid} not found') + if info and info.version == 6 and info.record_type in PAM_CONFIGURATIONS: + configuration = vault.vault_data.load_record(config_uid) + if configuration and isinstance(configuration, vault_record.TypedRecord): + return configuration - configuration = vault.vault_data.load_record(config_uid) - if not configuration or not isinstance(configuration, vault_record.TypedRecord): - return self._handle_error(format_type, f'Configuration {config_uid} not found') + nsf_record = self._load_nsf_pam_configuration(vault, config_uid) + if nsf_record: + return nsf_record - return configuration + return self._handle_error(format_type, f'Configuration {config_uid} not found') def _handle_error(self, format_type: str, error_message: str): """Handles errors based on output format.""" @@ -149,8 +182,77 @@ def _load_shared_folder(self, vault: vault_online.VaultOnline, folder_uid: str): return vault.vault_data.load_shared_folder(folder_uid) return None + def _load_folder_for_configuration(self, vault: vault_online.VaultOnline, configuration, facade): + """Resolve classic shared folder or NSF parent folder for display.""" + folder_uid = getattr(facade, 'folder_uid', None) or '' + shared = self._load_shared_folder(vault, folder_uid) + if shared: + return shared + + parents = vault_utils.get_folders_for_record(vault.vault_data, configuration.record_uid) + if parents: + parent = parents[0] + return _PamFolderRef(parent.name, parent.folder_uid) + + return self._resolve_nsf_folder(vault, configuration.record_uid, folder_uid) + + def _resolve_nsf_folder(self, vault: vault_online.VaultOnline, record_uid: str, preferred_folder_uid: str = ''): + """Build folder display info from NSF cache.""" + if not vault.nsf_data: + return None + try: + folder_uids = [] + if preferred_folder_uid and nsf_management.is_nsf_folder(vault, preferred_folder_uid): + folder_uids = [preferred_folder_uid] + if not folder_uids: + folder_uids = nsf_management.find_nsf_folders_for_record(vault, record_uid) + if not folder_uids: + return None + folder_uid = folder_uids[0] + if folder_uid == nsf_management.ROOT_FOLDER_UID: + return _PamFolderRef('My Vault', folder_uid) + folder = vault.nsf_data.get_folder(folder_uid) + name = (folder.name if folder and folder.name else None) or folder_uid + return _PamFolderRef(name, folder_uid) + except nsf_management.NsfError: + return None + + def _load_nsf_pam_configuration(self, vault: vault_online.VaultOnline, identifier: str): + """Load a PAM configuration from NSF by UID or exact title.""" + record_uid = pam_utils.resolve_nsf_record_uid(vault, identifier) + if not record_uid: + return None + typed = pam_utils.load_nsf_typed_record(vault, record_uid) + if typed and typed.record_type in PAM_CONFIGURATIONS: + return typed + return None + + def _find_nsf_pam_configurations(self, vault: vault_online.VaultOnline): + """Yield TypedRecord PAM configs stored under NSF.""" + if not vault.nsf_data: + logger.debug('No NSF record /folder found') + return + for entry in vault.nsf_data.records(): + rec_type = '' + if entry.decrypted_data: + try: + payload = json.loads(entry.decrypted_data) + if isinstance(payload, dict): + rec_type = str(payload.get('type') or '') + except json.JSONDecodeError: + pass + if rec_type and rec_type not in PAM_CONFIGURATIONS: + if 'Configuration' in rec_type: + logger.warning( + 'Following NSF configuration has unsupported type: UID: %s, Type: %s', + entry.record_uid, rec_type) + continue + typed = pam_utils.load_nsf_typed_record(vault, entry.record_uid) + if typed and typed.record_type in PAM_CONFIGURATIONS: + yield typed + def _find_pam_configurations(self, vault: vault_online.VaultOnline): - """Finds all PAM configuration records.""" + """Finds all classic vault PAM configuration records (version 6).""" for record in vault.vault_data.find_records(criteria='', record_type=None, record_version=6): if record.record_type in PAM_CONFIGURATIONS: yield record @@ -213,13 +315,14 @@ def _extract_config_fields(self, record, is_verbose: bool): def _build_config_json_data(self, config_record, facade, shared_folder, full_record, is_verbose: bool): """Builds JSON data structure for a configuration.""" + folder_uid = self._folder_uid(shared_folder) config_data = { "uid": config_record.record_uid, "config_name": config_record.title, "config_type": config_record.record_type, "shared_folder": { - "name": shared_folder.name, - "uid": shared_folder.folder_uid + "name": shared_folder.name if shared_folder else None, + "uid": folder_uid }, "gateway_uid": facade.controller_uid, "resource_record_uids": facade.resource_ref @@ -232,11 +335,15 @@ def _build_config_json_data(self, config_record, facade, shared_folder, full_rec def _build_config_table_row(self, config_record, facade, shared_folder, full_record, is_verbose: bool): """Builds a table row for a configuration.""" + folder_uid = self._folder_uid(shared_folder) + folder_label = '' + if shared_folder: + folder_label = f'{shared_folder.name} ({folder_uid})' row = [ config_record.record_uid, config_record.title, config_record.record_type, - f'{shared_folder.name} ({shared_folder.folder_uid})', + folder_label, facade.controller_uid, facade.resource_ref ] @@ -247,6 +354,12 @@ def _build_config_table_row(self, config_record, facade, shared_folder, full_rec return row + @staticmethod + def _folder_uid(folder) -> str: + if not folder: + return '' + return getattr(folder, 'folder_uid', None) or getattr(folder, 'shared_folder_uid', None) or '' + def _format_output(self, configs_data, table, headers, format_type: str): """Formats and outputs the final result.""" if format_type == 'json': @@ -258,13 +371,14 @@ def _format_output(self, configs_data, table, headers, format_type: str): def _format_single_config_json(self, configuration, facade, shared_folder): """Formats a single configuration as JSON.""" + folder_uid = self._folder_uid(shared_folder) config_data = { "uid": configuration.record_uid, "name": configuration.title, "config_type": configuration.record_type, "shared_folder": { "name": shared_folder.name if shared_folder else None, - "uid": shared_folder.shared_folder_uid if shared_folder else None + "uid": folder_uid } if shared_folder else None, "gateway_uid": facade.controller_uid, "resource_record_uids": facade.resource_ref, @@ -291,11 +405,12 @@ def _format_single_config_table(self, configuration, facade, shared_folder): """Formats a single configuration as a table.""" table = [] header = ['name', 'value'] + folder_uid = self._folder_uid(shared_folder) table.append(['UID', configuration.record_uid]) table.append(['Name', configuration.title]) table.append(['Config Type', configuration.record_type]) - table.append(['Shared Folder', f'{shared_folder.name} ({shared_folder.shared_folder_uid})' if shared_folder else '']) + table.append(['Shared Folder', f'{shared_folder.name} ({folder_uid})' if shared_folder else '']) table.append(['Gateway UID', facade.controller_uid]) table.append(['Resource Record UIDs', facade.resource_ref]) @@ -385,20 +500,31 @@ def _parse_shared_folder_uid(self, vault: vault_online.VaultOnline, record: vaul if shared_folder_uid: value['folderUid'] = shared_folder_uid else: - raise base.CommandError('Shared Folder not found') + raise base.CommandError('Shared Folder or NSF folder not found') def _find_shared_folder_by_name_or_uid(self, vault: vault_online.VaultOnline, folder_name: str): - """Finds a shared folder by UID or name.""" + """Finds a classic shared folder or NSF folder by UID or exact name.""" shared_folder_cache = vault.vault_data._shared_folders - + if folder_name in shared_folder_cache: return folder_name - + for sf_uid in shared_folder_cache: sf = vault.vault_data.load_shared_folder(sf_uid) if sf and sf.name.casefold() == folder_name.casefold(): return sf_uid - + + if vault.nsf_data is None: + logger.debug('No nsf data') + return None + try: + nsf_uid = nsf_management.resolve_nsf_folder_uid(vault, folder_name) + except nsf_management.NsfError: + nsf_uid = None + if nsf_uid and nsf_management.is_nsf_folder(vault, nsf_uid): + return nsf_uid + if nsf_management.is_nsf_folder(vault, folder_name): + return folder_name return None def _get_existing_shared_folder_uid(self, record: vault_record.TypedRecord): @@ -471,9 +597,15 @@ def _parse_common_properties(self, extra_properties: list, kwargs: dict): valid, err = validate_cron_expression(schedule, for_rotation=True) if not valid: raise base.CommandError(f'Invalid CRON "{schedule}" Error: {err}') - extra_properties.append(f'schedule.defaultRotationSchedule=$JSON:{{"type": "CRON", "cron": "{schedule}", "tz": "Etc/UTC"}}') - else: - extra_properties.append('schedule.defaultRotationSchedule=On-Demand') + schedule_json = json.dumps({"type": "CRON", "cron": schedule, "tz": "Etc/UTC"}) + extra_properties.append(f'schedule.defaultRotationSchedule=$JSON:{schedule_json}') + elif not kwargs.get('config_edit'): + # New configs default to On-Demand. On edit, omit --schedule to leave existing value. + extra_properties.append('schedule.defaultRotationSchedule=$JSON:{"type": "ON_DEMAND"}') + + identity_provider_uid = kwargs.get('identity_provider_uid') + if identity_provider_uid: + extra_properties.append(f'text.identityProviderUid={identity_provider_uid}') def _parse_type_specific_properties(self, vault: vault_online.VaultOnline, record: vault_record.TypedRecord, extra_properties: list, kwargs: dict): @@ -684,7 +816,9 @@ def _configure_tunneling(self, vault: vault_online.VaultOnline, record: vault_re kwargs.get('rotation'), kwargs.get('recording'), kwargs.get('typescriptrecording'), - kwargs.get('remotebrowserisolation') + kwargs.get('remotebrowserisolation'), + kwargs.get('ai_threat_detection'), + kwargs.get('ai_terminate_session_on_detection'), ) if admin_cred_ref: @@ -710,8 +844,8 @@ def _configure_tunneling(self, vault: vault_online.VaultOnline, record: vault_re common_parser.add_argument('--title', '-t', dest='title', action='store', help='Title of the PAM Configuration') common_parser.add_argument('--gateway', '-g', dest='gateway_uid', action='store', help='Gateway UID or Name') common_parser.add_argument('--shared-folder', '-sf', dest='shared_folder_uid', action='store', - help='Share Folder where this PAM Configuration is stored. Should be one of the folders to ' - 'which the gateway has access to.') + help='Classic shared folder or NSF folder (UID or name) where this PAM ' + 'Configuration is stored. Should be a folder the gateway can access.') common_parser.add_argument('--schedule', '-sc', dest='default_schedule', action='store', help='Default Schedule: Use CRON syntax') common_parser.add_argument('--port-mapping', '-pm', dest='port_mapping', action='append', help='Port Mapping') @@ -897,15 +1031,24 @@ def _warn_if_gateway_missing(self, gateway_uid: str, kwargs: dict): def _create_and_configure_record(self, vault: vault_online.VaultOnline, record: vault_record.TypedRecord, shared_folder_uid: str, gateway_uid: str, admin_cred_ref: str, kwargs: dict): - """Creates the record and configures tunneling, DAG, and controller.""" - config_utils.pam_configuration_create_record_v6(vault, record, shared_folder_uid) - + """Creates the record and configures tunneling, DAG, and controller. + + NSF folders use vault/records/v3/add_pam_configuration (record is created + in-folder). Classic shared folders use pam/add_configuration_record then + move the record into the folder after sync — same as Commander. + """ + from keepersdk.errors import KeeperApiError + + try: + is_nsf = config_utils.create_pam_configuration_in_folder( + vault, record, shared_folder_uid) + except (nsf_management.NsfError, KeeperApiError) as exc: + raise base.CommandError(str(exc)) from exc + self._configure_tunneling(vault, record, admin_cred_ref, kwargs) - - vault.sync_down() - record_management.move_vault_objects(vault, [record.record_uid], shared_folder_uid) vault.sync_down() - + if not is_nsf: + record_management.move_vault_objects(vault, [record.record_uid], shared_folder_uid) if gateway_uid: self._set_configuration_controller(vault, record.record_uid, gateway_uid) @@ -947,30 +1090,47 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser): help='Set recording connections permissions for the resource') parser.add_argument('--typescript-recording', '-tr', dest='typescriptrecording', choices=choices, help='Set TypeScript recording permissions for the resource') + parser.add_argument('--ai-threat-detection', dest='ai_threat_detection', choices=choices, + help='Set AI threat detection permissions') + parser.add_argument('--ai-terminate-session-on-detection', dest='ai_terminate_session_on_detection', + choices=choices, + help='Set AI session termination on threat detection permissions') def execute(self, context: KeeperParams, **kwargs): self.warnings.clear() self._validate_vault(context) vault = context.vault - configuration = self._find_configuration(vault, kwargs.get('uid')) - self._validate_configuration(vault, configuration, kwargs.get('uid')) + configuration, is_nsf = self._find_configuration(vault, kwargs.get('uid')) + self._validate_configuration(vault, configuration, kwargs.get('uid'), is_nsf=is_nsf) self._update_record_type_if_needed(vault, configuration, kwargs) self._update_title_if_provided(configuration, kwargs) orig_gateway_uid, orig_shared_folder_uid = self._get_original_values(configuration) - self.parse_properties(vault, configuration, **kwargs) + self.parse_properties(vault, configuration, config_edit=True, **kwargs) self.verify_required(configuration) - record_management.update_record(vault, configuration) - self._update_controller_and_folder_if_changed(vault, configuration, orig_gateway_uid, orig_shared_folder_uid) - - target_keys = { - 'connections', 'tunneling', 'rotation', 'recording', - 'typescriptrecording', 'remotebrowserisolation' - } - if target_keys.isdisjoint(kwargs): + if is_nsf: + self._update_nsf_configuration(vault, configuration) + else: + record_management.update_record(vault, configuration) + self._update_controller_and_folder_if_changed( + vault, configuration, orig_gateway_uid, orig_shared_folder_uid, is_nsf=is_nsf) + + # Apply DAG permission changes when any flag is explicitly set (Commander parity). + connections = kwargs.get('connections') + tunneling = kwargs.get('tunneling') + rotation = kwargs.get('rotation') + recording = kwargs.get('recording') + typescriptrecording = kwargs.get('typescriptrecording') + remotebrowserisolation = kwargs.get('remotebrowserisolation') + ai_threat_detection = kwargs.get('ai_threat_detection') + ai_terminate = kwargs.get('ai_terminate_session_on_detection') + if any(v is not None for v in ( + connections, tunneling, rotation, recording, + typescriptrecording, remotebrowserisolation, + ai_threat_detection, ai_terminate)): admin_cred_ref = None if configuration.record_type == PamConfigurationRecordType.DOMAIN and not kwargs.get('force_domain_admin'): pam_field = configuration.get_typed_field('pamResources') @@ -989,14 +1149,18 @@ def _validate_vault(self, context: KeeperParams): raise base.CommandError('Vault is not initialized. Login to initialize the vault.') def _find_configuration(self, vault: vault_online.VaultOnline, config_name: str): - """Finds a PAM configuration by UID or name.""" + """Finds a PAM configuration by UID or name (classic vault or NSF). + + Returns: + (TypedRecord, is_nsf) or (None, False) when not found. + """ if not config_name: - return None + return None, False info = vault.vault_data.get_record(config_name) if info and info.version == 6 and info.record_type in PAM_CONFIGURATIONS: loaded = vault.vault_data.load_record(config_name) if loaded and isinstance(loaded, vault_record.TypedRecord): - return loaded + return loaded, False name_lower = config_name.casefold() for record in vault.vault_data.find_records( criteria=None, @@ -1005,20 +1169,49 @@ def _find_configuration(self, vault: vault_online.VaultOnline, config_name: str) if record.record_uid == config_name or record.title.casefold() == name_lower: loaded = vault.vault_data.load_record(record.record_uid) if loaded and isinstance(loaded, vault_record.TypedRecord): - return loaded - return None + return loaded, False - def _validate_configuration(self, vault: vault_online.VaultOnline, configuration, config_name: str): - """Validates that the configuration exists and is a v6 PAM config in the vault index.""" + nsf_record = self._load_nsf_pam_configuration_for_edit(vault, config_name) + if nsf_record: + return nsf_record, True + return None, False + + def _load_nsf_pam_configuration_for_edit(self, vault: vault_online.VaultOnline, identifier: str): + """Load a PAM configuration from NSF by UID or exact title.""" + return self._load_nsf_pam_configuration(vault, identifier) + + def _validate_configuration(self, vault: vault_online.VaultOnline, configuration, config_name: str, + *, is_nsf: bool = False): + """Validates that the configuration exists and is a PAM config.""" if not configuration: raise base.CommandError(f'PAM configuration "{config_name}" not found') if not isinstance(configuration, vault_record.TypedRecord): raise base.CommandError(f'PAM configuration "{config_name}" not found') + if is_nsf: + if configuration.record_type not in PAM_CONFIGURATIONS: + raise base.CommandError(f'PAM configuration "{config_name}" not found') + return # Storage format is on KeeperRecordInfo, not TypedRecord.version() (that method returns 3). info = vault.vault_data.get_record(configuration.record_uid) if not info or info.version != 6 or info.record_type not in PAM_CONFIGURATIONS: raise base.CommandError(f'PAM configuration "{config_name}" not found') + def _update_nsf_configuration(self, vault: vault_online.VaultOnline, configuration: vault_record.TypedRecord): + """Persist PAM configuration field edits to an NSF record.""" + schema = vault.vault_data.get_record_type_by_name(configuration.record_type) + record_data = vault_extensions.extract_typed_record_data(configuration, schema) + try: + nsf_management.update_nsf_record( + vault, + configuration.record_uid, + title=configuration.title, + record_type=configuration.record_type, + record_data=record_data, + request_sync=True, + ) + except nsf_management.NsfError as e: + raise base.CommandError(str(e)) from e + def _update_record_type_if_needed(self, vault: vault_online.VaultOnline, configuration: vault_record.TypedRecord, kwargs: dict): """Updates the record type if config_type is provided and different.""" @@ -1054,7 +1247,8 @@ def _get_original_values(self, configuration: vault_record.TypedRecord): def _update_controller_and_folder_if_changed(self, vault: vault_online.VaultOnline, configuration: vault_record.TypedRecord, - orig_gateway_uid: str, orig_shared_folder_uid: str): + orig_gateway_uid: str, orig_shared_folder_uid: str, + *, is_nsf: bool = False): """Updates controller and shared folder if they changed.""" field = configuration.get_typed_field('pamResources') value = field.get_default_value(dict) @@ -1067,6 +1261,14 @@ def _update_controller_and_folder_if_changed(self, vault: vault_online.VaultOnli shared_folder_uid = value.get('folderUid') or '' if shared_folder_uid != orig_shared_folder_uid: + dest_is_nsf = ( + vault.nsf_data is not None + and nsf_management.is_nsf_folder(vault, shared_folder_uid) + ) + if is_nsf or dest_is_nsf: + raise base.CommandError( + 'Moving PAM configurations into or between NSF folders is not supported yet. ' + 'Create a new configuration in the target folder instead.') record_management.move_vault_objects(vault, [configuration.record_uid], shared_folder_uid) def _set_configuration_controller(self, vault: vault_online.VaultOnline, config_uid: str, gateway_uid: str): diff --git a/keepercli-package/src/keepercli/commands/pam/pam_connection.py b/keepercli-package/src/keepercli/commands/pam/pam_connection.py index 5a3dc9d6..e16915c6 100644 --- a/keepercli-package/src/keepercli/commands/pam/pam_connection.py +++ b/keepercli-package/src/keepercli/commands/pam/pam_connection.py @@ -3,13 +3,22 @@ from keepersdk import utils from keepersdk.helpers.keeper_dag import dag_utils +from keepersdk.helpers.keeper_dag.constants import ( + PAM_CONFIGURATIONS, + PAM_DATABASE, + PAM_DIRECTORY, + PAM_MACHINE, + PAM_RESOURCES, + PAM_USER, +) from keepersdk.helpers.tunnel.tunnel_graph import TunnelDAG from keepersdk.helpers.tunnel.tunnel_utils import get_keeper_tokens, get_config_uid -from keepersdk.vault import record_management, vault_record +from keepersdk.vault import vault_record from .. import base from ... import api from ...params import KeeperParams +from . import pam_utils logger = api.get_logger() @@ -18,6 +27,15 @@ protocols = ['', 'http', 'kubernetes', 'mysql', 'postgresql', 'rdp', 'sql-server', 'ssh', 'telnet', 'vnc'] choices = ['on', 'off', 'default'] +# Resource + RBI + PAM configs (reuse keeper_dag constants where they exist). +_PAM_CONNECTION_RECORD_TYPES = (*PAM_RESOURCES, 'pamRemoteBrowser', *PAM_CONFIGURATIONS) +_PAM_SEED_RECORD_TYPES = (PAM_DATABASE, PAM_DIRECTORY, PAM_MACHINE, 'pamRemoteBrowser') +_PAM_RESOURCE_USER_LINK_TYPES = (PAM_DATABASE, PAM_DIRECTORY, PAM_MACHINE) + + +def _is_pam_config_record(record: vault_record.TypedRecord) -> bool: + return record.record_type in PAM_CONFIGURATIONS + class PAMConnectionEditCommand(base.ArgparseCommand): @@ -75,29 +93,36 @@ def execute(self, context: KeeperParams, **kwargs): record_name = kwargs.get('record') if not record_name: raise base.CommandError(f'Record parameter is required.') - record = vault.vault_data.load_record(record_name) + record = pam_utils.load_typed_record(context, record_name) if not record: raise base.CommandError(f'Record \"{record_name}\" not found.') - if not isinstance(record, vault_record.TypedRecord): - raise base.CommandError(f'Record \"{record_name}\" can not be edited.') - - config_name = kwargs.get('config', None) - cfg_rec = vault.vault_data.load_record(config_name) - if not cfg_rec and record.version == 6: - cfg_rec = record - config_uid = cfg_rec.record_uid if cfg_rec else None record_uid = record.record_uid record_type = record.record_type - if record_type not in ("pamMachine pamDatabase pamDirectory pamNetworkConfiguration pamAwsConfiguration " - "pamRemoteBrowser pamAzureConfiguration").split(): + if record_type not in _PAM_CONNECTION_RECORD_TYPES: raise base.CommandError(f"This record's type is not supported for connections. " f"Connections are only supported on pamMachine, pamDatabase, pamDirectory, " f"pamRemoteBrowser, pamNetworkConfiguration pamAwsConfiguration, and " f"pamAzureConfiguration records") encrypted_session_token, encrypted_transmission_key, transmission_key = get_keeper_tokens(vault) - if record_type in "pamNetworkConfiguration pamAwsConfiguration pamAzureConfiguration".split(): + + config_name = kwargs.get('config', None) + cfg_rec = pam_utils.load_typed_record(context, config_name) if config_name else None + if not cfg_rec and _is_pam_config_record(record): + cfg_rec = record + + # For resource records, fall back to the PAM config already linked in the DAG. + existing_config_uid = None + if not _is_pam_config_record(record): + existing_config_uid = get_config_uid( + vault, encrypted_session_token, encrypted_transmission_key, record_uid) + existing_config_uid = str(existing_config_uid) if existing_config_uid else '' + if not cfg_rec and existing_config_uid: + cfg_rec = pam_utils.load_typed_record(context, existing_config_uid) + config_uid = cfg_rec.record_uid if cfg_rec else None + + if record_type in PAM_CONFIGURATIONS: tdag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, record_uid, is_config=True, transmission_key=transmission_key) tdag.edit_tunneling_config(connections=_connections, session_recording=_recording, typescript_recording=_typescript_recording) @@ -111,7 +136,7 @@ def execute(self, context: KeeperParams, **kwargs): base64_seed = utils.base64_url_encode(seed) record_seed = vault_record.TypedField.create_field('trafficEncryptionSeed', base64_seed, required=False) - record_types_with_seed = ("pamDatabase", "pamDirectory", "pamMachine", "pamRemoteBrowser") + record_types_with_seed = _PAM_SEED_RECORD_TYPES if traffic_encryption_key: traffic_encryption_key.value = [base64_seed] elif record.record_type in record_types_with_seed: @@ -183,8 +208,7 @@ def execute(self, context: KeeperParams, **kwargs): logger.debug(f'Unexpected value for --key-events {key_events} (ignored)') if dirty: - record_management.update_record(vault, record) - vault.sync_down() + pam_utils.save_typed_record(vault, record) traffic_encryption_key = record.get_typed_field('trafficEncryptionSeed') if not traffic_encryption_key: @@ -192,23 +216,33 @@ def execute(self, context: KeeperParams, **kwargs): f"Please make sure you have edit rights to record {record_uid}") dirty = False - existing_config_uid = get_config_uid(vault, encrypted_session_token, encrypted_transmission_key, record_uid) + if not config_uid: + raise base.CommandError( + "No PAM Configuration UID set. " + "This must be set or supplied for connections to work. " + "Pass --config [ConfigUID] (see `pam config list`)." + ) tdag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, config_uid, - transmission_key=transmission_key) - old_dag = TunnelDAG(vault, encrypted_session_token, encrypted_transmission_key, existing_config_uid, - transmission_key=transmission_key) + is_config=True, transmission_key=transmission_key) - if config_uid and existing_config_uid != config_uid: + if existing_config_uid and existing_config_uid != config_uid: + old_dag = TunnelDAG( + vault, encrypted_session_token, encrypted_transmission_key, existing_config_uid, + is_config=True, transmission_key=transmission_key, + ) old_dag.remove_from_dag(record_uid) tdag.link_resource_to_config(record_uid) + elif not tdag.is_tunneling_config_set_up(record_uid): + tdag.link_resource_to_config(record_uid) - if tdag is None or not tdag.linking_dag.has_graph: - raise base.CommandError(f"No PAM Configuration UID set. " - f"This must be set or supplied for connections to work. This can be done by adding " - f"' --config [ConfigUID] " - f" The ConfigUID can be found by running " - f"'pam config list'") + if not tdag.linking_dag.has_graph: + raise base.CommandError( + f"No PAM Configuration DAG found for {config_uid}. " + "Initialize tunnel settings on the config first, e.g.\n" + f" pam connection edit {config_uid} --connections on " + "--connections-recording on" + ) if not tdag.check_tunneling_enabled_config(enable_connections=_connections, enable_session_recording=_recording, @@ -255,20 +289,21 @@ def execute(self, context: KeeperParams, **kwargs): typescript_recording=kwargs.get('typescriptrecording', None)) admin_name = kwargs.get('admin') - adm_rec = vault.vault_data.load_record(admin_name) - admin_uid = adm_rec.record_uid if adm_rec else None - if admin_uid and record_type in ("pamDatabase", "pamDirectory", "pamMachine"): - tdag.link_user_to_resource(admin_uid, record_uid, is_admin=True, belongs_to=True) + if admin_name: + adm_rec = pam_utils.load_typed_record(context, admin_name) + admin_uid = adm_rec.record_uid if adm_rec else None + if admin_uid and record_type in _PAM_RESOURCE_USER_LINK_TYPES: + tdag.link_user_to_resource(admin_uid, record_uid, is_admin=True, belongs_to=True) launch_user_name = kwargs.get('launch_user') if launch_user_name: - launch_rec = vault.vault_data.load_record(launch_user_name) + launch_rec = pam_utils.load_typed_record(context, launch_user_name) if not launch_rec: raise base.CommandError(f'Launch user record "{launch_user_name}" not found.') - if not isinstance(launch_rec, vault_record.TypedRecord) or launch_rec.record_type != 'pamUser': + if launch_rec.record_type != PAM_USER: raise base.CommandError(f'Launch user record must be a pamUser record type.') launch_uid = launch_rec.record_uid - if record_type in ("pamDatabase", "pamDirectory", "pamMachine"): + if record_type in _PAM_RESOURCE_USER_LINK_TYPES: tdag.clear_launch_credential_for_resource(record_uid, exclude_user_uid=launch_uid) tdag.link_user_to_resource(launch_uid, record_uid, is_admin=True, belongs_to=True) tdag.upgrade_resource_meta_to_v1(record_uid) diff --git a/keepercli-package/src/keepercli/commands/pam/pam_rbi.py b/keepercli-package/src/keepercli/commands/pam/pam_rbi.py index 7bec570d..2557a76e 100644 --- a/keepercli-package/src/keepercli/commands/pam/pam_rbi.py +++ b/keepercli-package/src/keepercli/commands/pam/pam_rbi.py @@ -8,12 +8,20 @@ from keepersdk.helpers.keeper_dag.constants import PAM_CONFIGURATIONS from keepersdk.helpers.tunnel.tunnel_graph import TunnelDAG from keepersdk.helpers.tunnel.tunnel_utils import get_keeper_tokens, get_config_uid -from keepersdk.vault import record_management, vault_online, vault_record +from keepersdk.vault import ( + nsf_management, + record_management, + vault_extensions, + vault_online, + vault_record, +) from .. import base from ... import api from ...helpers import record_utils from ...params import KeeperParams +from . import pam_utils + choices = ['on', 'off', 'default'] logger = api.get_logger() @@ -47,8 +55,24 @@ def _bootstrap_rbi_record(record: vault_record.TypedRecord) -> bool: def _save_rbi_record(vault: vault_online.VaultOnline, record: vault_record.TypedRecord) -> None: - """Persist RBI record body changes with a fresh revision (sync + retry on out-of-sync).""" + """Persist RBI record body changes (classic or NSF) with sync + out-of-sync retry.""" vault.sync_down() + if pam_utils.is_nsf_record(vault, record.record_uid): + schema = vault.vault_data.get_record_type_by_name(record.record_type) + record_data = vault_extensions.extract_typed_record_data(record, schema) + try: + nsf_management.update_nsf_record( + vault, + record.record_uid, + title=record.title, + record_type=record.record_type, + record_data=record_data, + request_sync=True, + ) + except nsf_management.NsfError as err: + raise base.CommandError(str(err)) from err + vault.sync_down() + return try: record_management.update_record(vault, record) except KeeperApiError as err: @@ -69,18 +93,23 @@ def _resolve_pam_config_record( context: KeeperParams, config_ref: str, ) -> Optional[vault_record.TypedRecord]: - """Resolve a PAM configuration by UID or title (vault index version 6, not TypedRecord.version).""" + """Resolve a PAM configuration by UID or title (classic v6 or NSF).""" if not config_ref or context.vault is None: return None vault = context.vault info = vault.vault_data.get_record(config_ref) if not info: info = record_utils.try_resolve_single_record(config_ref, context) - if not info or info.version != 6 or info.record_type not in PAM_CONFIGURATIONS: - return None - loaded = vault.vault_data.load_record(info.record_uid) - if isinstance(loaded, vault_record.TypedRecord): - return loaded + if info and info.version == 6 and info.record_type in PAM_CONFIGURATIONS: + loaded = vault.vault_data.load_record(info.record_uid) + if isinstance(loaded, vault_record.TypedRecord): + return loaded + + nsf_uid = pam_utils.resolve_nsf_record_uid(vault, config_ref) + if nsf_uid: + typed = pam_utils.load_nsf_typed_record(vault, nsf_uid) + if typed and typed.record_type in PAM_CONFIGURATIONS: + return typed return None @@ -191,14 +220,9 @@ def execute(self, context: KeeperParams, **kwargs): vault = context.vault - record_info = record_utils.try_resolve_single_record(record_name, context) - if not record_info: - raise base.CommandError(f'Record \"{record_name}\" not found.') - record = vault.vault_data.load_record(record_info.record_uid) + record = pam_utils.load_typed_record(context, record_name) if not record: raise base.CommandError(f'Record \"{record_name}\" not found.') - if not isinstance(record, vault_record.TypedRecord): - raise base.CommandError(f'Record \"{record_name}\" can not be edited.') record_uid = record.record_uid record_type = record.record_type @@ -210,12 +234,14 @@ def execute(self, context: KeeperParams, **kwargs): dirty = _bootstrap_rbi_record(record) if autofill: - af_rec = vault.vault_data.load_record(autofill) + af_rec = pam_utils.load_typed_record(context, autofill) if not af_rec: raise base.CommandError(f'Record \"{autofill}\" not found.') - if not isinstance(af_rec, vault_record.TypedRecord) or af_rec.version != 3 or af_rec.record_type not in ("login", "pamUser"): - raise base.CommandError(f'Autofill credentials record \"{af_rec.record_uid}\" can not be linked. ' - ' RBI autofill credential records must be of type "login" or "pamUser"') + if af_rec.record_type not in ("login", "pamUser"): + raise base.CommandError( + f'Autofill credentials record \"{af_rec.record_uid}\" can not be linked. ' + ' RBI autofill credential records must be of type "login" or "pamUser"' + ) rbs_fld = record.get_typed_field('pamRemoteBrowserSettings') val1 = rbs_fld.value[0] if isinstance(rbs_fld, vault_record.TypedField) and rbs_fld.value else {} diff --git a/keepercli-package/src/keepercli/commands/pam/pam_rotation.py b/keepercli-package/src/keepercli/commands/pam/pam_rotation.py index c303be5b..0efcb4b9 100644 --- a/keepercli-package/src/keepercli/commands/pam/pam_rotation.py +++ b/keepercli-package/src/keepercli/commands/pam/pam_rotation.py @@ -4,11 +4,21 @@ import argparse import re +from typing import Optional + from keepersdk import crypto, utils from keepersdk.errors import KeeperApiError from keepersdk.helpers.tunnel.tunnel_graph import TunnelDAG from keepersdk.helpers.tunnel.tunnel_utils import get_keeper_tokens -from keepersdk.vault import record_management, vault_record, vault_types, vault_utils, record_facades, attachment +from keepersdk.vault import ( + record_management, + vault_online, + vault_record, + vault_types, + vault_utils, + record_facades, + attachment, +) from keepersdk.proto import pam_pb2, router_pb2 from .. import base @@ -16,6 +26,7 @@ from ...params import KeeperParams from ...helpers import gateway_utils, router_utils, report_utils, folder_utils, record_utils from keepersdk.helpers.keeper_dag.constants import PAM_CONFIGURATIONS +from . import pam_utils logger = api.get_logger() @@ -25,6 +36,212 @@ PAM_DEFAULT_SPECIAL_CHAR = '''!@#$%^?();',.=+[]<>{}-_/\\*&:"`~|''' +_PAM_ROTATION_RECORD_TYPES = ( + 'pamDatabase', 'pamDirectory', 'pamMachine', 'pamUser', 'pamRemoteBrowser', +) + +_PAM_SCRIPT_RECORD_TYPES = ('pamUser', 'pamDirectory') + + +def _resolve_nsf_record_uid(vault: vault_online.VaultOnline, identifier: str) -> Optional[str]: + """Resolve an NSF record UID from a UID or exact title.""" + if not vault.nsf_data or not identifier: + return None + if vault.nsf_data.get_record(identifier): + return identifier + try: + return nsf_management.resolve_nsf_record_uid(vault, identifier) + except nsf_management.NsfError: + return None + + +def _load_nsf_typed_record( + vault: vault_online.VaultOnline, record_uid: str) -> Optional[vault_record.TypedRecord]: + """Load an NSF record as TypedRecord (with record_key when available).""" + if not vault.nsf_data or not record_uid or not vault.nsf_data.get_record(record_uid): + return None + try: + meta = nsf_management.load_nsf_record_metadata(vault, record_uid) + except nsf_management.NsfError: + return None + typed = vault_record.TypedRecord() + typed.record_uid = record_uid + typed.load_record_data({ + 'type': meta.get('type') or '', + 'title': meta.get('title') or record_uid, + 'notes': meta.get('notes') or '', + 'fields': meta.get('fields') or [], + 'custom': meta.get('custom') or [], + }) + entry = vault.nsf_data.get_record(record_uid) + if entry and entry.record_key: + typed.record_key = entry.record_key + return typed + + +def _load_pam_typed_record( + vault: vault_online.VaultOnline, identifier: str) -> Optional[vault_record.TypedRecord]: + """Load a TypedRecord from classic vault or NSF by UID/title.""" + if not identifier: + return None + loaded = vault.vault_data.load_record(identifier) + if loaded and isinstance(loaded, vault_record.TypedRecord): + key = vault.vault_data.get_record_key(identifier) + if key: + loaded.record_key = key + return loaded + nsf_uid = _resolve_nsf_record_uid(vault, identifier) + if nsf_uid: + return _load_nsf_typed_record(vault, nsf_uid) + return None + + +def _is_nsf_pam_record(vault: vault_online.VaultOnline, record_uid: str) -> bool: + return bool(record_uid and vault.nsf_data and vault.nsf_data.get_record(record_uid)) + + +def _save_pam_typed_record( + vault: vault_online.VaultOnline, record: vault_record.TypedRecord) -> None: + """Persist a PAM typed record via NSF or classic update (including file/script links).""" + _attach_record_key(vault, record) + if _is_nsf_pam_record(vault, record.record_uid): + nsf_management.update_nsf_typed_record(vault, record) + else: + record_management.update_record(vault, record) + vault.sync_requested = True + + +def _iter_nsf_pam_script_records( + vault: vault_online.VaultOnline, pattern: Optional[str] = None): + """Yield NSF pamUser/pamDirectory records optionally filtered by UID/title pattern.""" + if not vault.nsf_data: + return + pattern_cf = pattern.casefold() if pattern else None + for entry in vault.nsf_data.records(): + typed = _load_nsf_typed_record(vault, entry.record_uid) + if not typed or typed.record_type not in _PAM_SCRIPT_RECORD_TYPES: + continue + if pattern_cf: + title = (typed.title or '').casefold() + if typed.record_uid != pattern and pattern_cf not in title and not fnmatch.fnmatch(title, pattern_cf): + continue + yield typed + + +def _find_pam_script_records( + vault: vault_online.VaultOnline, pattern: Optional[str] = None): + """Find pamUser/pamDirectory TypedRecords in classic vault and NSF.""" + found = [] + seen = set() + for rec in vault.vault_data.find_records( + criteria=pattern, record_version=3, record_type=_PAM_SCRIPT_RECORD_TYPES): + loaded = vault.vault_data.load_record(rec.record_uid) + if not isinstance(loaded, vault_record.TypedRecord): + continue + found.append(loaded) + seen.add(loaded.record_uid) + + if pattern: + nsf_uid = _resolve_nsf_record_uid(vault, pattern) + if nsf_uid and nsf_uid not in seen: + typed = _load_nsf_typed_record(vault, nsf_uid) + if typed and typed.record_type in _PAM_SCRIPT_RECORD_TYPES: + found.append(typed) + seen.add(typed.record_uid) + else: + for typed in _iter_nsf_pam_script_records(vault, pattern): + if typed.record_uid not in seen: + found.append(typed) + seen.add(typed.record_uid) + else: + for typed in _iter_nsf_pam_script_records(vault): + if typed.record_uid not in seen: + found.append(typed) + seen.add(typed.record_uid) + return found + + +def _get_unique_pam_script_record( + vault: vault_online.VaultOnline, record_name: str) -> vault_record.TypedRecord: + """Resolve a single pamUser/pamDirectory for script commands (classic or NSF).""" + records = _find_pam_script_records(vault, record_name) + if len(records) == 0: + raise base.CommandError(f'Record "{record_name}" not found') + if len(records) > 1: + raise base.CommandError(f'Record "{record_name}" is not unique. Use record UID.') + return records[0] + + +def _load_script_file_record(vault: vault_online.VaultOnline, file_ref: str): + """Load a script file attachment record from classic vault or NSF.""" + if not file_ref: + return None + file_record = vault.vault_data.load_record(file_ref) + if file_record: + return file_record + return _load_nsf_typed_record(vault, file_ref) + + +def _find_script_value(vault, script_field, script_name): + """Find a script value by fileRef UID or file title/name.""" + if not script_field or not script_name: + return None + script_value = next( + (x for x in script_field.value if isinstance(x, dict) and x.get('fileRef') == script_name), + None) + if script_value is not None: + return script_value + s_name = script_name.casefold() + for x in script_field.value: + if not isinstance(x, dict): + continue + file_uid = x.get('fileRef') + file_record = _load_script_file_record(vault, file_uid) + if not file_record: + continue + if getattr(file_record, 'record_uid', None) == script_name: + return x + title = (getattr(file_record, 'title', None) or '').casefold() + name = (getattr(file_record, 'name', None) or '').casefold() + if title == s_name or name == s_name: + return x + return None + + +def _resolve_script_credential_uid(vault: vault_online.VaultOnline, ref: str) -> Optional[str]: + """Resolve a credential UID for script recordRef (classic or NSF).""" + if not ref: + return None + loaded = _load_pam_typed_record(vault, ref) + if loaded: + return loaded.record_uid + if vault.vault_data.get_record_key(ref) or vault.vault_data.load_record(ref): + return ref + return None + + +def _iter_nsf_pam_configurations(vault: vault_online.VaultOnline): + """Yield NSF PAM configuration TypedRecords.""" + if not vault.nsf_data: + return + for entry in vault.nsf_data.records(): + typed = _load_nsf_typed_record(vault, entry.record_uid) + if typed and typed.record_type in PAM_CONFIGURATIONS: + yield typed + + +def _attach_record_key(vault: vault_online.VaultOnline, record: vault_record.TypedRecord) -> None: + """Ensure TypedRecord has record_key from classic or NSF storage when possible.""" + if getattr(record, 'record_key', None): + return + key = vault.vault_data.get_record_key(record.record_uid) + if not key and vault.nsf_data: + entry = vault.nsf_data.get_record(record.record_uid) + if entry: + key = entry.record_key + if key: + record.record_key = key + class PAMListRecordRotationCommand(base.ArgparseCommand): def __init__(self): @@ -59,7 +276,12 @@ def execute(self, context: KeeperParams, **kwargs): else: enterprise_controllers_connected_uids_bytes = [] - all_pam_config_records = record_utils.pam_configurations_get_all(vault) + all_pam_config_records = list(record_utils.pam_configurations_get_all(vault)) + seen_config_uids = {c.record_uid for c in all_pam_config_records} + for nsf_cfg in pam_utils.iter_nsf_pam_configurations(vault): + if nsf_cfg.record_uid not in seen_config_uids: + all_pam_config_records.append(nsf_cfg) + seen_config_uids.add(nsf_cfg.record_uid) table = [] headers = [] @@ -89,6 +311,10 @@ def execute(self, context: KeeperParams, **kwargs): (pam_config for pam_config in all_pam_config_records if pam_config.record_uid == configuration_uid_str), None) + if not pam_configuration and configuration_uid_str: + nsf_cfg = pam_utils.load_nsf_typed_record(vault, configuration_uid_str) + if nsf_cfg and nsf_cfg.record_type in PAM_CONFIGURATIONS: + pam_configuration = nsf_cfg is_controller_online = any( (poc for poc in enterprise_controllers_connected_uids_bytes if poc == controller_uid)) @@ -99,8 +325,13 @@ def execute(self, context: KeeperParams, **kwargs): record_title = rec.info.title record_type = rec.info.record_type else: - record_title = '[record inaccessible]' - record_type = '[record inaccessible]' + nsf_rec = pam_utils.load_nsf_typed_record(vault, record_uid) + if nsf_rec: + record_title = nsf_rec.title + record_type = nsf_rec.record_type + else: + record_title = '[record inaccessible]' + record_type = '[record inaccessible]' if record_type != "pamUser": continue @@ -310,7 +541,7 @@ def config_resource(_dag, target_record, target_config_uid, silent=None): _dag.link_resource_to_config(target_record.record_uid) admin = kwargs.get('admin') - adm_rec = vault.vault_data.load_record(admin) + adm_rec = pam_utils.load_pam_typed_record(vault, admin) if admin else None if adm_rec and isinstance(adm_rec, vault_record.TypedRecord): admin = adm_rec.record_uid @@ -428,13 +659,14 @@ def config_iam_aad_user(_dag, target_record, target_iam_aad_config_uid): if not record_config_uid: if current_record_rotation: record_config_uid = current_record_rotation.configuration_uid - pc = vault.vault_data.load_record(record_config_uid) + pc = pam_configurations.get(record_config_uid) or pam_utils.load_pam_typed_record( + vault, record_config_uid) if pc is None: skipped_records.append( [target_record.record_uid, target_record.title, 'PAM Configuration was deleted', 'Specify a configuration UID parameter [--config]']) return - if not isinstance(pc, vault_record.TypedRecord) or pc.version != 6: + if not isinstance(pc, vault_record.TypedRecord) or pc.record_type not in PAM_CONFIGURATIONS: skipped_records.append( [target_record.record_uid, target_record.title, 'PAM Configuration is invalid', 'Specify a configuration UID parameter [--config]']) @@ -702,13 +934,14 @@ def config_user(_dag, target_record, target_resource_uid, target_config_uid=None if not record_config_uid: if current_record_rotation: record_config_uid = current_record_rotation.configuration_uid - pc = vault.vault_data.load_record(record_config_uid) + pc = pam_configurations.get(record_config_uid) or pam_utils.load_pam_typed_record( + vault, record_config_uid) if pc is None: skipped_records.append( [target_record.record_uid, target_record.title, 'PAM Configuration was deleted', 'Specify a configuration UID parameter [--config]']) return - if not isinstance(pc, vault_record.TypedRecord) or pc.version != 6: + if not isinstance(pc, vault_record.TypedRecord) or pc.record_type not in PAM_CONFIGURATIONS: skipped_records.append( [target_record.record_uid, target_record.title, 'PAM Configuration is invalid', 'Specify a configuration UID parameter [--config]']) @@ -832,19 +1065,23 @@ def config_user(_dag, target_record, target_resource_uid, target_config_uid=None elif vault.vault_data.load_record(record_name): record_uids.add(record_name) else: - rs = folder_utils.try_resolve_path(context, record_name) - if rs is not None: - folder, record_title = rs - if record_title: - record_pattern = record_title - if isinstance(folder, vault_types.Folder): - folder_uids.add(folder.folder_uid) - elif isinstance(folder, list): - for f in folder: - if isinstance(f, vault_types.Folder): - folder_uids.add(f.folder_uid) - else: - logger.warning('Record \"%s\" not found. Skipping.', record_name) + nsf_uid = pam_utils.resolve_nsf_record_uid(vault, record_name) + if nsf_uid: + record_uids.add(nsf_uid) + else: + rs = folder_utils.try_resolve_path(context, record_name) + if rs is not None: + folder, record_title = rs + if record_title: + record_pattern = record_title + if isinstance(folder, vault_types.Folder): + folder_uids.add(folder.folder_uid) + elif isinstance(folder, list): + for f in folder: + if isinstance(f, vault_types.Folder): + folder_uids.add(f.folder_uid) + else: + logger.warning('Record \"%s\" not found. Skipping.', record_name) folder_name = kwargs.get('folder_name') if folder_name: @@ -891,10 +1128,13 @@ def add_folders(folder: vault_types.Folder): record_uids.add(record_uid) pam_records = [] - valid_record_types = ['pamDatabase', 'pamDirectory', 'pamMachine', 'pamUser', 'pamRemoteBrowser'] + valid_record_types = list(_PAM_ROTATION_RECORD_TYPES) for record_uid in record_uids: record = vault.vault_data.load_record(record_uid) + if not record: + record = pam_utils.load_nsf_typed_record(vault, record_uid) if record and isinstance(record, vault_record.TypedRecord) and record.record_type in valid_record_types: + pam_utils.attach_record_key(vault, record) pam_records.append(record) if len(pam_records) == 0: @@ -909,12 +1149,17 @@ def add_folders(folder: vault_types.Folder): criteria=None, record_type=PAM_CONFIGURATIONS, record_version=6): loaded = vault.vault_data.load_record(x.record_uid) if loaded and isinstance(loaded, vault_record.TypedRecord): + pam_utils.attach_record_key(vault, loaded) pam_configurations[x.record_uid] = loaded + for nsf_cfg in pam_utils.iter_nsf_pam_configurations(vault): + pam_configurations[nsf_cfg.record_uid] = nsf_cfg config_uid = kwargs.get('config') - cfg_rec = vault.vault_data.load_record(kwargs.get('config', None)) - if cfg_rec and cfg_rec.version == 6 and cfg_rec.record_uid in pam_configurations: - config_uid = cfg_rec.record_uid + if config_uid: + cfg_rec = pam_utils.load_pam_typed_record(vault, config_uid) + if cfg_rec and cfg_rec.record_type in PAM_CONFIGURATIONS: + pam_configurations[cfg_rec.record_uid] = cfg_rec + config_uid = cfg_rec.record_uid pam_config = None if config_uid: @@ -954,9 +1199,12 @@ def add_folders(folder: vault_types.Folder): pwd_complexity_rule_list = {} resource_uid = kwargs.get('resource') - res_rec = vault.vault_data.load_record(kwargs.get('resource', None)) - if res_rec and isinstance(res_rec, vault_record.TypedRecord): - resource_uid = res_rec.record_uid + if resource_uid: + res_rec = pam_utils.load_pam_typed_record(vault, resource_uid) + if res_rec and isinstance(res_rec, vault_record.TypedRecord): + resource_uid = res_rec.record_uid + elif not vault.vault_data.load_record(resource_uid): + raise base.CommandError(f'Resource "{resource_uid}" not found') skipped_header = ['record_uid', 'record_title', 'problem', 'description'] skipped_records = [] @@ -1073,37 +1321,21 @@ def execute(self, context: KeeperParams, **kwargs): logger.info( f"Gateway Uid: {(utils.base64_url_encode(rri.controllerUid) if rri.controllerUid else '-')}") - def is_resource_ok(resource_id, vault, configuration_uid): - if resource_id not in vault.vault_data._records: - return False - - configuration = vault.vault_data.load_record(configuration_uid) - if not isinstance(configuration, vault_record.TypedRecord): - return False - - field = configuration.get_typed_field('pamResources') - if not (field and isinstance(field.value, list) and len(field.value) == 1): - return False - - rv = field.value[0] - if not isinstance(rv, dict): - return False - - resources = rv.get('resourceRef') - return isinstance(resources, list) and resource_id in resources - if rri.resourceUid: - resource_id = utils.base64_url_encode(rri.resourceUid) - resource_ok = is_resource_ok(resource_id, vault, configuration_uid) - logger.info(f"Admin Resource Uid: {resource_id if resource_ok else 'FAIL'}") + logger.info( + f"Admin Resource Uid: {utils.base64_url_encode(rri.resourceUid)}") if rri.pwdComplexity: logger.info(f"Password Complexity: {rri.pwdComplexity}") try: - record = vault.vault_data._records[record_uid] - if record: + record = vault.vault_data._records.get(record_uid) + record_key = record.record_key if record else None + if not record_key: + typed = pam_utils.load_pam_typed_record(vault, record_uid) + record_key = getattr(typed, 'record_key', None) if typed else None + if record_key: complexity = crypto.decrypt_aes_v2(utils.base64_url_decode(rri.pwdComplexity), - record.record_key) + record_key) c = json.loads(complexity.decode()) logger.info(f"Password Complexity Data: " f"Length: {c.get('length')}; Lowercase: {c.get('lowercase')}; " @@ -1167,25 +1399,22 @@ def execute(self, context: KeeperParams, **kwargs): table = [] header = ['record_uid', 'title', 'record_type', 'script_uid', 'script_name', 'records', 'command'] - for rec in vault.vault_data.find_records(criteria=pattern, record_version=3, - record_type=('pamUser', 'pamDirectory')): - record = vault.vault_data.load_record(rec.record_uid) - if not isinstance(record, vault_record.TypedRecord): - continue + for record in _find_pam_script_records(vault, pattern): for field in (x for x in record.fields if x.type == 'script'): - value = field.get_default_value(dict) - if not value: - continue - file_ref = value.get('fileRef') - if not file_ref: - continue - file_record = vault.vault_data.load_record(file_ref) - if not file_record: - continue - records = value.get('recordRef') - command = value.get('command') - table.append([record.record_uid, record.title, record.record_type, file_record.record_uid, - file_record.title, records, command]) + for value in (field.value or []): + if not isinstance(value, dict): + continue + file_ref = value.get('fileRef') + if not file_ref: + continue + file_record = _load_script_file_record(vault, file_ref) + if not file_record: + continue + records = value.get('recordRef') + command = value.get('command') + table.append([record.record_uid, record.title, record.record_type, + getattr(file_record, 'record_uid', file_ref), + getattr(file_record, 'title', file_ref), records, command]) fmt = kwargs.get('format') if fmt != 'json': header = [report_utils.field_to_title(x) for x in header] @@ -1215,12 +1444,7 @@ def execute(self, context: KeeperParams, **kwargs): record_name = kwargs.get('record') if not record_name: raise base.CommandError('"record" argument is required') - records = list(vault.vault_data.find_records(criteria=record_name, record_version=3, record_type=('pamUser', 'pamDirectory'))) - if len(records) == 0: - raise base.CommandError(f'Record "{record_name}" not found') - if len(records) > 1: - raise base.CommandError(f'Record "{record_name}" is not unique. Use record UID.') - record = vault.vault_data.load_record(records[0].record_uid) + record = _get_unique_pam_script_record(vault, record_name) if not isinstance(record, vault_record.TypedRecord): raise base.CommandError(f'Record "{record.title}" is not a rotation record.') @@ -1253,14 +1477,14 @@ def execute(self, context: KeeperParams, **kwargs): record_refs = kwargs.get('add_credential') if isinstance(record_refs, list): for ref in record_refs: - if ref in vault.vault_data._records: - script_value['recordRef'].append(ref) + resolved = _resolve_script_credential_uid(vault, ref) + if resolved: + script_value['recordRef'].append(resolved) cmd = kwargs.get('script_command') if cmd: script_value['command'] = cmd - record_management.update_record(vault, record) - vault.sync_data = True + _save_pam_typed_record(vault, record) class PAMScriptEditCommand(base.ArgparseCommand): @@ -1293,31 +1517,14 @@ def execute(self, context: KeeperParams, **kwargs): if not script_name: raise base.CommandError('"script" argument is required') - records = list(vault.vault_data.find_records(criteria=record_name, record_version=3, record_type=('pamUser', 'pamDirectory'))) - if len(records) == 0: - raise base.CommandError(f'Record "{record_name}" not found') - if len(records) > 1: - raise base.CommandError(f'Record "{record_name}" is not unique. Use record UID.') - record = vault.vault_data.load_record(records[0].record_uid) + record = _get_unique_pam_script_record(vault, record_name) if not isinstance(record, vault_record.TypedRecord): raise base.CommandError(f'Record "{record.title}" is not a rotation record.') script_field = next((x for x in record.fields if x.type == 'script'), None) if script_field is None: raise base.CommandError(f'Record "{record.title}" has no rotation scripts.') - script_value = next((x for x in script_field.value if x.get('fileRef') == script_name), None) - if script_value is None: - s_name = script_name.casefold() - for x in script_field.value: - file_uid = x.get('fileRef') - file_record = vault.vault_data.load_record(file_uid) - if isinstance(file_record, vault_record.FileRecord): - if file_record.record_uid == s_name: - script_value = x - break - elif file_record.title.casefold() == s_name: - script_value = x - break + script_value = _find_script_value(vault, script_field, script_name) if not isinstance(script_value, dict): raise base.CommandError(f'Record "{record.title}" does not have script "{script_name}"') @@ -1329,11 +1536,17 @@ def execute(self, context: KeeperParams, **kwargs): refs.update(record_refs) remove_credential = kwargs.get('remove_credential') if isinstance(remove_credential, list) and remove_credential: - refs.difference_update(remove_credential) + for ref in remove_credential: + resolved = _resolve_script_credential_uid(vault, ref) or ref + refs.discard(resolved) + refs.discard(ref) modified = True add_credential = kwargs.get('add_credential') if isinstance(add_credential, list) and add_credential: - refs.update(add_credential) + for ref in add_credential: + resolved = _resolve_script_credential_uid(vault, ref) + if resolved: + refs.add(resolved) modified = True if modified: script_value['recordRef'] = list(refs) @@ -1345,8 +1558,7 @@ def execute(self, context: KeeperParams, **kwargs): if not modified: raise base.CommandError('Nothing to do') - record_management.update_record(vault, record) - vault.sync_data = True + _save_pam_typed_record(vault, record) class PAMScriptDeleteCommand(base.ArgparseCommand): @@ -1372,35 +1584,17 @@ def execute(self, context: KeeperParams, **kwargs): if not script_name: raise base.CommandError('"script" argument is required') - records = list(vault.vault_data.find_records(criteria=record_name, record_version=3, record_type=('pamUser', 'pamDirectory'))) - if len(records) == 0: - raise base.CommandError(f'Record "{record_name}" not found') - if len(records) > 1: - raise base.CommandError(f'Record "{record_name}" is not unique. Use record UID.') - record = vault.vault_data.load_record(records[0].record_uid) + record = _get_unique_pam_script_record(vault, record_name) if not isinstance(record, vault_record.TypedRecord): raise base.CommandError(f'Record "{record.title}" is not a rotation record.') script_field = next((x for x in record.fields if x.type == 'script'), None) if script_field is None: raise base.CommandError(f'Record "{record.title}" has no rotation scripts.') - script_value = next((x for x in script_field.value if x.get('fileRef') == script_name), None) - if script_value is None: - s_name = script_name.casefold() - for x in script_field.value: - file_uid = x.get('fileRef') - file_record = vault.vault_data.load_record(file_uid) - if isinstance(file_record, vault_record.FileRecord): - if file_record.record_uid == s_name: - script_value = x - break - elif file_record.title.casefold() == s_name: - script_value = x - break + script_value = _find_script_value(vault, script_field, script_name) if not isinstance(script_value, dict): raise base.CommandError(f'Record "{record.title}" does not have script "{script_name}"') script_field.value.remove(script_value) - record_management.update_record(vault, record) - vault.sync_data = True + _save_pam_typed_record(vault, record) diff --git a/keepercli-package/src/keepercli/commands/pam/pam_utils.py b/keepercli-package/src/keepercli/commands/pam/pam_utils.py new file mode 100644 index 00000000..2e3504f7 --- /dev/null +++ b/keepercli-package/src/keepercli/commands/pam/pam_utils.py @@ -0,0 +1,138 @@ +"""Shared PAM CLI helpers for classic vault + NSF typed-record access.""" + +from typing import Iterator, Optional + +from keepersdk.helpers.keeper_dag.constants import PAM_CONFIGURATIONS +from keepersdk.vault import ( + nsf_management, + record_management, + vault_extensions, + vault_online, + vault_record, +) + +from .. import base +from ...helpers import record_utils +from ...params import KeeperParams + + +def resolve_nsf_record_uid(vault: vault_online.VaultOnline, identifier: str) -> Optional[str]: + """Resolve an NSF record UID from a UID or exact title.""" + if not vault.nsf_data or not identifier: + return None + if vault.nsf_data.get_record(identifier): + return identifier + try: + return nsf_management.resolve_nsf_record_uid(vault, identifier) + except nsf_management.NsfError: + return None + + +def load_nsf_typed_record( + vault: vault_online.VaultOnline, record_uid: str) -> Optional[vault_record.TypedRecord]: + """Load an NSF record as TypedRecord (with record_key when available).""" + if not vault.nsf_data or not record_uid or not vault.nsf_data.get_record(record_uid): + return None + try: + meta = nsf_management.load_nsf_record_metadata(vault, record_uid) + except nsf_management.NsfError: + return None + typed = vault_record.TypedRecord() + typed.record_uid = record_uid + typed.load_record_data({ + 'type': meta.get('type') or '', + 'title': meta.get('title') or record_uid, + 'notes': meta.get('notes') or '', + 'fields': meta.get('fields') or [], + 'custom': meta.get('custom') or [], + }) + entry = vault.nsf_data.get_record(record_uid) + if entry and entry.record_key: + typed.record_key = entry.record_key + return typed + + +def is_nsf_record(vault: vault_online.VaultOnline, record_uid: str) -> bool: + return bool(record_uid and vault.nsf_data and vault.nsf_data.get_record(record_uid)) + + +def attach_record_key(vault: vault_online.VaultOnline, record: vault_record.TypedRecord) -> None: + """Ensure TypedRecord has record_key from classic or NSF storage when possible.""" + if getattr(record, 'record_key', None): + return + key = vault.vault_data.get_record_key(record.record_uid) + if not key and vault.nsf_data: + entry = vault.nsf_data.get_record(record.record_uid) + if entry: + key = entry.record_key + if key: + record.record_key = key + + +def load_typed_record( + context: KeeperParams, + identifier: str, +) -> Optional[vault_record.TypedRecord]: + """Load a TypedRecord from classic vault or NSF by UID/path/title.""" + vault = context.vault + if not vault or not identifier: + return None + loaded = vault.vault_data.load_record(identifier) + if isinstance(loaded, vault_record.TypedRecord): + return loaded + record_info = record_utils.try_resolve_single_record(identifier, context) + if record_info: + loaded = vault.vault_data.load_record(record_info.record_uid) + if isinstance(loaded, vault_record.TypedRecord): + return loaded + nsf_uid = resolve_nsf_record_uid(vault, identifier) + if nsf_uid: + return load_nsf_typed_record(vault, nsf_uid) + return None + + +def load_pam_typed_record( + vault: vault_online.VaultOnline, identifier: str) -> Optional[vault_record.TypedRecord]: + """Load a TypedRecord from classic vault or NSF by UID/title (vault-only).""" + if not identifier: + return None + loaded = vault.vault_data.load_record(identifier) + if loaded and isinstance(loaded, vault_record.TypedRecord): + attach_record_key(vault, loaded) + return loaded + nsf_uid = resolve_nsf_record_uid(vault, identifier) + if nsf_uid: + return load_nsf_typed_record(vault, nsf_uid) + return None + + +def save_typed_record(vault: vault_online.VaultOnline, record: vault_record.TypedRecord) -> None: + """Persist typed-record body changes via NSF or classic update.""" + if is_nsf_record(vault, record.record_uid): + schema = vault.vault_data.get_record_type_by_name(record.record_type) + record_data = vault_extensions.extract_typed_record_data(record, schema) + try: + nsf_management.update_nsf_record( + vault, + record.record_uid, + title=record.title, + record_type=record.record_type, + record_data=record_data, + request_sync=True, + ) + except nsf_management.NsfError as err: + raise base.CommandError(str(err)) from err + else: + record_management.update_record(vault, record) + vault.sync_down() + + +def iter_nsf_pam_configurations( + vault: vault_online.VaultOnline) -> Iterator[vault_record.TypedRecord]: + """Yield NSF PAM configuration TypedRecords.""" + if not vault.nsf_data: + return + for entry in vault.nsf_data.records(): + typed = load_nsf_typed_record(vault, entry.record_uid) + if typed and typed.record_type in PAM_CONFIGURATIONS: + yield typed diff --git a/keepercli-package/src/keepercli/commands/password_generate.py b/keepercli-package/src/keepercli/commands/password_generate.py index 16cbab89..0aa20eee 100644 --- a/keepercli-package/src/keepercli/commands/password_generate.py +++ b/keepercli-package/src/keepercli/commands/password_generate.py @@ -22,9 +22,11 @@ from . import base from .. import api +from keepersdk import generator + from ..helpers.password_utils import ( PasswordGenerationService, GenerationRequest, GeneratedPassword, - BreachStatus + BreachStatus, DEFAULT_PASSWORD_LENGTH, ) from ..params import KeeperParams @@ -86,6 +88,37 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser) -> None: help='Generate crypto-style strong password') special_group.add_argument('--recoveryphrase', dest='recoveryphrase', action='store_true', help='Generate 24-word recovery phrase') + + passphrase_group = parser.add_argument_group('Keeper Passphrase') + passphrase_group.add_argument( + '--passphrase', dest='passphrase', action='store_true', + help='Generate a vault-style passphrase from the EFF word list', + ) + passphrase_group.add_argument( + '--pp-separator', '-pps', dest='pp_separator', action='store', + help=( + f'Word separator (single character, or "space"). Allowed: ' + f'{generator.PASSPHRASE_SEPARATOR_HELP}. ' + 'Overrides enterprise policy for this command.' + ), + ) + passphrase_group.add_argument( + '--pp-capitalize', '-ppc', dest='pp_capitalize', action='store_true', + help='Capitalize the first letter of each word. Overrides enterprise policy for this command.', + ) + passphrase_group.add_argument( + '--pp-no-capitalize', dest='pp_capitalize', action='store_false', + help='Do not capitalize words. Overrides enterprise policy for this command.', + ) + passphrase_group.add_argument( + '--pp-number', '-ppn', dest='pp_number', action='store_true', + help='Append a digit (0-9) to the first word only. Overrides enterprise policy for this command.', + ) + passphrase_group.add_argument( + '--pp-no-number', dest='pp_number', action='store_false', + help='Do not append a digit to the first word. Overrides enterprise policy for this command.', + ) + passphrase_group.set_defaults(pp_capitalize=None, pp_number=None) diceware_group = parser.add_argument_group('Diceware Options') diceware_group.add_argument('--dice-rolls', '-dr', dest='dice_rolls', type=int, @@ -139,13 +172,17 @@ def _generate_passwords(self, service: PasswordGenerationService, request: Gener def _create_generation_request(self, **kwargs) -> GenerationRequest: """Create a GenerationRequest from command line arguments.""" count = self._validate_count(kwargs.get('number', 1)) - length = self._validate_length(kwargs.get('length', 20)) + length = self._validate_length(kwargs.get('length', DEFAULT_PASSWORD_LENGTH)) algorithm = self._determine_algorithm(kwargs) symbols, digits, uppercase, lowercase = self._validate_complexity_parameters(kwargs) rules = self._validate_rules(kwargs.get('rules')) dice_rolls = self._validate_dice_rolls(kwargs.get('dice_rolls')) - + pp_separator = self._parse_passphrase_separator(kwargs.get('pp_separator')) + passphrase_word_count = None + if algorithm == 'passphrase' and length != DEFAULT_PASSWORD_LENGTH: + passphrase_word_count = length + return GenerationRequest( length=length, count=count, @@ -158,6 +195,10 @@ def _create_generation_request(self, **kwargs) -> GenerationRequest: dice_rolls=dice_rolls, delimiter=kwargs.get('delimiter', ' '), word_list_file=kwargs.get('word_list'), + pp_separator=pp_separator, + pp_capitalize=kwargs.get('pp_capitalize'), + pp_number=kwargs.get('pp_number'), + passphrase_word_count=passphrase_word_count, enable_breach_scan=not kwargs.get('no_breachwatch', False) # max_breach_attempts uses GenerationRequest default value ) @@ -182,12 +223,24 @@ def _determine_algorithm(self, kwargs: Dict[str, Any]) -> str: """Determine password generation algorithm from arguments.""" if kwargs.get('crypto'): return 'crypto' + elif kwargs.get('passphrase'): + return 'passphrase' elif kwargs.get('recoveryphrase'): return 'recovery' elif kwargs.get('dice_rolls'): return 'diceware' else: return 'random' # default + + @staticmethod + def _parse_passphrase_separator(pp_separator: Optional[str]) -> Optional[str]: + """Parse and validate passphrase separator from CLI.""" + if not isinstance(pp_separator, str) or not pp_separator.strip(): + return None + separator, error = generator._parse_passphrase_separator_token(pp_separator.strip()) + if error: + raise base.CommandError(error) + return separator def _validate_complexity_parameters(self, kwargs: Dict[str, Any]) -> tuple: """Validate complexity parameters (symbols, digits, uppercase, lowercase).""" diff --git a/keepercli-package/src/keepercli/commands/record_edit.py b/keepercli-package/src/keepercli/commands/record_edit.py index 156eebaa..d6d295e9 100644 --- a/keepercli-package/src/keepercli/commands/record_edit.py +++ b/keepercli-package/src/keepercli/commands/record_edit.py @@ -6,7 +6,7 @@ import itertools import json import os -from typing import Iterable, Optional, List, Any, Sequence, Union, Dict +from typing import Iterable, Optional, List, Any, Sequence, Union, Dict, Tuple from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ed25519 @@ -14,6 +14,7 @@ from keepersdk.vault import (record_types, typed_field_utils, vault_record, attachment, record_facades, one_time_share, record_management, vault_online, vault_data, vault_types, vault_utils, vault_extensions, share_management_utils) from keepersdk import crypto, generator +from keepersdk.enterprise import enterprise_team_management from . import base, enterprise_utils from .. import prompt_utils, api, constants @@ -118,8 +119,9 @@ class ParsedFieldValue: Value Field type Description Example ==================== =============== =================== ============== $GEN:[alg],[n] password Generates a random password $GEN:dice,5 - Default algorith is rand alg: [rand | dice | crypto] + Default algorith is rand alg: [rand | dice | crypto | passphrase] Optional: password length + passphrase: $GEN:passphrase[,word_count][,separator][,capitalize][,number] $GEN oneTimeCode Generates TOTP URL $GEN:[alg,][enc] keyPair Generates a key pair and $GEN:ec,enc optional passcode alg: [rsa | ec | ed25519], enc @@ -183,8 +185,13 @@ def assign_legacy_fields(self, record: vault_record.PasswordRecord, fields: List if parsed_field.type == 'login': record.login = parsed_field.value elif parsed_field.type == 'password': + action_params.clear() if self.is_generate_value(parsed_field.value, action_params): - record.password = self.generate_password(action_params) + password, gen_error = self.generate_password(action_params) + if gen_error: + self.on_warning(gen_error) + elif password is not None: + record.password = password else: record.password = parsed_field.value elif parsed_field.type == 'url': @@ -260,22 +267,49 @@ def generate_key_pair(key_type: str, passphrase: str) -> Dict: } @staticmethod - def generate_password(parameters: Optional[Sequence[str]]=None) -> str: + def generate_password(parameters: Optional[Sequence[str]] = None, + policy: Optional[dict] = None) -> Tuple[Optional[str], Optional[str]]: + algorithm, error = generator.resolve_gen_password_algorithm( + parameters if isinstance(parameters, (tuple, list, set)) else None) + if error: + return None, error + + length = None if isinstance(parameters, (tuple, list, set)): - algorithm = next((x for x in parameters if x in ('rand', 'dice', 'crypto')), 'rand') length = next((x for x in parameters if x.isnumeric()), None) if isinstance(length, str) and len(length) > 0: try: length = int(length) except ValueError: pass - else: - algorithm = 'rand' - length = None gen: generator.PasswordGenerator if algorithm == 'crypto': gen = generator.CryptoPassphraseGenerator() + elif algorithm == 'passphrase': + pp_opts, pp_error = generator.parse_passphrase_gen_parameters(parameters) + if pp_error: + return None, pp_error + if policy and policy.get('passphrase-allow') is False: + logger.warning( + 'Passphrase generation is disabled by enterprise policy; using random password.') + fallback_length = pp_opts.word_count or length + if isinstance(fallback_length, int): + if fallback_length < 4: + fallback_length = 4 + elif fallback_length > 200: + fallback_length = 200 + else: + fallback_length = 20 + gen = generator.KeeperPasswordGenerator(length=fallback_length) + else: + gen = generator.KeeperPassphraseGenerator.create_with_options( + policy, + word_count=pp_opts.word_count if pp_opts.word_count is not None else length, + separator=pp_opts.separator, + capitalize=pp_opts.capitalize, + append_number=pp_opts.append_number, + ) elif algorithm == 'dice': if isinstance(length, int): if length < 1: @@ -294,7 +328,7 @@ def generate_password(parameters: Optional[Sequence[str]]=None) -> str: else: length = 20 gen = generator.KeeperPasswordGenerator(length=length) - return gen.generate() + return gen.generate(), None @staticmethod def generate_totp_url() -> str: @@ -456,12 +490,20 @@ def assign_typed_fields(self, record: vault_record.TypedRecord, fields: List[Par value: Any = None if self.is_generate_value(parsed_field.value, action_params): if record_field.type == 'password': - value = self.generate_password(action_params) + value, gen_error = self.generate_password(action_params) + if gen_error: + self.on_warning(gen_error) + value = None elif record_field.type in ('oneTimeCode', 'otp'): value = self.generate_totp_url() elif record_field.type in ('keyPair', 'privateKey'): should_encrypt = 'enc' in action_params - passphrase = self.generate_password() if should_encrypt else '' + passphrase = '' + if should_encrypt: + passphrase, gen_error = self.generate_password() + if gen_error: + self.on_warning(gen_error) + continue key_type = next((x for x in action_params if x in ('rsa', 'ec', 'ed25519')), 'rsa') value = self.generate_key_pair(key_type, passphrase) if passphrase: @@ -1116,7 +1158,7 @@ def execute(self, context: KeeperParams, **kwargs): else: raise base.CommandError('The given UID or title is not a valid folder') elif team: - team = self._find_team(context, team) + team = self._find_team(context, team, include_share_objects=True) if team: target_object = ('team', team) else: @@ -1194,13 +1236,32 @@ def _find_folder(self, vault: vault_online.VaultOnline, uid_or_title: str): None ) - def _find_team(self, context: KeeperParams, uid_or_title: str): - """Find a team by UID or name.""" - if not context.enterprise_data: - raise base.CommandError('You must be an enterprise admin to use this command') - - team = enterprise_utils.TeamUtils.resolve_single_team(context.enterprise_data, uid_or_title) - return team + def _find_team( + self, + context: KeeperParams, + uid_or_title: str, + *, + include_share_objects: bool = False, + ): + """Find a team by UID or name using the SDK.""" + is_admin = bool( + context.auth + and context.auth.auth_context + and context.auth.auth_context.is_enterprise_admin + ) + try: + return enterprise_team_management.get_team( + uid_or_title, + enterprise_data=context.enterprise_data if is_admin else None, + vault_data_obj=context.vault.vault_data if context.vault else None, + auth=context.auth, + vault=context.vault, + is_enterprise_admin=is_admin, + include_share_objects=include_share_objects, + fetch_live_members=True, + ) + except enterprise_team_management.EnterpriseTeamManagementError: + return None def _display_object(self, context: KeeperParams, target_object, output_format: str, unmask: bool): """Display the target object in the specified format.""" @@ -1241,12 +1302,28 @@ def _display_folder(self, vault: vault_online.VaultOnline, folder, output_format else: # detail format self._display_folder_detail(vault, folder.folder_uid) - def _display_team(self, context: KeeperParams, team, output_format: str): + def _display_team(self, context: KeeperParams, team_info: enterprise_team_management.EnterpriseTeamInfo, output_format: str): """Display a team in the specified format.""" if output_format == 'json': - self._display_team_json(context, team.team_uid) - else: # detail format - self._display_team_detail(context, team.team_uid) + self._display_team_json(team_info) + elif not team_info.is_member: + self._display_non_member_team(context, team_info) + else: + self._display_team_detail(team_info) + + def _display_non_member_team( + self, + context: KeeperParams, + team_info: enterprise_team_management.EnterpriseTeamInfo, + ): + """Display team info when the logged-in user is not a team member.""" + username = context.auth.auth_context.username if context.auth else '' + logger.info('') + logger.info('User {} does not belong to team {}'.format(username, team_info.team_name)) + logger.info('') + logger.info('{0:>20s}: {1:<20s}'.format('Team UID', team_info.team_uid)) + logger.info('{0:>20s}: {1}'.format('Name', team_info.team_name)) + logger.info('') def _display_record_json(self, vault: vault_online.VaultOnline, uid: str, unmask: bool = False): """Display record information in JSON format.""" @@ -1398,18 +1475,52 @@ def _display_folder_json(self, vault: vault_online.VaultOnline, uid: str): } logger.info(json.dumps(output, indent=2)) - def _display_team_json(self, context: KeeperParams, uid: str): + def _display_team_json(self, team_info: enterprise_team_management.EnterpriseTeamInfo): """Display team information in JSON format.""" - team = context.enterprise_data.teams.get_entity(uid) - user = enterprise_utils.UserUtils.resolve_single_user(context.enterprise_data, context.auth.auth_context.username) - team_users = {x.team_uid for x in context.enterprise_data.team_users.get_links_by_object(user.enterprise_user_id)} - if team.team_uid not in team_users: - logger.info(f'User {context.auth.auth_context.username} does not belong to team {team.name}') - output = { - 'Team UID:': uid, - 'Name:': team.name - } - logger.info(json.dumps(output, indent=2)) + logger.info(json.dumps(team_info.to_dict(), indent=2)) + + def _display_team_detail(self, team_info: enterprise_team_management.EnterpriseTeamInfo): + """Display team information in detailed format.""" + logger.info('') + logger.info('{0:>20s}: {1:<20s}'.format('Team UID', team_info.team_uid)) + logger.info('{0:>20s}: {1}'.format('Name', team_info.team_name)) + if team_info.node_name: + logger.info('{0:>20s}: {1}'.format('Node', team_info.node_name)) + logger.info('{0:>20s}: {1}'.format('Access Level', team_info.access_level)) + logger.info('{0:>20s}: {1}'.format('Restrict Edit', team_info.restrict_edit)) + logger.info('{0:>20s}: {1}'.format('Restrict View', team_info.restrict_view)) + logger.info('{0:>20s}: {1}'.format('Restrict Share', team_info.restrict_share)) + + if team_info.team_roles: + logger.info('{0:>20s}: {1}'.format('Role(s)', ', '.join(x.role_name for x in team_info.team_roles))) + + if team_info.team_users: + logger.info('{0:>20s}: {1}'.format('User(s)', ', '.join(x.username for x in team_info.team_users))) + + if team_info.queued_team_users: + logger.info( + '{0:>20s}: {1}'.format( + 'Queued User(s)', + ', '.join(x.username for x in team_info.queued_team_users), + ) + ) + + if team_info.members: + logger.info('') + logger.info('{0:>20s} {1:<40s} {2:<40s} {3}'.format( + 'Enterprise User ID', 'Email', 'Enterprise Username', 'Share Admin' + )) + for member in team_info.members: + logger.info('{0:>20d} {1:<40s} {2:<40s} {3}'.format( + member.enterprise_user_id, + member.email, + member.enterprise_username, + 'Yes' if member.is_share_admin else 'No', + )) + elif not team_info.team_users: + logger.info('') + logger.info('No team members found.') + logger.info('') def _display_record_detail(self, vault: vault_online.VaultOnline, uid: str, unmask: bool): """Display record information in detailed format.""" @@ -1631,26 +1742,6 @@ def _display_folder_detail(self, vault: vault_online.VaultOnline, uid: str): if folder.folder_type == 'shared_folder_folder': logger.info('{0:>20s}: {1:<20s}'.format('Shared Folder UID', folder.folder_scope_uid)) - def _display_team_detail(self, context: KeeperParams, uid: str): - """Display team information in detailed format.""" - team = context.enterprise_data.teams.get_entity(uid) - - user = enterprise_utils.UserUtils.resolve_single_user(context.enterprise_data, context.auth.auth_context.username) - team_users = {x.team_uid for x in context.enterprise_data.team_users.get_links_by_object(user.enterprise_user_id)} - team_user = True - if team.team_uid not in team_users: - logger.info(f'User {context.auth.auth_context.username} does not belong to team {team.name}') - team_user = False - - logger.info('') - logger.info('{0:>20s}: {1:<20s}'.format('Team UID', team.team_uid)) - logger.info('{0:>20s}: {1}'.format('Name', team.name)) - if team_user: - logger.info('{0:>20s}: {1}'.format('Restrict Edit', team.restrict_edit)) - logger.info('{0:>20s}: {1}'.format('Restrict View', team.restrict_view)) - logger.info('{0:>20s}: {1}'.format('Restrict Share', team.restrict_share)) - logger.info('') - def _display_record_password(self, vault: vault_online.VaultOnline, uid: str): """Display only the password field of a record.""" record_data = vault.vault_data.load_record(record_uid=uid) @@ -1976,4 +2067,6 @@ def _display_team_details(self, teams: Iterable[vault_types.TeamInfo], context: """Display detailed information for teams.""" get_command = RecordGetCommand() for team in teams: - get_command._display_team_detail(context=context, uid=team.team_uid) + team_info = get_command._find_team(context, team.team_uid) + if team_info is not None: + get_command._display_team(context, team_info, 'detail') diff --git a/keepercli-package/src/keepercli/commands/secrets_manager.py b/keepercli-package/src/keepercli/commands/secrets_manager.py index 6d7d0771..6261c9fa 100644 --- a/keepercli-package/src/keepercli/commands/secrets_manager.py +++ b/keepercli-package/src/keepercli/commands/secrets_manager.py @@ -531,7 +531,8 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser) -> None: ) parser.add_argument( '--secret', '-s', type=str, required=False, - help='Record UID(s) - space separated (e.g., "uid1 uid2 uid3")' + help='Record/folder UID(s) or NSF folder/record name — space separated ' + '(classic records, classic shared folders, NSF folders, NSF records)' ) def execute(self, context: KeeperParams, **kwargs) -> None: diff --git a/keepercli-package/src/keepercli/commands/vault_record.py b/keepercli-package/src/keepercli/commands/vault_record.py index 4557d6c3..365c3ae0 100644 --- a/keepercli-package/src/keepercli/commands/vault_record.py +++ b/keepercli-package/src/keepercli/commands/vault_record.py @@ -9,8 +9,7 @@ from .. import api, prompt_utils from ..params import KeeperParams from ..helpers import folder_utils, report_utils -from keepersdk import utils -from keepersdk.proto import enterprise_pb2 +from keepersdk.enterprise import enterprise_team_management from keepersdk.vault import record_management, vault_data, vault_types, vault_record, vault_utils, share_management_utils @@ -310,14 +309,11 @@ def get_enterprise_teams(): def fetch_members(team_uid: str) -> List[str]: if not allow_fetch: return [] - rq = enterprise_pb2.GetTeamMemberRequest() - rq.teamUid = utils.base64_url_decode(team_uid) - rs = context.vault.keeper_auth.execute_auth_rest( - rest_endpoint='vault/get_team_members', - request=rq, - response_type=enterprise_pb2.GetTeamMemberResponse - ) - return [x.email for x in rs.enterpriseUser] + auth = context.auth or (context.vault.keeper_auth if context.vault else None) + if auth is None: + return [] + members = enterprise_team_management.get_team_members(auth, team_uid) + return [x.email for x in members if x.email] enterprise_teams = get_enterprise_teams() for t in teams: @@ -325,6 +321,7 @@ def fetch_members(team_uid: str) -> List[str]: return teams + class ShortcutCommand(base.GroupCommand): def __init__(self): super(ShortcutCommand, self).__init__('Manage record shortcuts') diff --git a/keepercli-package/src/keepercli/helpers/ksm_utils.py b/keepercli-package/src/keepercli/helpers/ksm_utils.py index 56640cac..57d1f9ae 100644 --- a/keepercli-package/src/keepercli/helpers/ksm_utils.py +++ b/keepercli-package/src/keepercli/helpers/ksm_utils.py @@ -26,5 +26,6 @@ def print_shared_secrets_info(shared_secrets: List[ksm.SharedSecretsInfo]) -> No rows = [ [secrets.type, secrets.uid, secrets.name, secrets.permissions] for secrets in shared_secrets + if secrets is not None ] report_utils.dump_report_data(rows, shares_table_fields, fmt='table') \ No newline at end of file diff --git a/keepercli-package/src/keepercli/helpers/password_utils.py b/keepercli-package/src/keepercli/helpers/password_utils.py index a9bdb229..5bb3d40d 100644 --- a/keepercli-package/src/keepercli/helpers/password_utils.py +++ b/keepercli-package/src/keepercli/helpers/password_utils.py @@ -125,6 +125,11 @@ class GenerationRequest: dice_rolls: Optional[int] = None delimiter: str = ' ' word_list_file: Optional[str] = None + + pp_separator: Optional[str] = None + pp_capitalize: Optional[bool] = None + pp_number: Optional[bool] = None + passphrase_word_count: Optional[int] = None enable_breach_scan: bool = True max_breach_attempts: int = BREACHWATCH_MAX @@ -278,6 +283,14 @@ def _create_generator(self, request: GenerationRequest) -> generator.PasswordGen word_list_file=request.word_list_file, delimiter=request.delimiter ) + elif algorithm == 'passphrase': + return generator.KeeperPassphraseGenerator.create_with_options( + None, + word_count=request.passphrase_word_count, + separator=request.pp_separator, + capitalize=request.pp_capitalize, + append_number=request.pp_number, + ) else: if request.rules and all(i is None for i in (request.symbols, request.digits, request.uppercase, request.lowercase)): kpg = generator.KeeperPasswordGenerator.create_from_rules(request.rules, request.length) diff --git a/keepercli-package/src/keepercli/params.py b/keepercli-package/src/keepercli/params.py index bde54e08..8a89d093 100644 --- a/keepercli-package/src/keepercli/params.py +++ b/keepercli-package/src/keepercli/params.py @@ -227,6 +227,8 @@ def set_auth(self, value: keeper_auth.KeeperAuth, *, self._keeper_config.get_connection, enterprise_id) self._enterprise_loader = enterprise_loader.EnterpriseLoader(self._auth, enterprise_storage, tree_key=tree_key) self.enterprise_down() + # Persist rotations from the vault sync into PAM sqlite now that the plugin exists. + self.refresh_record_rotations() @property def enterprise_loader(self) -> enterprise_types.IEnterpriseLoader: @@ -256,21 +258,80 @@ def vault_down(self): self.refresh_record_rotations() def refresh_record_rotations(self) -> None: - """Reload vault ``recordRotations`` into :attr:`pam_plugin` after a vault sync (enterprise admins only).""" - if not self._auth or not self._auth.auth_context.is_enterprise_admin: - return - if self._enterprise_loader is None: + if not self._auth: return try: - self.pam_plugin.sync_record_rotations_from_vault() + if self._vault is not None: + self._merge_nsf_rotation_chunks_into_vault_cache() + if (not self._auth.auth_context.is_enterprise_admin + or self._enterprise_loader is None): + return + replace_all = False + rows = [] + if self._vault is not None: + replace_all = self._vault.consume_rotations_cleared() + from keepersdk.plugins.pam import pam_storage as pam_stor + rows = [ + pam_stor.PamRecordRotation( + record_uid=info.record_uid, + revision=info.revision, + configuration_uid=info.configuration_uid, + schedule=info.schedule, + pwd_complexity=info.pwd_complexity, + disabled=info.disabled, + resource_uid=info.resource_uid, + last_rotation=info.last_rotation, + last_rotation_status=info.last_rotation_status, + ) + for info in self._vault.record_rotation_cache.values() + ] + self.pam_plugin.merge_record_rotations(rows, replace_all=replace_all) except Exception as e: keepersdk_utils.get_logger().warning('refresh_record_rotations failed: %s', e) + def _merge_nsf_rotation_chunks_into_vault_cache(self) -> None: + """Secondary source: NSF list_chunks for recordRotationData (already written by sync).""" + if not self._vault: + return + try: + nsf_storage = self._vault.nsf + except Exception: + return + if nsf_storage is None: + return + from keepersdk.vault import nsf_sync + from keepersdk.plugins.pam import pam_storage as pam_stor + + for item in nsf_sync.load_list_chunks(nsf_storage, nsf_sync.CHUNK_RECORD_ROTATION): + if not isinstance(item, dict): + continue + row = pam_stor.pam_record_rotation_from_nsf_dict(item) + if not row or not row.record_uid: + continue + if row.record_uid in self._vault.record_rotation_cache: + continue + self._vault.record_rotation_cache[row.record_uid] = pam_types.PamRecordRotationInfo( + record_uid=row.record_uid, + revision=row.revision, + configuration_uid=row.configuration_uid, + schedule=row.schedule, + pwd_complexity=row.pwd_complexity, + disabled=row.disabled, + resource_uid=row.resource_uid, + last_rotation=row.last_rotation, + last_rotation_status=row.last_rotation_status, + ) + def get_record_rotation(self, record_uid: str) -> Optional[pam_types.PamRecordRotationInfo]: - """Rotation metadata for ``record_uid`` from the last vault / PAM rotation sync (or ``None``).""" - if not self._auth or not self._auth.auth_context.is_enterprise_admin: + """Rotation metadata for ``record_uid`` from vault sync cache / PAM sqlite (or ``None``).""" + if not self._auth or not record_uid: return None - if self._enterprise_loader is None: + if self._vault is not None: + info = self._vault.get_record_rotation(record_uid) + if info is not None: + return info + if (not self._auth.auth_context.is_enterprise_admin + or self._enterprise_loader is None): return None return self.pam_plugin.record_rotations.get_entity(record_uid) diff --git a/keepercli-package/src/keepercli/register_commands.py b/keepercli-package/src/keepercli/register_commands.py index b7e87e23..20138886 100644 --- a/keepercli-package/src/keepercli/register_commands.py +++ b/keepercli-package/src/keepercli/register_commands.py @@ -23,9 +23,9 @@ def register_commands(commands: base.CliCommands, scopes: Optional[base.CommandS commands.register_command('biometric', BiometricCommand(), base.CommandScope.Account) commands.register_command('logout', account_commands.LogoutCommand(), base.CommandScope.Account) commands.register_command('this-device', account_commands.ThisDeviceCommand(), base.CommandScope.Account) - commands.register_command('device-list', device_management.DeviceListCommand(), base.CommandScope.Account) - commands.register_command('device-action', device_management.DeviceActionCommand(), base.CommandScope.Account) - commands.register_command('device-rename', device_management.DeviceRenameCommand(), base.CommandScope.Account) + commands.register_command('device-list', device_management.DeviceListCommand(), base.CommandScope.DeviceManagement) + commands.register_command('device-action', device_management.DeviceActionCommand(), base.CommandScope.DeviceManagement) + commands.register_command('device-rename', device_management.DeviceRenameCommand(), base.CommandScope.DeviceManagement) commands.register_command('whoami', account_commands.WhoamiCommand(), base.CommandScope.Account) commands.register_command('reset-password', account_commands.ResetPasswordCommand(), base.CommandScope.Account) commands.register_command('2fa', two_fa.TwoFaCommand(), base.CommandScope.Account) @@ -127,8 +127,8 @@ def register_commands(commands: base.CliCommands, scopes: Optional[base.CommandS commands.register_command('download-membership', importer_commands.DownloadMembershipCommand(), base.CommandScope.Enterprise) commands.register_command('apply-membership', importer_commands.ApplyMembershipCommand(), base.CommandScope.Enterprise) commands.register_command('device-approve', enterprise_user.EnterpriseDeviceApprovalCommand(), base.CommandScope.Enterprise) - commands.register_command('device-admin-list', device_management.DeviceAdminListCommand(), base.CommandScope.Enterprise) - commands.register_command('device-admin-action', device_management.DeviceAdminActionCommand(), base.CommandScope.Enterprise) + commands.register_command('device-admin-list', device_management.DeviceAdminListCommand(), base.CommandScope.DeviceManagement) + commands.register_command('device-admin-action', device_management.DeviceAdminActionCommand(), base.CommandScope.DeviceManagement) commands.register_command('pedm', pedm_admin.PedmCommand(), base.CommandScope.Enterprise) commands.register_command('msp-down', msp.MspDownCommand(), base.CommandScope.Enterprise, 'md') commands.register_command('msp-info', msp.MspInfoCommand(), base.CommandScope.Enterprise, 'mi') diff --git a/keepersdk-package/mypy.ini b/keepersdk-package/mypy.ini index 4839e50a..f14dcf8b 100644 --- a/keepersdk-package/mypy.ini +++ b/keepersdk-package/mypy.ini @@ -1,7 +1,7 @@ [mypy] warn_no_return = False files = src/ -python_version = 3.9 +python_version = 3.10 [mypy-keepersdk.proto.*] ignore_errors = True diff --git a/keepersdk-package/setup.cfg b/keepersdk-package/setup.cfg index 6d933c6b..5b313776 100644 --- a/keepersdk-package/setup.cfg +++ b/keepersdk-package/setup.cfg @@ -15,25 +15,29 @@ classifiers = Operating System :: OS Independent Natural Language :: English Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 + Programming Language :: Python :: 3.14 Topic :: Security keywords = security, password [options] -python_requires = >=3.8 +python_requires = >=3.10 package_dir = = src include_package_data = True install_requires = - attrs>=23.1.0 - requests>=2.32.2 - cryptography>=45.0.1 - protobuf>=5.28.3 - websockets>=13.1 - fido2>=2.0.0; python_version>='3.10' - email-validator>=2.0.0 - pydantic>=2.6.4; python_version>='3.8' - google-api-core>=2.16.0 + attrs>=25.4.0 + requests>=2.32.5 + cryptography>=45.0.7 + protobuf>=5.29.5 + websockets>=14.2 + fido2>=2.1.0 + email-validator>=2.3.0 + pydantic>=2.12.5 + google-api-core>=2.25.2 [options.package_data] diff --git a/keepersdk-package/src/keepersdk/__init__.py b/keepersdk-package/src/keepersdk/__init__.py index f807aa9f..864bb3c7 100644 --- a/keepersdk-package/src/keepersdk/__init__.py +++ b/keepersdk-package/src/keepersdk/__init__.py @@ -10,6 +10,6 @@ # from . import background -__version__ = '1.2.2' +__version__ = '1.2.4' background.init() diff --git a/keepersdk-package/src/keepersdk/authentication/device_management.py b/keepersdk-package/src/keepersdk/authentication/device_management.py index 871d4114..7eb03229 100644 --- a/keepersdk-package/src/keepersdk/authentication/device_management.py +++ b/keepersdk-package/src/keepersdk/authentication/device_management.py @@ -12,7 +12,7 @@ from datetime import datetime from typing import Callable, List, Optional, Tuple -from .. import utils +from .. import errors, utils from ..proto import APIRequest_pb2, DeviceManagement_pb2 from . import keeper_auth @@ -24,6 +24,33 @@ URL_DEVICE_ADMIN_LIST = 'dm/device_admin_list' URL_DEVICE_ADMIN_ACTION = 'dm/device_admin_action' +DEVICE_FEATURE_UNAVAILABLE_MESSAGE = ( + 'Notice: This feature is not in production yet. It will be available soon.' +) + + +def is_device_api_unavailable(error: errors.KeeperApiError) -> bool: + return error.result_code in (404, '404', 'invalid_path_or_method') + + +def _execute_device_rest( + auth: keeper_auth.KeeperAuth, + rest_endpoint: str, + request, + response_type, +): + """Call a device-management REST endpoint with Commander-aligned unavailable-API handling.""" + try: + return auth.execute_auth_rest( + rest_endpoint=rest_endpoint, + request=request, + response_type=response_type, + ) + except errors.KeeperApiError as exc: + if is_device_api_unavailable(exc): + raise ValueError(DEVICE_FEATURE_UNAVAILABLE_MESSAGE) from exc + raise + @dataclass(frozen=True) class UserDeviceInfo: @@ -92,10 +119,11 @@ def rename_user_device( dr.encryptedDeviceToken = device_token dr.deviceNewName = sanitized - rs = auth.execute_auth_rest( - rest_endpoint=URL_DEVICE_USER_RENAME, - request=rq, - response_type=DeviceManagement_pb2.DeviceRenameResponse, + rs = _execute_device_rest( + auth, + URL_DEVICE_USER_RENAME, + rq, + DeviceManagement_pb2.DeviceRenameResponse, ) if not rs or not rs.deviceRenameResult: raise ValueError('No response returned from device rename') @@ -161,7 +189,7 @@ def list_admin_devices( """ if not enterprise_user_ids: raise ValueError( - 'Enterprise User ID is required. You can get enterprise user IDs by running: ei --users' + 'Enterprise User ID is required. You can get enterprise user IDs by running: enterprise-info user' ) for user_id in enterprise_user_ids: _validate_enterprise_user_id(user_id) @@ -304,11 +332,56 @@ def _validate_link_unlink_identifiers(device_identifiers: List[str]) -> None: raise ValueError('At least two device identifiers are required for link/unlink') +def lock_admin_user_devices( + auth: keeper_auth.KeeperAuth, + enterprise_user_id: int, + device_identifiers: List[str], +) -> List[str]: + """Lock devices for all users and linked devices; log out all users (enterprise admin).""" + return _execute_admin_device_action( + auth, enterprise_user_id, device_identifiers, DeviceManagement_pb2.DA_LOCK + ) + + +def unlock_admin_user_devices( + auth: keeper_auth.KeeperAuth, + enterprise_user_id: int, + device_identifiers: List[str], +) -> List[str]: + """Unlock devices and linked devices for the enterprise user (enterprise admin).""" + return _execute_admin_device_action( + auth, enterprise_user_id, device_identifiers, DeviceManagement_pb2.DA_UNLOCK + ) + + +def account_lock_admin_user_devices( + auth: keeper_auth.KeeperAuth, + enterprise_user_id: int, + device_identifiers: List[str], +) -> List[str]: + """Account-lock devices for the enterprise user only (enterprise admin).""" + return _execute_admin_device_action( + auth, enterprise_user_id, device_identifiers, DeviceManagement_pb2.DA_DEVICE_ACCOUNT_LOCK + ) + + +def account_unlock_admin_user_devices( + auth: keeper_auth.KeeperAuth, + enterprise_user_id: int, + device_identifiers: List[str], +) -> List[str]: + """Account-unlock devices for the enterprise user (enterprise admin).""" + return _execute_admin_device_action( + auth, enterprise_user_id, device_identifiers, DeviceManagement_pb2.DA_DEVICE_ACCOUNT_UNLOCK + ) + + def _fetch_devices(auth: keeper_auth.KeeperAuth) -> List[DeviceManagement_pb2.Device]: - rs = auth.execute_auth_rest( - rest_endpoint=URL_DEVICE_USER_LIST, - request=None, - response_type=DeviceManagement_pb2.DeviceUserResponse, + rs = _execute_device_rest( + auth, + URL_DEVICE_USER_LIST, + None, + DeviceManagement_pb2.DeviceUserResponse, ) if not rs: return [] @@ -351,10 +424,11 @@ def _fetch_admin_device_entries( ) -> List[Tuple[int, DeviceManagement_pb2.Device]]: rq = DeviceManagement_pb2.DeviceAdminRequest() rq.enterpriseUserIds.extend(enterprise_user_ids) - rs = auth.execute_auth_rest( - rest_endpoint=URL_DEVICE_ADMIN_LIST, - request=rq, - response_type=DeviceManagement_pb2.DeviceAdminResponse, + rs = _execute_device_rest( + auth, + URL_DEVICE_ADMIN_LIST, + rq, + DeviceManagement_pb2.DeviceAdminResponse, ) if not rs: return [] @@ -507,10 +581,11 @@ def _execute_device_action( device_action.deviceActionType = action_type device_action.encryptedDeviceToken.extend(list(token_to_device.keys())) - rs = auth.execute_auth_rest( - rest_endpoint=URL_DEVICE_USER_ACTION, - request=rq, - response_type=DeviceManagement_pb2.DeviceActionResponse, + rs = _execute_device_rest( + auth, + URL_DEVICE_USER_ACTION, + rq, + DeviceManagement_pb2.DeviceActionResponse, ) if not rs or not rs.deviceActionResult: raise ValueError('No response returned from device action') @@ -557,10 +632,11 @@ def _execute_admin_device_action( admin_action.enterpriseUserId = enterprise_user_id admin_action.encryptedDeviceToken.extend(list(token_to_device.keys())) - rs = auth.execute_auth_rest( - rest_endpoint=URL_DEVICE_ADMIN_ACTION, - request=rq, - response_type=DeviceManagement_pb2.DeviceAdminActionResponse, + rs = _execute_device_rest( + auth, + URL_DEVICE_ADMIN_ACTION, + rq, + DeviceManagement_pb2.DeviceAdminActionResponse, ) if not rs or not rs.deviceAdminActionResults: raise ValueError('No response returned from device admin action') diff --git a/keepersdk-package/src/keepersdk/authentication/login_auth.py b/keepersdk-package/src/keepersdk/authentication/login_auth.py index 2ef6cea6..2ff77e48 100644 --- a/keepersdk-package/src/keepersdk/authentication/login_auth.py +++ b/keepersdk-package/src/keepersdk/authentication/login_auth.py @@ -570,8 +570,12 @@ def decrypt_with_device_key(encrypted_data_key): _on_sso_redirect(login, sso_login_info, response.encryptedLoginToken) elif response.loginState == APIRequest_pb2.REQUIRES_DEVICE_ENCRYPTED_DATA_KEY: _on_request_data_key(login, response.encryptedLoginToken) - elif response.loginState in (APIRequest_pb2.DEVICE_ACCOUNT_LOCKED, APIRequest_pb2.DEVICE_LOCKED): - raise errors.InvalidDeviceTokenError(response.message) + elif response.loginState == APIRequest_pb2.DEVICE_ACCOUNT_LOCKED: + login.login_step = LoginStepError( + 'device_account_locked', 'Device for this account is locked') + elif response.loginState == APIRequest_pb2.DEVICE_LOCKED: + login.login_step = LoginStepError( + 'device_locked', 'This device is locked') else: state = APIRequest_pb2.LoginState.Name(response.loginState) # type: ignore message = f'State {state}: Not implemented: {response.message}' diff --git a/keepersdk-package/src/keepersdk/enterprise/batch_management.py b/keepersdk-package/src/keepersdk/enterprise/batch_management.py index 81d95a68..8e253560 100644 --- a/keepersdk-package/src/keepersdk/enterprise/batch_management.py +++ b/keepersdk-package/src/keepersdk/enterprise/batch_management.py @@ -31,7 +31,7 @@ class EntityAction(int, enum.Enum): class BatchManagement(enterprise_management.IEnterpriseManagement): def __init__(self, loader: enterprise_types.IEnterpriseLoader, - logger: enterprise_management.IEnterpriseManagementLogger): + logger: Optional[enterprise_management.IEnterpriseManagementLogger] = None): self.loader = loader self.logger = logger or _NilLogger() self._record_types: Optional[Dict[str, Tuple[int, record_pb2.RecordTypeScope]]] = None @@ -911,34 +911,47 @@ def _to_team_user_requests(self) -> Tuple[List[Dict[str, Any]], List[Dict[str, A if not t and not qt: raise Exception('team not found') if u.status == 'active' and t: - team_keys: Optional[keeper_auth.UserKeys] - if self._team_keys and team_user.team_uid in self._team_keys: - team_keys = self._team_keys[team_user.team_uid] + is_member = enterprise_data.team_users.get_link( + team_user.team_uid, team_user.enterprise_user_id) is not None + user_type = team_user.user_type if team_user.user_type is not None else 0 + if is_member: + if team_user.user_type is None: + raise Exception('user is already a team member') + rq['command'] = 'team_enterprise_user_update' + rq['user_type'] = team_user.user_type else: - team_keys = self.loader.keeper_auth.get_team_keys(team_user.team_uid) - if not team_keys: - raise Exception('team key is not loaded') - if not team_keys.aes: - team = enterprise_data.teams.get_entity(team_user.team_uid) - if team: - team_keys.aes = team.encrypted_team_key - user_keys = self.loader.keeper_auth.get_user_keys(u.username) - if not user_keys: - raise Exception('user key is not loaded') - rq['command'] = 'team_enterprise_user_add' - rq['user_type'] = 0 - if self.loader.keeper_auth.auth_context.forbid_rsa: - if user_keys.ec: - ec_public_key = crypto.load_ec_public_key(user_keys.ec) - team_key = crypto.encrypt_ec(team_keys.aes, ec_public_key) - rq['team_key'] = utils.base64_url_encode(team_key) - rq['team_key_type'] = 'encrypted_by_public_key_ecc' - else: - if user_keys.rsa: - rsa_public_key = crypto.load_rsa_public_key(user_keys.rsa) - team_key = crypto.encrypt_rsa(team_keys.aes, rsa_public_key) - rq['team_key'] = utils.base64_url_encode(team_key) - rq['team_key_type'] = 'encrypted_by_public_key' + team_keys: Optional[keeper_auth.UserKeys] + if self._team_keys and team_user.team_uid in self._team_keys: + team_keys = self._team_keys[team_user.team_uid] + else: + team_keys = self.loader.keeper_auth.get_team_keys(team_user.team_uid) + if not team_keys: + raise Exception('team key is not loaded') + if not team_keys.aes: + team = enterprise_data.teams.get_entity(team_user.team_uid) + if team: + team_keys.aes = team.encrypted_team_key + user_keys = self.loader.keeper_auth.get_user_keys(u.username) + if not user_keys: + raise Exception('user key is not loaded') + rq['command'] = 'team_enterprise_user_add' + rq['user_type'] = user_type + if self.loader.keeper_auth.auth_context.forbid_rsa: + if user_keys.ec: + ec_public_key = crypto.load_ec_public_key(user_keys.ec) + team_key = crypto.encrypt_ec(team_keys.aes, ec_public_key) + rq['team_key'] = utils.base64_url_encode(team_key) + rq['team_key_type'] = 'encrypted_by_public_key_ecc' + else: + raise Exception('user does not have EC key') + else: + if user_keys.rsa: + rsa_public_key = crypto.load_rsa_public_key(user_keys.rsa) + team_key = crypto.encrypt_rsa(team_keys.aes, rsa_public_key) + rq['team_key'] = utils.base64_url_encode(team_key) + rq['team_key_type'] = 'encrypted_by_public_key' + else: + raise Exception('user does not have RSA key') else: rq['command'] = 'team_queue_user' elif action == EntityAction.Remove: @@ -1026,7 +1039,14 @@ def _to_role_team_requests(self) -> Tuple[List[enterprise_pb2.RoleTeam], List[en add_rt_requests: List[enterprise_pb2.RoleTeam] = [] remove_rt_requests: List[enterprise_pb2.RoleTeam] = [] if self._role_teams: + enterprise_data = self.loader.enterprise_data for action, role_team in self._role_teams.values(): + if action == EntityAction.Add: + is_admin_role = any(enterprise_data.managed_nodes.get_links_by_subject(role_team.role_id)) + if is_admin_role: + self.logger.warning( + 'Teams cannot be assigned to roles with administrative permissions.') + continue rqs = add_rt_requests if action == EntityAction.Add else remove_rt_requests rt = enterprise_pb2.RoleTeam() rt.role_id = role_team.role_id diff --git a/keepersdk-package/src/keepersdk/enterprise/enterprise_management.py b/keepersdk-package/src/keepersdk/enterprise/enterprise_management.py index 6cff5db2..1df0e10e 100644 --- a/keepersdk-package/src/keepersdk/enterprise/enterprise_management.py +++ b/keepersdk-package/src/keepersdk/enterprise/enterprise_management.py @@ -8,6 +8,18 @@ from . import enterprise_types +def team_user_type_from_hide_shared_folders(hide_shared_folders: Optional[bool]) -> Optional[int]: + if hide_shared_folders is None: + return None + return 2 if hide_shared_folders else 1 + + +def team_user_type_from_hsf_flag(hsf_flag: Optional[str]) -> Optional[int]: + if not hsf_flag: + return None + return 2 if hsf_flag == 'on' else 1 + + @attrs.define(kw_only=True) class NodeEdit: _node_id: int diff --git a/keepersdk-package/src/keepersdk/enterprise/enterprise_team_management.py b/keepersdk-package/src/keepersdk/enterprise/enterprise_team_management.py new file mode 100644 index 00000000..75639baf --- /dev/null +++ b/keepersdk-package/src/keepersdk/enterprise/enterprise_team_management.py @@ -0,0 +1,587 @@ +# _ __ +# | |/ /___ ___ _ __ ___ _ _ ® +# | ' bool: + return self.team_uid is not None + + +@dataclass +class EnterpriseTeamInfo: + team_uid: str + team_name: str + node_id: int + node_name: str + restrict_edit: bool + restrict_share: bool + restrict_view: bool + access_level: str = 'enterprise_admin' + is_member: bool = True + team_roles: List[EnterpriseTeamRoleInfo] = field(default_factory=list) + team_users: List[EnterpriseTeamUserInfo] = field(default_factory=list) + queued_team_users: List[EnterpriseTeamUserInfo] = field(default_factory=list) + members: List[TeamMemberInfo] = field(default_factory=list) + + def to_dict(self) -> dict: + result = { + 'team_uid': self.team_uid, + 'team_name': self.team_name, + 'node_id': self.node_id, + 'node_name': self.node_name, + 'restrict_edit': self.restrict_edit, + 'restrict_share': self.restrict_share, + 'restrict_view': self.restrict_view, + 'access_level': self.access_level, + 'is_member': self.is_member, + } + if self.team_roles: + result['team_roles'] = [ + {'role_id': x.role_id, 'role_name': x.role_name} for x in self.team_roles + ] + if self.team_users: + result['team_users'] = [ + {'enterprise_user_id': x.enterprise_user_id, 'username': x.username} + for x in self.team_users + ] + if self.queued_team_users: + result['queued_team_users'] = [ + {'enterprise_user_id': x.enterprise_user_id, 'username': x.username} + for x in self.queued_team_users + ] + if self.members: + result['members'] = [ + { + 'enterprise_user_id': x.enterprise_user_id, + 'email': x.email, + 'enterprise_username': x.enterprise_username, + 'is_share_admin': x.is_share_admin, + } + for x in self.members + ] + return result + + +def _get_node_path( + enterprise_data: enterprise_types.IEnterpriseData, + node_id: int, + *, + omit_root: bool = False, +) -> str: + nodes: List[str] = [] + n_id = node_id + while isinstance(n_id, int) and n_id > 0: + node = enterprise_data.nodes.get_entity(n_id) + if not node: + break + n_id = node.parent_id or 0 + if not omit_root or n_id > 0: + node_name = node.name + if not node_name and node.node_id == enterprise_data.root_node.node_id: + node_name = enterprise_data.enterprise_info.enterprise_name + nodes.append(node_name) + nodes.reverse() + return '\\'.join(nodes) + + +def _teams_by_name( + teams: List[enterprise_types.Team], + team_name: str, +) -> List[enterprise_types.Team]: + name_lower = team_name.lower() + return [t for t in teams if t.name.lower() == name_lower] + + +def _vault_teams_by_name( + vault_data_obj: vault_data.VaultData, + team_name: str, +) -> List[vault_types.TeamInfo]: + name_lower = team_name.lower() + return [t for t in vault_data_obj.teams() if t.name.lower() == name_lower] + + +def _shareable_teams_by_name( + teams: List[vault_types.TeamInfo], + team_name: str, +) -> List[vault_types.TeamInfo]: + name_lower = team_name.lower() + return [t for t in teams if t.name.lower() == name_lower] + + +def _collect_shareable_teams( + *, + auth: Optional[keeper_auth.KeeperAuth] = None, + vault: Optional[vault_online.VaultOnline] = None, +) -> List[vault_types.TeamInfo]: + """Teams visible via share objects and get_available_teams (same sources as list-team).""" + teams: List[vault_types.TeamInfo] = [] + seen: set = set() + + if vault is not None: + share_objects = share_management_utils.get_share_objects(vault=vault) + for team_uid, team_info in share_objects.get('teams', {}).items(): + if team_uid in seen: + continue + seen.add(team_uid) + teams.append( + vault_types.TeamInfo( + team_uid=team_uid, + name=team_info.get('name') or '', + ) + ) + + if auth is not None: + for team in vault_utils.load_available_teams(auth): + if team.team_uid in seen: + continue + seen.add(team.team_uid) + teams.append(team) + + return teams + + +def resolve_team( + team_name_or_uid: str, + *, + vault_data_obj: Optional[vault_data.VaultData] = None, + enterprise_data: Optional[enterprise_types.IEnterpriseData] = None, + is_enterprise_admin: bool = False, + auth: Optional[keeper_auth.KeeperAuth] = None, + vault: Optional[vault_online.VaultOnline] = None, + include_share_objects: bool = False, +) -> TeamResolveResult: + """ + Resolve a team by UID or case-insensitive name. + + Resolution order: + 1. Vault cache by UID + 2. Vault cache by name + 3. Enterprise cache by UID (enterprise admin) + 4. Enterprise cache by name (enterprise admin) + 5. Share objects / available teams by UID (when include_share_objects) + 6. Share objects / available teams by name (when include_share_objects) + """ + if not team_name_or_uid: + return TeamResolveResult() + + if vault_data_obj is not None: + vault_team = vault_data_obj.get_team(team_name_or_uid) + if vault_team is not None: + return TeamResolveResult(team_uid=vault_team.team_uid, vault_team=vault_team) + + vault_matches = _vault_teams_by_name(vault_data_obj, team_name_or_uid) + if len(vault_matches) > 1: + return TeamResolveResult(multiple_found=True) + if len(vault_matches) == 1: + team = vault_matches[0] + return TeamResolveResult(team_uid=team.team_uid, vault_team=team) + + if enterprise_data is not None and is_enterprise_admin: + enterprise_team = enterprise_data.teams.get_entity(team_name_or_uid) + if enterprise_team is not None: + return TeamResolveResult( + team_uid=enterprise_team.team_uid, + enterprise_team=enterprise_team, + ) + + enterprise_matches = _teams_by_name( + list(enterprise_data.teams.get_all_entities()), + team_name_or_uid, + ) + if len(enterprise_matches) > 1: + return TeamResolveResult(multiple_found=True) + if len(enterprise_matches) == 1: + team = enterprise_matches[0] + return TeamResolveResult(team_uid=team.team_uid, enterprise_team=team) + + if include_share_objects and (auth is not None or vault is not None): + shareable_teams = _collect_shareable_teams(auth=auth, vault=vault) + share_team = next( + (team for team in shareable_teams if team.team_uid == team_name_or_uid), + None, + ) + if share_team is not None: + return TeamResolveResult( + team_uid=share_team.team_uid, + share_team=share_team, + ) + + share_matches = _shareable_teams_by_name(shareable_teams, team_name_or_uid) + if len(share_matches) > 1: + return TeamResolveResult(multiple_found=True) + if len(share_matches) == 1: + team = share_matches[0] + return TeamResolveResult(team_uid=team.team_uid, share_team=team) + + return TeamResolveResult() + + +def resolve_enterprise_team( + enterprise_data: enterprise_types.IEnterpriseData, + team_name_or_uid: str, +) -> enterprise_types.Team: + """Resolve an enterprise team by UID or case-insensitive name.""" + team = enterprise_data.teams.get_entity(team_name_or_uid) + if team is not None: + return team + + matches = _teams_by_name(list(enterprise_data.teams.get_all_entities()), team_name_or_uid) + if not matches: + raise EnterpriseTeamManagementError( + ERROR_MSG_TEAM_NOT_FOUND.format(team_name_or_uid) + ) + if len(matches) > 1: + raise EnterpriseTeamManagementError( + ERROR_MSG_MULTIPLE_TEAMS.format(team_name_or_uid) + ) + return matches[0] + + +def get_team_members( + auth: keeper_auth.KeeperAuth, + team_uid: str, +) -> List[TeamMemberInfo]: + """Return team members from vault/get_team_members.""" + if not team_uid: + return [] + + request = enterprise_pb2.GetTeamMemberRequest() + request.teamUid = utils.base64_url_decode(team_uid) + response = auth.execute_auth_rest( + rest_endpoint=TEAM_MEMBERS_ENDPOINT, + request=request, + response_type=enterprise_pb2.GetTeamMemberResponse, + ) + if response is None or not response.enterpriseUser: + return [] + + return [ + TeamMemberInfo( + enterprise_user_id=user.enterpriseUserId, + email=user.email or '', + enterprise_username=user.enterpriseUsername or '', + is_share_admin=bool(user.isShareAdmin), + ) + for user in response.enterpriseUser + ] + + +def _build_team_users( + enterprise_data: enterprise_types.IEnterpriseData, + user_ids: set, +) -> List[EnterpriseTeamUserInfo]: + users: List[EnterpriseTeamUserInfo] = [] + for user_id in user_ids: + user = enterprise_data.users.get_entity(user_id) + if user is None: + continue + users.append( + EnterpriseTeamUserInfo( + enterprise_user_id=user.enterprise_user_id, + username=user.username, + full_name=user.full_name, + ) + ) + users.sort(key=lambda x: x.username.lower()) + return users + + +def _build_team_roles( + enterprise_data: enterprise_types.IEnterpriseData, + role_ids: set, +) -> List[EnterpriseTeamRoleInfo]: + roles: List[EnterpriseTeamRoleInfo] = [] + for role_id in role_ids: + role = enterprise_data.roles.get_entity(role_id) + if role is None: + continue + roles.append(EnterpriseTeamRoleInfo(role_id=role.role_id, role_name=role.name)) + roles.sort(key=lambda x: x.role_name.lower()) + return roles + + +def _user_is_team_member( + auth: Optional[keeper_auth.KeeperAuth], + members: List[TeamMemberInfo], +) -> bool: + if auth is None: + return False + username = auth.auth_context.username.lower() + if not username: + return False + return any( + username in (member.email.lower(), member.enterprise_username.lower()) + for member in members + ) + + +def _build_basic_team_info( + team_uid: str, + team_name: str, + *, + auth: Optional[keeper_auth.KeeperAuth], + fetch_live_members: bool, + access_level: str, + is_member: Optional[bool] = None, +) -> EnterpriseTeamInfo: + members: List[TeamMemberInfo] = [] + if auth is not None and fetch_live_members: + members = get_team_members(auth, team_uid) + if is_member is None: + is_member = access_level == 'full_member' or _user_is_team_member(auth, members) + return EnterpriseTeamInfo( + team_uid=team_uid, + team_name=team_name, + node_id=0, + node_name='', + restrict_edit=False, + restrict_share=False, + restrict_view=False, + access_level=access_level, + is_member=is_member, + members=members, + ) + + +def get_team( + team_name_or_uid: str, + *, + enterprise_data: Optional[enterprise_types.IEnterpriseData] = None, + vault_data_obj: Optional[vault_data.VaultData] = None, + auth: Optional[keeper_auth.KeeperAuth] = None, + vault: Optional[vault_online.VaultOnline] = None, + is_enterprise_admin: bool = False, + include_share_objects: bool = False, + include_roles: bool = True, + include_users: bool = True, + include_queued_users: bool = True, + fetch_live_members: bool = False, +) -> EnterpriseTeamInfo: + """ + Get detailed information for a team by UID or name. + + When enterprise_data is available the result includes cached roles and users. + When auth is provided and fetch_live_members is True, members are loaded from + vault/get_team_members. + """ + resolved = resolve_team( + team_name_or_uid, + vault_data_obj=vault_data_obj, + enterprise_data=enterprise_data, + is_enterprise_admin=is_enterprise_admin, + auth=auth, + vault=vault, + include_share_objects=include_share_objects, + ) + if resolved.multiple_found: + raise EnterpriseTeamManagementError( + ERROR_MSG_MULTIPLE_TEAMS.format(team_name_or_uid) + ) + if not resolved.found: + raise EnterpriseTeamManagementError( + ERROR_MSG_TEAM_NOT_FOUND.format(team_name_or_uid) + ) + + enterprise_team = resolved.enterprise_team + if enterprise_team is None and enterprise_data is not None and resolved.team_uid: + enterprise_team = enterprise_data.teams.get_entity(resolved.team_uid) + + if enterprise_team is not None and enterprise_data is not None: + access_level = 'full_member' if resolved.vault_team is not None else 'enterprise_admin' + node_name = _get_node_path(enterprise_data, enterprise_team.node_id, omit_root=False) + + team_roles: List[EnterpriseTeamRoleInfo] = [] + team_users: List[EnterpriseTeamUserInfo] = [] + queued_team_users: List[EnterpriseTeamUserInfo] = [] + + if include_roles: + role_ids = { + x.role_id + for x in enterprise_data.role_teams.get_links_by_object(enterprise_team.team_uid) + } + team_roles = _build_team_roles(enterprise_data, role_ids) + + if include_users: + user_ids = { + x.enterprise_user_id + for x in enterprise_data.team_users.get_links_by_subject(enterprise_team.team_uid) + } + team_users = _build_team_users(enterprise_data, user_ids) + + if include_queued_users: + queued_user_ids = { + x.enterprise_user_id + for x in enterprise_data.queued_team_users.get_links_by_subject(enterprise_team.team_uid) + } + queued_team_users = _build_team_users(enterprise_data, queued_user_ids) + + members: List[TeamMemberInfo] = [] + if auth is not None and fetch_live_members: + members = get_team_members(auth, enterprise_team.team_uid) + + is_member = ( + resolved.vault_team is not None + or _user_is_team_member(auth, members) + ) + + return EnterpriseTeamInfo( + team_uid=enterprise_team.team_uid, + team_name=enterprise_team.name, + node_id=enterprise_team.node_id, + node_name=node_name, + restrict_edit=enterprise_team.restrict_edit, + restrict_share=enterprise_team.restrict_share, + restrict_view=enterprise_team.restrict_view, + access_level=access_level, + is_member=is_member, + team_roles=team_roles, + team_users=team_users, + queued_team_users=queued_team_users, + members=members, + ) + + if resolved.vault_team is not None: + return _build_basic_team_info( + resolved.vault_team.team_uid, + resolved.vault_team.name, + auth=auth, + fetch_live_members=fetch_live_members, + access_level='full_member', + ) + + if resolved.share_team is not None: + return _build_basic_team_info( + resolved.share_team.team_uid, + resolved.share_team.name, + auth=auth, + fetch_live_members=fetch_live_members, + access_level='share_reference', + ) + + raise EnterpriseTeamManagementError( + ERROR_MSG_TEAM_NOT_FOUND.format(team_name_or_uid) + ) + + +def list_teams( + enterprise_data: enterprise_types.IEnterpriseData, + pattern: Optional[str] = None, +) -> List[EnterpriseTeamSummary]: + """List enterprise teams, optionally filtered by case-insensitive substring.""" + pattern_lower = (pattern or '').lower() + + user_teams: dict = {} + for team_user in enterprise_data.team_users.get_all_links(): + user_teams.setdefault(team_user.team_uid, set()).add(team_user.enterprise_user_id) + + role_teams: dict = {} + for role_team in enterprise_data.role_teams.get_all_links(): + role_teams.setdefault(role_team.team_uid, set()).add(role_team.role_id) + + summaries: List[EnterpriseTeamSummary] = [] + for team in enterprise_data.teams.get_all_entities(): + if pattern_lower: + searchable = ' '.join( + str(x) + for x in ( + team.team_uid, + team.name, + team.node_id, + team.restrict_edit, + team.restrict_share, + team.restrict_view, + ) + ).lower() + if pattern_lower not in searchable: + continue + + node_name = _get_node_path(enterprise_data, team.node_id, omit_root=True) + user_count = len(user_teams.get(team.team_uid, set())) + role_count = len(role_teams.get(team.team_uid, set())) + summaries.append( + EnterpriseTeamSummary( + team_uid=team.team_uid, + team_name=team.name, + node_id=team.node_id, + node_name=node_name, + restrict_edit=team.restrict_edit, + restrict_share=team.restrict_share, + restrict_view=team.restrict_view, + user_count=user_count, + role_count=role_count, + ) + ) + + summaries.sort(key=lambda x: x.team_name.lower()) + return summaries diff --git a/keepersdk-package/src/keepersdk/enterprise/enterprise_user_management.py b/keepersdk-package/src/keepersdk/enterprise/enterprise_user_management.py index 4757cf9b..bf8cb4cb 100644 --- a/keepersdk-package/src/keepersdk/enterprise/enterprise_user_management.py +++ b/keepersdk-package/src/keepersdk/enterprise/enterprise_user_management.py @@ -574,9 +574,9 @@ def add_users_to_teams( enterprise_data = loader.enterprise_data - user_type: Optional[int] = None - if isinstance(hide_shared_folders, bool): - user_type = 0 if hide_shared_folders else 2 + user_type = enterprise_management.team_user_type_from_hide_shared_folders( + hide_shared_folders if isinstance(hide_shared_folders, bool) else None + ) batch = batch_management.BatchManagement(loader=loader, logger=logger) diff --git a/keepersdk-package/src/keepersdk/generator.py b/keepersdk-package/src/keepersdk/generator.py index a9d94bc5..e30d5303 100644 --- a/keepersdk-package/src/keepersdk/generator.py +++ b/keepersdk-package/src/keepersdk/generator.py @@ -1,15 +1,242 @@ import abc +import difflib import hashlib import logging import os import secrets import string -from typing import Optional, List, Any, Iterator +from collections import namedtuple +from typing import Optional, List, Any, Iterator, Sequence, Tuple from . import crypto DEFAULT_PASSWORD_LENGTH = 32 PW_SPECIAL_CHARACTERS = '!@#$%()+;<>=?[]{}^.,' +PP_SEPARATOR_CHARACTERS = '-._?! ' +DEFAULT_PASSPHRASE_SEPARATOR = '-' +DEFAULT_PASSPHRASE_WORD_COUNT = 5 +MIN_PASSPHRASE_WORD_COUNT = 5 +MAX_PASSPHRASE_WORD_COUNT = 9 +DEFAULT_PASSPHRASE_CAPITALIZE = True +DEFAULT_PASSPHRASE_NUMBER = True +GEN_PASSWORD_ALGORITHMS = ('rand', 'dice', 'crypto', 'passphrase') +DEFAULT_DICEWARE_WORDLIST = 'diceware.wordlist.asc.txt' +PASSPHRASE_SEPARATOR_HELP = '- . _ ? ! space' + +PassphraseGenOptions = namedtuple( + 'PassphraseGenOptions', ('word_count', 'separator', 'capitalize', 'append_number')) +PassphraseGenOptions.__doc__ = ( + 'Parsed optional parameters for $GEN:passphrase. ' + 'None fields use Vault/CLI defaults when building a generator.' +) + + +def clamp_passphrase_word_count(word_count: Optional[int]) -> int: + """Clamp passphrase word count to the Vault range (5-9 words).""" + if not isinstance(word_count, int): + return DEFAULT_PASSPHRASE_WORD_COUNT + original = word_count + if word_count < MIN_PASSPHRASE_WORD_COUNT: + word_count = MIN_PASSPHRASE_WORD_COUNT + elif word_count > MAX_PASSPHRASE_WORD_COUNT: + word_count = MAX_PASSPHRASE_WORD_COUNT + if word_count != original: + logging.warning( + 'Passphrase word count must be between %d and %d; using %d.', + MIN_PASSPHRASE_WORD_COUNT, MAX_PASSPHRASE_WORD_COUNT, word_count) + return word_count + + +def format_passphrase_separators_for_display(separators: Optional[str] = None) -> str: + """Human-readable list of allowed passphrase separator characters.""" + if not separators: + separators = PP_SEPARATOR_CHARACTERS + parts: List[str] = [] + for ch in separators: + parts.append('space' if ch == ' ' else ch) + return ', '.join(parts) + + +def _normalize_passphrase_separator(separator: Optional[str]) -> str: + """Normalize a separator string to a single allowed character.""" + if not separator: + return DEFAULT_PASSPHRASE_SEPARATOR + if separator == '\u2423': # OPEN BOX (Vault UI glyph for space) + return ' ' + return separator[0] + + +def _passphrase_separators_from_policy(policy_sep: str) -> str: + """Return allowed separators in Vault order (see getPasswordRules.ts).""" + normalized = policy_sep.replace('\u2423', ' ') + allowed = '' + for ch in PP_SEPARATOR_CHARACTERS: + if ch in normalized: + allowed += ch + return allowed + + +def _default_passphrase_separator_from_policy(policy_sep: Optional[str]) -> str: + """Pick the default generation separator matching Vault / PowerCommander.""" + if not policy_sep or not isinstance(policy_sep, str) or not policy_sep.strip(): + return DEFAULT_PASSPHRASE_SEPARATOR + allowed = _passphrase_separators_from_policy(policy_sep.strip()) + return allowed[0] if allowed else DEFAULT_PASSPHRASE_SEPARATOR + + +def resolve_gen_password_algorithm( + parameters: Optional[Sequence[str]]) -> Tuple[Optional[str], Optional[str]]: + """Resolve $GEN password algorithm; return (algorithm, error_message).""" + if not parameters: + return 'rand', None + first = parameters[0].strip() + first_lower = first.lower() + if first_lower in GEN_PASSWORD_ALGORITHMS: + return first_lower, None + if first.isdigit(): + return 'rand', None + suggestions = difflib.get_close_matches(first_lower, GEN_PASSWORD_ALGORITHMS, n=1, cutoff=0.6) + message = f'Unknown $GEN password algorithm "{first}".' + if suggestions: + message += f' Did you mean "{suggestions[0]}"?' + message += f' Valid algorithms: {", ".join(GEN_PASSWORD_ALGORITHMS)}.' + return None, message + + +def _is_strict_gen_bool_token(value: str) -> bool: + """Return True if value is exactly 'true' or 'false' (case-insensitive).""" + return value.strip().lower() in ('true', 'false') + + +def _parse_gen_bool_strict(value: str, param_name: str) -> Tuple[Optional[bool], Optional[str]]: + """Parse a strict true/false token for $GEN:passphrase; return (value, error).""" + normalized = value.strip().lower() + if normalized == 'true': + return True, None + if normalized == 'false': + return False, None + return None, ( + f'Invalid $GEN:passphrase {param_name} parameter "{value}". ' + f'Expected true or false.') + + +def _is_passphrase_separator_token(token: str) -> bool: + """Return True if token is a valid passphrase separator or 'space'/'sp' alias.""" + if token.lower() in ('space', 'sp'): + return True + return len(token) == 1 and token in PP_SEPARATOR_CHARACTERS + + +def _parse_passphrase_separator_token(token: str) -> Tuple[Optional[str], Optional[str]]: + """Parse a separator token; return (separator_char, error_message).""" + if token.lower() in ('space', 'sp'): + return ' ', None + if len(token) == 1 and token in PP_SEPARATOR_CHARACTERS: + return token, None + return None, ( + f'Invalid passphrase separator "{token}". ' + f'Allowed: {format_passphrase_separators_for_display(PP_SEPARATOR_CHARACTERS)}.') + + +def parse_passphrase_gen_parameters( + parameters: Optional[Sequence[str]]) -> Tuple[PassphraseGenOptions, Optional[str]]: + """Parse $GEN:passphrase optional parameters. + + Format: $GEN:passphrase[,word_count][,separator][,capitalize][,number] + word_count must be between 5 and 9 (Vault range). + """ + empty = PassphraseGenOptions(None, None, None, None) + if not parameters: + return empty, None + + tokens = [p if isinstance(p, str) else str(p) for p in parameters] + if not tokens or tokens[0].strip().lower() != 'passphrase': + return empty, None + + extras = tokens[1:] + if any(t.strip() == '' for t in extras): + return empty, ( + 'Incomplete $GEN:passphrase parameters: missing value after comma. ' + 'Format: $GEN:passphrase[,word_count][,separator][,capitalize][,number]') + + word_count = None + separator = None + capitalize = None + append_number = None + idx = 0 + + if idx < len(extras): + token = extras[idx].strip() + if token.isdigit(): + word_count = int(token) + if word_count < MIN_PASSPHRASE_WORD_COUNT or word_count > MAX_PASSPHRASE_WORD_COUNT: + return empty, ( + f'Passphrase word count must be between {MIN_PASSPHRASE_WORD_COUNT} ' + f'and {MAX_PASSPHRASE_WORD_COUNT} (got {word_count}).') + idx += 1 + elif not _is_passphrase_separator_token(token) and not _is_strict_gen_bool_token(token): + return empty, ( + f'Invalid passphrase word count "{token}". ' + f'Expected an integer between {MIN_PASSPHRASE_WORD_COUNT} ' + f'and {MAX_PASSPHRASE_WORD_COUNT}.') + + if idx < len(extras) and not _is_strict_gen_bool_token(extras[idx].strip()): + separator, sep_error = _parse_passphrase_separator_token(extras[idx].strip()) + if sep_error: + return empty, sep_error + idx += 1 + + if idx < len(extras): + capitalize, cap_error = _parse_gen_bool_strict(extras[idx].strip(), 'capitalize') + if cap_error: + return empty, cap_error + idx += 1 + + if idx < len(extras): + append_number, num_error = _parse_gen_bool_strict(extras[idx].strip(), 'number') + if num_error: + return empty, num_error + idx += 1 + + if idx < len(extras): + return empty, f'Unexpected $GEN:passphrase parameter "{extras[idx].strip()}".' + + return PassphraseGenOptions(word_count, separator, capitalize, append_number), None + + +def _resolve_wordlist_path(word_list_file: Optional[str] = None) -> str: + """Resolve bundled or user-supplied diceware word list path.""" + if word_list_file: + dice_path = os.path.join(os.path.dirname(__file__), 'resources', word_list_file) + if not os.path.isfile(dice_path): + dice_path = os.path.expanduser(word_list_file) + else: + dice_path = os.path.join(os.path.dirname(__file__), 'resources', DEFAULT_DICEWARE_WORDLIST) + return dice_path + + +def _load_wordlist(word_list_file: Optional[str] = None) -> List[str]: + """Load and validate the diceware word list from disk.""" + dice_path = _resolve_wordlist_path(word_list_file) + if not os.path.isfile(dice_path): + raise Exception(f'Word list file \"{dice_path}\" not found.') + + vocabulary: List[str] = [] + unique_words = set() + with open(dice_path, 'r', encoding='utf-8') as dw: + for line in dw: + line = line.strip() + if not line or line.startswith('--'): + continue + if line.lower().startswith('source url:') or line.lower().startswith('title:'): + continue + parts = line.split() + word = parts[1] if len(parts) >= 2 else parts[0] + vocabulary.append(word) + unique_words.add(word.lower()) + if len(vocabulary) != len(unique_words): + raise Exception(f'Word list file \"{dice_path}\" contains non-unique words.') + return vocabulary class PasswordGenerator(abc.ABC): @@ -170,3 +397,99 @@ def generate(self): words.reverse() return ' '.join((self._vocabulary[x] for x in words)) + + +class KeeperPassphraseGenerator(PasswordGenerator): + """Vault-style passphrase generator using the bundled EFF large word list. + + Each word is chosen with a cryptographically secure random selector (``secrets``), + using configurable word count (5-9), separator, optional capitalization of every word, + and an optional single digit appended to the first word only. Words are never + repeated within a single passphrase. + + Use :meth:`create_with_options` or :meth:`create_from_policy` to apply + enterprise passphrase policy defaults with optional CLI/$GEN overrides. + """ + + def __init__(self, word_count: int = DEFAULT_PASSPHRASE_WORD_COUNT, + separator: str = DEFAULT_PASSPHRASE_SEPARATOR, + capitalize: bool = DEFAULT_PASSPHRASE_CAPITALIZE, + append_number: bool = DEFAULT_PASSPHRASE_NUMBER, + word_list_file: Optional[str] = None) -> None: + """Initialize a Vault-style passphrase generator with the given options.""" + self.word_count = clamp_passphrase_word_count( + word_count if isinstance(word_count, int) else DEFAULT_PASSPHRASE_WORD_COUNT) + self.separator = _normalize_passphrase_separator(separator) + self.capitalize = capitalize + self.append_number = append_number + self._vocabulary = _load_wordlist(word_list_file) + + def _select_unique_words(self) -> List[str]: + """Select word_count unique words using secrets.randbelow (CSPRNG).""" + pool = list(self._vocabulary) + words: List[str] = [] + for _ in range(self.word_count): + idx = secrets.randbelow(len(pool)) + words.append(pool.pop(idx)) + return words + + def generate(self) -> str: + """Generate a passphrase using the configured word count, separator, and formatting.""" + if not self._vocabulary: + raise Exception('Passphrase word list was not loaded') + + passphrase = '' + first_word = True + for word in self._select_unique_words(): + if self.capitalize and word: + word = word[0].upper() + word[1:] # Vault UI: capitalize every word + if self.append_number and first_word: + word += str(secrets.randbelow(10)) # Vault UI: one digit on first word only + if not first_word: + passphrase += self.separator + passphrase += word + first_word = False + return passphrase + + @classmethod + def create_with_options(cls, policy: Optional[dict] = None, word_count: Optional[int] = None, + separator: Optional[str] = None, capitalize: Optional[bool] = None, + append_number: Optional[bool] = None) -> 'KeeperPassphraseGenerator': + """Build a generator from CLI/$GEN overrides with optional policy defaults.""" + wc = word_count + if wc is None: + if policy: + wc = policy.get('passphrase-length', DEFAULT_PASSPHRASE_WORD_COUNT) + else: + wc = DEFAULT_PASSPHRASE_WORD_COUNT + + sep = separator + if sep is None: + if policy: + policy_sep = policy.get('passphrase-separator') + sep = _default_passphrase_separator_from_policy( + policy_sep if isinstance(policy_sep, str) else None) + else: + sep = DEFAULT_PASSPHRASE_SEPARATOR + + cap = capitalize + if cap is None: + cap = DEFAULT_PASSPHRASE_CAPITALIZE + + num = append_number + if num is None: + num = DEFAULT_PASSPHRASE_NUMBER + + return cls( + word_count=clamp_passphrase_word_count(wc) if isinstance(wc, int) else wc, + separator=sep, capitalize=cap, append_number=num) + + @classmethod + def create_from_policy(cls, policy: dict, length_override: Optional[int] = None, + separator_override: Optional[str] = None) -> 'KeeperPassphraseGenerator': + """Build a generator using enterprise passphrase policy defaults.""" + return cls.create_with_options( + policy, + word_count=length_override, + separator=separator_override, + ) diff --git a/keepersdk-package/src/keepersdk/helpers/config_utils.py b/keepersdk-package/src/keepersdk/helpers/config_utils.py index e1bb9281..216e5f56 100644 --- a/keepersdk-package/src/keepersdk/helpers/config_utils.py +++ b/keepersdk-package/src/keepersdk/helpers/config_utils.py @@ -1,8 +1,14 @@ +from .. import crypto, utils +from ..errors import KeeperApiError from ..proto import pam_pb2 -from ..vault import vault_extensions, vault_online, vault_record -from .. import utils, crypto +from ..vault import nsf_management, vault_extensions, vault_online, vault_record -def pam_configuration_create_record_v6(vault: vault_online.VaultOnline, record: vault_record.TypedRecord, folder_uid: str): + +def pam_configuration_create_record_v6( + vault: vault_online.VaultOnline, + record: vault_record.TypedRecord, + folder_uid: str) -> None: + """Create a classic PAM configuration via pam/add_configuration_record.""" if not record.record_uid: record.record_uid = utils.generate_uid() @@ -22,6 +28,36 @@ def pam_configuration_create_record_v6(vault: vault_online.VaultOnline, record: vault.keeper_auth.execute_auth_rest('pam/add_configuration_record', car) +def pam_configuration_create_record_nsf( + vault: vault_online.VaultOnline, + record: vault_record.TypedRecord, + folder_uid: str) -> None: + """Create a PAM configuration in an NSF folder via vault/records/v3/add_pam_configuration.""" + try: + nsf_management.create_nsf_pam_configuration( + vault, record, folder_uid, request_sync=True) + except nsf_management.NsfError as exc: + raise KeeperApiError('nsf_error', str(exc)) from exc + + +def create_pam_configuration_in_folder( + vault: vault_online.VaultOnline, + record: vault_record.TypedRecord, + folder_uid: str) -> bool: + """Create a v6 PAM configuration in *folder_uid* using NSF or classic placement. + + Returns True when the config was created as an NSF record (already placed in + the folder). Returns False for classic configs, which still need a move into + the shared folder after sync. + """ + if vault.nsf_data is not None and nsf_management.is_nsf_folder(vault, folder_uid): + pam_configuration_create_record_nsf(vault, record, folder_uid) + return True + + pam_configuration_create_record_v6(vault, record, folder_uid) + return False + + def configuration_controller_get(vault: vault_online.VaultOnline, config_uid_bytes: bytes): """ Get the Controller UID that has access to the configuration UID @@ -31,7 +67,8 @@ def configuration_controller_get(vault: vault_online.VaultOnline, config_uid_byt rq = pam_pb2.PAMGenericUidRequest() rq.uid = config_uid_bytes - config_info_rs = vault.keeper_auth.execute_auth_rest('pam/get_configuration_controller', rq, response_type=pam_pb2.PAMController) + config_info_rs = vault.keeper_auth.execute_auth_rest( + 'pam/get_configuration_controller', rq, response_type=pam_pb2.PAMController) if config_info_rs: return config_info_rs diff --git a/keepersdk-package/src/keepersdk/helpers/tunnel/tunnel_graph.py b/keepersdk-package/src/keepersdk/helpers/tunnel/tunnel_graph.py index 99276b39..4ab3b3f8 100644 --- a/keepersdk-package/src/keepersdk/helpers/tunnel/tunnel_graph.py +++ b/keepersdk-package/src/keepersdk/helpers/tunnel/tunnel_graph.py @@ -169,7 +169,8 @@ def _convert_allowed_setting(value): def edit_tunneling_config(self, connections=None, tunneling=None, rotation=None, session_recording=None, typescript_recording=None, - remote_browser_isolation=None): + remote_browser_isolation=None, + ai_enabled=None, ai_session_terminate=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, vertex_type=RefType.PAM_NETWORK) @@ -244,6 +245,24 @@ def edit_tunneling_config(self, connections=None, tunneling=None, else: allowed_settings["remoteBrowserIsolation"] = remote_browser_isolation + if ai_enabled is not None: + ai_enabled = self._convert_allowed_setting(ai_enabled) + if ai_enabled != allowed_settings.get("aiEnabled", None): + dirty = True + if ai_enabled is None: + allowed_settings.pop("aiEnabled", None) + else: + allowed_settings["aiEnabled"] = ai_enabled + + if ai_session_terminate is not None: + ai_session_terminate = self._convert_allowed_setting(ai_session_terminate) + if ai_session_terminate != allowed_settings.get("aiSessionTerminate", None): + dirty = True + if ai_session_terminate is None: + allowed_settings.pop("aiSessionTerminate", None) + else: + allowed_settings["aiSessionTerminate"] = ai_session_terminate + if dirty: config_vertex.add_data(content=content, path='meta', needs_encryption=False) self.linking_dag.save() diff --git a/keepersdk-package/src/keepersdk/plugins/pam/pam_plugin.py b/keepersdk-package/src/keepersdk/plugins/pam/pam_plugin.py index d3b3ff4e..24a9d56d 100644 --- a/keepersdk-package/src/keepersdk/plugins/pam/pam_plugin.py +++ b/keepersdk-package/src/keepersdk/plugins/pam/pam_plugin.py @@ -1,13 +1,13 @@ from __future__ import annotations import abc -from typing import Dict, List, Tuple +from typing import Iterable, List, Tuple from . import pam_storage, pam_types from ... import utils from ...authentication.keeper_auth import KeeperAuth from ...enterprise import enterprise_loader, sqlite_enterprise_storage -from ...proto import SyncDown_pb2, pam_pb2 +from ...proto import pam_pb2 from ...storage import in_memory, storage_types @@ -50,6 +50,12 @@ def sync_down(self, *, reload: bool = False) -> None: def sync_record_rotations_from_vault(self) -> None: pass + @abc.abstractmethod + def merge_record_rotations( + self, rows: Iterable[pam_storage.PamRecordRotation], *, + replace_all: bool = False) -> None: + pass + @property @abc.abstractmethod def controllers(self) -> storage_types.IEntityReader[pam_types.PamController, str]: @@ -77,6 +83,8 @@ def __init__(self, loader: enterprise_loader.EnterpriseLoader): self._controllers = in_memory.InMemoryEntityStorage[pam_types.PamController, str]() self._record_rotations = in_memory.InMemoryEntityStorage[pam_types.PamRecordRotationInfo, str]() self.logger = utils.get_logger() + # Load any rotations already persisted from a prior vault sync. + self.sync_record_rotations_from_vault() @property def controllers(self) -> storage_types.IEntityReader[pam_types.PamController, str]: @@ -97,38 +105,33 @@ def _get_all_gateways(self, auth: KeeperAuth) -> List[pam_pb2.PAMController]: return [] def sync_record_rotations_from_vault(self) -> None: - - self._sync_record_rotations_from_vault_auth(self.loader.keeper_auth) - - def _sync_record_rotations_from_vault_auth(self, auth: KeeperAuth) -> None: - - merged: Dict[str, pam_storage.PamRecordRotation] = {} - rq = SyncDown_pb2.SyncDownRequest() - token = b'' - done = False - while not done: - rq.continuationToken = token - response = auth.execute_auth_rest( - 'vault/sync_down', rq, response_type=SyncDown_pb2.SyncDownResponse) - if response is None: - break - done = not response.hasMore - token = response.continuationToken or b'' - for rr in response.recordRotations: - row = pam_storage.pam_record_rotation_from_proto(rr) - if row.record_uid: - merged[row.record_uid] = row - if not merged: + """Reload rotations from PAM sqlite into memory (no vault/sync_down).""" + self._record_rotations.clear() + rows = list(self.storage.record_rotations.get_all_entities()) + if rows: + self._record_rotations.put_entities(_pam_rotation_to_domain(r) for r in rows) + + def merge_record_rotations( + self, rows: Iterable[pam_storage.PamRecordRotation], *, + replace_all: bool = False) -> None: + """Upsert rotation rows into PAM sqlite + memory (from normal vault sync).""" + row_list = [r for r in rows if r and r.record_uid] + if replace_all: + existing = [r.uid() for r in self.storage.record_rotations.get_all_entities()] + if existing: + self.storage.record_rotations.delete_uids(existing) + self._record_rotations.clear() + if not row_list: return - rows = list(merged.values()) - self.storage.record_rotations.put_entities(rows) - self._record_rotations.put_entities(_pam_rotation_to_domain(r) for r in rows) + self.storage.record_rotations.put_entities(row_list) + self._record_rotations.put_entities(_pam_rotation_to_domain(r) for r in row_list) def sync_down(self, *, reload: bool = False) -> None: _ = reload - self.storage.reset() + existing = [c.uid() for c in self.storage.controllers.get_all_entities()] + if existing: + self.storage.controllers.delete_uids(existing) self._controllers.clear() - self._record_rotations.clear() auth = self.loader.keeper_auth all_controllers = self._get_all_gateways(auth) @@ -143,7 +146,4 @@ def sync_down(self, *, reload: bool = False) -> None: if domain_rows: self._controllers.put_entities(domain_rows) - try: - self._sync_record_rotations_from_vault_auth(auth) - except Exception as e: - self.logger.warning('PAM: loading record rotations from vault/sync_down failed: %s', e) + self.sync_record_rotations_from_vault() \ No newline at end of file diff --git a/keepersdk-package/src/keepersdk/plugins/pam/pam_storage.py b/keepersdk-package/src/keepersdk/plugins/pam/pam_storage.py index 3ffdb675..c4017e9b 100644 --- a/keepersdk-package/src/keepersdk/plugins/pam/pam_storage.py +++ b/keepersdk-package/src/keepersdk/plugins/pam/pam_storage.py @@ -1,6 +1,6 @@ import abc import sqlite3 -from typing import Callable +from typing import Any, Callable, Dict, Optional import attrs @@ -59,6 +59,39 @@ def pam_record_rotation_from_proto(rr: SyncDown_pb2.RecordRotation) -> PamRecord ) +def pam_record_rotation_from_nsf_dict(data: Dict[str, Any]) -> Optional[PamRecordRotation]: + """Build a rotation row from NSF ``recordRotationData`` JSON (MessageToDict shape).""" + if not isinstance(data, dict): + return None + record_uid = data.get('recordUid') or data.get('record_uid') or '' + if not record_uid: + return None + revision = data.get('revision', 0) + try: + revision = int(revision) + except (TypeError, ValueError): + revision = 0 + pwd = data.get('pwdComplexity') or data.get('pwd_complexity') or b'' + if isinstance(pwd, str): + try: + pwd = utils.base64_url_decode(pwd) + except Exception: + pwd = b'' + return PamRecordRotation( + record_uid=str(record_uid), + revision=revision, + configuration_uid=str( + data.get('configurationUid') or data.get('configuration_uid') or ''), + schedule=str(data.get('schedule') or ''), + pwd_complexity=pwd if isinstance(pwd, (bytes, bytearray)) else b'', + disabled=bool(data.get('disabled', False)), + resource_uid=str(data.get('resourceUid') or data.get('resource_uid') or ''), + last_rotation=int(data.get('lastRotation') or data.get('last_rotation') or 0), + last_rotation_status=int( + data.get('lastRotationStatus') or data.get('last_rotation_status') or 0), + ) + + class IPamStorage(abc.ABC): @property @abc.abstractmethod diff --git a/keepersdk-package/src/keepersdk/proto/APIRequest_pb2.py b/keepersdk-package/src/keepersdk/proto/APIRequest_pb2.py index 1b85c2f4..e56ab96a 100644 --- a/keepersdk-package/src/keepersdk/proto/APIRequest_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/APIRequest_pb2.py @@ -25,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\"\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') +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\"\xc1\x01\n\x08KeysInfo\x12\x18\n\x10\x65ncryptionParams\x18\x01 \x01(\x0c\x12\x18\n\x10\x65ncryptedDataKey\x18\x02 \x01(\x0c\x12\x19\n\x11\x64\x61taKeyBackupDate\x18\x03 \x01(\x01\x12\x13\n\x0buserAuthUid\x18\x04 \x01(\x0c\x12\x1b\n\x13\x65ncryptedPrivateKey\x18\x05 \x01(\x0c\x12\x1e\n\x16\x65ncryptedEccPrivateKey\x18\x06 \x01(\x0c\x12\x14\n\x0c\x65\x63\x63PublicKey\x18\x07 \x01(\x0c\"\xe6\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\x12*\n\x08keysInfo\x18\x11 \x01(\x0b\x32\x18.Authentication.KeysInfo\x12\x11\n\tclientKey\x18\x12 \x01(\x0c\"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\"\xdc\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\x12\x16\n\x0e\x63hallengeToken\x18\n \x01(\x0c\"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\"\xe1\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\x12\x16\n\x0e\x63hallengeToken\x18\x06 \x01(\x0c\"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) @@ -33,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=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['_SUPPORTEDLANGUAGE']._serialized_start=21867 + _globals['_SUPPORTEDLANGUAGE']._serialized_end=22206 + _globals['_LOGINTYPE']._serialized_start=22208 + _globals['_LOGINTYPE']._serialized_end=22315 + _globals['_DEVICESTATUS']._serialized_start=22317 + _globals['_DEVICESTATUS']._serialized_end=22430 + _globals['_LICENSESTATUS']._serialized_start=22432 + _globals['_LICENSESTATUS']._serialized_end=22497 + _globals['_ACCOUNTTYPE']._serialized_start=22499 + _globals['_ACCOUNTTYPE']._serialized_end=22554 + _globals['_SESSIONTOKENTYPE']._serialized_start=22557 + _globals['_SESSIONTOKENTYPE']._serialized_end=22844 + _globals['_VERSION']._serialized_start=22846 + _globals['_VERSION']._serialized_end=22917 + _globals['_MASTERPASSWORDREENTRYACTIONTYPE']._serialized_start=22919 + _globals['_MASTERPASSWORDREENTRYACTIONTYPE']._serialized_end=22974 + _globals['_LOGINMETHOD']._serialized_start=22976 + _globals['_LOGINMETHOD']._serialized_end=23084 + _globals['_LOGINSTATE']._serialized_start=23087 + _globals['_LOGINSTATE']._serialized_end=23661 + _globals['_ENCRYPTEDDATAKEYTYPE']._serialized_start=23663 + _globals['_ENCRYPTEDDATAKEYTYPE']._serialized_end=23770 + _globals['_PASSWORDMETHOD']._serialized_start=23772 + _globals['_PASSWORDMETHOD']._serialized_end=23817 + _globals['_TWOFACTORPUSHTYPE']._serialized_start=23820 + _globals['_TWOFACTORPUSHTYPE']._serialized_end=24005 + _globals['_TWOFACTORVALUETYPE']._serialized_start=24008 + _globals['_TWOFACTORVALUETYPE']._serialized_end=24203 + _globals['_TWOFACTORCHANNELTYPE']._serialized_start=24206 + _globals['_TWOFACTORCHANNELTYPE']._serialized_end=24431 + _globals['_TWOFACTOREXPIRATION']._serialized_start=24434 + _globals['_TWOFACTOREXPIRATION']._serialized_end=24605 + _globals['_LICENSETYPE']._serialized_start=24607 + _globals['_LICENSETYPE']._serialized_end=24671 + _globals['_OBJECTTYPES']._serialized_start=24673 + _globals['_OBJECTTYPES']._serialized_end=24778 + _globals['_ENCRYPTEDOBJECTTYPE']._serialized_start=24781 + _globals['_ENCRYPTEDOBJECTTYPE']._serialized_end=25070 + _globals['_MASTERPASSWORDREENTRYSTATUS']._serialized_start=25072 + _globals['_MASTERPASSWORDREENTRYSTATUS']._serialized_end=25149 + _globals['_ALTERNATEAUTHENTICATIONTYPE']._serialized_start=25151 + _globals['_ALTERNATEAUTHENTICATIONTYPE']._serialized_end=25247 + _globals['_THROTTLETYPE']._serialized_start=25250 + _globals['_THROTTLETYPE']._serialized_end=25532 + _globals['_REGION']._serialized_start=25534 + _globals['_REGION']._serialized_end=25606 + _globals['_APPLICATIONSHARETYPE']._serialized_start=25608 + _globals['_APPLICATIONSHARETYPE']._serialized_end=25676 + _globals['_TIMELIMITEDACCESSTYPE']._serialized_start=25679 + _globals['_TIMELIMITEDACCESSTYPE']._serialized_end=25843 + _globals['_BACKUPKEYTYPE']._serialized_start=25845 + _globals['_BACKUPKEYTYPE']._serialized_end=25905 + _globals['_GENERICSTATUS']._serialized_start=25907 + _globals['_GENERICSTATUS']._serialized_end=26021 + _globals['_AUTHENTICATORATTACHMENT']._serialized_start=26023 + _globals['_AUTHENTICATORATTACHMENT']._serialized_end=26101 + _globals['_PASSKEYPURPOSE']._serialized_start=26103 + _globals['_PASSKEYPURPOSE']._serialized_end=26148 + _globals['_CLIENTFORMFACTOR']._serialized_start=26150 + _globals['_CLIENTFORMFACTOR']._serialized_end=26225 _globals['_QRCMESSAGEKEY']._serialized_start=54 _globals['_QRCMESSAGEKEY']._serialized_end=177 _globals['_APIREQUEST']._serialized_start=180 @@ -119,318 +119,320 @@ _globals['_TWOFACTORCHANNEL']._serialized_end=1636 _globals['_STARTLOGINREQUEST']._serialized_start=1639 _globals['_STARTLOGINREQUEST']._serialized_end=2019 - _globals['_LOGINRESPONSE']._serialized_start=2022 - _globals['_LOGINRESPONSE']._serialized_end=2573 - _globals['_SWITCHLISTELEMENT']._serialized_start=2575 - _globals['_SWITCHLISTELEMENT']._serialized_end=2693 - _globals['_SWITCHLISTRESPONSE']._serialized_start=2695 - _globals['_SWITCHLISTRESPONSE']._serialized_end=2768 - _globals['_SSOUSERINFO']._serialized_start=2771 - _globals['_SSOUSERINFO']._serialized_end=2911 - _globals['_PRELOGINRESPONSE']._serialized_start=2914 - _globals['_PRELOGINRESPONSE']._serialized_end=3128 - _globals['_LOGINASUSERREQUEST']._serialized_start=3130 - _globals['_LOGINASUSERREQUEST']._serialized_end=3168 - _globals['_LOGINASUSERRESPONSE']._serialized_start=3170 - _globals['_LOGINASUSERRESPONSE']._serialized_end=3257 - _globals['_VALIDATEAUTHHASHREQUEST']._serialized_start=3260 - _globals['_VALIDATEAUTHHASHREQUEST']._serialized_end=3392 - _globals['_TWOFACTORCHANNELINFO']._serialized_start=3395 - _globals['_TWOFACTORCHANNELINFO']._serialized_end=3719 - _globals['_TWOFACTORDUOSTATUS']._serialized_start=3721 - _globals['_TWOFACTORDUOSTATUS']._serialized_end=3821 - _globals['_TWOFACTORADDREQUEST']._serialized_start=3824 - _globals['_TWOFACTORADDREQUEST']._serialized_end=4023 - _globals['_TWOFACTORRENAMEREQUEST']._serialized_start=4025 - _globals['_TWOFACTORRENAMEREQUEST']._serialized_end=4091 - _globals['_TWOFACTORADDRESPONSE']._serialized_start=4093 - _globals['_TWOFACTORADDRESPONSE']._serialized_end=4154 - _globals['_TWOFACTORDELETEREQUEST']._serialized_start=4156 - _globals['_TWOFACTORDELETEREQUEST']._serialized_end=4201 - _globals['_TWOFACTORLISTRESPONSE']._serialized_start=4203 - _globals['_TWOFACTORLISTRESPONSE']._serialized_end=4300 - _globals['_TWOFACTORUPDATEEXPIRATIONREQUEST']._serialized_start=4302 - _globals['_TWOFACTORUPDATEEXPIRATIONREQUEST']._serialized_end=4391 - _globals['_TWOFACTORVALIDATEREQUEST']._serialized_start=4394 - _globals['_TWOFACTORVALIDATEREQUEST']._serialized_end=4595 - _globals['_TWOFACTORVALIDATERESPONSE']._serialized_start=4597 - _globals['_TWOFACTORVALIDATERESPONSE']._serialized_end=4653 - _globals['_TWOFACTORSENDPUSHREQUEST']._serialized_start=4656 - _globals['_TWOFACTORSENDPUSHREQUEST']._serialized_end=4840 - _globals['_LICENSE']._serialized_start=4843 - _globals['_LICENSE']._serialized_end=4974 - _globals['_OWNERLESSRECORD']._serialized_start=4976 - _globals['_OWNERLESSRECORD']._serialized_end=5047 - _globals['_OWNERLESSRECORDS']._serialized_start=5049 - _globals['_OWNERLESSRECORDS']._serialized_end=5125 - _globals['_USERAUTHREQUEST']._serialized_start=5128 - _globals['_USERAUTHREQUEST']._serialized_end=5343 - _globals['_UIDREQUEST']._serialized_start=5345 - _globals['_UIDREQUEST']._serialized_end=5370 - _globals['_DEVICEUPDATEREQUEST']._serialized_start=5373 - _globals['_DEVICEUPDATEREQUEST']._serialized_end=5628 - _globals['_DEVICEUPDATERESPONSE']._serialized_start=5631 - _globals['_DEVICEUPDATERESPONSE']._serialized_end=5887 - _globals['_REGISTERDEVICEINREGIONREQUEST']._serialized_start=5890 - _globals['_REGISTERDEVICEINREGIONREQUEST']._serialized_end=6103 - _globals['_REGISTRATIONREQUEST']._serialized_start=6106 - _globals['_REGISTRATIONREQUEST']._serialized_end=6482 - _globals['_CONVERTUSERTOV3REQUEST']._serialized_start=6485 - _globals['_CONVERTUSERTOV3REQUEST']._serialized_end=6693 - _globals['_REVISIONRESPONSE']._serialized_start=6695 - _globals['_REVISIONRESPONSE']._serialized_end=6731 - _globals['_CHANGEEMAILREQUEST']._serialized_start=6733 - _globals['_CHANGEEMAILREQUEST']._serialized_end=6771 - _globals['_CHANGEEMAILRESPONSE']._serialized_start=6773 - _globals['_CHANGEEMAILRESPONSE']._serialized_end=6829 - _globals['_EMAILVERIFICATIONLINKRESPONSE']._serialized_start=6831 - _globals['_EMAILVERIFICATIONLINKRESPONSE']._serialized_end=6885 - _globals['_SECURITYDATA']._serialized_start=6887 - _globals['_SECURITYDATA']._serialized_end=6928 - _globals['_SECURITYSCOREDATA']._serialized_start=6930 - _globals['_SECURITYSCOREDATA']._serialized_end=6994 - _globals['_SECURITYDATAREQUEST']._serialized_start=6997 - _globals['_SECURITYDATAREQUEST']._serialized_end=7264 - _globals['_SECURITYREPORTINCREMENTALDATA']._serialized_start=7267 - _globals['_SECURITYREPORTINCREMENTALDATA']._serialized_end=7593 - _globals['_SECURITYREPORT']._serialized_start=7596 - _globals['_SECURITYREPORT']._serialized_end=7883 - _globals['_SECURITYREPORTSAVEREQUEST']._serialized_start=7885 - _globals['_SECURITYREPORTSAVEREQUEST']._serialized_end=7995 - _globals['_SECURITYREPORTREQUEST']._serialized_start=7997 - _globals['_SECURITYREPORTREQUEST']._serialized_end=8038 - _globals['_SECURITYREPORTRESPONSE']._serialized_start=8041 - _globals['_SECURITYREPORTRESPONSE']._serialized_end=8286 - _globals['_INCREMENTALSECURITYDATAREQUEST']._serialized_start=8288 - _globals['_INCREMENTALSECURITYDATAREQUEST']._serialized_end=8347 - _globals['_INCREMENTALSECURITYDATARESPONSE']._serialized_start=8350 - _globals['_INCREMENTALSECURITYDATARESPONSE']._serialized_end=8496 - _globals['_REUSEDPASSWORDSREQUEST']._serialized_start=8498 - _globals['_REUSEDPASSWORDSREQUEST']._serialized_end=8537 - _globals['_SUMMARYCONSOLEREPORT']._serialized_start=8539 - _globals['_SUMMARYCONSOLEREPORT']._serialized_end=8601 - _globals['_CHANGETOKEYTYPEONE']._serialized_start=8603 - _globals['_CHANGETOKEYTYPEONE']._serialized_end=8727 - _globals['_CHANGETOKEYTYPEONEREQUEST']._serialized_start=8729 - _globals['_CHANGETOKEYTYPEONEREQUEST']._serialized_end=8820 - _globals['_CHANGETOKEYTYPEONESTATUS']._serialized_start=8822 - _globals['_CHANGETOKEYTYPEONESTATUS']._serialized_end=8907 - _globals['_CHANGETOKEYTYPEONERESPONSE']._serialized_start=8909 - _globals['_CHANGETOKEYTYPEONERESPONSE']._serialized_end=9013 - _globals['_GETCHANGEKEYTYPESREQUEST']._serialized_start=9016 - _globals['_GETCHANGEKEYTYPESREQUEST']._serialized_end=9201 - _globals['_GETCHANGEKEYTYPESRESPONSE']._serialized_start=9204 - _globals['_GETCHANGEKEYTYPESRESPONSE']._serialized_end=9334 - _globals['_ALLOWEDKEYTYPES']._serialized_start=9337 - _globals['_ALLOWEDKEYTYPES']._serialized_end=9466 - _globals['_CHANGEKEYTYPES']._serialized_start=9468 - _globals['_CHANGEKEYTYPES']._serialized_end=9529 - _globals['_CHANGEKEYTYPE']._serialized_start=9532 - _globals['_CHANGEKEYTYPE']._serialized_end=9746 - _globals['_SETKEY']._serialized_start=9748 - _globals['_SETKEY']._serialized_end=9781 - _globals['_SETKEYREQUEST']._serialized_start=9783 - _globals['_SETKEYREQUEST']._serialized_end=9836 - _globals['_CREATEUSERREQUEST']._serialized_start=9839 - _globals['_CREATEUSERREQUEST']._serialized_end=10497 - _globals['_NODEENFORCEMENTADDORUPDATEREQUEST']._serialized_start=10499 - _globals['_NODEENFORCEMENTADDORUPDATEREQUEST']._serialized_end=10586 - _globals['_NODEENFORCEMENTREMOVEREQUEST']._serialized_start=10588 - _globals['_NODEENFORCEMENTREMOVEREQUEST']._serialized_end=10655 - _globals['_APIREQUESTBYKEY']._serialized_start=10658 - _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 + _globals['_KEYSINFO']._serialized_start=2022 + _globals['_KEYSINFO']._serialized_end=2215 + _globals['_LOGINRESPONSE']._serialized_start=2218 + _globals['_LOGINRESPONSE']._serialized_end=2832 + _globals['_SWITCHLISTELEMENT']._serialized_start=2834 + _globals['_SWITCHLISTELEMENT']._serialized_end=2952 + _globals['_SWITCHLISTRESPONSE']._serialized_start=2954 + _globals['_SWITCHLISTRESPONSE']._serialized_end=3027 + _globals['_SSOUSERINFO']._serialized_start=3030 + _globals['_SSOUSERINFO']._serialized_end=3170 + _globals['_PRELOGINRESPONSE']._serialized_start=3173 + _globals['_PRELOGINRESPONSE']._serialized_end=3387 + _globals['_LOGINASUSERREQUEST']._serialized_start=3389 + _globals['_LOGINASUSERREQUEST']._serialized_end=3427 + _globals['_LOGINASUSERRESPONSE']._serialized_start=3429 + _globals['_LOGINASUSERRESPONSE']._serialized_end=3516 + _globals['_VALIDATEAUTHHASHREQUEST']._serialized_start=3519 + _globals['_VALIDATEAUTHHASHREQUEST']._serialized_end=3651 + _globals['_TWOFACTORCHANNELINFO']._serialized_start=3654 + _globals['_TWOFACTORCHANNELINFO']._serialized_end=4002 + _globals['_TWOFACTORDUOSTATUS']._serialized_start=4004 + _globals['_TWOFACTORDUOSTATUS']._serialized_end=4104 + _globals['_TWOFACTORADDREQUEST']._serialized_start=4107 + _globals['_TWOFACTORADDREQUEST']._serialized_end=4306 + _globals['_TWOFACTORRENAMEREQUEST']._serialized_start=4308 + _globals['_TWOFACTORRENAMEREQUEST']._serialized_end=4374 + _globals['_TWOFACTORADDRESPONSE']._serialized_start=4376 + _globals['_TWOFACTORADDRESPONSE']._serialized_end=4437 + _globals['_TWOFACTORDELETEREQUEST']._serialized_start=4439 + _globals['_TWOFACTORDELETEREQUEST']._serialized_end=4484 + _globals['_TWOFACTORLISTRESPONSE']._serialized_start=4486 + _globals['_TWOFACTORLISTRESPONSE']._serialized_end=4583 + _globals['_TWOFACTORUPDATEEXPIRATIONREQUEST']._serialized_start=4585 + _globals['_TWOFACTORUPDATEEXPIRATIONREQUEST']._serialized_end=4674 + _globals['_TWOFACTORVALIDATEREQUEST']._serialized_start=4677 + _globals['_TWOFACTORVALIDATEREQUEST']._serialized_end=4902 + _globals['_TWOFACTORVALIDATERESPONSE']._serialized_start=4904 + _globals['_TWOFACTORVALIDATERESPONSE']._serialized_end=4960 + _globals['_TWOFACTORSENDPUSHREQUEST']._serialized_start=4963 + _globals['_TWOFACTORSENDPUSHREQUEST']._serialized_end=5147 + _globals['_LICENSE']._serialized_start=5150 + _globals['_LICENSE']._serialized_end=5281 + _globals['_OWNERLESSRECORD']._serialized_start=5283 + _globals['_OWNERLESSRECORD']._serialized_end=5354 + _globals['_OWNERLESSRECORDS']._serialized_start=5356 + _globals['_OWNERLESSRECORDS']._serialized_end=5432 + _globals['_USERAUTHREQUEST']._serialized_start=5435 + _globals['_USERAUTHREQUEST']._serialized_end=5650 + _globals['_UIDREQUEST']._serialized_start=5652 + _globals['_UIDREQUEST']._serialized_end=5677 + _globals['_DEVICEUPDATEREQUEST']._serialized_start=5680 + _globals['_DEVICEUPDATEREQUEST']._serialized_end=5935 + _globals['_DEVICEUPDATERESPONSE']._serialized_start=5938 + _globals['_DEVICEUPDATERESPONSE']._serialized_end=6194 + _globals['_REGISTERDEVICEINREGIONREQUEST']._serialized_start=6197 + _globals['_REGISTERDEVICEINREGIONREQUEST']._serialized_end=6410 + _globals['_REGISTRATIONREQUEST']._serialized_start=6413 + _globals['_REGISTRATIONREQUEST']._serialized_end=6789 + _globals['_CONVERTUSERTOV3REQUEST']._serialized_start=6792 + _globals['_CONVERTUSERTOV3REQUEST']._serialized_end=7000 + _globals['_REVISIONRESPONSE']._serialized_start=7002 + _globals['_REVISIONRESPONSE']._serialized_end=7038 + _globals['_CHANGEEMAILREQUEST']._serialized_start=7040 + _globals['_CHANGEEMAILREQUEST']._serialized_end=7078 + _globals['_CHANGEEMAILRESPONSE']._serialized_start=7080 + _globals['_CHANGEEMAILRESPONSE']._serialized_end=7136 + _globals['_EMAILVERIFICATIONLINKRESPONSE']._serialized_start=7138 + _globals['_EMAILVERIFICATIONLINKRESPONSE']._serialized_end=7192 + _globals['_SECURITYDATA']._serialized_start=7194 + _globals['_SECURITYDATA']._serialized_end=7235 + _globals['_SECURITYSCOREDATA']._serialized_start=7237 + _globals['_SECURITYSCOREDATA']._serialized_end=7301 + _globals['_SECURITYDATAREQUEST']._serialized_start=7304 + _globals['_SECURITYDATAREQUEST']._serialized_end=7571 + _globals['_SECURITYREPORTINCREMENTALDATA']._serialized_start=7574 + _globals['_SECURITYREPORTINCREMENTALDATA']._serialized_end=7900 + _globals['_SECURITYREPORT']._serialized_start=7903 + _globals['_SECURITYREPORT']._serialized_end=8190 + _globals['_SECURITYREPORTSAVEREQUEST']._serialized_start=8192 + _globals['_SECURITYREPORTSAVEREQUEST']._serialized_end=8302 + _globals['_SECURITYREPORTREQUEST']._serialized_start=8304 + _globals['_SECURITYREPORTREQUEST']._serialized_end=8345 + _globals['_SECURITYREPORTRESPONSE']._serialized_start=8348 + _globals['_SECURITYREPORTRESPONSE']._serialized_end=8593 + _globals['_INCREMENTALSECURITYDATAREQUEST']._serialized_start=8595 + _globals['_INCREMENTALSECURITYDATAREQUEST']._serialized_end=8654 + _globals['_INCREMENTALSECURITYDATARESPONSE']._serialized_start=8657 + _globals['_INCREMENTALSECURITYDATARESPONSE']._serialized_end=8803 + _globals['_REUSEDPASSWORDSREQUEST']._serialized_start=8805 + _globals['_REUSEDPASSWORDSREQUEST']._serialized_end=8844 + _globals['_SUMMARYCONSOLEREPORT']._serialized_start=8846 + _globals['_SUMMARYCONSOLEREPORT']._serialized_end=8908 + _globals['_CHANGETOKEYTYPEONE']._serialized_start=8910 + _globals['_CHANGETOKEYTYPEONE']._serialized_end=9034 + _globals['_CHANGETOKEYTYPEONEREQUEST']._serialized_start=9036 + _globals['_CHANGETOKEYTYPEONEREQUEST']._serialized_end=9127 + _globals['_CHANGETOKEYTYPEONESTATUS']._serialized_start=9129 + _globals['_CHANGETOKEYTYPEONESTATUS']._serialized_end=9214 + _globals['_CHANGETOKEYTYPEONERESPONSE']._serialized_start=9216 + _globals['_CHANGETOKEYTYPEONERESPONSE']._serialized_end=9320 + _globals['_GETCHANGEKEYTYPESREQUEST']._serialized_start=9323 + _globals['_GETCHANGEKEYTYPESREQUEST']._serialized_end=9508 + _globals['_GETCHANGEKEYTYPESRESPONSE']._serialized_start=9511 + _globals['_GETCHANGEKEYTYPESRESPONSE']._serialized_end=9641 + _globals['_ALLOWEDKEYTYPES']._serialized_start=9644 + _globals['_ALLOWEDKEYTYPES']._serialized_end=9773 + _globals['_CHANGEKEYTYPES']._serialized_start=9775 + _globals['_CHANGEKEYTYPES']._serialized_end=9836 + _globals['_CHANGEKEYTYPE']._serialized_start=9839 + _globals['_CHANGEKEYTYPE']._serialized_end=10053 + _globals['_SETKEY']._serialized_start=10055 + _globals['_SETKEY']._serialized_end=10088 + _globals['_SETKEYREQUEST']._serialized_start=10090 + _globals['_SETKEYREQUEST']._serialized_end=10143 + _globals['_CREATEUSERREQUEST']._serialized_start=10146 + _globals['_CREATEUSERREQUEST']._serialized_end=10804 + _globals['_NODEENFORCEMENTADDORUPDATEREQUEST']._serialized_start=10806 + _globals['_NODEENFORCEMENTADDORUPDATEREQUEST']._serialized_end=10893 + _globals['_NODEENFORCEMENTREMOVEREQUEST']._serialized_start=10895 + _globals['_NODEENFORCEMENTREMOVEREQUEST']._serialized_end=10962 + _globals['_APIREQUESTBYKEY']._serialized_start=10965 + _globals['_APIREQUESTBYKEY']._serialized_end=11148 + _globals['_APIREQUESTBYKATOKAKEY']._serialized_start=11151 + _globals['_APIREQUESTBYKATOKAKEY']._serialized_end=11350 + _globals['_MEMCACHEREQUEST']._serialized_start=11352 + _globals['_MEMCACHEREQUEST']._serialized_end=11398 + _globals['_MEMCACHERESPONSE']._serialized_start=11400 + _globals['_MEMCACHERESPONSE']._serialized_end=11446 + _globals['_MASTERPASSWORDREENTRYREQUEST']._serialized_start=11448 + _globals['_MASTERPASSWORDREENTRYREQUEST']._serialized_end=11567 + _globals['_MASTERPASSWORDREENTRYRESPONSE']._serialized_start=11569 + _globals['_MASTERPASSWORDREENTRYRESPONSE']._serialized_end=11661 + _globals['_DEVICEREGISTRATIONREQUEST']._serialized_start=11664 + _globals['_DEVICEREGISTRATIONREQUEST']._serialized_end=11861 + _globals['_DEVICEVERIFICATIONREQUEST']._serialized_start=11864 + _globals['_DEVICEVERIFICATIONREQUEST']._serialized_end=12018 + _globals['_DEVICEVERIFICATIONRESPONSE']._serialized_start=12021 + _globals['_DEVICEVERIFICATIONRESPONSE']._serialized_end=12199 + _globals['_DEVICEAPPROVALREQUEST']._serialized_start=12202 + _globals['_DEVICEAPPROVALREQUEST']._serialized_end=12402 + _globals['_DEVICEAPPROVALRESPONSE']._serialized_start=12404 + _globals['_DEVICEAPPROVALRESPONSE']._serialized_end=12461 + _globals['_APPROVEDEVICEREQUEST']._serialized_start=12463 + _globals['_APPROVEDEVICEREQUEST']._serialized_end=12589 + _globals['_ENTERPRISEUSERALIASREQUEST']._serialized_start=12591 + _globals['_ENTERPRISEUSERALIASREQUEST']._serialized_end=12660 + _globals['_ENTERPRISEUSERADDALIASREQUEST']._serialized_start=12662 + _globals['_ENTERPRISEUSERADDALIASREQUEST']._serialized_end=12751 + _globals['_ENTERPRISEUSERADDALIASREQUESTV2']._serialized_start=12753 + _globals['_ENTERPRISEUSERADDALIASREQUESTV2']._serialized_end=12872 + _globals['_ENTERPRISEUSERADDALIASSTATUS']._serialized_start=12874 + _globals['_ENTERPRISEUSERADDALIASSTATUS']._serialized_end=12946 + _globals['_ENTERPRISEUSERADDALIASRESPONSE']._serialized_start=12948 + _globals['_ENTERPRISEUSERADDALIASRESPONSE']._serialized_end=13042 + _globals['_DEVICE']._serialized_start=13044 + _globals['_DEVICE']._serialized_end=13082 + _globals['_REGISTERDEVICEDATAKEYREQUEST']._serialized_start=13084 + _globals['_REGISTERDEVICEDATAKEYREQUEST']._serialized_end=13176 + _globals['_VALIDATECREATEUSERVERIFICATIONCODEREQUEST']._serialized_start=13178 + _globals['_VALIDATECREATEUSERVERIFICATIONCODEREQUEST']._serialized_end=13288 + _globals['_VALIDATEDEVICEVERIFICATIONCODEREQUEST']._serialized_start=13291 + _globals['_VALIDATEDEVICEVERIFICATIONCODEREQUEST']._serialized_end=13454 + _globals['_SENDSESSIONMESSAGEREQUEST']._serialized_start=13456 + _globals['_SENDSESSIONMESSAGEREQUEST']._serialized_end=13545 + _globals['_GLOBALUSERACCOUNT']._serialized_start=13547 + _globals['_GLOBALUSERACCOUNT']._serialized_end=13624 + _globals['_ACCOUNTUSERNAME']._serialized_start=13626 + _globals['_ACCOUNTUSERNAME']._serialized_end=13681 + _globals['_SSOSERVICEPROVIDERREQUEST']._serialized_start=13683 + _globals['_SSOSERVICEPROVIDERREQUEST']._serialized_end=13763 + _globals['_SSOSERVICEPROVIDERRESPONSE']._serialized_start=13765 + _globals['_SSOSERVICEPROVIDERRESPONSE']._serialized_end=13862 + _globals['_USERSETTINGREQUEST']._serialized_start=13864 + _globals['_USERSETTINGREQUEST']._serialized_end=13916 + _globals['_THROTTLESTATE']._serialized_start=13918 + _globals['_THROTTLESTATE']._serialized_end=14020 + _globals['_THROTTLESTATE2']._serialized_start=14023 + _globals['_THROTTLESTATE2']._serialized_end=14204 + _globals['_DEVICEINFORMATION']._serialized_start=14207 + _globals['_DEVICEINFORMATION']._serialized_end=14358 + _globals['_USERSETTING']._serialized_start=14360 + _globals['_USERSETTING']._serialized_end=14402 + _globals['_USERDATAKEYREQUEST']._serialized_start=14404 + _globals['_USERDATAKEYREQUEST']._serialized_end=14450 + _globals['_USERDATAKEYBYNODEREQUEST']._serialized_start=14452 + _globals['_USERDATAKEYBYNODEREQUEST']._serialized_end=14495 + _globals['_ENTERPRISEUSERIDDATAKEYPAIR']._serialized_start=14498 + _globals['_ENTERPRISEUSERIDDATAKEYPAIR']._serialized_end=14626 + _globals['_USERDATAKEY']._serialized_start=14629 + _globals['_USERDATAKEY']._serialized_end=14778 + _globals['_USERDATAKEYRESPONSE']._serialized_start=14780 + _globals['_USERDATAKEYRESPONSE']._serialized_end=14902 + _globals['_MASTERPASSWORDRECOVERYVERIFICATIONREQUEST']._serialized_start=14904 + _globals['_MASTERPASSWORDRECOVERYVERIFICATIONREQUEST']._serialized_end=14976 + _globals['_GETSECURITYQUESTIONV3REQUEST']._serialized_start=14978 + _globals['_GETSECURITYQUESTIONV3REQUEST']._serialized_end=15063 + _globals['_GETSECURITYQUESTIONV3RESPONSE']._serialized_start=15065 + _globals['_GETSECURITYQUESTIONV3RESPONSE']._serialized_end=15179 + _globals['_GETDATAKEYBACKUPV3REQUEST']._serialized_start=15181 + _globals['_GETDATAKEYBACKUPV3REQUEST']._serialized_end=15291 + _globals['_PASSWORDRULES']._serialized_start=15293 + _globals['_PASSWORDRULES']._serialized_end=15411 + _globals['_GETDATAKEYBACKUPV3RESPONSE']._serialized_start=15414 + _globals['_GETDATAKEYBACKUPV3RESPONSE']._serialized_end=15743 + _globals['_GETPUBLICKEYSREQUEST']._serialized_start=15745 + _globals['_GETPUBLICKEYSREQUEST']._serialized_end=15786 + _globals['_PUBLICKEYRESPONSE']._serialized_start=15789 + _globals['_PUBLICKEYRESPONSE']._serialized_end=15923 + _globals['_GETPUBLICKEYSRESPONSE']._serialized_start=15925 + _globals['_GETPUBLICKEYSRESPONSE']._serialized_end=16005 + _globals['_SETECCKEYPAIRREQUEST']._serialized_start=16007 + _globals['_SETECCKEYPAIRREQUEST']._serialized_end=16077 + _globals['_SETECCKEYPAIRSREQUEST']._serialized_start=16079 + _globals['_SETECCKEYPAIRSREQUEST']._serialized_end=16152 + _globals['_SETECCKEYPAIRSRESPONSE']._serialized_start=16154 + _globals['_SETECCKEYPAIRSRESPONSE']._serialized_end=16236 + _globals['_TEAMECCKEYPAIR']._serialized_start=16238 + _globals['_TEAMECCKEYPAIR']._serialized_end=16319 + _globals['_TEAMECCKEYPAIRRESPONSE']._serialized_start=16321 + _globals['_TEAMECCKEYPAIRRESPONSE']._serialized_end=16409 + _globals['_GETKSMPUBLICKEYSREQUEST']._serialized_start=16411 + _globals['_GETKSMPUBLICKEYSREQUEST']._serialized_end=16479 + _globals['_DEVICEPUBLICKEYRESPONSE']._serialized_start=16481 + _globals['_DEVICEPUBLICKEYRESPONSE']._serialized_end=16566 + _globals['_GETKSMPUBLICKEYSRESPONSE']._serialized_start=16568 + _globals['_GETKSMPUBLICKEYSRESPONSE']._serialized_end=16657 + _globals['_ADDAPPSHARESREQUEST']._serialized_start=16659 + _globals['_ADDAPPSHARESREQUEST']._serialized_end=16747 + _globals['_REMOVEAPPSHARESREQUEST']._serialized_start=16749 + _globals['_REMOVEAPPSHARESREQUEST']._serialized_end=16811 + _globals['_APPSHAREADD']._serialized_start=16814 + _globals['_APPSHAREADD']._serialized_end=16949 + _globals['_APPSHARE']._serialized_start=16952 + _globals['_APPSHARE']._serialized_end=17089 + _globals['_ADDAPPCLIENTREQUEST']._serialized_start=17092 + _globals['_ADDAPPCLIENTREQUEST']._serialized_end=17309 + _globals['_REMOVEAPPCLIENTSREQUEST']._serialized_start=17311 + _globals['_REMOVEAPPCLIENTSREQUEST']._serialized_end=17375 + _globals['_ADDEXTERNALSHAREREQUEST']._serialized_start=17378 + _globals['_ADDEXTERNALSHAREREQUEST']._serialized_end=17548 + _globals['_APPCLIENT']._serialized_start=17551 + _globals['_APPCLIENT']._serialized_end=17826 + _globals['_GETAPPINFOREQUEST']._serialized_start=17828 + _globals['_GETAPPINFOREQUEST']._serialized_end=17869 + _globals['_APPINFO']._serialized_start=17872 + _globals['_APPINFO']._serialized_end=18014 + _globals['_GETAPPINFORESPONSE']._serialized_start=18016 + _globals['_GETAPPINFORESPONSE']._serialized_end=18078 + _globals['_APPLICATIONSUMMARY']._serialized_start=18081 + _globals['_APPLICATIONSUMMARY']._serialized_end=18294 + _globals['_GETAPPLICATIONSSUMMARYRESPONSE']._serialized_start=18296 + _globals['_GETAPPLICATIONSSUMMARYRESPONSE']._serialized_end=18392 + _globals['_GETVERIFICATIONTOKENREQUEST']._serialized_start=18394 + _globals['_GETVERIFICATIONTOKENREQUEST']._serialized_end=18441 + _globals['_GETVERIFICATIONTOKENRESPONSE']._serialized_start=18443 + _globals['_GETVERIFICATIONTOKENRESPONSE']._serialized_end=18509 + _globals['_SENDSHAREINVITEREQUEST']._serialized_start=18511 + _globals['_SENDSHAREINVITEREQUEST']._serialized_end=18550 + _globals['_TIMELIMITEDACCESSREQUEST']._serialized_start=18553 + _globals['_TIMELIMITEDACCESSREQUEST']._serialized_end=18750 + _globals['_TIMELIMITEDACCESSSTATUS']._serialized_start=18752 + _globals['_TIMELIMITEDACCESSSTATUS']._serialized_end=18807 + _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_start=18810 + _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_end=19058 + _globals['_REQUESTDOWNLOADREQUEST']._serialized_start=19060 + _globals['_REQUESTDOWNLOADREQUEST']._serialized_end=19103 + _globals['_REQUESTDOWNLOADRESPONSE']._serialized_start=19105 + _globals['_REQUESTDOWNLOADRESPONSE']._serialized_end=19208 + _globals['_DOWNLOAD']._serialized_start=19210 + _globals['_DOWNLOAD']._serialized_end=19278 + _globals['_DELETEUSERREQUEST']._serialized_start=19280 + _globals['_DELETEUSERREQUEST']._serialized_end=19315 + _globals['_CHANGEMASTERPASSWORDREQUEST']._serialized_start=19318 + _globals['_CHANGEMASTERPASSWORDREQUEST']._serialized_end=19450 + _globals['_CHANGEMASTERPASSWORDRESPONSE']._serialized_start=19452 + _globals['_CHANGEMASTERPASSWORDRESPONSE']._serialized_end=19513 + _globals['_ACCOUNTRECOVERYSETUPREQUEST']._serialized_start=19515 + _globals['_ACCOUNTRECOVERYSETUPREQUEST']._serialized_end=19604 + _globals['_ACCOUNTRECOVERYVERIFYCODERESPONSE']._serialized_start=19607 + _globals['_ACCOUNTRECOVERYVERIFYCODERESPONSE']._serialized_end=19779 + _globals['_EMERGENCYACCESSLOGINREQUEST']._serialized_start=19781 + _globals['_EMERGENCYACCESSLOGINREQUEST']._serialized_end=19825 + _globals['_EMERGENCYACCESSLOGINRESPONSE']._serialized_start=19828 + _globals['_EMERGENCYACCESSLOGINRESPONSE']._serialized_end=20009 + _globals['_USERTEAMKEY']._serialized_start=20012 + _globals['_USERTEAMKEY']._serialized_end=20190 + _globals['_GENERICREQUESTRESPONSE']._serialized_start=20192 + _globals['_GENERICREQUESTRESPONSE']._serialized_end=20233 + _globals['_PASSKEYREGISTRATIONREQUEST']._serialized_start=20235 + _globals['_PASSKEYREGISTRATIONREQUEST']._serialized_end=20337 + _globals['_PASSKEYREGISTRATIONRESPONSE']._serialized_start=20339 + _globals['_PASSKEYREGISTRATIONRESPONSE']._serialized_end=20419 + _globals['_PASSKEYREGISTRATIONFINALIZATION']._serialized_start=20422 + _globals['_PASSKEYREGISTRATIONFINALIZATION']._serialized_end=20554 + _globals['_PASSKEYAUTHENTICATIONREQUEST']._serialized_start=20557 + _globals['_PASSKEYAUTHENTICATIONREQUEST']._serialized_end=20864 + _globals['_PASSKEYAUTHENTICATIONRESPONSE']._serialized_start=20867 + _globals['_PASSKEYAUTHENTICATIONRESPONSE']._serialized_end=21006 + _globals['_PASSKEYVALIDATIONREQUEST']._serialized_start=21009 + _globals['_PASSKEYVALIDATIONREQUEST']._serialized_end=21200 + _globals['_PASSKEYVALIDATIONRESPONSE']._serialized_start=21202 + _globals['_PASSKEYVALIDATIONRESPONSE']._serialized_end=21275 + _globals['_UPDATEPASSKEYREQUEST']._serialized_start=21277 + _globals['_UPDATEPASSKEYREQUEST']._serialized_end=21381 + _globals['_PASSKEYLISTREQUEST']._serialized_start=21383 + _globals['_PASSKEYLISTREQUEST']._serialized_end=21428 + _globals['_PASSKEYINFO']._serialized_start=21431 + _globals['_PASSKEYINFO']._serialized_end=21595 + _globals['_PASSKEYLISTRESPONSE']._serialized_start=21597 + _globals['_PASSKEYLISTRESPONSE']._serialized_end=21668 + _globals['_TRANSLATIONINFO']._serialized_start=21670 + _globals['_TRANSLATIONINFO']._serialized_end=21737 + _globals['_TRANSLATIONREQUEST']._serialized_start=21739 + _globals['_TRANSLATIONREQUEST']._serialized_end=21783 + _globals['_TRANSLATIONRESPONSE']._serialized_start=21785 + _globals['_TRANSLATIONRESPONSE']._serialized_end=21864 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/APIRequest_pb2.pyi b/keepersdk-package/src/keepersdk/proto/APIRequest_pb2.pyi index 571e1409..99de21ff 100644 --- a/keepersdk-package/src/keepersdk/proto/APIRequest_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/APIRequest_pb2.pyi @@ -1,4 +1,4 @@ -import enterprise_pb2 as _enterprise_pb2 +from . import enterprise_pb2 as _enterprise_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 @@ -651,8 +651,26 @@ class StartLoginRequest(_message.Message): 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: bool = ..., cloneCode: _Optional[bytes] = ..., v2TwoFactorToken: _Optional[str] = ..., accountUid: _Optional[bytes] = ..., fromSessionToken: _Optional[bytes] = ...) -> None: ... +class KeysInfo(_message.Message): + __slots__ = ("encryptionParams", "encryptedDataKey", "dataKeyBackupDate", "userAuthUid", "encryptedPrivateKey", "encryptedEccPrivateKey", "eccPublicKey") + ENCRYPTIONPARAMS_FIELD_NUMBER: _ClassVar[int] + ENCRYPTEDDATAKEY_FIELD_NUMBER: _ClassVar[int] + DATAKEYBACKUPDATE_FIELD_NUMBER: _ClassVar[int] + USERAUTHUID_FIELD_NUMBER: _ClassVar[int] + ENCRYPTEDPRIVATEKEY_FIELD_NUMBER: _ClassVar[int] + ENCRYPTEDECCPRIVATEKEY_FIELD_NUMBER: _ClassVar[int] + ECCPUBLICKEY_FIELD_NUMBER: _ClassVar[int] + encryptionParams: bytes + encryptedDataKey: bytes + dataKeyBackupDate: float + userAuthUid: bytes + encryptedPrivateKey: bytes + encryptedEccPrivateKey: bytes + eccPublicKey: bytes + def __init__(self, encryptionParams: _Optional[bytes] = ..., encryptedDataKey: _Optional[bytes] = ..., dataKeyBackupDate: _Optional[float] = ..., userAuthUid: _Optional[bytes] = ..., encryptedPrivateKey: _Optional[bytes] = ..., encryptedEccPrivateKey: _Optional[bytes] = ..., eccPublicKey: _Optional[bytes] = ...) -> None: ... + class LoginResponse(_message.Message): - __slots__ = ("loginState", "accountUid", "primaryUsername", "encryptedDataKey", "encryptedDataKeyType", "encryptedLoginToken", "encryptedSessionToken", "sessionTokenType", "message", "url", "channels", "salt", "cloneCode", "stateSpecificValue", "ssoClientVersion", "sessionTokenTypeModifier") + __slots__ = ("loginState", "accountUid", "primaryUsername", "encryptedDataKey", "encryptedDataKeyType", "encryptedLoginToken", "encryptedSessionToken", "sessionTokenType", "message", "url", "channels", "salt", "cloneCode", "stateSpecificValue", "ssoClientVersion", "sessionTokenTypeModifier", "keysInfo", "clientKey") LOGINSTATE_FIELD_NUMBER: _ClassVar[int] ACCOUNTUID_FIELD_NUMBER: _ClassVar[int] PRIMARYUSERNAME_FIELD_NUMBER: _ClassVar[int] @@ -669,6 +687,8 @@ class LoginResponse(_message.Message): STATESPECIFICVALUE_FIELD_NUMBER: _ClassVar[int] SSOCLIENTVERSION_FIELD_NUMBER: _ClassVar[int] SESSIONTOKENTYPEMODIFIER_FIELD_NUMBER: _ClassVar[int] + KEYSINFO_FIELD_NUMBER: _ClassVar[int] + CLIENTKEY_FIELD_NUMBER: _ClassVar[int] loginState: LoginState accountUid: bytes primaryUsername: str @@ -685,7 +705,9 @@ class LoginResponse(_message.Message): stateSpecificValue: str ssoClientVersion: str sessionTokenTypeModifier: str - def __init__(self, loginState: _Optional[_Union[LoginState, str]] = ..., accountUid: _Optional[bytes] = ..., primaryUsername: _Optional[str] = ..., encryptedDataKey: _Optional[bytes] = ..., encryptedDataKeyType: _Optional[_Union[EncryptedDataKeyType, str]] = ..., encryptedLoginToken: _Optional[bytes] = ..., encryptedSessionToken: _Optional[bytes] = ..., sessionTokenType: _Optional[_Union[SessionTokenType, str]] = ..., message: _Optional[str] = ..., url: _Optional[str] = ..., channels: _Optional[_Iterable[_Union[TwoFactorChannelInfo, _Mapping]]] = ..., salt: _Optional[_Iterable[_Union[Salt, _Mapping]]] = ..., cloneCode: _Optional[bytes] = ..., stateSpecificValue: _Optional[str] = ..., ssoClientVersion: _Optional[str] = ..., sessionTokenTypeModifier: _Optional[str] = ...) -> None: ... + keysInfo: KeysInfo + clientKey: bytes + def __init__(self, loginState: _Optional[_Union[LoginState, str]] = ..., accountUid: _Optional[bytes] = ..., primaryUsername: _Optional[str] = ..., encryptedDataKey: _Optional[bytes] = ..., encryptedDataKeyType: _Optional[_Union[EncryptedDataKeyType, str]] = ..., encryptedLoginToken: _Optional[bytes] = ..., encryptedSessionToken: _Optional[bytes] = ..., sessionTokenType: _Optional[_Union[SessionTokenType, str]] = ..., message: _Optional[str] = ..., url: _Optional[str] = ..., channels: _Optional[_Iterable[_Union[TwoFactorChannelInfo, _Mapping]]] = ..., salt: _Optional[_Iterable[_Union[Salt, _Mapping]]] = ..., cloneCode: _Optional[bytes] = ..., stateSpecificValue: _Optional[str] = ..., ssoClientVersion: _Optional[str] = ..., sessionTokenTypeModifier: _Optional[str] = ..., keysInfo: _Optional[_Union[KeysInfo, _Mapping]] = ..., clientKey: _Optional[bytes] = ...) -> None: ... class SwitchListElement(_message.Message): __slots__ = ("username", "fullName", "authRequired", "isLinked", "profilePicUrl") @@ -760,7 +782,7 @@ class ValidateAuthHashRequest(_message.Message): def __init__(self, passwordMethod: _Optional[_Union[PasswordMethod, str]] = ..., authResponse: _Optional[bytes] = ..., encryptedLoginToken: _Optional[bytes] = ...) -> None: ... class TwoFactorChannelInfo(_message.Message): - __slots__ = ("channelType", "channel_uid", "channelName", "challenge", "capabilities", "phoneNumber", "maxExpiration", "createdOn", "lastFrequency") + __slots__ = ("channelType", "channel_uid", "channelName", "challenge", "capabilities", "phoneNumber", "maxExpiration", "createdOn", "lastFrequency", "challengeToken") CHANNELTYPE_FIELD_NUMBER: _ClassVar[int] CHANNEL_UID_FIELD_NUMBER: _ClassVar[int] CHANNELNAME_FIELD_NUMBER: _ClassVar[int] @@ -770,6 +792,7 @@ class TwoFactorChannelInfo(_message.Message): MAXEXPIRATION_FIELD_NUMBER: _ClassVar[int] CREATEDON_FIELD_NUMBER: _ClassVar[int] LASTFREQUENCY_FIELD_NUMBER: _ClassVar[int] + CHALLENGETOKEN_FIELD_NUMBER: _ClassVar[int] channelType: TwoFactorChannelType channel_uid: bytes channelName: str @@ -779,7 +802,8 @@ class TwoFactorChannelInfo(_message.Message): maxExpiration: TwoFactorExpiration createdOn: int lastFrequency: TwoFactorExpiration - def __init__(self, channelType: _Optional[_Union[TwoFactorChannelType, str]] = ..., channel_uid: _Optional[bytes] = ..., channelName: _Optional[str] = ..., challenge: _Optional[str] = ..., capabilities: _Optional[_Iterable[str]] = ..., phoneNumber: _Optional[str] = ..., maxExpiration: _Optional[_Union[TwoFactorExpiration, str]] = ..., createdOn: _Optional[int] = ..., lastFrequency: _Optional[_Union[TwoFactorExpiration, str]] = ...) -> None: ... + challengeToken: bytes + def __init__(self, channelType: _Optional[_Union[TwoFactorChannelType, str]] = ..., channel_uid: _Optional[bytes] = ..., channelName: _Optional[str] = ..., challenge: _Optional[str] = ..., capabilities: _Optional[_Iterable[str]] = ..., phoneNumber: _Optional[str] = ..., maxExpiration: _Optional[_Union[TwoFactorExpiration, str]] = ..., createdOn: _Optional[int] = ..., lastFrequency: _Optional[_Union[TwoFactorExpiration, str]] = ..., challengeToken: _Optional[bytes] = ...) -> None: ... class TwoFactorDuoStatus(_message.Message): __slots__ = ("capabilities", "phoneNumber", "enroll_url", "message") @@ -844,18 +868,20 @@ class TwoFactorUpdateExpirationRequest(_message.Message): def __init__(self, expireIn: _Optional[_Union[TwoFactorExpiration, str]] = ...) -> None: ... class TwoFactorValidateRequest(_message.Message): - __slots__ = ("encryptedLoginToken", "valueType", "value", "channel_uid", "expireIn") + __slots__ = ("encryptedLoginToken", "valueType", "value", "channel_uid", "expireIn", "challengeToken") ENCRYPTEDLOGINTOKEN_FIELD_NUMBER: _ClassVar[int] VALUETYPE_FIELD_NUMBER: _ClassVar[int] VALUE_FIELD_NUMBER: _ClassVar[int] CHANNEL_UID_FIELD_NUMBER: _ClassVar[int] EXPIREIN_FIELD_NUMBER: _ClassVar[int] + CHALLENGETOKEN_FIELD_NUMBER: _ClassVar[int] encryptedLoginToken: bytes valueType: TwoFactorValueType value: str channel_uid: bytes expireIn: TwoFactorExpiration - def __init__(self, encryptedLoginToken: _Optional[bytes] = ..., valueType: _Optional[_Union[TwoFactorValueType, str]] = ..., value: _Optional[str] = ..., channel_uid: _Optional[bytes] = ..., expireIn: _Optional[_Union[TwoFactorExpiration, str]] = ...) -> None: ... + challengeToken: bytes + def __init__(self, encryptedLoginToken: _Optional[bytes] = ..., valueType: _Optional[_Union[TwoFactorValueType, str]] = ..., value: _Optional[str] = ..., channel_uid: _Optional[bytes] = ..., expireIn: _Optional[_Union[TwoFactorExpiration, str]] = ..., challengeToken: _Optional[bytes] = ...) -> None: ... class TwoFactorValidateResponse(_message.Message): __slots__ = ("encryptedLoginToken",) diff --git a/keepersdk-package/src/keepersdk/proto/BI_pb2.py b/keepersdk-package/src/keepersdk/proto/BI_pb2.py index 8f2304c7..3f14f0ba 100644 --- a/keepersdk-package/src/keepersdk/proto/BI_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/BI_pb2.py @@ -25,7 +25,7 @@ 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\"\xe1\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\"\x95\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\"\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\"\x84\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\"\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\"\xae\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\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_remoteBrowserIsolation\"\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\"\x8e\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\"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*\xd5\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*\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') +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\"\x9b\x04\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\x12\x1b\n\x03\x65pm\x18\x11 \x01(\x0b\x32\x0e.BI.EpmBilling\"\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\"\x95\x01\n\nEpmBilling\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(\x03\"\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\"\xb5\x02\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\x12\x1d\n\x10\x65xtensionVersion\x18\n \x01(\tH\x00\x88\x01\x01\x12\x1d\n\x10\x61iAutofillStatus\x18\x0b \x01(\tH\x01\x88\x01\x01\x12\x15\n\x08mlLabels\x18\x0c \x01(\tH\x02\x88\x01\x01\x42\x13\n\x11_extensionVersionB\x13\n\x11_aiAutofillStatusB\x0b\n\t_mlLabels\"\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) @@ -41,16 +41,16 @@ _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=9351 - _globals['_CURRENCY']._serialized_end=9428 - _globals['_GRADIENTINTEGRATIONSTATUS']._serialized_start=9430 - _globals['_GRADIENTINTEGRATIONSTATUS']._serialized_end=9513 - _globals['_EVENTTYPE']._serialized_start=9516 - _globals['_EVENTTYPE']._serialized_end=9739 - _globals['_PURCHASEPRODUCTTYPE']._serialized_start=9742 - _globals['_PURCHASEPRODUCTTYPE']._serialized_end=9955 - _globals['_IDENTIFIERTYPE']._serialized_start=9958 - _globals['_IDENTIFIERTYPE']._serialized_end=10182 + _globals['_CURRENCY']._serialized_start=10193 + _globals['_CURRENCY']._serialized_end=10270 + _globals['_GRADIENTINTEGRATIONSTATUS']._serialized_start=10272 + _globals['_GRADIENTINTEGRATIONSTATUS']._serialized_end=10355 + _globals['_EVENTTYPE']._serialized_start=10358 + _globals['_EVENTTYPE']._serialized_end=10581 + _globals['_PURCHASEPRODUCTTYPE']._serialized_start=10584 + _globals['_PURCHASEPRODUCTTYPE']._serialized_end=10809 + _globals['_IDENTIFIERTYPE']._serialized_start=10812 + _globals['_IDENTIFIERTYPE']._serialized_end=11036 _globals['_VALIDATESESSIONTOKENREQUEST']._serialized_start=46 _globals['_VALIDATESESSIONTOKENREQUEST']._serialized_end=148 _globals['_VALIDATESESSIONTOKENRESPONSE']._serialized_start=151 @@ -60,177 +60,185 @@ _globals['_SUBSCRIPTIONSTATUSREQUEST']._serialized_start=499 _globals['_SUBSCRIPTIONSTATUSREQUEST']._serialized_end=526 _globals['_SUBSCRIPTIONSTATUSRESPONSE']._serialized_start=529 - _globals['_SUBSCRIPTIONSTATUSRESPONSE']._serialized_end=1010 - _globals['_NHIBILLING']._serialized_start=1013 - _globals['_NHIBILLING']._serialized_end=1162 - _globals['_LICENSESTATS']._serialized_start=1165 - _globals['_LICENSESTATS']._serialized_end=1444 - _globals['_LICENSESTATS_TYPE']._serialized_start=1252 - _globals['_LICENSESTATS_TYPE']._serialized_end=1444 - _globals['_AUTORENEWAL']._serialized_start=1446 - _globals['_AUTORENEWAL']._serialized_end=1510 - _globals['_PAYMENTMETHOD']._serialized_start=1513 - _globals['_PAYMENTMETHOD']._serialized_end=2029 - _globals['_PAYMENTMETHOD_CARD']._serialized_start=1807 - _globals['_PAYMENTMETHOD_CARD']._serialized_end=1843 - _globals['_PAYMENTMETHOD_SEPA']._serialized_start=1845 - _globals['_PAYMENTMETHOD_SEPA']._serialized_end=1883 - _globals['_PAYMENTMETHOD_PAYPAL']._serialized_start=1885 - _globals['_PAYMENTMETHOD_PAYPAL']._serialized_end=1893 - _globals['_PAYMENTMETHOD_VENDOR']._serialized_start=1895 - _globals['_PAYMENTMETHOD_VENDOR']._serialized_end=1917 - _globals['_PAYMENTMETHOD_PURCHASEORDER']._serialized_start=1919 - _globals['_PAYMENTMETHOD_PURCHASEORDER']._serialized_end=1948 - _globals['_PAYMENTMETHOD_TYPE']._serialized_start=1950 - _globals['_PAYMENTMETHOD_TYPE']._serialized_end=2029 - _globals['_SUBSCRIPTIONMSPPRICINGREQUEST']._serialized_start=2031 - _globals['_SUBSCRIPTIONMSPPRICINGREQUEST']._serialized_end=2062 - _globals['_SUBSCRIPTIONMSPPRICINGRESPONSE']._serialized_start=2064 - _globals['_SUBSCRIPTIONMSPPRICINGRESPONSE']._serialized_end=2156 - _globals['_SUBSCRIPTIONMCPRICINGREQUEST']._serialized_start=2158 - _globals['_SUBSCRIPTIONMCPRICINGREQUEST']._serialized_end=2188 - _globals['_SUBSCRIPTIONMCPRICINGRESPONSE']._serialized_start=2190 - _globals['_SUBSCRIPTIONMCPRICINGRESPONSE']._serialized_end=2314 - _globals['_BASEPLAN']._serialized_start=2316 - _globals['_BASEPLAN']._serialized_end=2362 - _globals['_ADDON']._serialized_start=2364 - _globals['_ADDON']._serialized_end=2431 - _globals['_FILEPLAN']._serialized_start=2433 - _globals['_FILEPLAN']._serialized_end=2479 - _globals['_COST']._serialized_start=2482 - _globals['_COST']._serialized_end=2742 - _globals['_COST_AMOUNTPER']._serialized_start=2578 - _globals['_COST_AMOUNTPER']._serialized_end=2742 - _globals['_INVOICESEARCHREQUEST']._serialized_start=2744 - _globals['_INVOICESEARCHREQUEST']._serialized_end=2836 - _globals['_INVOICESEARCHRESPONSE']._serialized_start=2838 - _globals['_INVOICESEARCHRESPONSE']._serialized_end=2892 - _globals['_INVOICE']._serialized_start=2895 - _globals['_INVOICE']._serialized_end=3213 - _globals['_INVOICE_COST']._serialized_start=3060 - _globals['_INVOICE_COST']._serialized_end=3114 - _globals['_INVOICE_TYPE']._serialized_start=3116 - _globals['_INVOICE_TYPE']._serialized_end=3213 - _globals['_VAULTINVOICESLISTREQUEST']._serialized_start=3215 - _globals['_VAULTINVOICESLISTREQUEST']._serialized_end=3241 - _globals['_VAULTINVOICESLISTRESPONSE']._serialized_start=3243 - _globals['_VAULTINVOICESLISTRESPONSE']._serialized_end=3306 - _globals['_VAULTINVOICE']._serialized_start=3309 - _globals['_VAULTINVOICE']._serialized_end=3452 - _globals['_INVOICEDOWNLOADREQUEST']._serialized_start=3454 - _globals['_INVOICEDOWNLOADREQUEST']._serialized_end=3501 - _globals['_INVOICEDOWNLOADRESPONSE']._serialized_start=3503 - _globals['_INVOICEDOWNLOADRESPONSE']._serialized_end=3560 - _globals['_VAULTINVOICEDOWNLOADLINKREQUEST']._serialized_start=3562 - _globals['_VAULTINVOICEDOWNLOADLINKREQUEST']._serialized_end=3618 - _globals['_VAULTINVOICEDOWNLOADLINKRESPONSE']._serialized_start=3620 - _globals['_VAULTINVOICEDOWNLOADLINKRESPONSE']._serialized_end=3686 - _globals['_REPORTINGDAILYSNAPSHOTREQUEST']._serialized_start=3688 - _globals['_REPORTINGDAILYSNAPSHOTREQUEST']._serialized_end=3748 - _globals['_REPORTINGDAILYSNAPSHOTRESPONSE']._serialized_start=3750 - _globals['_REPORTINGDAILYSNAPSHOTRESPONSE']._serialized_end=3868 - _globals['_SNAPSHOTRECORD']._serialized_start=3871 - _globals['_SNAPSHOTRECORD']._serialized_end=4086 - _globals['_SNAPSHOTRECORD_ADDON']._serialized_start=4044 - _globals['_SNAPSHOTRECORD_ADDON']._serialized_end=4086 - _globals['_SNAPSHOTMCENTERPRISE']._serialized_start=4088 - _globals['_SNAPSHOTMCENTERPRISE']._serialized_end=4136 - _globals['_MAPPINGADDONSREQUEST']._serialized_start=4138 - _globals['_MAPPINGADDONSREQUEST']._serialized_end=4160 - _globals['_MAPPINGADDONSRESPONSE']._serialized_start=4162 - _globals['_MAPPINGADDONSRESPONSE']._serialized_end=4254 - _globals['_MAPPINGITEM']._serialized_start=4256 - _globals['_MAPPINGITEM']._serialized_end=4295 - _globals['_GRADIENTVALIDATEKEYREQUEST']._serialized_start=4297 - _globals['_GRADIENTVALIDATEKEYREQUEST']._serialized_end=4346 - _globals['_GRADIENTVALIDATEKEYRESPONSE']._serialized_start=4348 - _globals['_GRADIENTVALIDATEKEYRESPONSE']._serialized_end=4411 - _globals['_GRADIENTSAVEREQUEST']._serialized_start=4413 - _globals['_GRADIENTSAVEREQUEST']._serialized_end=4481 - _globals['_GRADIENTSAVERESPONSE']._serialized_start=4483 - _globals['_GRADIENTSAVERESPONSE']._serialized_end=4586 - _globals['_GRADIENTREMOVEREQUEST']._serialized_start=4588 - _globals['_GRADIENTREMOVEREQUEST']._serialized_end=4637 - _globals['_GRADIENTREMOVERESPONSE']._serialized_start=4639 - _globals['_GRADIENTREMOVERESPONSE']._serialized_end=4697 - _globals['_GRADIENTSYNCREQUEST']._serialized_start=4699 - _globals['_GRADIENTSYNCREQUEST']._serialized_end=4746 - _globals['_GRADIENTSYNCRESPONSE']._serialized_start=4748 - _globals['_GRADIENTSYNCRESPONSE']._serialized_end=4851 - _globals['_NETPROMOTERSCORESURVEYSUBMISSIONREQUEST']._serialized_start=4853 - _globals['_NETPROMOTERSCORESURVEYSUBMISSIONREQUEST']._serialized_end=4931 - _globals['_NETPROMOTERSCORESURVEYSUBMISSIONRESPONSE']._serialized_start=4933 - _globals['_NETPROMOTERSCORESURVEYSUBMISSIONRESPONSE']._serialized_end=4975 - _globals['_NETPROMOTERSCOREPOPUPSCHEDULEREQUEST']._serialized_start=4977 - _globals['_NETPROMOTERSCOREPOPUPSCHEDULEREQUEST']._serialized_end=5015 - _globals['_NETPROMOTERSCOREPOPUPSCHEDULERESPONSE']._serialized_start=5017 - _globals['_NETPROMOTERSCOREPOPUPSCHEDULERESPONSE']._serialized_end=5076 - _globals['_NETPROMOTERSCOREPOPUPDISMISSALREQUEST']._serialized_start=5078 - _globals['_NETPROMOTERSCOREPOPUPDISMISSALREQUEST']._serialized_end=5117 - _globals['_NETPROMOTERSCOREPOPUPDISMISSALRESPONSE']._serialized_start=5119 - _globals['_NETPROMOTERSCOREPOPUPDISMISSALRESPONSE']._serialized_end=5159 - _globals['_KCMLICENSEREQUEST']._serialized_start=5161 - _globals['_KCMLICENSEREQUEST']._serialized_end=5206 - _globals['_KCMLICENSERESPONSE']._serialized_start=5208 - _globals['_KCMLICENSERESPONSE']._serialized_end=5245 - _globals['_EVENTREQUEST']._serialized_start=5248 - _globals['_EVENTREQUEST']._serialized_end=5380 - _globals['_EVENTSREQUEST']._serialized_start=5382 - _globals['_EVENTSREQUEST']._serialized_end=5430 - _globals['_EVENTRESPONSE']._serialized_start=5432 - _globals['_EVENTRESPONSE']._serialized_end=5478 - _globals['_EVENTSRESPONSE']._serialized_start=5480 - _globals['_EVENTSRESPONSE']._serialized_end=5533 - _globals['_CUSTOMERCAPTUREREQUEST']._serialized_start=5536 - _globals['_CUSTOMERCAPTUREREQUEST']._serialized_end=5705 - _globals['_CUSTOMERCAPTURERESPONSE']._serialized_start=5707 - _globals['_CUSTOMERCAPTURERESPONSE']._serialized_end=5732 - _globals['_ERROR']._serialized_start=5734 - _globals['_ERROR']._serialized_end=5858 - _globals['_ERROR_EXTRASENTRY']._serialized_start=5813 - _globals['_ERROR_EXTRASENTRY']._serialized_end=5858 - _globals['_QUOTEPURCHASE']._serialized_start=5861 - _globals['_QUOTEPURCHASE']._serialized_end=6011 - _globals['_PURCHASEOPTIONS']._serialized_start=6013 - _globals['_PURCHASEOPTIONS']._serialized_end=6120 - _globals['_ADDONPURCHASEOPTIONS']._serialized_start=6123 - _globals['_ADDONPURCHASEOPTIONS']._serialized_end=6937 - _globals['_AVAILABLEPURCHASEOPTIONS']._serialized_start=6940 - _globals['_AVAILABLEPURCHASEOPTIONS']._serialized_end=7083 - _globals['_UPGRADELICENSESTATUSREQUEST']._serialized_start=7085 - _globals['_UPGRADELICENSESTATUSREQUEST']._serialized_end=7114 - _globals['_UPGRADELICENSESTATUSRESPONSE']._serialized_start=7117 - _globals['_UPGRADELICENSESTATUSRESPONSE']._serialized_end=7262 - _globals['_UPGRADELICENSEQUOTEPURCHASEREQUEST']._serialized_start=7264 - _globals['_UPGRADELICENSEQUOTEPURCHASEREQUEST']._serialized_end=7378 - _globals['_UPGRADELICENSEQUOTEPURCHASERESPONSE']._serialized_start=7381 - _globals['_UPGRADELICENSEQUOTEPURCHASERESPONSE']._serialized_end=7528 - _globals['_UPGRADELICENSECOMPLETEPURCHASEREQUEST']._serialized_start=7531 - _globals['_UPGRADELICENSECOMPLETEPURCHASEREQUEST']._serialized_end=7690 - _globals['_UPGRADELICENSECOMPLETEPURCHASERESPONSE']._serialized_start=7693 - _globals['_UPGRADELICENSECOMPLETEPURCHASERESPONSE']._serialized_end=7841 - _globals['_ENTERPRISEBASEPLAN']._serialized_start=7844 - _globals['_ENTERPRISEBASEPLAN']._serialized_end=8057 - _globals['_ENTERPRISEBASEPLAN_ENTERPRISEBASEPLANVERSION']._serialized_start=7965 - _globals['_ENTERPRISEBASEPLAN_ENTERPRISEBASEPLANVERSION']._serialized_end=8057 - _globals['_SUBSCRIPTIONENTERPRISEPRICINGREQUEST']._serialized_start=8059 - _globals['_SUBSCRIPTIONENTERPRISEPRICINGREQUEST']._serialized_end=8097 - _globals['_SUBSCRIPTIONENTERPRISEPRICINGRESPONSE']._serialized_start=8100 - _globals['_SUBSCRIPTIONENTERPRISEPRICINGRESPONSE']._serialized_end=8242 - _globals['_SINGULARDEVICEIDENTIFIER']._serialized_start=8244 - _globals['_SINGULARDEVICEIDENTIFIER']._serialized_end=8318 - _globals['_SINGULARSHAREDDATA']._serialized_start=8321 - _globals['_SINGULARSHAREDDATA']._serialized_end=8493 - _globals['_SINGULARSESSIONREQUEST']._serialized_start=8496 - _globals['_SINGULARSESSIONREQUEST']._serialized_end=8891 - _globals['_SINGULAREVENTREQUEST']._serialized_start=8894 - _globals['_SINGULAREVENTREQUEST']._serialized_end=9036 - _globals['_ACTIVEPAMCOUNTREQUEST']._serialized_start=9038 - _globals['_ACTIVEPAMCOUNTREQUEST']._serialized_end=9083 - _globals['_ACTIVEPAMCOUNTRESPONSE']._serialized_start=9085 - _globals['_ACTIVEPAMCOUNTRESPONSE']._serialized_end=9127 - _globals['_NHIENTERPRISEREQUEST']._serialized_start=9129 - _globals['_NHIENTERPRISEREQUEST']._serialized_end=9209 - _globals['_NHIMETRICSREQUEST']._serialized_start=9212 - _globals['_NHIMETRICSREQUEST']._serialized_end=9349 + _globals['_SUBSCRIPTIONSTATUSRESPONSE']._serialized_end=1068 + _globals['_KSMBILLING']._serialized_start=1071 + _globals['_KSMBILLING']._serialized_end=1220 + _globals['_EPMBILLING']._serialized_start=1223 + _globals['_EPMBILLING']._serialized_end=1372 + _globals['_NHIBILLING']._serialized_start=1375 + _globals['_NHIBILLING']._serialized_end=1570 + _globals['_NHIBILLINGPERIOD']._serialized_start=1572 + _globals['_NHIBILLINGPERIOD']._serialized_end=1636 + _globals['_LICENSESTATS']._serialized_start=1639 + _globals['_LICENSESTATS']._serialized_end=1918 + _globals['_LICENSESTATS_TYPE']._serialized_start=1726 + _globals['_LICENSESTATS_TYPE']._serialized_end=1918 + _globals['_AUTORENEWAL']._serialized_start=1920 + _globals['_AUTORENEWAL']._serialized_end=1984 + _globals['_PAYMENTMETHOD']._serialized_start=1987 + _globals['_PAYMENTMETHOD']._serialized_end=2503 + _globals['_PAYMENTMETHOD_CARD']._serialized_start=2281 + _globals['_PAYMENTMETHOD_CARD']._serialized_end=2317 + _globals['_PAYMENTMETHOD_SEPA']._serialized_start=2319 + _globals['_PAYMENTMETHOD_SEPA']._serialized_end=2357 + _globals['_PAYMENTMETHOD_PAYPAL']._serialized_start=2359 + _globals['_PAYMENTMETHOD_PAYPAL']._serialized_end=2367 + _globals['_PAYMENTMETHOD_VENDOR']._serialized_start=2369 + _globals['_PAYMENTMETHOD_VENDOR']._serialized_end=2391 + _globals['_PAYMENTMETHOD_PURCHASEORDER']._serialized_start=2393 + _globals['_PAYMENTMETHOD_PURCHASEORDER']._serialized_end=2422 + _globals['_PAYMENTMETHOD_TYPE']._serialized_start=2424 + _globals['_PAYMENTMETHOD_TYPE']._serialized_end=2503 + _globals['_SUBSCRIPTIONMSPPRICINGREQUEST']._serialized_start=2505 + _globals['_SUBSCRIPTIONMSPPRICINGREQUEST']._serialized_end=2536 + _globals['_SUBSCRIPTIONMSPPRICINGRESPONSE']._serialized_start=2538 + _globals['_SUBSCRIPTIONMSPPRICINGRESPONSE']._serialized_end=2630 + _globals['_SUBSCRIPTIONMCPRICINGREQUEST']._serialized_start=2632 + _globals['_SUBSCRIPTIONMCPRICINGREQUEST']._serialized_end=2662 + _globals['_SUBSCRIPTIONMCPRICINGRESPONSE']._serialized_start=2664 + _globals['_SUBSCRIPTIONMCPRICINGRESPONSE']._serialized_end=2788 + _globals['_BASEPLAN']._serialized_start=2790 + _globals['_BASEPLAN']._serialized_end=2836 + _globals['_ADDON']._serialized_start=2838 + _globals['_ADDON']._serialized_end=2905 + _globals['_FILEPLAN']._serialized_start=2907 + _globals['_FILEPLAN']._serialized_end=2953 + _globals['_COST']._serialized_start=2956 + _globals['_COST']._serialized_end=3238 + _globals['_COST_AMOUNTPER']._serialized_start=3074 + _globals['_COST_AMOUNTPER']._serialized_end=3238 + _globals['_INVOICESEARCHREQUEST']._serialized_start=3240 + _globals['_INVOICESEARCHREQUEST']._serialized_end=3332 + _globals['_INVOICESEARCHRESPONSE']._serialized_start=3334 + _globals['_INVOICESEARCHRESPONSE']._serialized_end=3388 + _globals['_INVOICE']._serialized_start=3391 + _globals['_INVOICE']._serialized_end=3709 + _globals['_INVOICE_COST']._serialized_start=3556 + _globals['_INVOICE_COST']._serialized_end=3610 + _globals['_INVOICE_TYPE']._serialized_start=3612 + _globals['_INVOICE_TYPE']._serialized_end=3709 + _globals['_VAULTINVOICESLISTREQUEST']._serialized_start=3711 + _globals['_VAULTINVOICESLISTREQUEST']._serialized_end=3737 + _globals['_VAULTINVOICESLISTRESPONSE']._serialized_start=3739 + _globals['_VAULTINVOICESLISTRESPONSE']._serialized_end=3802 + _globals['_VAULTINVOICE']._serialized_start=3805 + _globals['_VAULTINVOICE']._serialized_end=3948 + _globals['_INVOICEDOWNLOADREQUEST']._serialized_start=3950 + _globals['_INVOICEDOWNLOADREQUEST']._serialized_end=3997 + _globals['_INVOICEDOWNLOADRESPONSE']._serialized_start=3999 + _globals['_INVOICEDOWNLOADRESPONSE']._serialized_end=4056 + _globals['_VAULTINVOICEDOWNLOADLINKREQUEST']._serialized_start=4058 + _globals['_VAULTINVOICEDOWNLOADLINKREQUEST']._serialized_end=4114 + _globals['_VAULTINVOICEDOWNLOADLINKRESPONSE']._serialized_start=4116 + _globals['_VAULTINVOICEDOWNLOADLINKRESPONSE']._serialized_end=4182 + _globals['_REPORTINGDAILYSNAPSHOTREQUEST']._serialized_start=4184 + _globals['_REPORTINGDAILYSNAPSHOTREQUEST']._serialized_end=4244 + _globals['_REPORTINGDAILYSNAPSHOTRESPONSE']._serialized_start=4246 + _globals['_REPORTINGDAILYSNAPSHOTRESPONSE']._serialized_end=4364 + _globals['_SNAPSHOTRECORD']._serialized_start=4367 + _globals['_SNAPSHOTRECORD']._serialized_end=4582 + _globals['_SNAPSHOTRECORD_ADDON']._serialized_start=4540 + _globals['_SNAPSHOTRECORD_ADDON']._serialized_end=4582 + _globals['_SNAPSHOTMCENTERPRISE']._serialized_start=4584 + _globals['_SNAPSHOTMCENTERPRISE']._serialized_end=4632 + _globals['_MAPPINGADDONSREQUEST']._serialized_start=4634 + _globals['_MAPPINGADDONSREQUEST']._serialized_end=4656 + _globals['_MAPPINGADDONSRESPONSE']._serialized_start=4658 + _globals['_MAPPINGADDONSRESPONSE']._serialized_end=4750 + _globals['_MAPPINGITEM']._serialized_start=4752 + _globals['_MAPPINGITEM']._serialized_end=4791 + _globals['_GRADIENTVALIDATEKEYREQUEST']._serialized_start=4793 + _globals['_GRADIENTVALIDATEKEYREQUEST']._serialized_end=4842 + _globals['_GRADIENTVALIDATEKEYRESPONSE']._serialized_start=4844 + _globals['_GRADIENTVALIDATEKEYRESPONSE']._serialized_end=4907 + _globals['_GRADIENTSAVEREQUEST']._serialized_start=4909 + _globals['_GRADIENTSAVEREQUEST']._serialized_end=4977 + _globals['_GRADIENTSAVERESPONSE']._serialized_start=4979 + _globals['_GRADIENTSAVERESPONSE']._serialized_end=5082 + _globals['_GRADIENTREMOVEREQUEST']._serialized_start=5084 + _globals['_GRADIENTREMOVEREQUEST']._serialized_end=5133 + _globals['_GRADIENTREMOVERESPONSE']._serialized_start=5135 + _globals['_GRADIENTREMOVERESPONSE']._serialized_end=5193 + _globals['_GRADIENTSYNCREQUEST']._serialized_start=5195 + _globals['_GRADIENTSYNCREQUEST']._serialized_end=5242 + _globals['_GRADIENTSYNCRESPONSE']._serialized_start=5244 + _globals['_GRADIENTSYNCRESPONSE']._serialized_end=5347 + _globals['_NETPROMOTERSCORESURVEYSUBMISSIONREQUEST']._serialized_start=5349 + _globals['_NETPROMOTERSCORESURVEYSUBMISSIONREQUEST']._serialized_end=5427 + _globals['_NETPROMOTERSCORESURVEYSUBMISSIONRESPONSE']._serialized_start=5429 + _globals['_NETPROMOTERSCORESURVEYSUBMISSIONRESPONSE']._serialized_end=5471 + _globals['_NETPROMOTERSCOREPOPUPSCHEDULEREQUEST']._serialized_start=5473 + _globals['_NETPROMOTERSCOREPOPUPSCHEDULEREQUEST']._serialized_end=5511 + _globals['_NETPROMOTERSCOREPOPUPSCHEDULERESPONSE']._serialized_start=5513 + _globals['_NETPROMOTERSCOREPOPUPSCHEDULERESPONSE']._serialized_end=5572 + _globals['_NETPROMOTERSCOREPOPUPDISMISSALREQUEST']._serialized_start=5574 + _globals['_NETPROMOTERSCOREPOPUPDISMISSALREQUEST']._serialized_end=5613 + _globals['_NETPROMOTERSCOREPOPUPDISMISSALRESPONSE']._serialized_start=5615 + _globals['_NETPROMOTERSCOREPOPUPDISMISSALRESPONSE']._serialized_end=5655 + _globals['_KCMLICENSEREQUEST']._serialized_start=5657 + _globals['_KCMLICENSEREQUEST']._serialized_end=5702 + _globals['_KCMLICENSERESPONSE']._serialized_start=5704 + _globals['_KCMLICENSERESPONSE']._serialized_end=5741 + _globals['_EVENTREQUEST']._serialized_start=5744 + _globals['_EVENTREQUEST']._serialized_end=5876 + _globals['_EVENTSREQUEST']._serialized_start=5878 + _globals['_EVENTSREQUEST']._serialized_end=5926 + _globals['_EVENTRESPONSE']._serialized_start=5928 + _globals['_EVENTRESPONSE']._serialized_end=5974 + _globals['_EVENTSRESPONSE']._serialized_start=5976 + _globals['_EVENTSRESPONSE']._serialized_end=6029 + _globals['_CUSTOMERCAPTUREREQUEST']._serialized_start=6032 + _globals['_CUSTOMERCAPTUREREQUEST']._serialized_end=6341 + _globals['_CUSTOMERCAPTURERESPONSE']._serialized_start=6343 + _globals['_CUSTOMERCAPTURERESPONSE']._serialized_end=6368 + _globals['_ERROR']._serialized_start=6370 + _globals['_ERROR']._serialized_end=6494 + _globals['_ERROR_EXTRASENTRY']._serialized_start=6449 + _globals['_ERROR_EXTRASENTRY']._serialized_end=6494 + _globals['_QUOTEPURCHASE']._serialized_start=6497 + _globals['_QUOTEPURCHASE']._serialized_end=6647 + _globals['_PURCHASEOPTIONS']._serialized_start=6649 + _globals['_PURCHASEOPTIONS']._serialized_end=6756 + _globals['_ADDONPURCHASEOPTIONS']._serialized_start=6759 + _globals['_ADDONPURCHASEOPTIONS']._serialized_end=7628 + _globals['_AVAILABLEPURCHASEOPTIONS']._serialized_start=7631 + _globals['_AVAILABLEPURCHASEOPTIONS']._serialized_end=7774 + _globals['_UPGRADELICENSESTATUSREQUEST']._serialized_start=7776 + _globals['_UPGRADELICENSESTATUSREQUEST']._serialized_end=7805 + _globals['_UPGRADELICENSESTATUSRESPONSE']._serialized_start=7808 + _globals['_UPGRADELICENSESTATUSRESPONSE']._serialized_end=7953 + _globals['_UPGRADELICENSEQUOTEPURCHASEREQUEST']._serialized_start=7955 + _globals['_UPGRADELICENSEQUOTEPURCHASEREQUEST']._serialized_end=8069 + _globals['_UPGRADELICENSEQUOTEPURCHASERESPONSE']._serialized_start=8072 + _globals['_UPGRADELICENSEQUOTEPURCHASERESPONSE']._serialized_end=8219 + _globals['_UPGRADELICENSECOMPLETEPURCHASEREQUEST']._serialized_start=8222 + _globals['_UPGRADELICENSECOMPLETEPURCHASEREQUEST']._serialized_end=8381 + _globals['_UPGRADELICENSECOMPLETEPURCHASERESPONSE']._serialized_start=8384 + _globals['_UPGRADELICENSECOMPLETEPURCHASERESPONSE']._serialized_end=8532 + _globals['_ENTERPRISEBASEPLAN']._serialized_start=8535 + _globals['_ENTERPRISEBASEPLAN']._serialized_end=8748 + _globals['_ENTERPRISEBASEPLAN_ENTERPRISEBASEPLANVERSION']._serialized_start=8656 + _globals['_ENTERPRISEBASEPLAN_ENTERPRISEBASEPLANVERSION']._serialized_end=8748 + _globals['_SUBSCRIPTIONENTERPRISEPRICINGREQUEST']._serialized_start=8750 + _globals['_SUBSCRIPTIONENTERPRISEPRICINGREQUEST']._serialized_end=8788 + _globals['_NHITIERPLAN']._serialized_start=8790 + _globals['_NHITIERPLAN']._serialized_end=8900 + _globals['_SUBSCRIPTIONENTERPRISEPRICINGRESPONSE']._serialized_start=8903 + _globals['_SUBSCRIPTIONENTERPRISEPRICINGRESPONSE']._serialized_end=9084 + _globals['_SINGULARDEVICEIDENTIFIER']._serialized_start=9086 + _globals['_SINGULARDEVICEIDENTIFIER']._serialized_end=9160 + _globals['_SINGULARSHAREDDATA']._serialized_start=9163 + _globals['_SINGULARSHAREDDATA']._serialized_end=9335 + _globals['_SINGULARSESSIONREQUEST']._serialized_start=9338 + _globals['_SINGULARSESSIONREQUEST']._serialized_end=9733 + _globals['_SINGULAREVENTREQUEST']._serialized_start=9736 + _globals['_SINGULAREVENTREQUEST']._serialized_end=9878 + _globals['_ACTIVEPAMCOUNTREQUEST']._serialized_start=9880 + _globals['_ACTIVEPAMCOUNTREQUEST']._serialized_end=9925 + _globals['_ACTIVEPAMCOUNTRESPONSE']._serialized_start=9927 + _globals['_ACTIVEPAMCOUNTRESPONSE']._serialized_end=9969 + _globals['_NHIENTERPRISEREQUEST']._serialized_start=9971 + _globals['_NHIENTERPRISEREQUEST']._serialized_end=10051 + _globals['_NHIMETRICSREQUEST']._serialized_start=10054 + _globals['_NHIMETRICSREQUEST']._serialized_end=10191 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/BI_pb2.pyi b/keepersdk-package/src/keepersdk/proto/BI_pb2.pyi index d077ba08..571d0907 100644 --- a/keepersdk-package/src/keepersdk/proto/BI_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/BI_pb2.pyi @@ -48,6 +48,7 @@ class PurchaseProductType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): addSilverSupport: _ClassVar[PurchaseProductType] addPlatinumSupport: _ClassVar[PurchaseProductType] addKEPM: _ClassVar[PurchaseProductType] + addNhi: _ClassVar[PurchaseProductType] class IdentifierType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -90,6 +91,7 @@ addPAM: PurchaseProductType addSilverSupport: PurchaseProductType addPlatinumSupport: PurchaseProductType addKEPM: PurchaseProductType +addNhi: PurchaseProductType UNKNOWN_IDENTIFIER_TYPE: IdentifierType IOS_ID: IdentifierType ANDROID_GOOGLE_PLAY_ID: IdentifierType @@ -147,7 +149,7 @@ class SubscriptionStatusRequest(_message.Message): def __init__(self) -> None: ... class SubscriptionStatusResponse(_message.Message): - __slots__ = ("autoRenewal", "currentPaymentMethod", "checkoutLink", "licenseCreateDate", "isDistributor", "isLegacyMsp", "licenseStats", "gradientStatus", "hideTrialBanner", "gradientLastSyncDate", "gradientNextSyncDate", "isGradientMappingPending", "nhi", "freeKsmApiCallsCount") + __slots__ = ("autoRenewal", "currentPaymentMethod", "checkoutLink", "licenseCreateDate", "isDistributor", "isLegacyMsp", "licenseStats", "gradientStatus", "hideTrialBanner", "gradientLastSyncDate", "gradientNextSyncDate", "isGradientMappingPending", "nhi", "freeKsmApiCallsCount", "ksm", "epm") AUTORENEWAL_FIELD_NUMBER: _ClassVar[int] CURRENTPAYMENTMETHOD_FIELD_NUMBER: _ClassVar[int] CHECKOUTLINK_FIELD_NUMBER: _ClassVar[int] @@ -162,6 +164,8 @@ class SubscriptionStatusResponse(_message.Message): ISGRADIENTMAPPINGPENDING_FIELD_NUMBER: _ClassVar[int] NHI_FIELD_NUMBER: _ClassVar[int] FREEKSMAPICALLSCOUNT_FIELD_NUMBER: _ClassVar[int] + KSM_FIELD_NUMBER: _ClassVar[int] + EPM_FIELD_NUMBER: _ClassVar[int] autoRenewal: AutoRenewal currentPaymentMethod: PaymentMethod checkoutLink: str @@ -176,9 +180,25 @@ class SubscriptionStatusResponse(_message.Message): isGradientMappingPending: bool nhi: NhiBilling freeKsmApiCallsCount: int - 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] = ...) -> None: ... + ksm: KsmBilling + epm: EpmBilling + 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]] = ..., epm: _Optional[_Union[EpmBilling, _Mapping]] = ...) -> None: ... -class NhiBilling(_message.Message): +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 EpmBilling(_message.Message): __slots__ = ("billingStartTimestamp", "billingEndTimestamp", "currentTierId", "enterpriseBlocks", "currentTierCeiling") BILLINGSTARTTIMESTAMP_FIELD_NUMBER: _ClassVar[int] BILLINGENDTIMESTAMP_FIELD_NUMBER: _ClassVar[int] @@ -192,6 +212,30 @@ class NhiBilling(_message.Message): 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") class Type(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): @@ -344,7 +388,7 @@ 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__ = () UNKNOWN: _ClassVar[Cost.AmountPer] @@ -368,10 +412,12 @@ class Cost(_message.Message): 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") @@ -684,7 +730,7 @@ class EventsResponse(_message.Message): def __init__(self, response: _Optional[_Iterable[_Union[EventResponse, _Mapping]]] = ...) -> None: ... class CustomerCaptureRequest(_message.Message): - __slots__ = ("pageUrl", "tree", "hash", "image", "pageLoadTime", "keyId", "test", "issueType", "notes") + __slots__ = ("pageUrl", "tree", "hash", "image", "pageLoadTime", "keyId", "test", "issueType", "notes", "extensionVersion", "aiAutofillStatus", "mlLabels") PAGEURL_FIELD_NUMBER: _ClassVar[int] TREE_FIELD_NUMBER: _ClassVar[int] HASH_FIELD_NUMBER: _ClassVar[int] @@ -694,6 +740,9 @@ class CustomerCaptureRequest(_message.Message): TEST_FIELD_NUMBER: _ClassVar[int] ISSUETYPE_FIELD_NUMBER: _ClassVar[int] NOTES_FIELD_NUMBER: _ClassVar[int] + EXTENSIONVERSION_FIELD_NUMBER: _ClassVar[int] + AIAUTOFILLSTATUS_FIELD_NUMBER: _ClassVar[int] + MLLABELS_FIELD_NUMBER: _ClassVar[int] pageUrl: str tree: str hash: str @@ -703,7 +752,10 @@ class CustomerCaptureRequest(_message.Message): test: bool 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: ... + extensionVersion: str + aiAutofillStatus: str + mlLabels: 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] = ..., extensionVersion: _Optional[str] = ..., aiAutofillStatus: _Optional[str] = ..., mlLabels: _Optional[str] = ...) -> None: ... class CustomerCaptureResponse(_message.Message): __slots__ = () @@ -751,7 +803,7 @@ class PurchaseOptions(_message.Message): 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") + __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] @@ -764,6 +816,7 @@ class AddonPurchaseOptions(_message.Message): 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 @@ -776,7 +829,8 @@ class AddonPurchaseOptions(_message.Message): secretsManager: PurchaseOptions connectionManager: PurchaseOptions remoteBrowserIsolation: 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]] = ...) -> None: ... + 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") @@ -870,15 +924,31 @@ 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") + __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] - def __init__(self, basePlans: _Optional[_Iterable[_Union[EnterpriseBasePlan, _Mapping]]] = ..., addons: _Optional[_Iterable[_Union[Addon, _Mapping]]] = ..., filePlans: _Optional[_Iterable[_Union[FilePlan, _Mapping]]] = ...) -> None: ... + 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") diff --git a/keepersdk-package/src/keepersdk/proto/automator_pb2.py b/keepersdk-package/src/keepersdk/proto/automator_pb2.py index 3cba9106..27e689cc 100644 --- a/keepersdk-package/src/keepersdk/proto/automator_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/automator_pb2.py @@ -27,7 +27,7 @@ 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\"\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') +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*v\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\x12\r\n\tREPORTING\x10\x04*\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) @@ -42,9 +42,9 @@ _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['_SKILLTYPE']._serialized_end=7699 + _globals['_AUTOMATORSTATE']._serialized_start=7702 + _globals['_AUTOMATORSTATE']._serialized_end=7837 _globals['_AUTOMATORSETTINGVALUE']._serialized_start=80 _globals['_AUTOMATORSETTINGVALUE']._serialized_end=399 _globals['_APPROVEDEVICEREQUEST']._serialized_start=402 diff --git a/keepersdk-package/src/keepersdk/proto/automator_pb2.pyi b/keepersdk-package/src/keepersdk/proto/automator_pb2.pyi index c83a01ae..cf038cde 100644 --- a/keepersdk-package/src/keepersdk/proto/automator_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/automator_pb2.pyi @@ -1,6 +1,6 @@ -import ssocloud_pb2 as _ssocloud_pb2 -import enterprise_pb2 as _enterprise_pb2 -import version_pb2 as _version_pb2 +from . import ssocloud_pb2 as _ssocloud_pb2 +from . import enterprise_pb2 as _enterprise_pb2 +from . import version_pb2 as _version_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 @@ -27,6 +27,7 @@ class SkillType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): DEVICE_APPROVAL: _ClassVar[SkillType] TEAM_APPROVAL: _ClassVar[SkillType] TEAM_FOR_USER_APPROVAL: _ClassVar[SkillType] + REPORTING: _ClassVar[SkillType] class AutomatorState(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -46,6 +47,7 @@ UNKNOWN_SKILL_TYPE: SkillType DEVICE_APPROVAL: SkillType TEAM_APPROVAL: SkillType TEAM_FOR_USER_APPROVAL: SkillType +REPORTING: SkillType UNKNOWN_STATE: AutomatorState RUNNING: AutomatorState ERROR: AutomatorState diff --git a/keepersdk-package/src/keepersdk/proto/enterprise_pb2.py b/keepersdk-package/src/keepersdk/proto/enterprise_pb2.py index 2646c89c..c9ad3e2c 100644 --- a/keepersdk-package/src/keepersdk/proto/enterprise_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/enterprise_pb2.py @@ -21,10 +21,10 @@ _sym_db = _symbol_database.Default() +from . import folder_pb2 as folder__pb2 - -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') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x65nterprise.proto\x12\nEnterprise\x1a\x0c\x66older.proto\"\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\"\x82\x01\n\x0fRoleUserAddKeys\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x13\n\x07treeKey\x18\x02 \x01(\tB\x02\x18\x01\x12\x14\n\x0croleAdminKey\x18\x03 \x01(\t\x12*\n\x0ctypedTreeKey\x18\x04 \x01(\x0b\x32\x14.Enterprise.TypedKey\"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\"\xe7\x01\n\nUserUpdate\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12\x0e\n\x06nodeId\x18\x02 \x01(\x03\x12\x19\n\rencryptedData\x18\x03 \x01(\x0c\x42\x02\x18\x01\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\x15\n\rinviteeLocale\x18\x08 \x01(\t\x12\x1b\n\x13\x65ncryptedDataString\x18\t \x01(\t\"A\n\x12UserUpdateResponse\x12+\n\x05users\x18\x01 \x03(\x0b\x32\x1c.Enterprise.UserUpdateResult\"\x88\x01\n\x10UserUpdateResult\x12\x18\n\x10\x65nterpriseUserId\x18\x01 \x01(\x03\x12,\n\x06status\x18\x02 \x01(\x0e\x32\x1c.Enterprise.UserUpdateStatus\x12\x14\n\x0c\x65rrorMessage\x18\x03 \x01(\t\x12\x16\n\x0e\x61\x64\x64itionalInfo\x18\x04 \x01(\t\"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\"b\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\x12\x15\n\risDriveRecord\x18\x04 \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\"\x98\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\x12\x15\n\risDriveRecord\x18\x07 \x01(\x08\"\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\x18\n\x0epermissionBits\x18\x02 \x01(\x05H\x00\x12,\n\x05\x64rive\x18\x03 \x01(\x0b\x32\x1b.Enterprise.DrivePermissionH\x00\x42\x0c\n\npermission\"\xc7\x01\n\x0f\x44rivePermission\x12\r\n\x05owner\x18\x01 \x01(\x08\x12\x0e\n\x06\x64\x65nied\x18\x02 \x01(\x08\x12\x0f\n\x07\x63\x61nEdit\x18\x03 \x01(\x08\x12\x10\n\x08\x63\x61nShare\x18\x04 \x01(\x08\x12\x14\n\x0cisShareAdmin\x18\x05 \x01(\x08\x12&\n\naccessType\x18\x06 \x01(\x0e\x32\x12.Folder.AccessType\x12\x34\n\x11\x66olderPermissions\x18\x07 \x01(\x0b\x32\x19.Folder.FolderPermissions\"_\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*\xaf\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\x12\x13\n\x0fWOULD_LOCK_SELF\x10\n*=\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*\xf3\x01\n\x10UserUpdateStatus\x12\x12\n\x0eUSER_UPDATE_OK\x10\x00\x12\x1d\n\x19USER_UPDATE_ACCESS_DENIED\x10\x01\x12&\n\"USER_UPDATE_EXCEEDED_LICENSE_SEATS\x10\x02\x12\x1b\n\x17USER_UPDATE_BAD_REQUEST\x10\x03\x12\x19\n\x15USER_UPDATE_DUPLICATE\x10\x04\x12\x1d\n\x19USER_UPDATE_INVALID_STATE\x10\x05\x12\x16\n\x12USER_UPDATE_FAILED\x10\x06\x12\x15\n\x11USER_UPDATE_ERROR\x10\x07*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) @@ -32,382 +32,388 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\030com.keepersecurity.protoB\nEnterprise' + _globals['_ROLEUSERADDKEYS'].fields_by_name['treeKey']._loaded_options = None + _globals['_ROLEUSERADDKEYS'].fields_by_name['treeKey']._serialized_options = b'\030\001' _globals['_NODE'].fields_by_name['ssoServiceProviderId']._loaded_options = None _globals['_NODE'].fields_by_name['ssoServiceProviderId']._serialized_options = b'\030\001' _globals['_NODE'].fields_by_name['ssoServiceProviderIds']._loaded_options = None _globals['_NODE'].fields_by_name['ssoServiceProviderIds']._serialized_options = b'\020\001' + _globals['_USERUPDATE'].fields_by_name['encryptedData']._loaded_options = None + _globals['_USERUPDATE'].fields_by_name['encryptedData']._serialized_options = b'\030\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=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 - _globals['_GETTEAMMEMBERREQUEST']._serialized_end=206 - _globals['_ENTERPRISEUSER']._serialized_start=208 - _globals['_ENTERPRISEUSER']._serialized_end=333 - _globals['_GETTEAMMEMBERRESPONSE']._serialized_start=335 - _globals['_GETTEAMMEMBERRESPONSE']._serialized_end=410 - _globals['_ENTERPRISEUSERIDS']._serialized_start=412 - _globals['_ENTERPRISEUSERIDS']._serialized_end=457 - _globals['_ENTERPRISEPERSONALACCOUNT']._serialized_start=459 - _globals['_ENTERPRISEPERSONALACCOUNT']._serialized_end=525 - _globals['_ENCRYPTEDTEAMKEYREQUEST']._serialized_start=527 - _globals['_ENCRYPTEDTEAMKEYREQUEST']._serialized_end=610 - _globals['_REENCRYPTEDDATA']._serialized_start=612 - _globals['_REENCRYPTEDDATA']._serialized_end=655 - _globals['_REENCRYPTEDROLEKEY']._serialized_start=657 - _globals['_REENCRYPTEDROLEKEY']._serialized_end=720 - _globals['_REENCRYPTEDUSERDATAKEY']._serialized_start=722 - _globals['_REENCRYPTEDUSERDATAKEY']._serialized_end=802 - _globals['_NODETOMANAGEDCOMPANYREQUEST']._serialized_start=805 - _globals['_NODETOMANAGEDCOMPANYREQUEST']._serialized_end=1149 - _globals['_ROLETEAM']._serialized_start=1151 - _globals['_ROLETEAM']._serialized_end=1195 - _globals['_ROLETEAMS']._serialized_start=1197 - _globals['_ROLETEAMS']._serialized_end=1249 - _globals['_TEAMSBYROLE']._serialized_start=1251 - _globals['_TEAMSBYROLE']._serialized_end=1298 - _globals['_MANAGEDNODESBYROLE']._serialized_start=1300 - _globals['_MANAGEDNODESBYROLE']._serialized_end=1360 - _globals['_ROLEUSERADDKEYS']._serialized_start=1362 - _globals['_ROLEUSERADDKEYS']._serialized_end=1444 - _globals['_ROLEUSERADD']._serialized_start=1446 - _globals['_ROLEUSERADD']._serialized_end=1530 - _globals['_ROLEUSERSADDREQUEST']._serialized_start=1532 - _globals['_ROLEUSERSADDREQUEST']._serialized_end=1600 - _globals['_ROLEUSERADDRESULT']._serialized_start=1603 - _globals['_ROLEUSERADDRESULT']._serialized_end=1731 - _globals['_ROLEUSERSADDRESPONSE']._serialized_start=1733 - _globals['_ROLEUSERSADDRESPONSE']._serialized_end=1803 - _globals['_ROLEUSERREMOVE']._serialized_start=1805 - _globals['_ROLEUSERREMOVE']._serialized_end=1865 - _globals['_ROLEUSERSREMOVEREQUEST']._serialized_start=1867 - _globals['_ROLEUSERSREMOVEREQUEST']._serialized_end=1944 - _globals['_ROLEUSERREMOVERESULT']._serialized_start=1947 - _globals['_ROLEUSERREMOVERESULT']._serialized_end=2078 - _globals['_ROLEUSERSREMOVERESPONSE']._serialized_start=2080 - _globals['_ROLEUSERSREMOVERESPONSE']._serialized_end=2156 - _globals['_ENTERPRISEREGISTRATION']._serialized_start=2159 - _globals['_ENTERPRISEREGISTRATION']._serialized_end=2703 - _globals['_DOMAINPASSWORDRULESREQUEST']._serialized_start=2705 - _globals['_DOMAINPASSWORDRULESREQUEST']._serialized_end=2777 - _globals['_DOMAINPASSWORDRULESFIELDS']._serialized_start=2779 - _globals['_DOMAINPASSWORDRULESFIELDS']._serialized_end=2871 - _globals['_LOGINTOMCREQUEST']._serialized_start=2873 - _globals['_LOGINTOMCREQUEST']._serialized_end=2942 - _globals['_LOGINTOMCRESPONSE']._serialized_start=2944 - _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 + _globals['_KEYTYPE']._serialized_start=21125 + _globals['_KEYTYPE']._serialized_end=21152 + _globals['_ROLEUSERMODIFYSTATUS']._serialized_start=21155 + _globals['_ROLEUSERMODIFYSTATUS']._serialized_end=21458 + _globals['_ENTERPRISETYPE']._serialized_start=21460 + _globals['_ENTERPRISETYPE']._serialized_end=21521 + _globals['_TRANSFERACCEPTANCESTATUS']._serialized_start=21523 + _globals['_TRANSFERACCEPTANCESTATUS']._serialized_end=21638 + _globals['_ENTERPRISEDATAENTITY']._serialized_start=21641 + _globals['_ENTERPRISEDATAENTITY']._serialized_end=22122 + _globals['_CACHESTATUS']._serialized_start=22124 + _globals['_CACHESTATUS']._serialized_end=22158 + _globals['_BACKUPKEYTYPE']._serialized_start=22161 + _globals['_BACKUPKEYTYPE']._serialized_end=22308 + _globals['_BACKUPUSERDATAKEYTYPE']._serialized_start=22310 + _globals['_BACKUPUSERDATAKEYTYPE']._serialized_end=22368 + _globals['_ENCRYPTEDKEYTYPE']._serialized_start=22371 + _globals['_ENCRYPTEDKEYTYPE']._serialized_end=22536 + _globals['_ENTERPRISEFLAGTYPE']._serialized_start=22539 + _globals['_ENTERPRISEFLAGTYPE']._serialized_end=22850 + _globals['_USERUPDATESTATUS']._serialized_start=22853 + _globals['_USERUPDATESTATUS']._serialized_end=23096 + _globals['_AUDITUSERSTATUS']._serialized_start=23098 + _globals['_AUDITUSERSTATUS']._serialized_end=23171 + _globals['_TEAMUSERTYPE']._serialized_start=23173 + _globals['_TEAMUSERTYPE']._serialized_end=23224 + _globals['_APPCLIENTTYPE']._serialized_start=23226 + _globals['_APPCLIENTTYPE']._serialized_end=23346 + _globals['_DELETEENTERPRISEUSERSRESULT']._serialized_start=23349 + _globals['_DELETEENTERPRISEUSERSRESULT']._serialized_end=23492 + _globals['_CLEARSECURITYDATATYPE']._serialized_start=23495 + _globals['_CLEARSECURITYDATATYPE']._serialized_end=23630 + _globals['_RESERVEDOMAINACTION']._serialized_start=23632 + _globals['_RESERVEDOMAINACTION']._serialized_end=23706 + _globals['_USERLOCKSTATUS']._serialized_start=23708 + _globals['_USERLOCKSTATUS']._serialized_end=23823 + _globals['_EXTERNALCLOUDSECRETSSTORETYPE']._serialized_start=23826 + _globals['_EXTERNALCLOUDSECRETSSTORETYPE']._serialized_end=23954 + _globals['_ENTERPRISEKEYPAIRREQUEST']._serialized_start=47 + _globals['_ENTERPRISEKEYPAIRREQUEST']._serialized_end=179 + _globals['_GETTEAMMEMBERREQUEST']._serialized_start=181 + _globals['_GETTEAMMEMBERREQUEST']._serialized_end=220 + _globals['_ENTERPRISEUSER']._serialized_start=222 + _globals['_ENTERPRISEUSER']._serialized_end=347 + _globals['_GETTEAMMEMBERRESPONSE']._serialized_start=349 + _globals['_GETTEAMMEMBERRESPONSE']._serialized_end=424 + _globals['_ENTERPRISEUSERIDS']._serialized_start=426 + _globals['_ENTERPRISEUSERIDS']._serialized_end=471 + _globals['_ENTERPRISEPERSONALACCOUNT']._serialized_start=473 + _globals['_ENTERPRISEPERSONALACCOUNT']._serialized_end=539 + _globals['_ENCRYPTEDTEAMKEYREQUEST']._serialized_start=541 + _globals['_ENCRYPTEDTEAMKEYREQUEST']._serialized_end=624 + _globals['_REENCRYPTEDDATA']._serialized_start=626 + _globals['_REENCRYPTEDDATA']._serialized_end=669 + _globals['_REENCRYPTEDROLEKEY']._serialized_start=671 + _globals['_REENCRYPTEDROLEKEY']._serialized_end=734 + _globals['_REENCRYPTEDUSERDATAKEY']._serialized_start=736 + _globals['_REENCRYPTEDUSERDATAKEY']._serialized_end=816 + _globals['_NODETOMANAGEDCOMPANYREQUEST']._serialized_start=819 + _globals['_NODETOMANAGEDCOMPANYREQUEST']._serialized_end=1163 + _globals['_ROLETEAM']._serialized_start=1165 + _globals['_ROLETEAM']._serialized_end=1209 + _globals['_ROLETEAMS']._serialized_start=1211 + _globals['_ROLETEAMS']._serialized_end=1263 + _globals['_TEAMSBYROLE']._serialized_start=1265 + _globals['_TEAMSBYROLE']._serialized_end=1312 + _globals['_MANAGEDNODESBYROLE']._serialized_start=1314 + _globals['_MANAGEDNODESBYROLE']._serialized_end=1374 + _globals['_ROLEUSERADDKEYS']._serialized_start=1377 + _globals['_ROLEUSERADDKEYS']._serialized_end=1507 + _globals['_ROLEUSERADD']._serialized_start=1509 + _globals['_ROLEUSERADD']._serialized_end=1593 + _globals['_ROLEUSERSADDREQUEST']._serialized_start=1595 + _globals['_ROLEUSERSADDREQUEST']._serialized_end=1663 + _globals['_ROLEUSERADDRESULT']._serialized_start=1666 + _globals['_ROLEUSERADDRESULT']._serialized_end=1794 + _globals['_ROLEUSERSADDRESPONSE']._serialized_start=1796 + _globals['_ROLEUSERSADDRESPONSE']._serialized_end=1866 + _globals['_ROLEUSERREMOVE']._serialized_start=1868 + _globals['_ROLEUSERREMOVE']._serialized_end=1928 + _globals['_ROLEUSERSREMOVEREQUEST']._serialized_start=1930 + _globals['_ROLEUSERSREMOVEREQUEST']._serialized_end=2007 + _globals['_ROLEUSERREMOVERESULT']._serialized_start=2010 + _globals['_ROLEUSERREMOVERESULT']._serialized_end=2141 + _globals['_ROLEUSERSREMOVERESPONSE']._serialized_start=2143 + _globals['_ROLEUSERSREMOVERESPONSE']._serialized_end=2219 + _globals['_ENTERPRISEREGISTRATION']._serialized_start=2222 + _globals['_ENTERPRISEREGISTRATION']._serialized_end=2766 + _globals['_DOMAINPASSWORDRULESREQUEST']._serialized_start=2768 + _globals['_DOMAINPASSWORDRULESREQUEST']._serialized_end=2840 + _globals['_DOMAINPASSWORDRULESFIELDS']._serialized_start=2842 + _globals['_DOMAINPASSWORDRULESFIELDS']._serialized_end=2934 + _globals['_LOGINTOMCREQUEST']._serialized_start=2936 + _globals['_LOGINTOMCREQUEST']._serialized_end=3005 + _globals['_LOGINTOMCRESPONSE']._serialized_start=3007 + _globals['_LOGINTOMCRESPONSE']._serialized_end=3126 + _globals['_DOMAINPASSWORDRULESRESPONSE']._serialized_start=3128 + _globals['_DOMAINPASSWORDRULESRESPONSE']._serialized_end=3231 + _globals['_APPROVEUSERDEVICEREQUEST']._serialized_start=3234 + _globals['_APPROVEUSERDEVICEREQUEST']._serialized_end=3370 + _globals['_APPROVEUSERDEVICERESPONSE']._serialized_start=3372 + _globals['_APPROVEUSERDEVICERESPONSE']._serialized_end=3488 + _globals['_APPROVEUSERDEVICESREQUEST']._serialized_start=3490 + _globals['_APPROVEUSERDEVICESREQUEST']._serialized_end=3579 + _globals['_APPROVEUSERDEVICESRESPONSE']._serialized_start=3581 + _globals['_APPROVEUSERDEVICESRESPONSE']._serialized_end=3673 + _globals['_ENTERPRISEUSERDATAKEY']._serialized_start=3676 + _globals['_ENTERPRISEUSERDATAKEY']._serialized_end=3811 + _globals['_ENTERPRISEUSERDATAKEYS']._serialized_start=3813 + _globals['_ENTERPRISEUSERDATAKEYS']._serialized_end=3886 + _globals['_ENTERPRISEUSERDATAKEYLIGHT']._serialized_start=3888 + _globals['_ENTERPRISEUSERDATAKEYLIGHT']._serialized_end=3991 + _globals['_ENTERPRISEUSERDATAKEYSBYNODE']._serialized_start=3993 + _globals['_ENTERPRISEUSERDATAKEYSBYNODE']._serialized_end=4093 + _globals['_ENTERPRISEUSERDATAKEYSBYNODERESPONSE']._serialized_start=4095 + _globals['_ENTERPRISEUSERDATAKEYSBYNODERESPONSE']._serialized_end=4189 + _globals['_ENTERPRISEDATAREQUEST']._serialized_start=4191 + _globals['_ENTERPRISEDATAREQUEST']._serialized_end=4241 + _globals['_SPECIALPROVISIONING']._serialized_start=4243 + _globals['_SPECIALPROVISIONING']._serialized_end=4291 + _globals['_GENERALDATAENTITY']._serialized_start=4294 + _globals['_GENERALDATAENTITY']._serialized_end=4554 + _globals['_NODE']._serialized_start=4557 + _globals['_NODE']._serialized_end=4810 + _globals['_ROLE']._serialized_start=4813 + _globals['_ROLE']._serialized_end=4955 + _globals['_USER']._serialized_start=4958 + _globals['_USER']._serialized_end=5270 + _globals['_USERALIAS']._serialized_start=5272 + _globals['_USERALIAS']._serialized_end=5327 + _globals['_COMPLIANCEREPORTMETADATA']._serialized_start=5330 + _globals['_COMPLIANCEREPORTMETADATA']._serialized_end=5502 + _globals['_MANAGEDNODE']._serialized_start=5504 + _globals['_MANAGEDNODE']._serialized_end=5587 + _globals['_USERMANAGEDNODE']._serialized_start=5589 + _globals['_USERMANAGEDNODE']._serialized_end=5673 + _globals['_USERPRIVILEGE']._serialized_start=5675 + _globals['_USERPRIVILEGE']._serialized_end=5794 + _globals['_ROLEUSER']._serialized_start=5796 + _globals['_ROLEUSER']._serialized_end=5848 + _globals['_ROLEPRIVILEGE']._serialized_start=5850 + _globals['_ROLEPRIVILEGE']._serialized_end=5927 + _globals['_PRIVILEGESBYMANAGEDNODE']._serialized_start=5929 + _globals['_PRIVILEGESBYMANAGEDNODE']._serialized_end=6013 + _globals['_ROLEENFORCEMENT']._serialized_start=6015 + _globals['_ROLEENFORCEMENT']._serialized_end=6088 + _globals['_TEAM']._serialized_start=6091 + _globals['_TEAM']._serialized_end=6260 + _globals['_TEAMUSER']._serialized_start=6262 + _globals['_TEAMUSER']._serialized_end=6333 + _globals['_GETDISTRIBUTORINFORESPONSE']._serialized_start=6335 + _globals['_GETDISTRIBUTORINFORESPONSE']._serialized_end=6410 + _globals['_DISTRIBUTOR']._serialized_start=6412 + _globals['_DISTRIBUTOR']._serialized_end=6478 + _globals['_MSPINFO']._serialized_start=6481 + _globals['_MSPINFO']._serialized_end=6766 + _globals['_MANAGEDCOMPANY']._serialized_start=6769 + _globals['_MANAGEDCOMPANY']._serialized_end=7065 + _globals['_MSPPOOL']._serialized_start=7067 + _globals['_MSPPOOL']._serialized_end=7149 + _globals['_MSPCONTACT']._serialized_start=7151 + _globals['_MSPCONTACT']._serialized_end=7209 + _globals['_LICENSEADDON']._serialized_start=7212 + _globals['_LICENSEADDON']._serialized_end=7472 + _globals['_MCDEFAULT']._serialized_start=7474 + _globals['_MCDEFAULT']._serialized_end=7589 + _globals['_MSPPERMITS']._serialized_start=7592 + _globals['_MSPPERMITS']._serialized_end=7802 + _globals['_LICENSE']._serialized_start=7805 + _globals['_LICENSE']._serialized_end=8349 + _globals['_BRIDGE']._serialized_start=8351 + _globals['_BRIDGE']._serialized_end=8461 + _globals['_SCIM']._serialized_start=8463 + _globals['_SCIM']._serialized_end=8579 + _globals['_EMAILPROVISION']._serialized_start=8581 + _globals['_EMAILPROVISION']._serialized_end=8657 + _globals['_QUEUEDTEAM']._serialized_start=8659 + _globals['_QUEUEDTEAM']._serialized_end=8741 + _globals['_QUEUEDTEAMUSER']._serialized_start=8743 + _globals['_QUEUEDTEAMUSER']._serialized_end=8791 + _globals['_TEAMSADDRESULT']._serialized_start=8794 + _globals['_TEAMSADDRESULT']._serialized_end=8958 + _globals['_TEAMADDRESULT']._serialized_start=8960 + _globals['_TEAMADDRESULT']._serialized_end=9045 + _globals['_SSOSERVICE']._serialized_start=9048 + _globals['_SSOSERVICE']._serialized_end=9193 + _globals['_REPORTFILTERUSER']._serialized_start=9195 + _globals['_REPORTFILTERUSER']._serialized_end=9244 + _globals['_DEVICEREQUESTFORADMINAPPROVAL']._serialized_start=9247 + _globals['_DEVICEREQUESTFORADMINAPPROVAL']._serialized_end=9526 + _globals['_ENTERPRISEDATA']._serialized_start=9528 + _globals['_ENTERPRISEDATA']._serialized_end=9624 + _globals['_ENTERPRISEDATARESPONSE']._serialized_start=9627 + _globals['_ENTERPRISEDATARESPONSE']._serialized_end=9835 + _globals['_BACKUPREQUEST']._serialized_start=9837 + _globals['_BACKUPREQUEST']._serialized_end=9879 + _globals['_BACKUPRECORD']._serialized_start=9882 + _globals['_BACKUPRECORD']._serialized_end=10034 + _globals['_BACKUPKEY']._serialized_start=10036 + _globals['_BACKUPKEY']._serialized_end=10082 + _globals['_BACKUPUSER']._serialized_start=10085 + _globals['_BACKUPUSER']._serialized_end=10354 + _globals['_BACKUPRESPONSE']._serialized_start=10357 + _globals['_BACKUPRESPONSE']._serialized_end=10515 + _globals['_BACKUPFILE']._serialized_start=10517 + _globals['_BACKUPFILE']._serialized_end=10618 + _globals['_BACKUPSRESPONSE']._serialized_start=10620 + _globals['_BACKUPSRESPONSE']._serialized_end=10676 + _globals['_GETENTERPRISEDATAKEYSREQUEST']._serialized_start=10678 + _globals['_GETENTERPRISEDATAKEYSREQUEST']._serialized_end=10724 + _globals['_GETENTERPRISEDATAKEYSRESPONSE']._serialized_start=10727 + _globals['_GETENTERPRISEDATAKEYSRESPONSE']._serialized_end=10982 + _globals['_ROLEKEY']._serialized_start=10984 + _globals['_ROLEKEY']._serialized_end=11078 + _globals['_MSPKEY']._serialized_start=11080 + _globals['_MSPKEY']._serialized_end=11180 + _globals['_ENTERPRISEKEYS']._serialized_start=11182 + _globals['_ENTERPRISEKEYS']._serialized_end=11306 + _globals['_TREEKEY']._serialized_start=11308 + _globals['_TREEKEY']._serialized_end=11380 + _globals['_SHAREDRECORDRESPONSE']._serialized_start=11382 + _globals['_SHAREDRECORDRESPONSE']._serialized_end=11451 + _globals['_SHAREDRECORDEVENT']._serialized_start=11453 + _globals['_SHAREDRECORDEVENT']._serialized_end=11565 + _globals['_SETRESTRICTVISIBILITYREQUEST']._serialized_start=11567 + _globals['_SETRESTRICTVISIBILITYREQUEST']._serialized_end=11613 + _globals['_USERADDREQUEST']._serialized_start=11616 + _globals['_USERADDREQUEST']._serialized_end=11824 + _globals['_USERUPDATEREQUEST']._serialized_start=11826 + _globals['_USERUPDATEREQUEST']._serialized_end=11884 + _globals['_USERUPDATE']._serialized_start=11887 + _globals['_USERUPDATE']._serialized_end=12118 + _globals['_USERUPDATERESPONSE']._serialized_start=12120 + _globals['_USERUPDATERESPONSE']._serialized_end=12185 + _globals['_USERUPDATERESULT']._serialized_start=12188 + _globals['_USERUPDATERESULT']._serialized_end=12324 + _globals['_COMPLIANCERECORDOWNERSREQUEST']._serialized_start=12326 + _globals['_COMPLIANCERECORDOWNERSREQUEST']._serialized_end=12400 + _globals['_COMPLIANCERECORDOWNERSRESPONSE']._serialized_start=12402 + _globals['_COMPLIANCERECORDOWNERSRESPONSE']._serialized_end=12481 + _globals['_RECORDOWNER']._serialized_start=12483 + _globals['_RECORDOWNER']._serialized_end=12538 + _globals['_PRELIMINARYCOMPLIANCEDATAREQUEST']._serialized_start=12541 + _globals['_PRELIMINARYCOMPLIANCEDATAREQUEST']._serialized_end=12707 + _globals['_PRELIMINARYCOMPLIANCEDATARESPONSE']._serialized_start=12710 + _globals['_PRELIMINARYCOMPLIANCEDATARESPONSE']._serialized_end=12869 + _globals['_AUDITUSERRECORD']._serialized_start=12871 + _globals['_AUDITUSERRECORD']._serialized_end=12969 + _globals['_AUDITUSERDATA']._serialized_start=12972 + _globals['_AUDITUSERDATA']._serialized_end=13113 + _globals['_COMPLIANCEREPORTFILTERS']._serialized_start=13115 + _globals['_COMPLIANCEREPORTFILTERS']._serialized_end=13242 + _globals['_COMPLIANCEREPORTREQUEST']._serialized_start=13244 + _globals['_COMPLIANCEREPORTREQUEST']._serialized_end=13371 + _globals['_COMPLIANCEREPORTRUN']._serialized_start=13374 + _globals['_COMPLIANCEREPORTRUN']._serialized_end=13507 + _globals['_COMPLIANCEREPORTCRITERIAANDFILTER']._serialized_start=13510 + _globals['_COMPLIANCEREPORTCRITERIAANDFILTER']._serialized_end=13762 + _globals['_COMPLIANCEREPORTCRITERIA']._serialized_start=13764 + _globals['_COMPLIANCEREPORTCRITERIA']._serialized_end=13862 + _globals['_COMPLIANCEREPORTFILTER']._serialized_start=13864 + _globals['_COMPLIANCEREPORTFILTER']._serialized_end=13984 + _globals['_COMPLIANCEREPORTRESPONSE']._serialized_start=13987 + _globals['_COMPLIANCEREPORTRESPONSE']._serialized_end=14660 + _globals['_AUDITRECORD']._serialized_start=14663 + _globals['_AUDITRECORD']._serialized_end=14815 + _globals['_AUDITROLE']._serialized_start=14818 + _globals['_AUDITROLE']._serialized_end=15074 + _globals['_ROLENODEMANAGEMENT']._serialized_start=15076 + _globals['_ROLENODEMANAGEMENT']._serialized_end=15170 + _globals['_USERPROFILE']._serialized_start=15172 + _globals['_USERPROFILE']._serialized_end=15279 + _globals['_RECORDPERMISSION']._serialized_start=15281 + _globals['_RECORDPERMISSION']._serialized_end=15404 + _globals['_DRIVEPERMISSION']._serialized_start=15407 + _globals['_DRIVEPERMISSION']._serialized_end=15606 + _globals['_USERRECORD']._serialized_start=15608 + _globals['_USERRECORD']._serialized_end=15703 + _globals['_AUDITTEAM']._serialized_start=15705 + _globals['_AUDITTEAM']._serialized_end=15796 + _globals['_AUDITTEAMUSER']._serialized_start=15798 + _globals['_AUDITTEAMUSER']._serialized_end=15857 + _globals['_SHAREDFOLDERRECORD']._serialized_start=15860 + _globals['_SHAREDFOLDERRECORD']._serialized_end=16019 + _globals['_SHAREADMINRECORD']._serialized_start=16021 + _globals['_SHAREADMINRECORD']._serialized_end=16098 + _globals['_SHAREDFOLDERUSER']._serialized_start=16100 + _globals['_SHAREDFOLDERUSER']._serialized_end=16170 + _globals['_SHAREDFOLDERTEAM']._serialized_start=16172 + _globals['_SHAREDFOLDERTEAM']._serialized_end=16233 + _globals['_GETCOMPLIANCEREPORTREQUEST']._serialized_start=16235 + _globals['_GETCOMPLIANCEREPORTREQUEST']._serialized_end=16282 + _globals['_GETCOMPLIANCEREPORTRESPONSE']._serialized_start=16284 + _globals['_GETCOMPLIANCEREPORTRESPONSE']._serialized_end=16334 + _globals['_COMPLIANCEREPORTCRITERIAREQUEST']._serialized_start=16336 + _globals['_COMPLIANCEREPORTCRITERIAREQUEST']._serialized_end=16390 + _globals['_SAVECOMPLIANCEREPORTCRITERIARESPONSE']._serialized_start=16392 + _globals['_SAVECOMPLIANCEREPORTCRITERIARESPONSE']._serialized_end=16451 + _globals['_LINKEDRECORD']._serialized_start=16453 + _globals['_LINKEDRECORD']._serialized_end=16505 + _globals['_GETSHARINGADMINSREQUEST']._serialized_start=16507 + _globals['_GETSHARINGADMINSREQUEST']._serialized_end=16594 + _globals['_USERPROFILEEXT']._serialized_start=16597 + _globals['_USERPROFILEEXT']._serialized_end=16821 + _globals['_GETSHARINGADMINSRESPONSE']._serialized_start=16823 + _globals['_GETSHARINGADMINSRESPONSE']._serialized_end=16902 + _globals['_TEAMSENTERPRISEUSERSADDREQUEST']._serialized_start=16904 + _globals['_TEAMSENTERPRISEUSERSADDREQUEST']._serialized_end=16999 + _globals['_TEAMSENTERPRISEUSERSADDTEAMREQUEST']._serialized_start=17001 + _globals['_TEAMSENTERPRISEUSERSADDTEAMREQUEST']._serialized_end=17117 + _globals['_TEAMSENTERPRISEUSERSADDUSERREQUEST']._serialized_start=17120 + _globals['_TEAMSENTERPRISEUSERSADDUSERREQUEST']._serialized_end=17291 + _globals['_TYPEDKEY']._serialized_start=17293 + _globals['_TYPEDKEY']._serialized_end=17363 + _globals['_TEAMSENTERPRISEUSERSADDRESPONSE']._serialized_start=17365 + _globals['_TEAMSENTERPRISEUSERSADDRESPONSE']._serialized_end=17480 + _globals['_TEAMSENTERPRISEUSERSADDTEAMRESPONSE']._serialized_start=17483 + _globals['_TEAMSENTERPRISEUSERSADDTEAMRESPONSE']._serialized_end=17679 + _globals['_TEAMSENTERPRISEUSERSADDUSERRESPONSE']._serialized_start=17682 + _globals['_TEAMSENTERPRISEUSERSADDUSERRESPONSE']._serialized_end=17841 + _globals['_TEAMENTERPRISEUSERREMOVE']._serialized_start=17843 + _globals['_TEAMENTERPRISEUSERREMOVE']._serialized_end=17912 + _globals['_TEAMENTERPRISEUSERREMOVESREQUEST']._serialized_start=17914 + _globals['_TEAMENTERPRISEUSERREMOVESREQUEST']._serialized_end=18020 + _globals['_TEAMENTERPRISEUSERREMOVESRESPONSE']._serialized_start=18022 + _globals['_TEAMENTERPRISEUSERREMOVESRESPONSE']._serialized_end=18145 + _globals['_TEAMENTERPRISEUSERREMOVERESPONSE']._serialized_start=18148 + _globals['_TEAMENTERPRISEUSERREMOVERESPONSE']._serialized_end=18332 + _globals['_DOMAINALIAS']._serialized_start=18334 + _globals['_DOMAINALIAS']._serialized_end=18411 + _globals['_DOMAINALIASREQUEST']._serialized_start=18413 + _globals['_DOMAINALIASREQUEST']._serialized_end=18479 + _globals['_DOMAINALIASRESPONSE']._serialized_start=18481 + _globals['_DOMAINALIASRESPONSE']._serialized_end=18548 + _globals['_ENTERPRISEUSERSPROVISIONREQUEST']._serialized_start=18550 + _globals['_ENTERPRISEUSERSPROVISIONREQUEST']._serialized_end=18659 + _globals['_ENTERPRISEUSERSPROVISION']._serialized_start=18662 + _globals['_ENTERPRISEUSERSPROVISION']._serialized_end=19100 + _globals['_ENTERPRISEUSERSPROVISIONRESPONSE']._serialized_start=19102 + _globals['_ENTERPRISEUSERSPROVISIONRESPONSE']._serialized_end=19197 + _globals['_ENTERPRISEUSERSPROVISIONRESULT']._serialized_start=19199 + _globals['_ENTERPRISEUSERSPROVISIONRESULT']._serialized_end=19312 + _globals['_ENTERPRISEUSERSADDREQUEST']._serialized_start=19314 + _globals['_ENTERPRISEUSERSADDREQUEST']._serialized_end=19411 + _globals['_ENTERPRISEUSERSADD']._serialized_start=19414 + _globals['_ENTERPRISEUSERSADD']._serialized_end=19682 + _globals['_ENTERPRISEUSERSADDRESPONSE']._serialized_start=19685 + _globals['_ENTERPRISEUSERSADDRESPONSE']._serialized_end=19840 + _globals['_ENTERPRISEUSERSADDRESULT']._serialized_start=19843 + _globals['_ENTERPRISEUSERSADDRESULT']._serialized_end=19993 + _globals['_UPDATEMSPPERMITSREQUEST']._serialized_start=19996 + _globals['_UPDATEMSPPERMITSREQUEST']._serialized_end=20181 + _globals['_DELETEENTERPRISEUSERSREQUEST']._serialized_start=20183 + _globals['_DELETEENTERPRISEUSERSREQUEST']._serialized_end=20240 + _globals['_DELETEENTERPRISEUSERSTATUS']._serialized_start=20242 + _globals['_DELETEENTERPRISEUSERSTATUS']._serialized_end=20353 + _globals['_DELETEENTERPRISEUSERSRESPONSE']._serialized_start=20355 + _globals['_DELETEENTERPRISEUSERSRESPONSE']._serialized_end=20448 + _globals['_CLEARSECURITYDATAREQUEST']._serialized_start=20450 + _globals['_CLEARSECURITYDATAREQUEST']._serialized_end=20569 + _globals['_LISTDOMAINSRESPONSE']._serialized_start=20571 + _globals['_LISTDOMAINSRESPONSE']._serialized_end=20608 + _globals['_RESERVEDOMAINREQUEST']._serialized_start=20610 + _globals['_RESERVEDOMAINREQUEST']._serialized_end=20710 + _globals['_RESERVEDOMAINRESPONSE']._serialized_start=20712 + _globals['_RESERVEDOMAINRESPONSE']._serialized_end=20750 + _globals['_ROLESBYTEAM']._serialized_start=20752 + _globals['_ROLESBYTEAM']._serialized_end=20798 + _globals['_LOCKUSERSREQUEST']._serialized_start=20801 + _globals['_LOCKUSERSREQUEST']._serialized_end=20942 + _globals['_LOCKUSERSRESPONSE']._serialized_start=20944 + _globals['_LOCKUSERSRESPONSE']._serialized_end=21011 + _globals['_LOCKUSERRESPONSE']._serialized_start=21013 + _globals['_LOCKUSERRESPONSE']._serialized_end=21123 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/enterprise_pb2.pyi b/keepersdk-package/src/keepersdk/proto/enterprise_pb2.pyi index 2d693182..19d2a13e 100644 --- a/keepersdk-package/src/keepersdk/proto/enterprise_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/enterprise_pb2.pyi @@ -1,3 +1,4 @@ +from . 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 @@ -23,6 +24,7 @@ class RoleUserModifyStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): MUST_HAVE_ONE_USER_ADMIN: _ClassVar[RoleUserModifyStatus] INVALID_ROLE_ID: _ClassVar[RoleUserModifyStatus] PAM_LICENSE_SEAT_EXCEEDED: _ClassVar[RoleUserModifyStatus] + WOULD_LOCK_SELF: _ClassVar[RoleUserModifyStatus] class EnterpriseType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -110,6 +112,12 @@ class UserUpdateStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () USER_UPDATE_OK: _ClassVar[UserUpdateStatus] USER_UPDATE_ACCESS_DENIED: _ClassVar[UserUpdateStatus] + USER_UPDATE_EXCEEDED_LICENSE_SEATS: _ClassVar[UserUpdateStatus] + USER_UPDATE_BAD_REQUEST: _ClassVar[UserUpdateStatus] + USER_UPDATE_DUPLICATE: _ClassVar[UserUpdateStatus] + USER_UPDATE_INVALID_STATE: _ClassVar[UserUpdateStatus] + USER_UPDATE_FAILED: _ClassVar[UserUpdateStatus] + USER_UPDATE_ERROR: _ClassVar[UserUpdateStatus] class AuditUserStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -178,6 +186,7 @@ MAY_NOT_REMOVE_SELF_FROM_ROLE: RoleUserModifyStatus MUST_HAVE_ONE_USER_ADMIN: RoleUserModifyStatus INVALID_ROLE_ID: RoleUserModifyStatus PAM_LICENSE_SEAT_EXCEEDED: RoleUserModifyStatus +WOULD_LOCK_SELF: RoleUserModifyStatus ENTERPRISE_STANDARD: EnterpriseType ENTERPRISE_MSP: EnterpriseType UNDEFINED: TransferAcceptanceStatus @@ -238,6 +247,12 @@ FORBID_KEY_TYPE_1: EnterpriseFlagType KEEPER_DRIVE: EnterpriseFlagType USER_UPDATE_OK: UserUpdateStatus USER_UPDATE_ACCESS_DENIED: UserUpdateStatus +USER_UPDATE_EXCEEDED_LICENSE_SEATS: UserUpdateStatus +USER_UPDATE_BAD_REQUEST: UserUpdateStatus +USER_UPDATE_DUPLICATE: UserUpdateStatus +USER_UPDATE_INVALID_STATE: UserUpdateStatus +USER_UPDATE_FAILED: UserUpdateStatus +USER_UPDATE_ERROR: UserUpdateStatus OK: AuditUserStatus ACCESS_DENIED: AuditUserStatus NO_LONGER_IN_ENTERPRISE: AuditUserStatus @@ -404,14 +419,16 @@ class ManagedNodesByRole(_message.Message): def __init__(self, role_id: _Optional[int] = ..., managedNodeId: _Optional[_Iterable[int]] = ...) -> None: ... class RoleUserAddKeys(_message.Message): - __slots__ = ("enterpriseUserId", "treeKey", "roleAdminKey") + __slots__ = ("enterpriseUserId", "treeKey", "roleAdminKey", "typedTreeKey") ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] TREEKEY_FIELD_NUMBER: _ClassVar[int] ROLEADMINKEY_FIELD_NUMBER: _ClassVar[int] + TYPEDTREEKEY_FIELD_NUMBER: _ClassVar[int] enterpriseUserId: int treeKey: str roleAdminKey: str - def __init__(self, enterpriseUserId: _Optional[int] = ..., treeKey: _Optional[str] = ..., roleAdminKey: _Optional[str] = ...) -> None: ... + typedTreeKey: TypedKey + def __init__(self, enterpriseUserId: _Optional[int] = ..., treeKey: _Optional[str] = ..., roleAdminKey: _Optional[str] = ..., typedTreeKey: _Optional[_Union[TypedKey, _Mapping]] = ...) -> None: ... class RoleUserAdd(_message.Message): __slots__ = ("role_id", "roleUserAddKeys") @@ -1418,7 +1435,7 @@ class UserUpdateRequest(_message.Message): def __init__(self, users: _Optional[_Iterable[_Union[UserUpdate, _Mapping]]] = ...) -> None: ... class UserUpdate(_message.Message): - __slots__ = ("enterpriseUserId", "nodeId", "encryptedData", "keyType", "fullName", "jobTitle", "email") + __slots__ = ("enterpriseUserId", "nodeId", "encryptedData", "keyType", "fullName", "jobTitle", "email", "inviteeLocale", "encryptedDataString") ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] NODEID_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] @@ -1426,6 +1443,8 @@ class UserUpdate(_message.Message): FULLNAME_FIELD_NUMBER: _ClassVar[int] JOBTITLE_FIELD_NUMBER: _ClassVar[int] EMAIL_FIELD_NUMBER: _ClassVar[int] + INVITEELOCALE_FIELD_NUMBER: _ClassVar[int] + ENCRYPTEDDATASTRING_FIELD_NUMBER: _ClassVar[int] enterpriseUserId: int nodeId: int encryptedData: bytes @@ -1433,7 +1452,9 @@ class UserUpdate(_message.Message): fullName: str jobTitle: str email: str - 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] = ...) -> None: ... + inviteeLocale: str + encryptedDataString: str + 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] = ..., inviteeLocale: _Optional[str] = ..., encryptedDataString: _Optional[str] = ...) -> None: ... class UserUpdateResponse(_message.Message): __slots__ = ("users",) @@ -1442,12 +1463,16 @@ class UserUpdateResponse(_message.Message): def __init__(self, users: _Optional[_Iterable[_Union[UserUpdateResult, _Mapping]]] = ...) -> None: ... class UserUpdateResult(_message.Message): - __slots__ = ("enterpriseUserId", "status") + __slots__ = ("enterpriseUserId", "status", "errorMessage", "additionalInfo") ENTERPRISEUSERID_FIELD_NUMBER: _ClassVar[int] STATUS_FIELD_NUMBER: _ClassVar[int] + ERRORMESSAGE_FIELD_NUMBER: _ClassVar[int] + ADDITIONALINFO_FIELD_NUMBER: _ClassVar[int] enterpriseUserId: int status: UserUpdateStatus - def __init__(self, enterpriseUserId: _Optional[int] = ..., status: _Optional[_Union[UserUpdateStatus, str]] = ...) -> None: ... + errorMessage: str + additionalInfo: str + def __init__(self, enterpriseUserId: _Optional[int] = ..., status: _Optional[_Union[UserUpdateStatus, str]] = ..., errorMessage: _Optional[str] = ..., additionalInfo: _Optional[str] = ...) -> None: ... class ComplianceRecordOwnersRequest(_message.Message): __slots__ = ("nodeIds", "includeNonShared") @@ -1496,14 +1521,16 @@ class PreliminaryComplianceDataResponse(_message.Message): 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") + __slots__ = ("recordUid", "encryptedData", "shared", "isDriveRecord") RECORDUID_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDDATA_FIELD_NUMBER: _ClassVar[int] SHARED_FIELD_NUMBER: _ClassVar[int] + ISDRIVERECORD_FIELD_NUMBER: _ClassVar[int] recordUid: bytes encryptedData: bytes shared: bool - def __init__(self, recordUid: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., shared: bool = ...) -> None: ... + isDriveRecord: bool + def __init__(self, recordUid: _Optional[bytes] = ..., encryptedData: _Optional[bytes] = ..., shared: bool = ..., isDriveRecord: bool = ...) -> None: ... class AuditUserData(_message.Message): __slots__ = ("enterpriseUserId", "auditUserRecords", "status") @@ -1626,20 +1653,22 @@ class ComplianceReportResponse(_message.Message): def __init__(self, dateGenerated: _Optional[int] = ..., runByUserName: _Optional[str] = ..., reportName: _Optional[str] = ..., reportUid: _Optional[bytes] = ..., complianceReportRun: _Optional[_Union[ComplianceReportRun, _Mapping]] = ..., userProfiles: _Optional[_Iterable[_Union[UserProfile, _Mapping]]] = ..., auditTeams: _Optional[_Iterable[_Union[AuditTeam, _Mapping]]] = ..., auditRecords: _Optional[_Iterable[_Union[AuditRecord, _Mapping]]] = ..., userRecords: _Optional[_Iterable[_Union[UserRecord, _Mapping]]] = ..., sharedFolderRecords: _Optional[_Iterable[_Union[SharedFolderRecord, _Mapping]]] = ..., sharedFolderUsers: _Optional[_Iterable[_Union[SharedFolderUser, _Mapping]]] = ..., sharedFolderTeams: _Optional[_Iterable[_Union[SharedFolderTeam, _Mapping]]] = ..., auditTeamUsers: _Optional[_Iterable[_Union[AuditTeamUser, _Mapping]]] = ..., auditRoles: _Optional[_Iterable[_Union[AuditRole, _Mapping]]] = ..., linkedRecords: _Optional[_Iterable[_Union[LinkedRecord, _Mapping]]] = ...) -> None: ... class AuditRecord(_message.Message): - __slots__ = ("recordUid", "auditData", "hasAttachments", "inTrash", "treeLeft", "treeRight") + __slots__ = ("recordUid", "auditData", "hasAttachments", "inTrash", "treeLeft", "treeRight", "isDriveRecord") RECORDUID_FIELD_NUMBER: _ClassVar[int] AUDITDATA_FIELD_NUMBER: _ClassVar[int] HASATTACHMENTS_FIELD_NUMBER: _ClassVar[int] INTRASH_FIELD_NUMBER: _ClassVar[int] TREELEFT_FIELD_NUMBER: _ClassVar[int] TREERIGHT_FIELD_NUMBER: _ClassVar[int] + ISDRIVERECORD_FIELD_NUMBER: _ClassVar[int] recordUid: bytes auditData: bytes hasAttachments: bool inTrash: bool treeLeft: int treeRight: int - def __init__(self, recordUid: _Optional[bytes] = ..., auditData: _Optional[bytes] = ..., hasAttachments: bool = ..., inTrash: bool = ..., treeLeft: _Optional[int] = ..., treeRight: _Optional[int] = ...) -> None: ... + isDriveRecord: bool + def __init__(self, recordUid: _Optional[bytes] = ..., auditData: _Optional[bytes] = ..., hasAttachments: bool = ..., inTrash: bool = ..., treeLeft: _Optional[int] = ..., treeRight: _Optional[int] = ..., isDriveRecord: bool = ...) -> None: ... class AuditRole(_message.Message): __slots__ = ("roleId", "encryptedData", "restrictShareOutsideEnterprise", "restrictShareAll", "restrictShareOfAttachments", "restrictMaskPasswordsWhileEditing", "roleNodeManagements") @@ -1686,12 +1715,32 @@ class UserProfile(_message.Message): def __init__(self, enterpriseUserId: _Optional[int] = ..., fullName: _Optional[str] = ..., jobTitle: _Optional[str] = ..., email: _Optional[str] = ..., roleIds: _Optional[_Iterable[int]] = ...) -> None: ... class RecordPermission(_message.Message): - __slots__ = ("recordUid", "permissionBits") + __slots__ = ("recordUid", "permissionBits", "drive") RECORDUID_FIELD_NUMBER: _ClassVar[int] PERMISSIONBITS_FIELD_NUMBER: _ClassVar[int] + DRIVE_FIELD_NUMBER: _ClassVar[int] recordUid: bytes permissionBits: int - def __init__(self, recordUid: _Optional[bytes] = ..., permissionBits: _Optional[int] = ...) -> None: ... + drive: DrivePermission + def __init__(self, recordUid: _Optional[bytes] = ..., permissionBits: _Optional[int] = ..., drive: _Optional[_Union[DrivePermission, _Mapping]] = ...) -> None: ... + +class DrivePermission(_message.Message): + __slots__ = ("owner", "denied", "canEdit", "canShare", "isShareAdmin", "accessType", "folderPermissions") + OWNER_FIELD_NUMBER: _ClassVar[int] + DENIED_FIELD_NUMBER: _ClassVar[int] + CANEDIT_FIELD_NUMBER: _ClassVar[int] + CANSHARE_FIELD_NUMBER: _ClassVar[int] + ISSHAREADMIN_FIELD_NUMBER: _ClassVar[int] + ACCESSTYPE_FIELD_NUMBER: _ClassVar[int] + FOLDERPERMISSIONS_FIELD_NUMBER: _ClassVar[int] + owner: bool + denied: bool + canEdit: bool + canShare: bool + isShareAdmin: bool + accessType: _folder_pb2.AccessType + folderPermissions: _folder_pb2.FolderPermissions + def __init__(self, owner: bool = ..., denied: bool = ..., canEdit: bool = ..., canShare: bool = ..., isShareAdmin: bool = ..., accessType: _Optional[_Union[_folder_pb2.AccessType, str]] = ..., folderPermissions: _Optional[_Union[_folder_pb2.FolderPermissions, _Mapping]] = ...) -> None: ... class UserRecord(_message.Message): __slots__ = ("enterpriseUserId", "recordPermissions") diff --git a/keepersdk-package/src/keepersdk/proto/folder_pb2.py b/keepersdk-package/src/keepersdk/proto/folder_pb2.py index 466f3cdf..affb1570 100644 --- a/keepersdk-package/src/keepersdk/proto/folder_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/folder_pb2.py @@ -26,7 +26,7 @@ from . import tla_pb2 as tla__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x66older.proto\x12\x06\x46older\x1a\x0crecord.proto\x1a\ttla.proto\"\\\n\x10\x45ncryptedDataKey\x12\x14\n\x0c\x65ncryptedKey\x18\x01 \x01(\x0c\x12\x32\n\x10\x65ncryptedKeyType\x18\x02 \x01(\x0e\x32\x18.Folder.EncryptedKeyType\"\x82\x01\n\x16SharedFolderRecordData\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x0e\n\x06userId\x18\x03 \x01(\x05\x12\x32\n\x10\x65ncryptedDataKey\x18\x04 \x03(\x0b\x32\x18.Folder.EncryptedDataKey\"\\\n\x1aSharedFolderRecordDataList\x12>\n\x16sharedFolderRecordData\x18\x01 \x03(\x0b\x32\x1e.Folder.SharedFolderRecordData\"_\n\x15SharedFolderRecordFix\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12 \n\x18\x65ncryptedRecordFolderKey\x18\x03 \x01(\x0c\"Y\n\x19SharedFolderRecordFixList\x12<\n\x15sharedFolderRecordFix\x18\x01 \x03(\x0b\x32\x1d.Folder.SharedFolderRecordFix\"\xa2\x02\n\rRecordRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12&\n\nrecordType\x18\x02 \x01(\x0e\x32\x12.Folder.RecordType\x12\x12\n\nrecordData\x18\x03 \x01(\x0c\x12\x1a\n\x12\x65ncryptedRecordKey\x18\x04 \x01(\x0c\x12&\n\nfolderType\x18\x05 \x01(\x0e\x32\x12.Folder.FolderType\x12\x12\n\nhowLongAgo\x18\x06 \x01(\x03\x12\x11\n\tfolderUid\x18\x07 \x01(\x0c\x12 \n\x18\x65ncryptedRecordFolderKey\x18\x08 \x01(\x0c\x12\r\n\x05\x65xtra\x18\t \x01(\x0c\x12\x15\n\rnonSharedData\x18\n \x01(\x0c\x12\x0f\n\x07\x66ileIds\x18\x0b \x03(\x03\"E\n\x0eRecordResponse\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x0e\n\x06status\x18\x03 \x01(\t\"\x80\x01\n\x12SharedFolderFields\x12\x1b\n\x13\x65ncryptedFolderName\x18\x01 \x01(\x0c\x12\x13\n\x0bmanageUsers\x18\x02 \x01(\x08\x12\x15\n\rmanageRecords\x18\x03 \x01(\x08\x12\x0f\n\x07\x63\x61nEdit\x18\x04 \x01(\x08\x12\x10\n\x08\x63\x61nShare\x18\x05 \x01(\x08\"3\n\x18SharedFolderFolderFields\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\"\x8f\x02\n\rFolderRequest\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12&\n\nfolderType\x18\x02 \x01(\x0e\x32\x12.Folder.FolderType\x12\x17\n\x0fparentFolderUid\x18\x03 \x01(\x0c\x12\x12\n\nfolderData\x18\x04 \x01(\x0c\x12\x1a\n\x12\x65ncryptedFolderKey\x18\x05 \x01(\x0c\x12\x36\n\x12sharedFolderFields\x18\x06 \x01(\x0b\x32\x1a.Folder.SharedFolderFields\x12\x42\n\x18sharedFolderFolderFields\x18\x07 \x01(\x0b\x32 .Folder.SharedFolderFolderFields\"E\n\x0e\x46olderResponse\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x0e\n\x06status\x18\x03 \x01(\t\"w\n\x19ImportFolderRecordRequest\x12,\n\rfolderRequest\x18\x01 \x03(\x0b\x32\x15.Folder.FolderRequest\x12,\n\rrecordRequest\x18\x02 \x03(\x0b\x32\x15.Folder.RecordRequest\"|\n\x1aImportFolderRecordResponse\x12.\n\x0e\x66olderResponse\x18\x01 \x03(\x0b\x32\x16.Folder.FolderResponse\x12.\n\x0erecordResponse\x18\x02 \x03(\x0b\x32\x16.Folder.RecordResponse\"\xc9\x02\n\x18SharedFolderUpdateRecord\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x17\n\x0fsharedFolderUid\x18\x02 \x01(\x0c\x12\x0f\n\x07teamUid\x18\x03 \x01(\x0c\x12(\n\x07\x63\x61nEdit\x18\x04 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12)\n\x08\x63\x61nShare\x18\x05 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x1a\n\x12\x65ncryptedRecordKey\x18\x06 \x01(\x0c\x12\x10\n\x08revision\x18\x07 \x01(\x05\x12\x12\n\nexpiration\x18\x08 \x01(\x12\x12=\n\x15timerNotificationType\x18\t \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x1a\n\x12rotateOnExpiration\x18\n \x01(\x08\"\xcc\x02\n\x16SharedFolderUpdateUser\x12\x10\n\x08username\x18\x01 \x01(\t\x12,\n\x0bmanageUsers\x18\x02 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12.\n\rmanageRecords\x18\x03 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x1b\n\x0fsharedFolderKey\x18\x04 \x01(\x0c\x42\x02\x18\x01\x12\x12\n\nexpiration\x18\x05 \x01(\x12\x12=\n\x15timerNotificationType\x18\x06 \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x36\n\x14typedSharedFolderKey\x18\x07 \x01(\x0b\x32\x18.Folder.EncryptedDataKey\x12\x1a\n\x12rotateOnExpiration\x18\x08 \x01(\x08\"\x99\x02\n\x16SharedFolderUpdateTeam\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x13\n\x0bmanageUsers\x18\x02 \x01(\x08\x12\x15\n\rmanageRecords\x18\x03 \x01(\x08\x12\x1b\n\x0fsharedFolderKey\x18\x04 \x01(\x0c\x42\x02\x18\x01\x12\x12\n\nexpiration\x18\x05 \x01(\x12\x12=\n\x15timerNotificationType\x18\x06 \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x36\n\x14typedSharedFolderKey\x18\x07 \x01(\x0b\x32\x18.Folder.EncryptedDataKey\x12\x1a\n\x12rotateOnExpiration\x18\x08 \x01(\x08\"\x8e\x07\n\x1bSharedFolderUpdateV3Request\x12,\n$sharedFolderUpdateOperation_dont_use\x18\x01 \x01(\x05\x12\x17\n\x0fsharedFolderUid\x18\x02 \x01(\x0c\x12!\n\x19\x65ncryptedSharedFolderName\x18\x03 \x01(\x0c\x12\x10\n\x08revision\x18\x04 \x01(\x03\x12\x13\n\x0b\x66orceUpdate\x18\x05 \x01(\x08\x12\x13\n\x0b\x66romTeamUid\x18\x06 \x01(\x0c\x12\x33\n\x12\x64\x65\x66\x61ultManageUsers\x18\x07 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x35\n\x14\x64\x65\x66\x61ultManageRecords\x18\x08 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x64\x65\x66\x61ultCanEdit\x18\t \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x30\n\x0f\x64\x65\x66\x61ultCanShare\x18\n \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12?\n\x15sharedFolderAddRecord\x18\x0b \x03(\x0b\x32 .Folder.SharedFolderUpdateRecord\x12;\n\x13sharedFolderAddUser\x18\x0c \x03(\x0b\x32\x1e.Folder.SharedFolderUpdateUser\x12;\n\x13sharedFolderAddTeam\x18\r \x03(\x0b\x32\x1e.Folder.SharedFolderUpdateTeam\x12\x42\n\x18sharedFolderUpdateRecord\x18\x0e \x03(\x0b\x32 .Folder.SharedFolderUpdateRecord\x12>\n\x16sharedFolderUpdateUser\x18\x0f \x03(\x0b\x32\x1e.Folder.SharedFolderUpdateUser\x12>\n\x16sharedFolderUpdateTeam\x18\x10 \x03(\x0b\x32\x1e.Folder.SharedFolderUpdateTeam\x12 \n\x18sharedFolderRemoveRecord\x18\x11 \x03(\x0c\x12\x1e\n\x16sharedFolderRemoveUser\x18\x12 \x03(\t\x12\x1e\n\x16sharedFolderRemoveTeam\x18\x13 \x03(\x0c\x12\x19\n\x11sharedFolderOwner\x18\x14 \x01(\t\"c\n\x1dSharedFolderUpdateV3RequestV2\x12\x42\n\x15sharedFoldersUpdateV3\x18\x01 \x03(\x0b\x32#.Folder.SharedFolderUpdateV3Request\"C\n\x1eSharedFolderUpdateRecordStatus\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x0e\n\x06status\x18\x02 \x01(\t\"@\n\x1cSharedFolderUpdateUserStatus\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x0e\n\x06status\x18\x02 \x01(\t\"?\n\x1cSharedFolderUpdateTeamStatus\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x0e\n\x06status\x18\x02 \x01(\t\"\x88\x06\n\x1cSharedFolderUpdateV3Response\x12\x10\n\x08revision\x18\x01 \x01(\x03\x12K\n\x1bsharedFolderAddRecordStatus\x18\x02 \x03(\x0b\x32&.Folder.SharedFolderUpdateRecordStatus\x12G\n\x19sharedFolderAddUserStatus\x18\x03 \x03(\x0b\x32$.Folder.SharedFolderUpdateUserStatus\x12G\n\x19sharedFolderAddTeamStatus\x18\x04 \x03(\x0b\x32$.Folder.SharedFolderUpdateTeamStatus\x12N\n\x1esharedFolderUpdateRecordStatus\x18\x05 \x03(\x0b\x32&.Folder.SharedFolderUpdateRecordStatus\x12J\n\x1csharedFolderUpdateUserStatus\x18\x06 \x03(\x0b\x32$.Folder.SharedFolderUpdateUserStatus\x12J\n\x1csharedFolderUpdateTeamStatus\x18\x07 \x03(\x0b\x32$.Folder.SharedFolderUpdateTeamStatus\x12N\n\x1esharedFolderRemoveRecordStatus\x18\x08 \x03(\x0b\x32&.Folder.SharedFolderUpdateRecordStatus\x12J\n\x1csharedFolderRemoveUserStatus\x18\t \x03(\x0b\x32$.Folder.SharedFolderUpdateUserStatus\x12J\n\x1csharedFolderRemoveTeamStatus\x18\n \x03(\x0b\x32$.Folder.SharedFolderUpdateTeamStatus\x12\x17\n\x0fsharedFolderUid\x18\x0c \x01(\x0c\x12\x0e\n\x06status\x18\r \x01(\t\"m\n\x1eSharedFolderUpdateV3ResponseV2\x12K\n\x1dsharedFoldersUpdateV3Response\x18\x01 \x03(\x0b\x32$.Folder.SharedFolderUpdateV3Response\"\xfa\x01\n)GetDeletedSharedFoldersAndRecordsResponse\x12\x32\n\rsharedFolders\x18\x01 \x03(\x0b\x32\x1b.Folder.DeletedSharedFolder\x12>\n\x13sharedFolderRecords\x18\x02 \x03(\x0b\x32!.Folder.DeletedSharedFolderRecord\x12\x34\n\x11\x64\x65letedRecordData\x18\x03 \x03(\x0b\x32\x19.Folder.DeletedRecordData\x12#\n\tusernames\x18\x04 \x03(\x0b\x32\x10.Folder.Username\"\xd1\x01\n\x13\x44\x65letedSharedFolder\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x11\n\tfolderUid\x18\x02 \x01(\x0c\x12\x11\n\tparentUid\x18\x03 \x01(\x0c\x12\x17\n\x0fsharedFolderKey\x18\x04 \x01(\x0c\x12-\n\rfolderKeyType\x18\x05 \x01(\x0e\x32\x16.Records.RecordKeyType\x12\x0c\n\x04\x64\x61ta\x18\x06 \x01(\x0c\x12\x13\n\x0b\x64\x61teDeleted\x18\x07 \x01(\x03\x12\x10\n\x08revision\x18\x08 \x01(\x03\"\x81\x01\n\x19\x44\x65letedSharedFolderRecord\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x17\n\x0fsharedRecordKey\x18\x03 \x01(\x0c\x12\x13\n\x0b\x64\x61teDeleted\x18\x04 \x01(\x03\x12\x10\n\x08revision\x18\x05 \x01(\x03\"\x85\x01\n\x11\x44\x65letedRecordData\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08ownerUid\x18\x02 \x01(\x0c\x12\x10\n\x08revision\x18\x03 \x01(\x03\x12\x1a\n\x12\x63lientModifiedTime\x18\x04 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\x12\x0f\n\x07version\x18\x06 \x01(\x05\"0\n\x08Username\x12\x12\n\naccountUid\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\"\x8a\x01\n,RestoreDeletedSharedFoldersAndRecordsRequest\x12,\n\x07\x66olders\x18\x01 \x03(\x0b\x32\x1b.Folder.RestoreSharedObject\x12,\n\x07records\x18\x02 \x03(\x0b\x32\x1b.Folder.RestoreSharedObject\"<\n\x13RestoreSharedObject\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x12\n\nrecordUids\x18\x02 \x03(\x0c\"\x83\x02\n\nFolderData\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\tparentUid\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12%\n\x04type\x18\x04 \x01(\x0e\x32\x17.Folder.FolderUsageType\x12\x37\n\x16inheritUserPermissions\x18\x05 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x11\n\tfolderKey\x18\x06 \x01(\x0c\x12#\n\townerInfo\x18\x07 \x01(\x0b\x32\x10.Folder.UserInfo\x12\x13\n\x0b\x64\x61teCreated\x18\x08 \x01(\x03\x12\x14\n\x0clastModified\x18\t \x01(\x03\"z\n\tFolderKey\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\tparentUid\x18\x02 \x01(\x0c\x12\x11\n\tfolderKey\x18\x03 \x01(\x0c\x12\x34\n\x0b\x65ncryptedBy\x18\x04 \x01(\x0e\x32\x1f.Folder.FolderKeyEncryptionType\":\n\x10\x46olderAddRequest\x12&\n\nfolderData\x18\x01 \x03(\x0b\x32\x12.Folder.FolderData\"d\n\x12\x46olderModifyResult\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.Folder.FolderModifyStatus\x12\x0f\n\x07message\x18\x03 \x01(\t\"I\n\x11\x46olderAddResponse\x12\x34\n\x10\x66olderAddResults\x18\x01 \x03(\x0b\x32\x1a.Folder.FolderModifyResult\"=\n\x13\x46olderUpdateRequest\x12&\n\nfolderData\x18\x01 \x03(\x0b\x32\x12.Folder.FolderData\"O\n\x14\x46olderUpdateResponse\x12\x37\n\x13\x66olderUpdateResults\x18\x01 \x03(\x0b\x32\x1a.Folder.FolderModifyResult\"\xc3\x02\n\x11\x46olderPermissions\x12\x0e\n\x06\x63\x61nAdd\x18\x01 \x01(\x08\x12\x11\n\tcanRemove\x18\x02 \x01(\x08\x12\x11\n\tcanDelete\x18\x03 \x01(\x08\x12\x15\n\rcanListAccess\x18\x04 \x01(\x08\x12\x17\n\x0f\x63\x61nUpdateAccess\x18\x05 \x01(\x08\x12\x1a\n\x12\x63\x61nChangeOwnership\x18\x06 \x01(\x08\x12\x16\n\x0e\x63\x61nEditRecords\x18\x07 \x01(\x08\x12\x16\n\x0e\x63\x61nViewRecords\x18\x08 \x01(\x08\x12\x18\n\x10\x63\x61nApproveAccess\x18\t \x01(\x08\x12\x18\n\x10\x63\x61nRequestAccess\x18\n \x01(\x08\x12\x18\n\x10\x63\x61nUpdateSetting\x18\x0b \x01(\x08\x12\x16\n\x0e\x63\x61nListRecords\x18\x0c \x01(\x08\x12\x16\n\x0e\x63\x61nListFolders\x18\r \x01(\x08\"\x83\x05\n\x0c\x43\x61pabilities\x12\'\n\x06\x63\x61nAdd\x18\x01 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12*\n\tcanRemove\x18\x02 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12*\n\tcanDelete\x18\x03 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12.\n\rcanListAccess\x18\x04 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x30\n\x0f\x63\x61nUpdateAccess\x18\x05 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x33\n\x12\x63\x61nChangeOwnership\x18\x06 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x63\x61nEditRecords\x18\x07 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x63\x61nViewRecords\x18\x08 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x31\n\x10\x63\x61nApproveAccess\x18\t \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x31\n\x10\x63\x61nRequestAccess\x18\n \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x31\n\x10\x63\x61nUpdateSetting\x18\x0b \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x63\x61nListRecords\x18\x0c \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x63\x61nListFolders\x18\r \x01(\x0e\x32\x17.Folder.SetBooleanValue\"\xb8\x01\n\x19\x46olderRecordUpdateRequest\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12*\n\naddRecords\x18\x02 \x03(\x0b\x32\x16.Folder.RecordMetadata\x12-\n\rupdateRecords\x18\x03 \x03(\x0b\x32\x16.Folder.RecordMetadata\x12-\n\rremoveRecords\x18\x04 \x03(\x0b\x32\x16.Folder.RecordMetadata\"\xab\x01\n\x0eRecordMetadata\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x1a\n\x12\x65ncryptedRecordKey\x18\x02 \x01(\x0c\x12\x38\n\x16\x65ncryptedRecordKeyType\x18\x03 \x01(\x0e\x32\x18.Folder.EncryptedKeyType\x12\x30\n\rtlaProperties\x18\x05 \x01(\x0b\x32\x19.common.tla.TLAProperties\"\x93\x01\n\x0c\x46olderRecord\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12.\n\x0erecordMetadata\x18\x02 \x01(\x0b\x32\x16.Folder.RecordMetadata\x12@\n\x17\x66olderKeyEncryptionType\x18\x03 \x01(\x0e\x32\x1f.Folder.FolderKeyEncryptionType\"s\n\x1a\x46olderRecordUpdateResponse\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x42\n\x18\x66olderRecordUpdateResult\x18\x04 \x03(\x0b\x32 .Folder.FolderRecordUpdateResult\"j\n\x18\x46olderRecordUpdateResult\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.Folder.FolderModifyStatus\x12\x0f\n\x07message\x18\x03 \x01(\t\"\x87\x03\n\x10\x46olderAccessData\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x15\n\raccessTypeUid\x18\x02 \x01(\x0c\x12&\n\naccessType\x18\x03 \x01(\x0e\x32\x12.Folder.AccessType\x12.\n\x0e\x61\x63\x63\x65ssRoleType\x18\x04 \x01(\x0e\x32\x16.Folder.AccessRoleType\x12+\n\tfolderKey\x18\x05 \x01(\x0b\x32\x18.Folder.EncryptedDataKey\x12\x11\n\tinherited\x18\x06 \x01(\x08\x12\x0e\n\x06hidden\x18\x07 \x01(\x08\x12.\n\x0bpermissions\x18\x08 \x01(\x0b\x32\x19.Folder.FolderPermissions\x12\x30\n\rtlaProperties\x18\t \x01(\x0b\x32\x19.common.tla.TLAProperties\x12\x13\n\x0b\x64\x61teCreated\x18\n \x01(\x03\x12\x14\n\x0clastModified\x18\x0b \x01(\x03\x12\x14\n\x0c\x64\x65niedAccess\x18\x0c \x01(\x08\"\\\n\rRevokedAccess\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x10\n\x08\x61\x63torUid\x18\x02 \x01(\x0c\x12&\n\naccessType\x18\x03 \x01(\x0e\x32\x12.Folder.AccessType\"#\n\rFolderRemoved\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\"\x93\x04\n\x10RecordAccessData\x12\x15\n\raccessTypeUid\x18\x01 \x01(\x0c\x12&\n\naccessType\x18\x02 \x01(\x0e\x32\x12.Folder.AccessType\x12\x11\n\trecordUid\x18\x03 \x01(\x0c\x12.\n\x0e\x61\x63\x63\x65ssRoleType\x18\x04 \x01(\x0e\x32\x16.Folder.AccessRoleType\x12\r\n\x05owner\x18\x05 \x01(\x08\x12\x11\n\tinherited\x18\x06 \x01(\x08\x12\x0e\n\x06hidden\x18\x07 \x01(\x08\x12\x14\n\x0c\x64\x65niedAccess\x18\x08 \x01(\x08\x12\x16\n\x0e\x63\x61n_view_title\x18\t \x01(\x08\x12\x10\n\x08\x63\x61n_edit\x18\n \x01(\x08\x12\x10\n\x08\x63\x61n_view\x18\x0b \x01(\x08\x12\x17\n\x0f\x63\x61n_list_access\x18\x0c \x01(\x08\x12\x19\n\x11\x63\x61n_update_access\x18\r \x01(\x08\x12\x12\n\ncan_delete\x18\x0e \x01(\x08\x12\x1c\n\x14\x63\x61n_change_ownership\x18\x0f \x01(\x08\x12\x1a\n\x12\x63\x61n_request_access\x18\x10 \x01(\x08\x12\x1a\n\x12\x63\x61n_approve_access\x18\x11 \x01(\x08\x12\x13\n\x0b\x64\x61teCreated\x18\x12 \x01(\x03\x12\x14\n\x0clastModified\x18\x13 \x01(\x03\x12\x30\n\rtlaProperties\x18\x14 \x01(\x0b\x32\x19.common.tla.TLAProperties\"\xb8\x01\n\nAccessData\x12\x15\n\raccessTypeUid\x18\x01 \x01(\x0c\x12.\n\x0e\x61\x63\x63\x65ssRoleType\x18\x02 \x01(\x0e\x32\x16.Folder.AccessRoleType\x12\x14\n\x0c\x64\x65niedAccess\x18\x03 \x01(\x08\x12\x11\n\tinherited\x18\x04 \x01(\x08\x12\x0e\n\x06hidden\x18\x05 \x01(\x08\x12*\n\x0c\x63\x61pabilities\x18\x06 \x01(\x0b\x32\x14.Folder.Capabilities\"\xb7\x01\n\x13\x46olderAccessRequest\x12\x32\n\x10\x66olderAccessAdds\x18\x01 \x03(\x0b\x32\x18.Folder.FolderAccessData\x12\x35\n\x13\x66olderAccessUpdates\x18\x02 \x03(\x0b\x32\x18.Folder.FolderAccessData\x12\x35\n\x13\x66olderAccessRemoves\x18\x03 \x03(\x0b\x32\x18.Folder.FolderAccessData\"\x9f\x01\n\x12\x46olderAccessResult\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\taccessUid\x18\x02 \x01(\x0c\x12&\n\naccessType\x18\x03 \x01(\x0e\x32\x12.Folder.AccessType\x12*\n\x06status\x18\x04 \x01(\x0e\x32\x1a.Folder.FolderModifyStatus\x12\x0f\n\x07message\x18\x05 \x01(\t\"O\n\x14\x46olderAccessResponse\x12\x37\n\x13\x66olderAccessResults\x18\x01 \x03(\x0b\x32\x1a.Folder.FolderAccessResult\"0\n\x08UserInfo\x12\x12\n\naccountUid\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\"M\n\nRecordData\x12\x1e\n\x04user\x18\x01 \x01(\x0b\x32\x10.Folder.UserInfo\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x11\n\trecordUid\x18\x03 \x01(\x0c\"{\n\tRecordKey\x12\x10\n\x08user_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_uid\x18\x02 \x01(\x0c\x12\x12\n\nrecord_key\x18\x03 \x01(\x0c\x12\x34\n\x12\x65ncrypted_key_type\x18\x04 \x01(\x0e\x32\x18.Folder.EncryptedKeyType*\x1a\n\nRecordType\x12\x0c\n\x08password\x10\x00*^\n\nFolderType\x12\x12\n\x0e\x64\x65\x66\x61ult_folder\x10\x00\x12\x0f\n\x0buser_folder\x10\x01\x12\x11\n\rshared_folder\x10\x02\x12\x18\n\x14shared_folder_folder\x10\x03*\x96\x01\n\x10\x45ncryptedKeyType\x12\n\n\x06no_key\x10\x00\x12\x19\n\x15\x65ncrypted_by_data_key\x10\x01\x12\x1b\n\x17\x65ncrypted_by_public_key\x10\x02\x12\x1d\n\x19\x65ncrypted_by_data_key_gcm\x10\x03\x12\x1f\n\x1b\x65ncrypted_by_public_key_ecc\x10\x04*M\n\x0fSetBooleanValue\x12\x15\n\x11\x42OOLEAN_NO_CHANGE\x10\x00\x12\x10\n\x0c\x42OOLEAN_TRUE\x10\x01\x12\x11\n\rBOOLEAN_FALSE\x10\x02*R\n\x0f\x46olderUsageType\x12\x0e\n\nUT_UNKNOWN\x10\x00\x12\r\n\tUT_NORMAL\x10\x01\x12\x0f\n\x0bUT_WORKFLOW\x10\x02\x12\x0f\n\x0bUT_TRASHCAN\x10\x03*l\n\x17\x46olderKeyEncryptionType\x12\x19\n\x15\x45NCRYPTED_BY_USER_KEY\x10\x00\x12\x1b\n\x17\x45NCRYPTED_BY_PARENT_KEY\x10\x01\x12\x19\n\x15\x45NCRYPTED_BY_TEAM_KEY\x10\x02*T\n\x12\x46olderModifyStatus\x12\x0b\n\x07SUCCESS\x10\x00\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10\x01\x12\x11\n\rACCESS_DENIED\x10\x02\x12\r\n\tNOT_FOUND\x10\x03*\xa4\x02\n\x14\x46olderPermissionBits\x12\n\n\x06noBits\x10\x00\x12\n\n\x06\x63\x61nAdd\x10\x01\x12\r\n\tcanRemove\x10\x02\x12\r\n\tcanDelete\x10\x04\x12\x11\n\rcanListAccess\x10\x08\x12\x13\n\x0f\x63\x61nUpdateAccess\x10\x10\x12\x16\n\x12\x63\x61nChangeOwnership\x10 \x12\x12\n\x0e\x63\x61nEditRecords\x10@\x12\x13\n\x0e\x63\x61nViewRecords\x10\x80\x01\x12\x15\n\x10\x63\x61nApproveAccess\x10\x80\x02\x12\x15\n\x10\x63\x61nRequestAccess\x10\x80\x04\x12\x15\n\x10\x63\x61nUpdateSetting\x10\x80\x08\x12\x13\n\x0e\x63\x61nListRecords\x10\x80\x10\x12\x13\n\x0e\x63\x61nListFolders\x10\x80 *\x9b\x01\n\x0e\x41\x63\x63\x65ssRoleType\x12\r\n\tNAVIGATOR\x10\x00\x12\r\n\tREQUESTOR\x10\x01\x12\n\n\x06VIEWER\x10\x02\x12\x12\n\x0eSHARED_MANAGER\x10\x03\x12\x13\n\x0f\x43ONTENT_MANAGER\x10\x04\x12\x19\n\x15\x43ONTENT_SHARE_MANAGER\x10\x05\x12\x0b\n\x07MANAGER\x10\x06\x12\x0e\n\nUNRESOLVED\x10\x07*z\n\nAccessType\x12\x0e\n\nAT_UNKNOWN\x10\x00\x12\x0c\n\x08\x41T_OWNER\x10\x01\x12\x0b\n\x07\x41T_USER\x10\x02\x12\x0b\n\x07\x41T_TEAM\x10\x03\x12\x11\n\rAT_ENTERPRISE\x10\x04\x12\r\n\tAT_FOLDER\x10\x05\x12\x12\n\x0e\x41T_APPLICATION\x10\x06*:\n\nObjectType\x12\x0e\n\nOT_UNKNOWN\x10\x00\x12\r\n\tOT_RECORD\x10\x01\x12\r\n\tOT_FOLDER\x10\x02\x42\"\n\x18\x63om.keepersecurity.protoB\x06\x46olderb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x66older.proto\x12\x06\x46older\x1a\x0crecord.proto\x1a\ttla.proto\"\\\n\x10\x45ncryptedDataKey\x12\x14\n\x0c\x65ncryptedKey\x18\x01 \x01(\x0c\x12\x32\n\x10\x65ncryptedKeyType\x18\x02 \x01(\x0e\x32\x18.Folder.EncryptedKeyType\"\x82\x01\n\x16SharedFolderRecordData\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x0e\n\x06userId\x18\x03 \x01(\x05\x12\x32\n\x10\x65ncryptedDataKey\x18\x04 \x03(\x0b\x32\x18.Folder.EncryptedDataKey\"\\\n\x1aSharedFolderRecordDataList\x12>\n\x16sharedFolderRecordData\x18\x01 \x03(\x0b\x32\x1e.Folder.SharedFolderRecordData\"_\n\x15SharedFolderRecordFix\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12 \n\x18\x65ncryptedRecordFolderKey\x18\x03 \x01(\x0c\"Y\n\x19SharedFolderRecordFixList\x12<\n\x15sharedFolderRecordFix\x18\x01 \x03(\x0b\x32\x1d.Folder.SharedFolderRecordFix\"\xa2\x02\n\rRecordRequest\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12&\n\nrecordType\x18\x02 \x01(\x0e\x32\x12.Folder.RecordType\x12\x12\n\nrecordData\x18\x03 \x01(\x0c\x12\x1a\n\x12\x65ncryptedRecordKey\x18\x04 \x01(\x0c\x12&\n\nfolderType\x18\x05 \x01(\x0e\x32\x12.Folder.FolderType\x12\x12\n\nhowLongAgo\x18\x06 \x01(\x03\x12\x11\n\tfolderUid\x18\x07 \x01(\x0c\x12 \n\x18\x65ncryptedRecordFolderKey\x18\x08 \x01(\x0c\x12\r\n\x05\x65xtra\x18\t \x01(\x0c\x12\x15\n\rnonSharedData\x18\n \x01(\x0c\x12\x0f\n\x07\x66ileIds\x18\x0b \x03(\x03\"E\n\x0eRecordResponse\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x0e\n\x06status\x18\x03 \x01(\t\"\x80\x01\n\x12SharedFolderFields\x12\x1b\n\x13\x65ncryptedFolderName\x18\x01 \x01(\x0c\x12\x13\n\x0bmanageUsers\x18\x02 \x01(\x08\x12\x15\n\rmanageRecords\x18\x03 \x01(\x08\x12\x0f\n\x07\x63\x61nEdit\x18\x04 \x01(\x08\x12\x10\n\x08\x63\x61nShare\x18\x05 \x01(\x08\"3\n\x18SharedFolderFolderFields\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\"\x8f\x02\n\rFolderRequest\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12&\n\nfolderType\x18\x02 \x01(\x0e\x32\x12.Folder.FolderType\x12\x17\n\x0fparentFolderUid\x18\x03 \x01(\x0c\x12\x12\n\nfolderData\x18\x04 \x01(\x0c\x12\x1a\n\x12\x65ncryptedFolderKey\x18\x05 \x01(\x0c\x12\x36\n\x12sharedFolderFields\x18\x06 \x01(\x0b\x32\x1a.Folder.SharedFolderFields\x12\x42\n\x18sharedFolderFolderFields\x18\x07 \x01(\x0b\x32 .Folder.SharedFolderFolderFields\"E\n\x0e\x46olderResponse\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x0e\n\x06status\x18\x03 \x01(\t\"w\n\x19ImportFolderRecordRequest\x12,\n\rfolderRequest\x18\x01 \x03(\x0b\x32\x15.Folder.FolderRequest\x12,\n\rrecordRequest\x18\x02 \x03(\x0b\x32\x15.Folder.RecordRequest\"|\n\x1aImportFolderRecordResponse\x12.\n\x0e\x66olderResponse\x18\x01 \x03(\x0b\x32\x16.Folder.FolderResponse\x12.\n\x0erecordResponse\x18\x02 \x03(\x0b\x32\x16.Folder.RecordResponse\"\xc9\x02\n\x18SharedFolderUpdateRecord\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x17\n\x0fsharedFolderUid\x18\x02 \x01(\x0c\x12\x0f\n\x07teamUid\x18\x03 \x01(\x0c\x12(\n\x07\x63\x61nEdit\x18\x04 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12)\n\x08\x63\x61nShare\x18\x05 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x1a\n\x12\x65ncryptedRecordKey\x18\x06 \x01(\x0c\x12\x10\n\x08revision\x18\x07 \x01(\x05\x12\x12\n\nexpiration\x18\x08 \x01(\x12\x12=\n\x15timerNotificationType\x18\t \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x1a\n\x12rotateOnExpiration\x18\n \x01(\x08\"\xcc\x02\n\x16SharedFolderUpdateUser\x12\x10\n\x08username\x18\x01 \x01(\t\x12,\n\x0bmanageUsers\x18\x02 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12.\n\rmanageRecords\x18\x03 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x1b\n\x0fsharedFolderKey\x18\x04 \x01(\x0c\x42\x02\x18\x01\x12\x12\n\nexpiration\x18\x05 \x01(\x12\x12=\n\x15timerNotificationType\x18\x06 \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x36\n\x14typedSharedFolderKey\x18\x07 \x01(\x0b\x32\x18.Folder.EncryptedDataKey\x12\x1a\n\x12rotateOnExpiration\x18\x08 \x01(\x08\"\x99\x02\n\x16SharedFolderUpdateTeam\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x13\n\x0bmanageUsers\x18\x02 \x01(\x08\x12\x15\n\rmanageRecords\x18\x03 \x01(\x08\x12\x1b\n\x0fsharedFolderKey\x18\x04 \x01(\x0c\x42\x02\x18\x01\x12\x12\n\nexpiration\x18\x05 \x01(\x12\x12=\n\x15timerNotificationType\x18\x06 \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x36\n\x14typedSharedFolderKey\x18\x07 \x01(\x0b\x32\x18.Folder.EncryptedDataKey\x12\x1a\n\x12rotateOnExpiration\x18\x08 \x01(\x08\"\x8e\x07\n\x1bSharedFolderUpdateV3Request\x12,\n$sharedFolderUpdateOperation_dont_use\x18\x01 \x01(\x05\x12\x17\n\x0fsharedFolderUid\x18\x02 \x01(\x0c\x12!\n\x19\x65ncryptedSharedFolderName\x18\x03 \x01(\x0c\x12\x10\n\x08revision\x18\x04 \x01(\x03\x12\x13\n\x0b\x66orceUpdate\x18\x05 \x01(\x08\x12\x13\n\x0b\x66romTeamUid\x18\x06 \x01(\x0c\x12\x33\n\x12\x64\x65\x66\x61ultManageUsers\x18\x07 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x35\n\x14\x64\x65\x66\x61ultManageRecords\x18\x08 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x64\x65\x66\x61ultCanEdit\x18\t \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x30\n\x0f\x64\x65\x66\x61ultCanShare\x18\n \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12?\n\x15sharedFolderAddRecord\x18\x0b \x03(\x0b\x32 .Folder.SharedFolderUpdateRecord\x12;\n\x13sharedFolderAddUser\x18\x0c \x03(\x0b\x32\x1e.Folder.SharedFolderUpdateUser\x12;\n\x13sharedFolderAddTeam\x18\r \x03(\x0b\x32\x1e.Folder.SharedFolderUpdateTeam\x12\x42\n\x18sharedFolderUpdateRecord\x18\x0e \x03(\x0b\x32 .Folder.SharedFolderUpdateRecord\x12>\n\x16sharedFolderUpdateUser\x18\x0f \x03(\x0b\x32\x1e.Folder.SharedFolderUpdateUser\x12>\n\x16sharedFolderUpdateTeam\x18\x10 \x03(\x0b\x32\x1e.Folder.SharedFolderUpdateTeam\x12 \n\x18sharedFolderRemoveRecord\x18\x11 \x03(\x0c\x12\x1e\n\x16sharedFolderRemoveUser\x18\x12 \x03(\t\x12\x1e\n\x16sharedFolderRemoveTeam\x18\x13 \x03(\x0c\x12\x19\n\x11sharedFolderOwner\x18\x14 \x01(\t\"c\n\x1dSharedFolderUpdateV3RequestV2\x12\x42\n\x15sharedFoldersUpdateV3\x18\x01 \x03(\x0b\x32#.Folder.SharedFolderUpdateV3Request\"C\n\x1eSharedFolderUpdateRecordStatus\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x0e\n\x06status\x18\x02 \x01(\t\"@\n\x1cSharedFolderUpdateUserStatus\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x0e\n\x06status\x18\x02 \x01(\t\"?\n\x1cSharedFolderUpdateTeamStatus\x12\x0f\n\x07teamUid\x18\x01 \x01(\x0c\x12\x0e\n\x06status\x18\x02 \x01(\t\"\x88\x06\n\x1cSharedFolderUpdateV3Response\x12\x10\n\x08revision\x18\x01 \x01(\x03\x12K\n\x1bsharedFolderAddRecordStatus\x18\x02 \x03(\x0b\x32&.Folder.SharedFolderUpdateRecordStatus\x12G\n\x19sharedFolderAddUserStatus\x18\x03 \x03(\x0b\x32$.Folder.SharedFolderUpdateUserStatus\x12G\n\x19sharedFolderAddTeamStatus\x18\x04 \x03(\x0b\x32$.Folder.SharedFolderUpdateTeamStatus\x12N\n\x1esharedFolderUpdateRecordStatus\x18\x05 \x03(\x0b\x32&.Folder.SharedFolderUpdateRecordStatus\x12J\n\x1csharedFolderUpdateUserStatus\x18\x06 \x03(\x0b\x32$.Folder.SharedFolderUpdateUserStatus\x12J\n\x1csharedFolderUpdateTeamStatus\x18\x07 \x03(\x0b\x32$.Folder.SharedFolderUpdateTeamStatus\x12N\n\x1esharedFolderRemoveRecordStatus\x18\x08 \x03(\x0b\x32&.Folder.SharedFolderUpdateRecordStatus\x12J\n\x1csharedFolderRemoveUserStatus\x18\t \x03(\x0b\x32$.Folder.SharedFolderUpdateUserStatus\x12J\n\x1csharedFolderRemoveTeamStatus\x18\n \x03(\x0b\x32$.Folder.SharedFolderUpdateTeamStatus\x12\x17\n\x0fsharedFolderUid\x18\x0c \x01(\x0c\x12\x0e\n\x06status\x18\r \x01(\t\"m\n\x1eSharedFolderUpdateV3ResponseV2\x12K\n\x1dsharedFoldersUpdateV3Response\x18\x01 \x03(\x0b\x32$.Folder.SharedFolderUpdateV3Response\"\xfa\x01\n)GetDeletedSharedFoldersAndRecordsResponse\x12\x32\n\rsharedFolders\x18\x01 \x03(\x0b\x32\x1b.Folder.DeletedSharedFolder\x12>\n\x13sharedFolderRecords\x18\x02 \x03(\x0b\x32!.Folder.DeletedSharedFolderRecord\x12\x34\n\x11\x64\x65letedRecordData\x18\x03 \x03(\x0b\x32\x19.Folder.DeletedRecordData\x12#\n\tusernames\x18\x04 \x03(\x0b\x32\x10.Folder.Username\"\xd1\x01\n\x13\x44\x65letedSharedFolder\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x11\n\tfolderUid\x18\x02 \x01(\x0c\x12\x11\n\tparentUid\x18\x03 \x01(\x0c\x12\x17\n\x0fsharedFolderKey\x18\x04 \x01(\x0c\x12-\n\rfolderKeyType\x18\x05 \x01(\x0e\x32\x16.Records.RecordKeyType\x12\x0c\n\x04\x64\x61ta\x18\x06 \x01(\x0c\x12\x13\n\x0b\x64\x61teDeleted\x18\x07 \x01(\x03\x12\x10\n\x08revision\x18\x08 \x01(\x03\"\x81\x01\n\x19\x44\x65letedSharedFolderRecord\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x17\n\x0fsharedRecordKey\x18\x03 \x01(\x0c\x12\x13\n\x0b\x64\x61teDeleted\x18\x04 \x01(\x03\x12\x10\n\x08revision\x18\x05 \x01(\x03\"\x85\x01\n\x11\x44\x65letedRecordData\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x10\n\x08ownerUid\x18\x02 \x01(\x0c\x12\x10\n\x08revision\x18\x03 \x01(\x03\x12\x1a\n\x12\x63lientModifiedTime\x18\x04 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\x12\x0f\n\x07version\x18\x06 \x01(\x05\"0\n\x08Username\x12\x12\n\naccountUid\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\"\x8a\x01\n,RestoreDeletedSharedFoldersAndRecordsRequest\x12,\n\x07\x66olders\x18\x01 \x03(\x0b\x32\x1b.Folder.RestoreSharedObject\x12,\n\x07records\x18\x02 \x03(\x0b\x32\x1b.Folder.RestoreSharedObject\"<\n\x13RestoreSharedObject\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x12\n\nrecordUids\x18\x02 \x03(\x0c\"\x83\x02\n\nFolderData\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\tparentUid\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12%\n\x04type\x18\x04 \x01(\x0e\x32\x17.Folder.FolderUsageType\x12\x37\n\x16inheritUserPermissions\x18\x05 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x11\n\tfolderKey\x18\x06 \x01(\x0c\x12#\n\townerInfo\x18\x07 \x01(\x0b\x32\x10.Folder.UserInfo\x12\x13\n\x0b\x64\x61teCreated\x18\x08 \x01(\x03\x12\x14\n\x0clastModified\x18\t \x01(\x03\"z\n\tFolderKey\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\tparentUid\x18\x02 \x01(\x0c\x12\x11\n\tfolderKey\x18\x03 \x01(\x0c\x12\x34\n\x0b\x65ncryptedBy\x18\x04 \x01(\x0e\x32\x1f.Folder.FolderKeyEncryptionType\":\n\x10\x46olderAddRequest\x12&\n\nfolderData\x18\x01 \x03(\x0b\x32\x12.Folder.FolderData\"d\n\x12\x46olderModifyResult\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.Folder.FolderModifyStatus\x12\x0f\n\x07message\x18\x03 \x01(\t\"I\n\x11\x46olderAddResponse\x12\x34\n\x10\x66olderAddResults\x18\x01 \x03(\x0b\x32\x1a.Folder.FolderModifyResult\"=\n\x13\x46olderUpdateRequest\x12&\n\nfolderData\x18\x01 \x03(\x0b\x32\x12.Folder.FolderData\"O\n\x14\x46olderUpdateResponse\x12\x37\n\x13\x66olderUpdateResults\x18\x01 \x03(\x0b\x32\x1a.Folder.FolderModifyResult\"\xc3\x02\n\x11\x46olderPermissions\x12\x0e\n\x06\x63\x61nAdd\x18\x01 \x01(\x08\x12\x11\n\tcanRemove\x18\x02 \x01(\x08\x12\x11\n\tcanDelete\x18\x03 \x01(\x08\x12\x15\n\rcanListAccess\x18\x04 \x01(\x08\x12\x17\n\x0f\x63\x61nUpdateAccess\x18\x05 \x01(\x08\x12\x1a\n\x12\x63\x61nChangeOwnership\x18\x06 \x01(\x08\x12\x16\n\x0e\x63\x61nEditRecords\x18\x07 \x01(\x08\x12\x16\n\x0e\x63\x61nViewRecords\x18\x08 \x01(\x08\x12\x18\n\x10\x63\x61nApproveAccess\x18\t \x01(\x08\x12\x18\n\x10\x63\x61nRequestAccess\x18\n \x01(\x08\x12\x18\n\x10\x63\x61nUpdateSetting\x18\x0b \x01(\x08\x12\x16\n\x0e\x63\x61nListRecords\x18\x0c \x01(\x08\x12\x16\n\x0e\x63\x61nListFolders\x18\r \x01(\x08\"\x83\x05\n\x0c\x43\x61pabilities\x12\'\n\x06\x63\x61nAdd\x18\x01 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12*\n\tcanRemove\x18\x02 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12*\n\tcanDelete\x18\x03 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12.\n\rcanListAccess\x18\x04 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x30\n\x0f\x63\x61nUpdateAccess\x18\x05 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x33\n\x12\x63\x61nChangeOwnership\x18\x06 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x63\x61nEditRecords\x18\x07 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x63\x61nViewRecords\x18\x08 \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x31\n\x10\x63\x61nApproveAccess\x18\t \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x31\n\x10\x63\x61nRequestAccess\x18\n \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12\x31\n\x10\x63\x61nUpdateSetting\x18\x0b \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x63\x61nListRecords\x18\x0c \x01(\x0e\x32\x17.Folder.SetBooleanValue\x12/\n\x0e\x63\x61nListFolders\x18\r \x01(\x0e\x32\x17.Folder.SetBooleanValue\"\xb8\x01\n\x19\x46olderRecordUpdateRequest\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12*\n\naddRecords\x18\x02 \x03(\x0b\x32\x16.Folder.RecordMetadata\x12-\n\rupdateRecords\x18\x03 \x03(\x0b\x32\x16.Folder.RecordMetadata\x12-\n\rremoveRecords\x18\x04 \x03(\x0b\x32\x16.Folder.RecordMetadata\"\xd1\x01\n\x0eRecordMetadata\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x1a\n\x12\x65ncryptedRecordKey\x18\x02 \x01(\x0c\x12\x38\n\x16\x65ncryptedRecordKeyType\x18\x03 \x01(\x0e\x32\x18.Folder.EncryptedKeyType\x12\x30\n\rtlaProperties\x18\x05 \x01(\x0b\x32\x19.common.tla.TLAProperties\x12$\n\x1crecordKeyEncryptedByOwnerKey\x18\x06 \x01(\x0c\"\x93\x01\n\x0c\x46olderRecord\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12.\n\x0erecordMetadata\x18\x02 \x01(\x0b\x32\x16.Folder.RecordMetadata\x12@\n\x17\x66olderKeyEncryptionType\x18\x03 \x01(\x0e\x32\x1f.Folder.FolderKeyEncryptionType\"s\n\x1a\x46olderRecordUpdateResponse\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x42\n\x18\x66olderRecordUpdateResult\x18\x04 \x03(\x0b\x32 .Folder.FolderRecordUpdateResult\"j\n\x18\x46olderRecordUpdateResult\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.Folder.FolderModifyStatus\x12\x0f\n\x07message\x18\x03 \x01(\t\"\x87\x03\n\x10\x46olderAccessData\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x15\n\raccessTypeUid\x18\x02 \x01(\x0c\x12&\n\naccessType\x18\x03 \x01(\x0e\x32\x12.Folder.AccessType\x12.\n\x0e\x61\x63\x63\x65ssRoleType\x18\x04 \x01(\x0e\x32\x16.Folder.AccessRoleType\x12+\n\tfolderKey\x18\x05 \x01(\x0b\x32\x18.Folder.EncryptedDataKey\x12\x11\n\tinherited\x18\x06 \x01(\x08\x12\x0e\n\x06hidden\x18\x07 \x01(\x08\x12.\n\x0bpermissions\x18\x08 \x01(\x0b\x32\x19.Folder.FolderPermissions\x12\x30\n\rtlaProperties\x18\t \x01(\x0b\x32\x19.common.tla.TLAProperties\x12\x13\n\x0b\x64\x61teCreated\x18\n \x01(\x03\x12\x14\n\x0clastModified\x18\x0b \x01(\x03\x12\x14\n\x0c\x64\x65niedAccess\x18\x0c \x01(\x08\"\\\n\rRevokedAccess\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x10\n\x08\x61\x63torUid\x18\x02 \x01(\x0c\x12&\n\naccessType\x18\x03 \x01(\x0e\x32\x12.Folder.AccessType\"#\n\rFolderRemoved\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\"\x93\x04\n\x10RecordAccessData\x12\x15\n\raccessTypeUid\x18\x01 \x01(\x0c\x12&\n\naccessType\x18\x02 \x01(\x0e\x32\x12.Folder.AccessType\x12\x11\n\trecordUid\x18\x03 \x01(\x0c\x12.\n\x0e\x61\x63\x63\x65ssRoleType\x18\x04 \x01(\x0e\x32\x16.Folder.AccessRoleType\x12\r\n\x05owner\x18\x05 \x01(\x08\x12\x11\n\tinherited\x18\x06 \x01(\x08\x12\x0e\n\x06hidden\x18\x07 \x01(\x08\x12\x14\n\x0c\x64\x65niedAccess\x18\x08 \x01(\x08\x12\x16\n\x0e\x63\x61n_view_title\x18\t \x01(\x08\x12\x10\n\x08\x63\x61n_edit\x18\n \x01(\x08\x12\x10\n\x08\x63\x61n_view\x18\x0b \x01(\x08\x12\x17\n\x0f\x63\x61n_list_access\x18\x0c \x01(\x08\x12\x19\n\x11\x63\x61n_update_access\x18\r \x01(\x08\x12\x12\n\ncan_delete\x18\x0e \x01(\x08\x12\x1c\n\x14\x63\x61n_change_ownership\x18\x0f \x01(\x08\x12\x1a\n\x12\x63\x61n_request_access\x18\x10 \x01(\x08\x12\x1a\n\x12\x63\x61n_approve_access\x18\x11 \x01(\x08\x12\x13\n\x0b\x64\x61teCreated\x18\x12 \x01(\x03\x12\x14\n\x0clastModified\x18\x13 \x01(\x03\x12\x30\n\rtlaProperties\x18\x14 \x01(\x0b\x32\x19.common.tla.TLAProperties\"\xb8\x01\n\nAccessData\x12\x15\n\raccessTypeUid\x18\x01 \x01(\x0c\x12.\n\x0e\x61\x63\x63\x65ssRoleType\x18\x02 \x01(\x0e\x32\x16.Folder.AccessRoleType\x12\x14\n\x0c\x64\x65niedAccess\x18\x03 \x01(\x08\x12\x11\n\tinherited\x18\x04 \x01(\x08\x12\x0e\n\x06hidden\x18\x05 \x01(\x08\x12*\n\x0c\x63\x61pabilities\x18\x06 \x01(\x0b\x32\x14.Folder.Capabilities\"\xb7\x01\n\x13\x46olderAccessRequest\x12\x32\n\x10\x66olderAccessAdds\x18\x01 \x03(\x0b\x32\x18.Folder.FolderAccessData\x12\x35\n\x13\x66olderAccessUpdates\x18\x02 \x03(\x0b\x32\x18.Folder.FolderAccessData\x12\x35\n\x13\x66olderAccessRemoves\x18\x03 \x03(\x0b\x32\x18.Folder.FolderAccessData\"\x9f\x01\n\x12\x46olderAccessResult\x12\x11\n\tfolderUid\x18\x01 \x01(\x0c\x12\x11\n\taccessUid\x18\x02 \x01(\x0c\x12&\n\naccessType\x18\x03 \x01(\x0e\x32\x12.Folder.AccessType\x12*\n\x06status\x18\x04 \x01(\x0e\x32\x1a.Folder.FolderModifyStatus\x12\x0f\n\x07message\x18\x05 \x01(\t\"O\n\x14\x46olderAccessResponse\x12\x37\n\x13\x66olderAccessResults\x18\x01 \x03(\x0b\x32\x1a.Folder.FolderAccessResult\"0\n\x08UserInfo\x12\x12\n\naccountUid\x18\x01 \x01(\x0c\x12\x10\n\x08username\x18\x02 \x01(\t\"M\n\nRecordData\x12\x1e\n\x04user\x18\x01 \x01(\x0b\x32\x10.Folder.UserInfo\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x11\n\trecordUid\x18\x03 \x01(\x0c\"{\n\tRecordKey\x12\x10\n\x08user_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_uid\x18\x02 \x01(\x0c\x12\x12\n\nrecord_key\x18\x03 \x01(\x0c\x12\x34\n\x12\x65ncrypted_key_type\x18\x04 \x01(\x0e\x32\x18.Folder.EncryptedKeyType*\x1a\n\nRecordType\x12\x0c\n\x08password\x10\x00*^\n\nFolderType\x12\x12\n\x0e\x64\x65\x66\x61ult_folder\x10\x00\x12\x0f\n\x0buser_folder\x10\x01\x12\x11\n\rshared_folder\x10\x02\x12\x18\n\x14shared_folder_folder\x10\x03*\x96\x01\n\x10\x45ncryptedKeyType\x12\n\n\x06no_key\x10\x00\x12\x19\n\x15\x65ncrypted_by_data_key\x10\x01\x12\x1b\n\x17\x65ncrypted_by_public_key\x10\x02\x12\x1d\n\x19\x65ncrypted_by_data_key_gcm\x10\x03\x12\x1f\n\x1b\x65ncrypted_by_public_key_ecc\x10\x04*M\n\x0fSetBooleanValue\x12\x15\n\x11\x42OOLEAN_NO_CHANGE\x10\x00\x12\x10\n\x0c\x42OOLEAN_TRUE\x10\x01\x12\x11\n\rBOOLEAN_FALSE\x10\x02*R\n\x0f\x46olderUsageType\x12\x0e\n\nUT_UNKNOWN\x10\x00\x12\r\n\tUT_NORMAL\x10\x01\x12\x0f\n\x0bUT_WORKFLOW\x10\x02\x12\x0f\n\x0bUT_TRASHCAN\x10\x03*l\n\x17\x46olderKeyEncryptionType\x12\x19\n\x15\x45NCRYPTED_BY_USER_KEY\x10\x00\x12\x1b\n\x17\x45NCRYPTED_BY_PARENT_KEY\x10\x01\x12\x19\n\x15\x45NCRYPTED_BY_TEAM_KEY\x10\x02*T\n\x12\x46olderModifyStatus\x12\x0b\n\x07SUCCESS\x10\x00\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10\x01\x12\x11\n\rACCESS_DENIED\x10\x02\x12\r\n\tNOT_FOUND\x10\x03*\xa4\x02\n\x14\x46olderPermissionBits\x12\n\n\x06noBits\x10\x00\x12\n\n\x06\x63\x61nAdd\x10\x01\x12\r\n\tcanRemove\x10\x02\x12\r\n\tcanDelete\x10\x04\x12\x11\n\rcanListAccess\x10\x08\x12\x13\n\x0f\x63\x61nUpdateAccess\x10\x10\x12\x16\n\x12\x63\x61nChangeOwnership\x10 \x12\x12\n\x0e\x63\x61nEditRecords\x10@\x12\x13\n\x0e\x63\x61nViewRecords\x10\x80\x01\x12\x15\n\x10\x63\x61nApproveAccess\x10\x80\x02\x12\x15\n\x10\x63\x61nRequestAccess\x10\x80\x04\x12\x15\n\x10\x63\x61nUpdateSetting\x10\x80\x08\x12\x13\n\x0e\x63\x61nListRecords\x10\x80\x10\x12\x13\n\x0e\x63\x61nListFolders\x10\x80 *\x9b\x01\n\x0e\x41\x63\x63\x65ssRoleType\x12\r\n\tNAVIGATOR\x10\x00\x12\r\n\tREQUESTOR\x10\x01\x12\n\n\x06VIEWER\x10\x02\x12\x12\n\x0eSHARED_MANAGER\x10\x03\x12\x13\n\x0f\x43ONTENT_MANAGER\x10\x04\x12\x19\n\x15\x43ONTENT_SHARE_MANAGER\x10\x05\x12\x0b\n\x07MANAGER\x10\x06\x12\x0e\n\nUNRESOLVED\x10\x07*z\n\nAccessType\x12\x0e\n\nAT_UNKNOWN\x10\x00\x12\x0c\n\x08\x41T_OWNER\x10\x01\x12\x0b\n\x07\x41T_USER\x10\x02\x12\x0b\n\x07\x41T_TEAM\x10\x03\x12\x11\n\rAT_ENTERPRISE\x10\x04\x12\r\n\tAT_FOLDER\x10\x05\x12\x12\n\x0e\x41T_APPLICATION\x10\x06*:\n\nObjectType\x12\x0e\n\nOT_UNKNOWN\x10\x00\x12\r\n\tOT_RECORD\x10\x01\x12\r\n\tOT_FOLDER\x10\x02\x42\"\n\x18\x63om.keepersecurity.protoB\x06\x46olderb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -38,28 +38,28 @@ _globals['_SHAREDFOLDERUPDATEUSER'].fields_by_name['sharedFolderKey']._serialized_options = b'\030\001' _globals['_SHAREDFOLDERUPDATETEAM'].fields_by_name['sharedFolderKey']._loaded_options = None _globals['_SHAREDFOLDERUPDATETEAM'].fields_by_name['sharedFolderKey']._serialized_options = b'\030\001' - _globals['_RECORDTYPE']._serialized_start=10143 - _globals['_RECORDTYPE']._serialized_end=10169 - _globals['_FOLDERTYPE']._serialized_start=10171 - _globals['_FOLDERTYPE']._serialized_end=10265 - _globals['_ENCRYPTEDKEYTYPE']._serialized_start=10268 - _globals['_ENCRYPTEDKEYTYPE']._serialized_end=10418 - _globals['_SETBOOLEANVALUE']._serialized_start=10420 - _globals['_SETBOOLEANVALUE']._serialized_end=10497 - _globals['_FOLDERUSAGETYPE']._serialized_start=10499 - _globals['_FOLDERUSAGETYPE']._serialized_end=10581 - _globals['_FOLDERKEYENCRYPTIONTYPE']._serialized_start=10583 - _globals['_FOLDERKEYENCRYPTIONTYPE']._serialized_end=10691 - _globals['_FOLDERMODIFYSTATUS']._serialized_start=10693 - _globals['_FOLDERMODIFYSTATUS']._serialized_end=10777 - _globals['_FOLDERPERMISSIONBITS']._serialized_start=10780 - _globals['_FOLDERPERMISSIONBITS']._serialized_end=11072 - _globals['_ACCESSROLETYPE']._serialized_start=11075 - _globals['_ACCESSROLETYPE']._serialized_end=11230 - _globals['_ACCESSTYPE']._serialized_start=11232 - _globals['_ACCESSTYPE']._serialized_end=11354 - _globals['_OBJECTTYPE']._serialized_start=11356 - _globals['_OBJECTTYPE']._serialized_end=11414 + _globals['_RECORDTYPE']._serialized_start=10181 + _globals['_RECORDTYPE']._serialized_end=10207 + _globals['_FOLDERTYPE']._serialized_start=10209 + _globals['_FOLDERTYPE']._serialized_end=10303 + _globals['_ENCRYPTEDKEYTYPE']._serialized_start=10306 + _globals['_ENCRYPTEDKEYTYPE']._serialized_end=10456 + _globals['_SETBOOLEANVALUE']._serialized_start=10458 + _globals['_SETBOOLEANVALUE']._serialized_end=10535 + _globals['_FOLDERUSAGETYPE']._serialized_start=10537 + _globals['_FOLDERUSAGETYPE']._serialized_end=10619 + _globals['_FOLDERKEYENCRYPTIONTYPE']._serialized_start=10621 + _globals['_FOLDERKEYENCRYPTIONTYPE']._serialized_end=10729 + _globals['_FOLDERMODIFYSTATUS']._serialized_start=10731 + _globals['_FOLDERMODIFYSTATUS']._serialized_end=10815 + _globals['_FOLDERPERMISSIONBITS']._serialized_start=10818 + _globals['_FOLDERPERMISSIONBITS']._serialized_end=11110 + _globals['_ACCESSROLETYPE']._serialized_start=11113 + _globals['_ACCESSROLETYPE']._serialized_end=11268 + _globals['_ACCESSTYPE']._serialized_start=11270 + _globals['_ACCESSTYPE']._serialized_end=11392 + _globals['_OBJECTTYPE']._serialized_start=11394 + _globals['_OBJECTTYPE']._serialized_end=11452 _globals['_ENCRYPTEDDATAKEY']._serialized_start=49 _globals['_ENCRYPTEDDATAKEY']._serialized_end=141 _globals['_SHAREDFOLDERRECORDDATA']._serialized_start=144 @@ -141,33 +141,33 @@ _globals['_FOLDERRECORDUPDATEREQUEST']._serialized_start=7479 _globals['_FOLDERRECORDUPDATEREQUEST']._serialized_end=7663 _globals['_RECORDMETADATA']._serialized_start=7666 - _globals['_RECORDMETADATA']._serialized_end=7837 - _globals['_FOLDERRECORD']._serialized_start=7840 - _globals['_FOLDERRECORD']._serialized_end=7987 - _globals['_FOLDERRECORDUPDATERESPONSE']._serialized_start=7989 - _globals['_FOLDERRECORDUPDATERESPONSE']._serialized_end=8104 - _globals['_FOLDERRECORDUPDATERESULT']._serialized_start=8106 - _globals['_FOLDERRECORDUPDATERESULT']._serialized_end=8212 - _globals['_FOLDERACCESSDATA']._serialized_start=8215 - _globals['_FOLDERACCESSDATA']._serialized_end=8606 - _globals['_REVOKEDACCESS']._serialized_start=8608 - _globals['_REVOKEDACCESS']._serialized_end=8700 - _globals['_FOLDERREMOVED']._serialized_start=8702 - _globals['_FOLDERREMOVED']._serialized_end=8737 - _globals['_RECORDACCESSDATA']._serialized_start=8740 - _globals['_RECORDACCESSDATA']._serialized_end=9271 - _globals['_ACCESSDATA']._serialized_start=9274 - _globals['_ACCESSDATA']._serialized_end=9458 - _globals['_FOLDERACCESSREQUEST']._serialized_start=9461 - _globals['_FOLDERACCESSREQUEST']._serialized_end=9644 - _globals['_FOLDERACCESSRESULT']._serialized_start=9647 - _globals['_FOLDERACCESSRESULT']._serialized_end=9806 - _globals['_FOLDERACCESSRESPONSE']._serialized_start=9808 - _globals['_FOLDERACCESSRESPONSE']._serialized_end=9887 - _globals['_USERINFO']._serialized_start=9889 - _globals['_USERINFO']._serialized_end=9937 - _globals['_RECORDDATA']._serialized_start=9939 - _globals['_RECORDDATA']._serialized_end=10016 - _globals['_RECORDKEY']._serialized_start=10018 - _globals['_RECORDKEY']._serialized_end=10141 + _globals['_RECORDMETADATA']._serialized_end=7875 + _globals['_FOLDERRECORD']._serialized_start=7878 + _globals['_FOLDERRECORD']._serialized_end=8025 + _globals['_FOLDERRECORDUPDATERESPONSE']._serialized_start=8027 + _globals['_FOLDERRECORDUPDATERESPONSE']._serialized_end=8142 + _globals['_FOLDERRECORDUPDATERESULT']._serialized_start=8144 + _globals['_FOLDERRECORDUPDATERESULT']._serialized_end=8250 + _globals['_FOLDERACCESSDATA']._serialized_start=8253 + _globals['_FOLDERACCESSDATA']._serialized_end=8644 + _globals['_REVOKEDACCESS']._serialized_start=8646 + _globals['_REVOKEDACCESS']._serialized_end=8738 + _globals['_FOLDERREMOVED']._serialized_start=8740 + _globals['_FOLDERREMOVED']._serialized_end=8775 + _globals['_RECORDACCESSDATA']._serialized_start=8778 + _globals['_RECORDACCESSDATA']._serialized_end=9309 + _globals['_ACCESSDATA']._serialized_start=9312 + _globals['_ACCESSDATA']._serialized_end=9496 + _globals['_FOLDERACCESSREQUEST']._serialized_start=9499 + _globals['_FOLDERACCESSREQUEST']._serialized_end=9682 + _globals['_FOLDERACCESSRESULT']._serialized_start=9685 + _globals['_FOLDERACCESSRESULT']._serialized_end=9844 + _globals['_FOLDERACCESSRESPONSE']._serialized_start=9846 + _globals['_FOLDERACCESSRESPONSE']._serialized_end=9925 + _globals['_USERINFO']._serialized_start=9927 + _globals['_USERINFO']._serialized_end=9975 + _globals['_RECORDDATA']._serialized_start=9977 + _globals['_RECORDDATA']._serialized_end=10054 + _globals['_RECORDKEY']._serialized_start=10056 + _globals['_RECORDKEY']._serialized_end=10179 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/folder_pb2.pyi b/keepersdk-package/src/keepersdk/proto/folder_pb2.pyi index 57a85797..78615825 100644 --- a/keepersdk-package/src/keepersdk/proto/folder_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/folder_pb2.pyi @@ -1,5 +1,5 @@ -import record_pb2 as _record_pb2 -import tla_pb2 as _tla_pb2 +from . import record_pb2 as _record_pb2 +from . import tla_pb2 as _tla_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 @@ -694,16 +694,18 @@ class FolderRecordUpdateRequest(_message.Message): def __init__(self, folderUid: _Optional[bytes] = ..., addRecords: _Optional[_Iterable[_Union[RecordMetadata, _Mapping]]] = ..., updateRecords: _Optional[_Iterable[_Union[RecordMetadata, _Mapping]]] = ..., removeRecords: _Optional[_Iterable[_Union[RecordMetadata, _Mapping]]] = ...) -> None: ... class RecordMetadata(_message.Message): - __slots__ = ("recordUid", "encryptedRecordKey", "encryptedRecordKeyType", "tlaProperties") + __slots__ = ("recordUid", "encryptedRecordKey", "encryptedRecordKeyType", "tlaProperties", "recordKeyEncryptedByOwnerKey") RECORDUID_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDRECORDKEY_FIELD_NUMBER: _ClassVar[int] ENCRYPTEDRECORDKEYTYPE_FIELD_NUMBER: _ClassVar[int] TLAPROPERTIES_FIELD_NUMBER: _ClassVar[int] + RECORDKEYENCRYPTEDBYOWNERKEY_FIELD_NUMBER: _ClassVar[int] recordUid: bytes encryptedRecordKey: bytes encryptedRecordKeyType: EncryptedKeyType tlaProperties: _tla_pb2.TLAProperties - def __init__(self, recordUid: _Optional[bytes] = ..., encryptedRecordKey: _Optional[bytes] = ..., encryptedRecordKeyType: _Optional[_Union[EncryptedKeyType, str]] = ..., tlaProperties: _Optional[_Union[_tla_pb2.TLAProperties, _Mapping]] = ...) -> None: ... + recordKeyEncryptedByOwnerKey: bytes + def __init__(self, recordUid: _Optional[bytes] = ..., encryptedRecordKey: _Optional[bytes] = ..., encryptedRecordKeyType: _Optional[_Union[EncryptedKeyType, str]] = ..., tlaProperties: _Optional[_Union[_tla_pb2.TLAProperties, _Mapping]] = ..., recordKeyEncryptedByOwnerKey: _Optional[bytes] = ...) -> None: ... class FolderRecord(_message.Message): __slots__ = ("folderUid", "recordMetadata", "folderKeyEncryptionType") diff --git a/keepersdk-package/src/keepersdk/proto/pagination_pb2.py b/keepersdk-package/src/keepersdk/proto/pagination_pb2.py index 85b834b6..81be7ea4 100644 --- a/keepersdk-package/src/keepersdk/proto/pagination_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/pagination_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: pagination.proto -# Protobuf Python Version: 5.29.5 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -13,7 +13,7 @@ _runtime_version.Domain.PUBLIC, 5, 29, - 5, + 3, '', 'pagination.proto' ) diff --git a/keepersdk-package/src/keepersdk/proto/pam_pb2.py b/keepersdk-package/src/keepersdk/proto/pam_pb2.py index e0770d73..c4c44a84 100644 --- a/keepersdk-package/src/keepersdk/proto/pam_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/pam_pb2.py @@ -26,7 +26,7 @@ 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\"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\"\xc1\x01\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.PamUsageByUser\x12\x10\n\x08nhiCount\x18\x07 \x01(\x05\"D\n\x16NhiBulkMetricsResponse\x12*\n\tresponses\x18\x01 \x03(\x0b\x32\x17.PAM.NhiMetricsResponse*\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\x42\x1f\n\x18\x63om.keepersecurity.protoB\x03PAMb\x06proto3') +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\"\xab\x02\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\x12\x18\n\x10maxInstanceCount\x18\x0b \x01(\x05\x12\x10\n\x08lastSeen\x18\x0c \x01(\x03\"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\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\x12\x0e\n\x06\x61ppUid\x18\x04 \x01(\t\"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\"6\n\x1dSetNhiKsmEffectiveDateRequest\x12\x15\n\reffectiveDate\x18\x01 \x01(\x03\"L\n\x1eGetNhiKsmEffectiveDateResponse\x12\x15\n\reffectiveDate\x18\x01 \x01(\x03\x12\x13\n\x0b\x64\x65\x66\x61ultDate\x18\x02 \x01(\x03\"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) @@ -34,18 +34,22 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\030com.keepersecurity.protoB\003PAM' - _globals['_WEBRTCCONNECTIONTYPE']._serialized_start=4700 - _globals['_WEBRTCCONNECTIONTYPE']._serialized_end=4858 - _globals['_PAMOPERATIONTYPE']._serialized_start=4860 - _globals['_PAMOPERATIONTYPE']._serialized_end=4924 - _globals['_PAMOPERATIONRESULTTYPE']._serialized_start=4926 - _globals['_PAMOPERATIONRESULTTYPE']._serialized_end=5038 - _globals['_CONTROLLERMESSAGETYPE']._serialized_start=5041 - _globals['_CONTROLLERMESSAGETYPE']._serialized_end=5242 - _globals['_PAMRECORDINGTYPE']._serialized_start=5244 - _globals['_PAMRECORDINGTYPE']._serialized_end=5330 - _globals['_PAMRECORDINGRISKLEVEL']._serialized_start=5332 - _globals['_PAMRECORDINGRISKLEVEL']._serialized_end=5437 + _globals['_NHIMETRICSRESPONSE'].fields_by_name['pamUsageByUser']._loaded_options = None + _globals['_NHIMETRICSRESPONSE'].fields_by_name['pamUsageByUser']._serialized_options = b'\030\001' + _globals['_WEBRTCCONNECTIONTYPE']._serialized_start=5548 + _globals['_WEBRTCCONNECTIONTYPE']._serialized_end=5706 + _globals['_PAMOPERATIONTYPE']._serialized_start=5708 + _globals['_PAMOPERATIONTYPE']._serialized_end=5772 + _globals['_PAMOPERATIONRESULTTYPE']._serialized_start=5774 + _globals['_PAMOPERATIONRESULTTYPE']._serialized_end=5886 + _globals['_CONTROLLERMESSAGETYPE']._serialized_start=5889 + _globals['_CONTROLLERMESSAGETYPE']._serialized_end=6090 + _globals['_PAMRECORDINGTYPE']._serialized_start=6092 + _globals['_PAMRECORDINGTYPE']._serialized_end=6178 + _globals['_PAMRECORDINGRISKLEVEL']._serialized_start=6180 + _globals['_PAMRECORDINGRISKLEVEL']._serialized_end=6285 + _globals['_NHICATEGORY']._serialized_start=6287 + _globals['_NHICATEGORY']._serialized_end=6383 _globals['_PAMROTATIONSCHEDULE']._serialized_start=51 _globals['_PAMROTATIONSCHEDULE']._serialized_end=182 _globals['_PAMROTATIONSCHEDULESRESPONSE']._serialized_start=184 @@ -87,39 +91,57 @@ _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=3793 - _globals['_PAMUNIVERSALSYNCFOLDER']._serialized_start=3795 - _globals['_PAMUNIVERSALSYNCFOLDER']._serialized_end=3832 - _globals['_PAMUNIVERSALSYNCCONFIG']._serialized_start=3835 - _globals['_PAMUNIVERSALSYNCCONFIG']._serialized_end=4087 - _globals['_NHIMETRICSREQUEST']._serialized_start=4089 - _globals['_NHIMETRICSREQUEST']._serialized_end=4144 - _globals['_PAMUSAGEBYUSER']._serialized_start=4147 - _globals['_PAMUSAGEBYUSER']._serialized_end=4431 - _globals['_NHIMETRICSRESPONSE']._serialized_start=4434 - _globals['_NHIMETRICSRESPONSE']._serialized_end=4627 - _globals['_NHIBULKMETRICSRESPONSE']._serialized_start=4629 - _globals['_NHIBULKMETRICSRESPONSE']._serialized_end=4697 + _globals['_PAMCONTROLLER']._serialized_end=2251 + _globals['_PAMSETMAXINSTANCECOUNTREQUEST']._serialized_start=2253 + _globals['_PAMSETMAXINSTANCECOUNTREQUEST']._serialized_end=2333 + _globals['_CONTROLLERRESPONSE']._serialized_start=2335 + _globals['_CONTROLLERRESPONSE']._serialized_end=2372 + _globals['_PAMCONFIGURATIONCONTROLLER']._serialized_start=2374 + _globals['_PAMCONFIGURATIONCONTROLLER']._serialized_end=2451 + _globals['_CONFIGURATIONADDREQUEST']._serialized_start=2454 + _globals['_CONFIGURATIONADDREQUEST']._serialized_end=2617 + _globals['_RELAYACCESSCREDS']._serialized_start=2619 + _globals['_RELAYACCESSCREDS']._serialized_end=2693 + _globals['_PAMRECORDINGSREQUEST']._serialized_start=2696 + _globals['_PAMRECORDINGSREQUEST']._serialized_end=2953 + _globals['_PAMRECORDING']._serialized_start=2956 + _globals['_PAMRECORDING']._serialized_end=3296 + _globals['_PAMRECORDINGSRESPONSE']._serialized_start=3298 + _globals['_PAMRECORDINGSRESPONSE']._serialized_end=3377 + _globals['_PAMDATA']._serialized_start=3379 + _globals['_PAMDATA']._serialized_end=3421 + _globals['_UIDLIST']._serialized_start=3423 + _globals['_UIDLIST']._serialized_end=3446 + _globals['_PAMRESOURCECONFIG']._serialized_start=3449 + _globals['_PAMRESOURCECONFIG']._serialized_end=3885 + _globals['_PAMUNIVERSALSYNCFOLDER']._serialized_start=3887 + _globals['_PAMUNIVERSALSYNCFOLDER']._serialized_end=3924 + _globals['_PAMUNIVERSALSYNCCONFIG']._serialized_start=3927 + _globals['_PAMUNIVERSALSYNCCONFIG']._serialized_end=4179 + _globals['_NHIMETRICSREQUEST']._serialized_start=4181 + _globals['_NHIMETRICSREQUEST']._serialized_end=4236 + _globals['_PAMUSAGEBYUSER']._serialized_start=4239 + _globals['_PAMUSAGEBYUSER']._serialized_end=4523 + _globals['_NHIUSAGEBYUSER']._serialized_start=4525 + _globals['_NHIUSAGEBYUSER']._serialized_end=4637 + _globals['_NHIMETRICSRESPONSE']._serialized_start=4640 + _globals['_NHIMETRICSRESPONSE']._serialized_end=4900 + _globals['_NHIBULKMETRICSRESPONSE']._serialized_start=4902 + _globals['_NHIBULKMETRICSRESPONSE']._serialized_end=4970 + _globals['_NHIUIDENTRY']._serialized_start=4972 + _globals['_NHIUIDENTRY']._serialized_end=5066 + _globals['_GETNHIUIDSREQUEST']._serialized_start=5068 + _globals['_GETNHIUIDSREQUEST']._serialized_end=5123 + _globals['_GETNHIUIDSRESPONSE']._serialized_start=5125 + _globals['_GETNHIUIDSRESPONSE']._serialized_end=5177 + _globals['_SETNHIKSMEFFECTIVEDATEREQUEST']._serialized_start=5179 + _globals['_SETNHIKSMEFFECTIVEDATEREQUEST']._serialized_end=5233 + _globals['_GETNHIKSMEFFECTIVEDATERESPONSE']._serialized_start=5235 + _globals['_GETNHIKSMEFFECTIVEDATERESPONSE']._serialized_end=5311 + _globals['_PAMUNIVERSALSYNCPRECHECKREQUEST']._serialized_start=5313 + _globals['_PAMUNIVERSALSYNCPRECHECKREQUEST']._serialized_end=5386 + _globals['_PAMUNIVERSALSYNCPRECHECKRESULT']._serialized_start=5388 + _globals['_PAMUNIVERSALSYNCPRECHECKRESULT']._serialized_end=5455 + _globals['_PAMUNIVERSALSYNCPRECHECKRESPONSE']._serialized_start=5457 + _globals['_PAMUNIVERSALSYNCPRECHECKRESPONSE']._serialized_end=5545 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/pam_pb2.pyi b/keepersdk-package/src/keepersdk/proto/pam_pb2.pyi index 030a298e..61330eee 100644 --- a/keepersdk-package/src/keepersdk/proto/pam_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/pam_pb2.pyi @@ -1,5 +1,5 @@ -import enterprise_pb2 as _enterprise_pb2 -import record_pb2 as _record_pb2 +from . import enterprise_pb2 as _enterprise_pb2 +from . import record_pb2 as _record_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 @@ -62,6 +62,14 @@ class PAMRecordingRiskLevel(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 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 @@ -99,6 +107,11 @@ 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") @@ -291,7 +304,7 @@ class PAMConfigurations(_message.Message): 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", "maxInstanceCount", "lastSeen") CONTROLLERUID_FIELD_NUMBER: _ClassVar[int] CONTROLLERNAME_FIELD_NUMBER: _ClassVar[int] DEVICETOKEN_FIELD_NUMBER: _ClassVar[int] @@ -302,6 +315,8 @@ class PAMController(_message.Message): APPLICATIONUID_FIELD_NUMBER: _ClassVar[int] APPCLIENTTYPE_FIELD_NUMBER: _ClassVar[int] ISINITIALIZED_FIELD_NUMBER: _ClassVar[int] + MAXINSTANCECOUNT_FIELD_NUMBER: _ClassVar[int] + LASTSEEN_FIELD_NUMBER: _ClassVar[int] controllerUid: bytes controllerName: str deviceToken: str @@ -312,7 +327,9 @@ class PAMController(_message.Message): applicationUid: bytes appClientType: _enterprise_pb2.AppClientType isInitialized: bool - 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: ... + maxInstanceCount: int + lastSeen: int + 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 = ..., maxInstanceCount: _Optional[int] = ..., lastSeen: _Optional[int] = ...) -> None: ... class PAMSetMaxInstanceCountRequest(_message.Message): __slots__ = ("controllerUid", "maxInstanceCount") @@ -433,7 +450,7 @@ class UidList(_message.Message): 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] @@ -443,6 +460,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 @@ -452,7 +470,8 @@ 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",) @@ -506,8 +525,22 @@ class PamUsageByUser(_message.Message): 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") + __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] @@ -515,6 +548,8 @@ class NhiMetricsResponse(_message.Message): 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 @@ -522,10 +557,74 @@ class NhiMetricsResponse(_message.Message): pamGatewayOnline: int pamUsageByUser: _containers.RepeatedCompositeFieldContainer[PamUsageByUser] nhiCount: int - 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] = ...) -> None: ... + 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", "appUid") + UID_FIELD_NUMBER: _ClassVar[int] + CATEGORY_FIELD_NUMBER: _ClassVar[int] + KSMNHI_FIELD_NUMBER: _ClassVar[int] + APPUID_FIELD_NUMBER: _ClassVar[int] + uid: str + category: NhiCategory + ksmNhi: bool + appUid: str + def __init__(self, uid: _Optional[str] = ..., category: _Optional[_Union[NhiCategory, str]] = ..., ksmNhi: bool = ..., appUid: _Optional[str] = ...) -> 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 SetNhiKsmEffectiveDateRequest(_message.Message): + __slots__ = ("effectiveDate",) + EFFECTIVEDATE_FIELD_NUMBER: _ClassVar[int] + effectiveDate: int + def __init__(self, effectiveDate: _Optional[int] = ...) -> None: ... + +class GetNhiKsmEffectiveDateResponse(_message.Message): + __slots__ = ("effectiveDate", "defaultDate") + EFFECTIVEDATE_FIELD_NUMBER: _ClassVar[int] + DEFAULTDATE_FIELD_NUMBER: _ClassVar[int] + effectiveDate: int + defaultDate: int + def __init__(self, effectiveDate: _Optional[int] = ..., defaultDate: _Optional[int] = ...) -> 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/keepersdk-package/src/keepersdk/proto/pedm_pb2.py b/keepersdk-package/src/keepersdk/proto/pedm_pb2.py index e1d72a4d..a9f8fe43 100644 --- a/keepersdk-package/src/keepersdk/proto/pedm_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/pedm_pb2.py @@ -26,7 +26,7 @@ from . import NotificationCenter_pb2 as NotificationCenter__pb2 -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') +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\"t\n\x1eGetAgenticWorkloadCountRequest\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x03(\x05\x12\x10\n\x06preset\x18\x02 \x01(\tH\x00\x12 \n\x05range\x18\x03 \x01(\x0b\x32\x0f.PEDM.DateRangeH\x00\x42\x08\n\x06period\"F\n\x17\x45nterpriseWorkloadCount\x12\x14\n\x0c\x65nterpriseId\x18\x01 \x01(\x05\x12\x15\n\rworkloadCount\x18\x02 \x01(\x03\"P\n\x1fGetAgenticWorkloadCountResponse\x12-\n\x06\x63ounts\x18\x01 \x03(\x0b\x32\x1d.PEDM.EnterpriseWorkloadCount*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) @@ -34,10 +34,10 @@ 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=5890 - _globals['_COLLECTIONLINKTYPE']._serialized_end=5996 - _globals['_APPROVALSTATUSTYPE']._serialized_start=5998 - _globals['_APPROVALSTATUSTYPE']._serialized_end=6109 + _globals['_COLLECTIONLINKTYPE']._serialized_start=6162 + _globals['_COLLECTIONLINKTYPE']._serialized_end=6268 + _globals['_APPROVALSTATUSTYPE']._serialized_start=6270 + _globals['_APPROVALSTATUSTYPE']._serialized_end=6381 _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_start=34 _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_end=113 _globals['_PEDMSTATUS']._serialized_start=115 @@ -152,4 +152,10 @@ _globals['_AGENTCOUNTFORENTERPRISE']._serialized_end=5801 _globals['_GETAGENTDAILYCOUNTRESPONSE']._serialized_start=5803 _globals['_GETAGENTDAILYCOUNTRESPONSE']._serialized_end=5888 + _globals['_GETAGENTICWORKLOADCOUNTREQUEST']._serialized_start=5890 + _globals['_GETAGENTICWORKLOADCOUNTREQUEST']._serialized_end=6006 + _globals['_ENTERPRISEWORKLOADCOUNT']._serialized_start=6008 + _globals['_ENTERPRISEWORKLOADCOUNT']._serialized_end=6078 + _globals['_GETAGENTICWORKLOADCOUNTRESPONSE']._serialized_start=6080 + _globals['_GETAGENTICWORKLOADCOUNTRESPONSE']._serialized_end=6160 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/pedm_pb2.pyi b/keepersdk-package/src/keepersdk/proto/pedm_pb2.pyi index 52f82d9b..d1c44561 100644 --- a/keepersdk-package/src/keepersdk/proto/pedm_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/pedm_pb2.pyi @@ -1,4 +1,4 @@ -import folder_pb2 as _folder_pb2 +from . 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 @@ -612,3 +612,27 @@ class GetAgentDailyCountResponse(_message.Message): ENTERPRISECOUNTS_FIELD_NUMBER: _ClassVar[int] enterpriseCounts: _containers.RepeatedCompositeFieldContainer[AgentCountForEnterprise] def __init__(self, enterpriseCounts: _Optional[_Iterable[_Union[AgentCountForEnterprise, _Mapping]]] = ...) -> None: ... + +class GetAgenticWorkloadCountRequest(_message.Message): + __slots__ = ("enterpriseId", "preset", "range") + ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] + PRESET_FIELD_NUMBER: _ClassVar[int] + RANGE_FIELD_NUMBER: _ClassVar[int] + enterpriseId: _containers.RepeatedScalarFieldContainer[int] + preset: str + range: DateRange + def __init__(self, enterpriseId: _Optional[_Iterable[int]] = ..., preset: _Optional[str] = ..., range: _Optional[_Union[DateRange, _Mapping]] = ...) -> None: ... + +class EnterpriseWorkloadCount(_message.Message): + __slots__ = ("enterpriseId", "workloadCount") + ENTERPRISEID_FIELD_NUMBER: _ClassVar[int] + WORKLOADCOUNT_FIELD_NUMBER: _ClassVar[int] + enterpriseId: int + workloadCount: int + def __init__(self, enterpriseId: _Optional[int] = ..., workloadCount: _Optional[int] = ...) -> None: ... + +class GetAgenticWorkloadCountResponse(_message.Message): + __slots__ = ("counts",) + COUNTS_FIELD_NUMBER: _ClassVar[int] + counts: _containers.RepeatedCompositeFieldContainer[EnterpriseWorkloadCount] + def __init__(self, counts: _Optional[_Iterable[_Union[EnterpriseWorkloadCount, _Mapping]]] = ...) -> None: ... diff --git a/keepersdk-package/src/keepersdk/proto/record_details_pb2.py b/keepersdk-package/src/keepersdk/proto/record_details_pb2.py index 39bd1e04..9a8eca0c 100644 --- a/keepersdk-package/src/keepersdk/proto/record_details_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/record_details_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: record_details.proto -# Protobuf Python Version: 5.29.5 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -13,7 +13,7 @@ _runtime_version.Domain.PUBLIC, 5, 29, - 5, + 3, '', 'record_details.proto' ) diff --git a/keepersdk-package/src/keepersdk/proto/record_endpoints_pb2.py b/keepersdk-package/src/keepersdk/proto/record_endpoints_pb2.py index 55cbe6d3..685a3de9 100644 --- a/keepersdk-package/src/keepersdk/proto/record_endpoints_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/record_endpoints_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: record_endpoints.proto -# Protobuf Python Version: 5.29.5 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -13,7 +13,7 @@ _runtime_version.Domain.PUBLIC, 5, 29, - 5, + 3, '', 'record_endpoints.proto' ) @@ -26,7 +26,7 @@ from . import folder_pb2 as folder__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16record_endpoints.proto\x12\trecord.v3\x1a\x0crecord.proto\x1a\x0c\x66older.proto\"\x83\x01\n\x11RecordsAddRequest\x12%\n\x07records\x18\x01 \x03(\x0b\x32\x14.record.v3.RecordAdd\x12\x12\n\nclientTime\x18\x02 \x01(\x03\x12\x33\n\x13securityDataKeyType\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\"\xa8\x03\n\tRecordAdd\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x11\n\trecordKey\x18\x02 \x01(\x0c\x12/\n\rrecordKeyType\x18\x03 \x01(\x0e\x32\x18.Folder.EncryptedKeyType\x12=\n\x14recordKeyEncryptedBy\x18\x04 \x01(\x0e\x32\x1f.Folder.FolderKeyEncryptionType\x12\x1a\n\x12\x63lientModifiedTime\x18\x05 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x06 \x01(\x0c\x12\x15\n\rnonSharedData\x18\x07 \x01(\x0c\x12\x11\n\tfolderUid\x18\x08 \x01(\x0c\x12(\n\x0brecordLinks\x18\t \x03(\x0b\x32\x13.Records.RecordLink\x12#\n\x05\x61udit\x18\n \x01(\x0b\x32\x14.Records.RecordAudit\x12+\n\x0csecurityData\x18\x0b \x01(\x0b\x32\x15.Records.SecurityData\x12\x35\n\x11securityScoreData\x18\x0c \x01(\x0b\x32\x1a.Records.SecurityScoreDataB*\n&com.keepersecurity.proto.api.record.v3P\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16record_endpoints.proto\x12\trecord.v3\x1a\x0crecord.proto\x1a\x0c\x66older.proto\"\x83\x01\n\x11RecordsAddRequest\x12%\n\x07records\x18\x01 \x03(\x0b\x32\x14.record.v3.RecordAdd\x12\x12\n\nclientTime\x18\x02 \x01(\x03\x12\x33\n\x13securityDataKeyType\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\"\xce\x03\n\tRecordAdd\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x11\n\trecordKey\x18\x02 \x01(\x0c\x12/\n\rrecordKeyType\x18\x03 \x01(\x0e\x32\x18.Folder.EncryptedKeyType\x12=\n\x14recordKeyEncryptedBy\x18\x04 \x01(\x0e\x32\x1f.Folder.FolderKeyEncryptionType\x12\x1a\n\x12\x63lientModifiedTime\x18\x05 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x06 \x01(\x0c\x12\x15\n\rnonSharedData\x18\x07 \x01(\x0c\x12\x11\n\tfolderUid\x18\x08 \x01(\x0c\x12(\n\x0brecordLinks\x18\t \x03(\x0b\x32\x13.Records.RecordLink\x12#\n\x05\x61udit\x18\n \x01(\x0b\x32\x14.Records.RecordAudit\x12+\n\x0csecurityData\x18\x0b \x01(\x0b\x32\x15.Records.SecurityData\x12\x35\n\x11securityScoreData\x18\x0c \x01(\x0b\x32\x1a.Records.SecurityScoreData\x12$\n\x1crecordKeyEncryptedByOwnerKey\x18\r \x01(\x0c\x42*\n&com.keepersecurity.proto.api.record.v3P\x01\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -37,5 +37,5 @@ _globals['_RECORDSADDREQUEST']._serialized_start=66 _globals['_RECORDSADDREQUEST']._serialized_end=197 _globals['_RECORDADD']._serialized_start=200 - _globals['_RECORDADD']._serialized_end=624 + _globals['_RECORDADD']._serialized_end=662 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/record_endpoints_pb2.pyi b/keepersdk-package/src/keepersdk/proto/record_endpoints_pb2.pyi index d60f1357..a4a85db0 100644 --- a/keepersdk-package/src/keepersdk/proto/record_endpoints_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/record_endpoints_pb2.pyi @@ -18,7 +18,7 @@ class RecordsAddRequest(_message.Message): def __init__(self, records: _Optional[_Iterable[_Union[RecordAdd, _Mapping]]] = ..., clientTime: _Optional[int] = ..., securityDataKeyType: _Optional[_Union[_record_pb2.RecordKeyType, str]] = ...) -> None: ... class RecordAdd(_message.Message): - __slots__ = ("recordUid", "recordKey", "recordKeyType", "recordKeyEncryptedBy", "clientModifiedTime", "data", "nonSharedData", "folderUid", "recordLinks", "audit", "securityData", "securityScoreData") + __slots__ = ("recordUid", "recordKey", "recordKeyType", "recordKeyEncryptedBy", "clientModifiedTime", "data", "nonSharedData", "folderUid", "recordLinks", "audit", "securityData", "securityScoreData", "recordKeyEncryptedByOwnerKey") RECORDUID_FIELD_NUMBER: _ClassVar[int] RECORDKEY_FIELD_NUMBER: _ClassVar[int] RECORDKEYTYPE_FIELD_NUMBER: _ClassVar[int] @@ -31,6 +31,7 @@ class RecordAdd(_message.Message): AUDIT_FIELD_NUMBER: _ClassVar[int] SECURITYDATA_FIELD_NUMBER: _ClassVar[int] SECURITYSCOREDATA_FIELD_NUMBER: _ClassVar[int] + RECORDKEYENCRYPTEDBYOWNERKEY_FIELD_NUMBER: _ClassVar[int] recordUid: bytes recordKey: bytes recordKeyType: _folder_pb2.EncryptedKeyType @@ -43,4 +44,5 @@ class RecordAdd(_message.Message): audit: _record_pb2.RecordAudit securityData: _record_pb2.SecurityData securityScoreData: _record_pb2.SecurityScoreData - def __init__(self, recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., recordKeyType: _Optional[_Union[_folder_pb2.EncryptedKeyType, str]] = ..., recordKeyEncryptedBy: _Optional[_Union[_folder_pb2.FolderKeyEncryptionType, str]] = ..., clientModifiedTime: _Optional[int] = ..., data: _Optional[bytes] = ..., nonSharedData: _Optional[bytes] = ..., folderUid: _Optional[bytes] = ..., recordLinks: _Optional[_Iterable[_Union[_record_pb2.RecordLink, _Mapping]]] = ..., audit: _Optional[_Union[_record_pb2.RecordAudit, _Mapping]] = ..., securityData: _Optional[_Union[_record_pb2.SecurityData, _Mapping]] = ..., securityScoreData: _Optional[_Union[_record_pb2.SecurityScoreData, _Mapping]] = ...) -> None: ... + recordKeyEncryptedByOwnerKey: bytes + def __init__(self, recordUid: _Optional[bytes] = ..., recordKey: _Optional[bytes] = ..., recordKeyType: _Optional[_Union[_folder_pb2.EncryptedKeyType, str]] = ..., recordKeyEncryptedBy: _Optional[_Union[_folder_pb2.FolderKeyEncryptionType, str]] = ..., clientModifiedTime: _Optional[int] = ..., data: _Optional[bytes] = ..., nonSharedData: _Optional[bytes] = ..., folderUid: _Optional[bytes] = ..., recordLinks: _Optional[_Iterable[_Union[_record_pb2.RecordLink, _Mapping]]] = ..., audit: _Optional[_Union[_record_pb2.RecordAudit, _Mapping]] = ..., securityData: _Optional[_Union[_record_pb2.SecurityData, _Mapping]] = ..., securityScoreData: _Optional[_Union[_record_pb2.SecurityScoreData, _Mapping]] = ..., recordKeyEncryptedByOwnerKey: _Optional[bytes] = ...) -> None: ... diff --git a/keepersdk-package/src/keepersdk/proto/record_pb2.py b/keepersdk-package/src/keepersdk/proto/record_pb2.py index a07fc5a6..4a9bd236 100644 --- a/keepersdk-package/src/keepersdk/proto/record_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/record_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0crecord.proto\x12\x07Records\"\\\n\nRecordType\x12\x14\n\x0crecordTypeId\x18\x01 \x01(\x05\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\'\n\x05scope\x18\x03 \x01(\x0e\x32\x18.Records.RecordTypeScope\"U\n\x12RecordTypesRequest\x12\x10\n\x08standard\x18\x01 \x01(\x08\x12\x0c\n\x04user\x18\x02 \x01(\x08\x12\x12\n\nenterprise\x18\x03 \x01(\x08\x12\x0b\n\x03pam\x18\x04 \x01(\x08\"\x9c\x01\n\x13RecordTypesResponse\x12(\n\x0brecordTypes\x18\x01 \x03(\x0b\x32\x13.Records.RecordType\x12\x17\n\x0fstandardCounter\x18\x02 \x01(\x05\x12\x13\n\x0buserCounter\x18\x03 \x01(\x05\x12\x19\n\x11\x65nterpriseCounter\x18\x04 \x01(\x05\x12\x12\n\npamCounter\x18\x05 \x01(\x05\"A\n\x18RecordTypeModifyResponse\x12\x14\n\x0crecordTypeId\x18\x01 \x01(\x05\x12\x0f\n\x07\x63ounter\x18\x02 \x01(\x05\"=\n\x11RecordsGetRequest\x12\x13\n\x0brecord_uids\x18\x01 \x03(\x0c\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\"\xd1\x01\n\x06Record\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\x12/\n\x0frecord_key_type\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\r\n\x05\x65xtra\x18\x05 \x01(\x0c\x12\x0f\n\x07version\x18\x06 \x01(\x05\x12\x1c\n\x14\x63lient_modified_time\x18\x07 \x01(\x03\x12\x10\n\x08revision\x18\x08 \x01(\x03\x12\x10\n\x08\x66ile_ids\x18\t \x03(\x0c\"M\n\x0f\x46olderRecordKey\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_uid\x18\x02 \x01(\x0c\x12\x12\n\nrecord_key\x18\x03 \x01(\x0c\"a\n\x06\x46older\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x12\n\nfolder_key\x18\x02 \x01(\x0c\x12/\n\x0f\x66older_key_type\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\"\x95\x01\n\x04Team\x12\x10\n\x08team_uid\x18\x01 \x01(\x0c\x12\x10\n\x08team_key\x18\x02 \x01(\x0c\x12\x18\n\x10team_private_key\x18\x03 \x01(\x0c\x12-\n\rteam_key_type\x18\x04 \x01(\x0e\x32\x16.Records.RecordKeyType\x12 \n\x07\x66olders\x18\x05 \x03(\x0b\x32\x0f.Records.Folder\"\xac\x01\n\x12RecordsGetResponse\x12 \n\x07records\x18\x01 \x03(\x0b\x32\x0f.Records.Record\x12\x34\n\x12\x66older_record_keys\x18\x02 \x03(\x0b\x32\x18.Records.FolderRecordKey\x12 \n\x07\x66olders\x18\x03 \x03(\x0b\x32\x0f.Records.Folder\x12\x1c\n\x05teams\x18\x04 \x03(\x0b\x32\r.Records.Team\"4\n\nRecordLink\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\",\n\x0bRecordAudit\x12\x0f\n\x07version\x18\x01 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"\x1c\n\x0cSecurityData\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"!\n\x11SecurityScoreData\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x84\x03\n\tRecordAdd\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\x12\x1c\n\x14\x63lient_modified_time\x18\x03 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x17\n\x0fnon_shared_data\x18\x05 \x01(\x0c\x12.\n\x0b\x66older_type\x18\x06 \x01(\x0e\x32\x19.Records.RecordFolderType\x12\x12\n\nfolder_uid\x18\x07 \x01(\x0c\x12\x12\n\nfolder_key\x18\x08 \x01(\x0c\x12)\n\x0crecord_links\x18\t \x03(\x0b\x32\x13.Records.RecordLink\x12#\n\x05\x61udit\x18\n \x01(\x0b\x32\x14.Records.RecordAudit\x12+\n\x0csecurityData\x18\x0b \x01(\x0b\x32\x15.Records.SecurityData\x12\x35\n\x11securityScoreData\x18\x0c \x01(\x0b\x32\x1a.Records.SecurityScoreData\"\x85\x01\n\x11RecordsAddRequest\x12#\n\x07records\x18\x01 \x03(\x0b\x32\x12.Records.RecordAdd\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\x12\x36\n\x16security_data_key_type\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\"\xce\x02\n\x0cRecordUpdate\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x1c\n\x14\x63lient_modified_time\x18\x02 \x01(\x03\x12\x10\n\x08revision\x18\x03 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x17\n\x0fnon_shared_data\x18\x05 \x01(\x0c\x12-\n\x10record_links_add\x18\x06 \x03(\x0b\x32\x13.Records.RecordLink\x12\x1b\n\x13record_links_remove\x18\x07 \x03(\x0c\x12#\n\x05\x61udit\x18\x08 \x01(\x0b\x32\x14.Records.RecordAudit\x12+\n\x0csecurityData\x18\t \x01(\x0b\x32\x15.Records.SecurityData\x12\x35\n\x11securityScoreData\x18\n \x01(\x0b\x32\x1a.Records.SecurityScoreData\"\x8b\x01\n\x14RecordsUpdateRequest\x12&\n\x07records\x18\x01 \x03(\x0b\x32\x15.Records.RecordUpdate\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\x12\x36\n\x16security_data_key_type\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\"\x8e\x01\n\x17RecordFileForConversion\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x14\n\x0c\x66ile_file_id\x18\x02 \x01(\t\x12\x15\n\rthumb_file_id\x18\x03 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x12\n\nrecord_key\x18\x05 \x01(\x0c\x12\x10\n\x08link_key\x18\x06 \x01(\x0c\"J\n\x19RecordFolderForConversion\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x19\n\x11record_folder_key\x18\x02 \x01(\x0c\"\x92\x02\n\x11RecordConvertToV3\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x1c\n\x14\x63lient_modified_time\x18\x02 \x01(\x03\x12\x10\n\x08revision\x18\x03 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x17\n\x0fnon_shared_data\x18\x05 \x01(\x0c\x12#\n\x05\x61udit\x18\x06 \x01(\x0b\x32\x14.Records.RecordAudit\x12\x35\n\x0brecord_file\x18\x07 \x03(\x0b\x32 .Records.RecordFileForConversion\x12\x36\n\nfolder_key\x18\x08 \x03(\x0b\x32\".Records.RecordFolderForConversion\"]\n\x19RecordsConvertToV3Request\x12+\n\x07records\x18\x01 \x03(\x0b\x32\x1a.Records.RecordConvertToV3\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\"\'\n\x14RecordsRemoveRequest\x12\x0f\n\x07records\x18\x01 \x03(\x0c\">\n\x0cRecordRevert\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x1a\n\x12revert_to_revision\x18\x02 \x01(\x03\">\n\x14RecordsRevertRequest\x12&\n\x07records\x18\x01 \x03(\x0b\x32\x15.Records.RecordRevert\"c\n\x0fRecordLinkError\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12+\n\x06status\x18\x02 \x01(\x0e\x32\x1b.Records.RecordModifyResult\x12\x0f\n\x07message\x18\x03 \x01(\t\"\x95\x01\n\x12RecordModifyStatus\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12+\n\x06status\x18\x02 \x01(\x0e\x32\x1b.Records.RecordModifyResult\x12\x0f\n\x07message\x18\x03 \x01(\t\x12-\n\x0blink_errors\x18\x04 \x03(\x0b\x32\x18.Records.RecordLinkError\"W\n\x15RecordsModifyResponse\x12,\n\x07records\x18\x01 \x03(\x0b\x32\x1b.Records.RecordModifyStatus\x12\x10\n\x08revision\x18\x02 \x01(\x03\"Y\n\x12RecordAddAuditData\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12\x0f\n\x07version\x18\x04 \x01(\x05\"C\n\x13\x41\x64\x64\x41uditDataRequest\x12,\n\x07records\x18\x01 \x03(\x0b\x32\x1b.Records.RecordAddAuditData\"t\n\x04\x46ile\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12\x10\n\x08\x66ileSize\x18\x04 \x01(\x03\x12\x11\n\tthumbSize\x18\x05 \x01(\x05\x12\x11\n\tis_script\x18\x06 \x01(\x08\"D\n\x0f\x46ilesAddRequest\x12\x1c\n\x05\x66iles\x18\x01 \x03(\x0b\x32\r.Records.File\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\"\xa7\x01\n\rFileAddStatus\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12&\n\x06status\x18\x02 \x01(\x0e\x32\x16.Records.FileAddResult\x12\x0b\n\x03url\x18\x03 \x01(\t\x12\x12\n\nparameters\x18\x04 \x01(\t\x12\x1c\n\x14thumbnail_parameters\x18\x05 \x01(\t\x12\x1b\n\x13success_status_code\x18\x06 \x01(\x05\"K\n\x10\x46ilesAddResponse\x12%\n\x05\x66iles\x18\x01 \x03(\x0b\x32\x16.Records.FileAddStatus\x12\x10\n\x08revision\x18\x02 \x01(\x03\"f\n\x0f\x46ilesGetRequest\x12\x13\n\x0brecord_uids\x18\x01 \x03(\x0c\x12\x16\n\x0e\x66or_thumbnails\x18\x02 \x01(\x08\x12&\n\x1e\x65mergency_access_account_owner\x18\x03 \x01(\t\"\xa2\x01\n\rFileGetStatus\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12&\n\x06status\x18\x02 \x01(\x0e\x32\x16.Records.FileGetResult\x12\x0b\n\x03url\x18\x03 \x01(\t\x12\x1b\n\x13success_status_code\x18\x04 \x01(\x05\x12+\n\x0b\x66ileKeyType\x18\x05 \x01(\x0e\x32\x16.Records.RecordKeyType\"9\n\x10\x46ilesGetResponse\x12%\n\x05\x66iles\x18\x01 \x03(\x0b\x32\x16.Records.FileGetStatus\"\x8d\x01\n\x15\x41pplicationAddRequest\x12\x0f\n\x07\x61pp_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\x12\x1c\n\x14\x63lient_modified_time\x18\x03 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12#\n\x05\x61udit\x18\x05 \x01(\x0b\x32\x14.Records.RecordAudit\"\x88\x01\n\"GetRecordDataWithAccessInfoRequest\x12\x12\n\nclientTime\x18\x01 \x01(\x03\x12\x11\n\trecordUid\x18\x02 \x03(\x0c\x12;\n\x14recordDetailsInclude\x18\x03 \x01(\x0e\x32\x1d.Records.RecordDetailsInclude\"\x86\x02\n\x0eUserPermission\x12\x10\n\x08username\x18\x01 \x01(\t\x12\r\n\x05owner\x18\x02 \x01(\x08\x12\x12\n\nshareAdmin\x18\x03 \x01(\x08\x12\x10\n\x08sharable\x18\x04 \x01(\x08\x12\x10\n\x08\x65\x64itable\x18\x05 \x01(\x08\x12\x18\n\x10\x61waitingApproval\x18\x06 \x01(\x08\x12\x12\n\nexpiration\x18\x07 \x01(\x03\x12\x12\n\naccountUid\x18\x08 \x01(\x0c\x12=\n\x15timerNotificationType\x18\t \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x1a\n\x12rotateOnExpiration\x18\n \x01(\x08\"\xd8\x01\n\x16SharedFolderPermission\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x12\n\nresharable\x18\x02 \x01(\x08\x12\x10\n\x08\x65\x64itable\x18\x03 \x01(\x08\x12\x10\n\x08revision\x18\x04 \x01(\x03\x12\x12\n\nexpiration\x18\x05 \x01(\x03\x12=\n\x15timerNotificationType\x18\x06 \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x1a\n\x12rotateOnExpiration\x18\x07 \x01(\x08\"\xe8\x02\n\nRecordData\x12\x10\n\x08revision\x18\x01 \x01(\x03\x12\x0f\n\x07version\x18\x02 \x01(\x05\x12\x0e\n\x06shared\x18\x03 \x01(\x08\x12\x1b\n\x13\x65ncryptedRecordData\x18\x04 \x01(\t\x12\x1a\n\x12\x65ncryptedExtraData\x18\x05 \x01(\t\x12\x1a\n\x12\x63lientModifiedTime\x18\x06 \x01(\x03\x12\x15\n\rnonSharedData\x18\x07 \x01(\t\x12-\n\x10linkedRecordData\x18\x08 \x03(\x0b\x32\x13.Records.RecordData\x12\x0e\n\x06\x66ileId\x18\t \x03(\x0c\x12\x10\n\x08\x66ileSize\x18\n \x01(\x03\x12\x15\n\rthumbnailSize\x18\x0b \x01(\x03\x12-\n\rrecordKeyType\x18\x0c \x01(\x0e\x32\x16.Records.RecordKeyType\x12\x11\n\trecordKey\x18\r \x01(\x0c\x12\x11\n\trecordUid\x18\x0e \x01(\x0c\"\xc8\x01\n\x18RecordDataWithAccessInfo\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\'\n\nrecordData\x18\x02 \x01(\x0b\x32\x13.Records.RecordData\x12/\n\x0euserPermission\x18\x03 \x03(\x0b\x32\x17.Records.UserPermission\x12?\n\x16sharedFolderPermission\x18\x04 \x03(\x0b\x32\x1f.Records.SharedFolderPermission\"\x89\x01\n#GetRecordDataWithAccessInfoResponse\x12\x43\n\x18recordDataWithAccessInfo\x18\x01 \x03(\x0b\x32!.Records.RecordDataWithAccessInfo\x12\x1d\n\x15noPermissionRecordUid\x18\x02 \x03(\x0c\"j\n\x12IsObjectShareAdmin\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0f\n\x07isAdmin\x18\x02 \x01(\x08\x12\x36\n\nobjectType\x18\x03 \x01(\x0e\x32\".Records.CheckShareAdminObjectType\"H\n\rAmIShareAdmin\x12\x37\n\x12isObjectShareAdmin\x18\x01 \x03(\x0b\x32\x1b.Records.IsObjectShareAdmin\"\xbc\x01\n\x18RecordShareUpdateRequest\x12.\n\x0f\x61\x64\x64SharedRecord\x18\x01 \x03(\x0b\x32\x15.Records.SharedRecord\x12\x31\n\x12updateSharedRecord\x18\x02 \x03(\x0b\x32\x15.Records.SharedRecord\x12\x31\n\x12removeSharedRecord\x18\x03 \x03(\x0b\x32\x15.Records.SharedRecord\x12\n\n\x02pt\x18\x04 \x01(\t\"\xc4\x02\n\x0cSharedRecord\x12\x12\n\ntoUsername\x18\x01 \x01(\t\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x11\n\trecordKey\x18\x03 \x01(\x0c\x12\x17\n\x0fsharedFolderUid\x18\x04 \x01(\x0c\x12\x0f\n\x07teamUid\x18\x05 \x01(\x0c\x12\x10\n\x08\x65\x64itable\x18\x06 \x01(\x08\x12\x11\n\tshareable\x18\x07 \x01(\x08\x12\x10\n\x08transfer\x18\x08 \x01(\x08\x12\x11\n\tuseEccKey\x18\t \x01(\x08\x12\x17\n\x0fremoveVaultData\x18\n \x01(\x08\x12\x12\n\nexpiration\x18\x0b \x01(\x03\x12=\n\x15timerNotificationType\x18\x0c \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x1a\n\x12rotateOnExpiration\x18\r \x01(\x08\"\xd5\x01\n\x19RecordShareUpdateResponse\x12:\n\x15\x61\x64\x64SharedRecordStatus\x18\x01 \x03(\x0b\x32\x1b.Records.SharedRecordStatus\x12=\n\x18updateSharedRecordStatus\x18\x02 \x03(\x0b\x32\x1b.Records.SharedRecordStatus\x12=\n\x18removeSharedRecordStatus\x18\x03 \x03(\x0b\x32\x1b.Records.SharedRecordStatus\"Z\n\x12SharedRecordStatus\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x0e\n\x06status\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x10\n\x08username\x18\x04 \x01(\t\"G\n\x1bGetRecordPermissionsRequest\x12\x12\n\nrecordUids\x18\x01 \x03(\x0c\x12\x14\n\x0cisShareAdmin\x18\x02 \x01(\x08\"T\n\x1cGetRecordPermissionsResponse\x12\x34\n\x11recordPermissions\x18\x01 \x03(\x0b\x32\x19.Records.RecordPermission\"l\n\x10RecordPermission\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\r\n\x05owner\x18\x02 \x01(\x08\x12\x0f\n\x07\x63\x61nEdit\x18\x03 \x01(\x08\x12\x10\n\x08\x63\x61nShare\x18\x04 \x01(\x08\x12\x13\n\x0b\x63\x61nTransfer\x18\x05 \x01(\x08\"h\n\x16GetShareObjectsRequest\x12\x11\n\tstartWith\x18\x01 \x01(\t\x12\x10\n\x08\x63ontains\x18\x02 \x01(\t\x12\x10\n\x08\x66iltered\x18\x03 \x01(\x08\x12\x17\n\x0fsharedFolderUid\x18\x04 \x01(\x0c\"\xe7\x02\n\x17GetShareObjectsResponse\x12.\n\x12shareRelationships\x18\x01 \x03(\x0b\x32\x12.Records.ShareUser\x12,\n\x10shareFamilyUsers\x18\x02 \x03(\x0b\x32\x12.Records.ShareUser\x12\x30\n\x14shareEnterpriseUsers\x18\x03 \x03(\x0b\x32\x12.Records.ShareUser\x12&\n\nshareTeams\x18\x04 \x03(\x0b\x32\x12.Records.ShareTeam\x12(\n\x0cshareMCTeams\x18\x05 \x03(\x0b\x32\x12.Records.ShareTeam\x12\x32\n\x16shareMCEnterpriseUsers\x18\x06 \x03(\x0b\x32\x12.Records.ShareUser\x12\x36\n\x14shareEnterpriseNames\x18\x07 \x03(\x0b\x32\x18.Records.ShareEnterprise\"\xbd\x01\n\tShareUser\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08\x66ullname\x18\x02 \x01(\t\x12\x14\n\x0c\x65nterpriseId\x18\x03 \x01(\x05\x12$\n\x06status\x18\x04 \x01(\x0e\x32\x14.Records.ShareStatus\x12\x14\n\x0cisShareAdmin\x18\x05 \x01(\x08\x12\"\n\x1aisAdminOfSharedFolderOwner\x18\x06 \x01(\x08\x12\x16\n\x0euserAccountUid\x18\x07 \x01(\x0c\"D\n\tShareTeam\x12\x10\n\x08teamname\x18\x01 \x01(\t\x12\x14\n\x0c\x65nterpriseId\x18\x02 \x01(\x05\x12\x0f\n\x07teamUid\x18\x03 \x01(\x0c\"?\n\x0fShareEnterprise\x12\x16\n\x0e\x65nterprisename\x18\x01 \x01(\t\x12\x14\n\x0c\x65nterpriseId\x18\x02 \x01(\x05\"S\n\x1fRecordsOnwershipTransferRequest\x12\x30\n\x0ftransferRecords\x18\x01 \x03(\x0b\x32\x17.Records.TransferRecord\"[\n\x0eTransferRecord\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x11\n\trecordKey\x18\x03 \x01(\x0c\x12\x11\n\tuseEccKey\x18\x04 \x01(\x08\"_\n RecordsOnwershipTransferResponse\x12;\n\x14transferRecordStatus\x18\x01 \x03(\x0b\x32\x1d.Records.TransferRecordStatus\"\\\n\x14TransferRecordStatus\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\"y\n\x15RecordsUnshareRequest\x12\x34\n\rsharedFolders\x18\x01 \x03(\x0b\x32\x1d.Records.RecordsUnshareFolder\x12*\n\x05users\x18\x02 \x03(\x0b\x32\x1b.Records.RecordsUnshareUser\"\x86\x01\n\x16RecordsUnshareResponse\x12:\n\rsharedFolders\x18\x01 \x03(\x0b\x32#.Records.RecordsUnshareFolderStatus\x12\x30\n\x05users\x18\x02 \x03(\x0b\x32!.Records.RecordsUnshareUserStatus\"B\n\x14RecordsUnshareFolder\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x17\n\x0fsharedFolderUid\x18\x02 \x01(\x0c\";\n\x12RecordsUnshareUser\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x12\n\naccountUid\x18\x02 \x01(\x0c\"H\n\x1aRecordsUnshareFolderStatus\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x17\n\x0fsharedFolderUid\x18\x02 \x01(\x0c\"A\n\x18RecordsUnshareUserStatus\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x12\n\naccountUid\x18\x02 \x01(\x0c\"[\n\x1aTimedAccessCallbackPayload\x12=\n\x15timeLimitedAccessType\x18\x01 \x01(\x0e\x32\x1e.Records.TimeLimitedAccessType\"\xfd\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=\n\x15timeLimitedAccessType\x18\x05 \x01(\x0e\x32\x1e.Records.TimeLimitedAccessType\x12\x12\n\nexpiration\x18\x06 \x01(\x03\x12=\n\x15timerNotificationType\x18\x07 \x01(\x0e\x32\x1e.Records.TimerNotificationType\"7\n\x17TimeLimitedAccessStatus\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xe3\x01\n\x19TimeLimitedAccessResponse\x12\x10\n\x08revision\x18\x01 \x01(\x03\x12:\n\x10userAccessStatus\x18\x02 \x03(\x0b\x32 .Records.TimeLimitedAccessStatus\x12:\n\x10teamAccessStatus\x18\x03 \x03(\x0b\x32 .Records.TimeLimitedAccessStatus\x12<\n\x12recordAccessStatus\x18\x04 \x03(\x0b\x32 .Records.TimeLimitedAccessStatus*h\n\x0fRecordTypeScope\x12\x0f\n\x0bRT_STANDARD\x10\x00\x12\x0b\n\x07RT_USER\x10\x01\x12\x11\n\rRT_ENTERPRISE\x10\x02\x12\n\n\x06RT_PAM\x10\x03\x12\x18\n\x14RT_PAM_CONFIGURATION\x10\x04*\xd1\x01\n\rRecordKeyType\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\x12\x1d\n\x19\x45NCRYPTED_BY_ROOT_KEY_CBC\x10\x05\x12\x1d\n\x19\x45NCRYPTED_BY_ROOT_KEY_GCM\x10\x06*P\n\x10RecordFolderType\x12\x0f\n\x0buser_folder\x10\x00\x12\x11\n\rshared_folder\x10\x01\x12\x18\n\x14shared_folder_folder\x10\x02*\xec\x02\n\x12RecordModifyResult\x12\x0e\n\nRS_SUCCESS\x10\x00\x12\x12\n\x0eRS_OUT_OF_SYNC\x10\x01\x12\x14\n\x10RS_ACCESS_DENIED\x10\x02\x12\x13\n\x0fRS_SHARE_DENIED\x10\x03\x12\x14\n\x10RS_RECORD_EXISTS\x10\x04\x12\x1e\n\x1aRS_OLD_RECORD_VERSION_TYPE\x10\x05\x12\x1e\n\x1aRS_NEW_RECORD_VERSION_TYPE\x10\x06\x12\x16\n\x12RS_FILES_NOT_MATCH\x10\x07\x12\x1b\n\x17RS_RECORD_NOT_SHAREABLE\x10\x08\x12\x1f\n\x1bRS_ATTACHMENT_NOT_SHAREABLE\x10\t\x12\x19\n\x15RS_FILE_LIMIT_REACHED\x10\n\x12\x1a\n\x16RS_SIZE_EXCEEDED_LIMIT\x10\x0b\x12$\n RS_ONLY_OWNER_CAN_MODIFY_SCRIPTS\x10\x0c*-\n\rFileAddResult\x12\x0e\n\nFA_SUCCESS\x10\x00\x12\x0c\n\x08\x46\x41_ERROR\x10\x01*C\n\rFileGetResult\x12\x0e\n\nFG_SUCCESS\x10\x00\x12\x0c\n\x08\x46G_ERROR\x10\x01\x12\x14\n\x10\x46G_ACCESS_DENIED\x10\x02*J\n\x14RecordDetailsInclude\x12\x13\n\x0f\x44\x41TA_PLUS_SHARE\x10\x00\x12\r\n\tDATA_ONLY\x10\x01\x12\x0e\n\nSHARE_ONLY\x10\x02*b\n\x19\x43heckShareAdminObjectType\x12\x19\n\x15\x43HECK_SA_INVALID_TYPE\x10\x00\x12\x12\n\x0e\x43HECK_SA_ON_SF\x10\x01\x12\x16\n\x12\x43HECK_SA_ON_RECORD\x10\x02*1\n\x0bShareStatus\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\t\n\x05\x42LOCK\x10\x01\x12\x0b\n\x07INVITED\x10\x02*:\n\x15RecordTransactionType\x12\x0f\n\x0bRTT_GENERAL\x10\x00\x12\x10\n\x0cRTT_ROTATION\x10\x01*\xdc\x02\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\x12\x1f\n\x1bUSER_ACCESS_TO_SHAREDFOLDER\x10\x04\x12\x1f\n\x1bTEAM_ACCESS_TO_SHAREDFOLDER\x10\x05\x12\x1b\n\x17RECORD_ACCESS_TO_FOLDER\x10\x06\x12\x19\n\x15USER_ACCESS_TO_FOLDER\x10\x07\x12\x19\n\x15TEAM_ACCESS_TO_FOLDER\x10\x08\x12!\n\x1dUSER_OR_TEAM_ACCESS_TO_FOLDER\x10\t*\\\n\x15TimerNotificationType\x12\x14\n\x10NOTIFICATION_OFF\x10\x00\x12\x10\n\x0cNOTIFY_OWNER\x10\x01\x12\x1b\n\x17NOTIFY_PRIVILEGED_USERS\x10\x02\x42#\n\x18\x63om.keepersecurity.protoB\x07Recordsb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0crecord.proto\x12\x07Records\"\\\n\nRecordType\x12\x14\n\x0crecordTypeId\x18\x01 \x01(\x05\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\'\n\x05scope\x18\x03 \x01(\x0e\x32\x18.Records.RecordTypeScope\"U\n\x12RecordTypesRequest\x12\x10\n\x08standard\x18\x01 \x01(\x08\x12\x0c\n\x04user\x18\x02 \x01(\x08\x12\x12\n\nenterprise\x18\x03 \x01(\x08\x12\x0b\n\x03pam\x18\x04 \x01(\x08\"\x9c\x01\n\x13RecordTypesResponse\x12(\n\x0brecordTypes\x18\x01 \x03(\x0b\x32\x13.Records.RecordType\x12\x17\n\x0fstandardCounter\x18\x02 \x01(\x05\x12\x13\n\x0buserCounter\x18\x03 \x01(\x05\x12\x19\n\x11\x65nterpriseCounter\x18\x04 \x01(\x05\x12\x12\n\npamCounter\x18\x05 \x01(\x05\"A\n\x18RecordTypeModifyResponse\x12\x14\n\x0crecordTypeId\x18\x01 \x01(\x05\x12\x0f\n\x07\x63ounter\x18\x02 \x01(\x05\"=\n\x11RecordsGetRequest\x12\x13\n\x0brecord_uids\x18\x01 \x03(\x0c\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\"\xd1\x01\n\x06Record\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\x12/\n\x0frecord_key_type\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\r\n\x05\x65xtra\x18\x05 \x01(\x0c\x12\x0f\n\x07version\x18\x06 \x01(\x05\x12\x1c\n\x14\x63lient_modified_time\x18\x07 \x01(\x03\x12\x10\n\x08revision\x18\x08 \x01(\x03\x12\x10\n\x08\x66ile_ids\x18\t \x03(\x0c\"~\n\x0f\x46olderRecordKey\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_uid\x18\x02 \x01(\x0c\x12\x12\n\nrecord_key\x18\x03 \x01(\x0c\x12/\n\x0frecord_key_type\x18\x04 \x01(\x0e\x32\x16.Records.RecordKeyType\"a\n\x06\x46older\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x12\n\nfolder_key\x18\x02 \x01(\x0c\x12/\n\x0f\x66older_key_type\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\"\x95\x01\n\x04Team\x12\x10\n\x08team_uid\x18\x01 \x01(\x0c\x12\x10\n\x08team_key\x18\x02 \x01(\x0c\x12\x18\n\x10team_private_key\x18\x03 \x01(\x0c\x12-\n\rteam_key_type\x18\x04 \x01(\x0e\x32\x16.Records.RecordKeyType\x12 \n\x07\x66olders\x18\x05 \x03(\x0b\x32\x0f.Records.Folder\"\xac\x01\n\x12RecordsGetResponse\x12 \n\x07records\x18\x01 \x03(\x0b\x32\x0f.Records.Record\x12\x34\n\x12\x66older_record_keys\x18\x02 \x03(\x0b\x32\x18.Records.FolderRecordKey\x12 \n\x07\x66olders\x18\x03 \x03(\x0b\x32\x0f.Records.Folder\x12\x1c\n\x05teams\x18\x04 \x03(\x0b\x32\r.Records.Team\"4\n\nRecordLink\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\",\n\x0bRecordAudit\x12\x0f\n\x07version\x18\x01 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"\x1c\n\x0cSecurityData\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"!\n\x11SecurityScoreData\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x84\x03\n\tRecordAdd\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\x12\x1c\n\x14\x63lient_modified_time\x18\x03 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x17\n\x0fnon_shared_data\x18\x05 \x01(\x0c\x12.\n\x0b\x66older_type\x18\x06 \x01(\x0e\x32\x19.Records.RecordFolderType\x12\x12\n\nfolder_uid\x18\x07 \x01(\x0c\x12\x12\n\nfolder_key\x18\x08 \x01(\x0c\x12)\n\x0crecord_links\x18\t \x03(\x0b\x32\x13.Records.RecordLink\x12#\n\x05\x61udit\x18\n \x01(\x0b\x32\x14.Records.RecordAudit\x12+\n\x0csecurityData\x18\x0b \x01(\x0b\x32\x15.Records.SecurityData\x12\x35\n\x11securityScoreData\x18\x0c \x01(\x0b\x32\x1a.Records.SecurityScoreData\"\x85\x01\n\x11RecordsAddRequest\x12#\n\x07records\x18\x01 \x03(\x0b\x32\x12.Records.RecordAdd\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\x12\x36\n\x16security_data_key_type\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\"\xce\x02\n\x0cRecordUpdate\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x1c\n\x14\x63lient_modified_time\x18\x02 \x01(\x03\x12\x10\n\x08revision\x18\x03 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x17\n\x0fnon_shared_data\x18\x05 \x01(\x0c\x12-\n\x10record_links_add\x18\x06 \x03(\x0b\x32\x13.Records.RecordLink\x12\x1b\n\x13record_links_remove\x18\x07 \x03(\x0c\x12#\n\x05\x61udit\x18\x08 \x01(\x0b\x32\x14.Records.RecordAudit\x12+\n\x0csecurityData\x18\t \x01(\x0b\x32\x15.Records.SecurityData\x12\x35\n\x11securityScoreData\x18\n \x01(\x0b\x32\x1a.Records.SecurityScoreData\"\x8b\x01\n\x14RecordsUpdateRequest\x12&\n\x07records\x18\x01 \x03(\x0b\x32\x15.Records.RecordUpdate\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\x12\x36\n\x16security_data_key_type\x18\x03 \x01(\x0e\x32\x16.Records.RecordKeyType\"\x8e\x01\n\x17RecordFileForConversion\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x14\n\x0c\x66ile_file_id\x18\x02 \x01(\t\x12\x15\n\rthumb_file_id\x18\x03 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x12\n\nrecord_key\x18\x05 \x01(\x0c\x12\x10\n\x08link_key\x18\x06 \x01(\x0c\"J\n\x19RecordFolderForConversion\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x19\n\x11record_folder_key\x18\x02 \x01(\x0c\"\x92\x02\n\x11RecordConvertToV3\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x1c\n\x14\x63lient_modified_time\x18\x02 \x01(\x03\x12\x10\n\x08revision\x18\x03 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\x17\n\x0fnon_shared_data\x18\x05 \x01(\x0c\x12#\n\x05\x61udit\x18\x06 \x01(\x0b\x32\x14.Records.RecordAudit\x12\x35\n\x0brecord_file\x18\x07 \x03(\x0b\x32 .Records.RecordFileForConversion\x12\x36\n\nfolder_key\x18\x08 \x03(\x0b\x32\".Records.RecordFolderForConversion\"]\n\x19RecordsConvertToV3Request\x12+\n\x07records\x18\x01 \x03(\x0b\x32\x1a.Records.RecordConvertToV3\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\"\'\n\x14RecordsRemoveRequest\x12\x0f\n\x07records\x18\x01 \x03(\x0c\">\n\x0cRecordRevert\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x1a\n\x12revert_to_revision\x18\x02 \x01(\x03\">\n\x14RecordsRevertRequest\x12&\n\x07records\x18\x01 \x03(\x0b\x32\x15.Records.RecordRevert\"c\n\x0fRecordLinkError\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12+\n\x06status\x18\x02 \x01(\x0e\x32\x1b.Records.RecordModifyResult\x12\x0f\n\x07message\x18\x03 \x01(\t\"\x95\x01\n\x12RecordModifyStatus\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12+\n\x06status\x18\x02 \x01(\x0e\x32\x1b.Records.RecordModifyResult\x12\x0f\n\x07message\x18\x03 \x01(\t\x12-\n\x0blink_errors\x18\x04 \x03(\x0b\x32\x18.Records.RecordLinkError\"W\n\x15RecordsModifyResponse\x12,\n\x07records\x18\x01 \x03(\x0b\x32\x1b.Records.RecordModifyStatus\x12\x10\n\x08revision\x18\x02 \x01(\x03\"Y\n\x12RecordAddAuditData\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12\x0f\n\x07version\x18\x04 \x01(\x05\"C\n\x13\x41\x64\x64\x41uditDataRequest\x12,\n\x07records\x18\x01 \x03(\x0b\x32\x1b.Records.RecordAddAuditData\"t\n\x04\x46ile\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\x12\x10\n\x08\x66ileSize\x18\x04 \x01(\x03\x12\x11\n\tthumbSize\x18\x05 \x01(\x05\x12\x11\n\tis_script\x18\x06 \x01(\x08\"D\n\x0f\x46ilesAddRequest\x12\x1c\n\x05\x66iles\x18\x01 \x03(\x0b\x32\r.Records.File\x12\x13\n\x0b\x63lient_time\x18\x02 \x01(\x03\"\xa7\x01\n\rFileAddStatus\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12&\n\x06status\x18\x02 \x01(\x0e\x32\x16.Records.FileAddResult\x12\x0b\n\x03url\x18\x03 \x01(\t\x12\x12\n\nparameters\x18\x04 \x01(\t\x12\x1c\n\x14thumbnail_parameters\x18\x05 \x01(\t\x12\x1b\n\x13success_status_code\x18\x06 \x01(\x05\"K\n\x10\x46ilesAddResponse\x12%\n\x05\x66iles\x18\x01 \x03(\x0b\x32\x16.Records.FileAddStatus\x12\x10\n\x08revision\x18\x02 \x01(\x03\"f\n\x0f\x46ilesGetRequest\x12\x13\n\x0brecord_uids\x18\x01 \x03(\x0c\x12\x16\n\x0e\x66or_thumbnails\x18\x02 \x01(\x08\x12&\n\x1e\x65mergency_access_account_owner\x18\x03 \x01(\t\"\xa2\x01\n\rFileGetStatus\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12&\n\x06status\x18\x02 \x01(\x0e\x32\x16.Records.FileGetResult\x12\x0b\n\x03url\x18\x03 \x01(\t\x12\x1b\n\x13success_status_code\x18\x04 \x01(\x05\x12+\n\x0b\x66ileKeyType\x18\x05 \x01(\x0e\x32\x16.Records.RecordKeyType\"9\n\x10\x46ilesGetResponse\x12%\n\x05\x66iles\x18\x01 \x03(\x0b\x32\x16.Records.FileGetStatus\"\x8d\x01\n\x15\x41pplicationAddRequest\x12\x0f\n\x07\x61pp_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_key\x18\x02 \x01(\x0c\x12\x1c\n\x14\x63lient_modified_time\x18\x03 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12#\n\x05\x61udit\x18\x05 \x01(\x0b\x32\x14.Records.RecordAudit\"\x88\x01\n\"GetRecordDataWithAccessInfoRequest\x12\x12\n\nclientTime\x18\x01 \x01(\x03\x12\x11\n\trecordUid\x18\x02 \x03(\x0c\x12;\n\x14recordDetailsInclude\x18\x03 \x01(\x0e\x32\x1d.Records.RecordDetailsInclude\"\x86\x02\n\x0eUserPermission\x12\x10\n\x08username\x18\x01 \x01(\t\x12\r\n\x05owner\x18\x02 \x01(\x08\x12\x12\n\nshareAdmin\x18\x03 \x01(\x08\x12\x10\n\x08sharable\x18\x04 \x01(\x08\x12\x10\n\x08\x65\x64itable\x18\x05 \x01(\x08\x12\x18\n\x10\x61waitingApproval\x18\x06 \x01(\x08\x12\x12\n\nexpiration\x18\x07 \x01(\x03\x12\x12\n\naccountUid\x18\x08 \x01(\x0c\x12=\n\x15timerNotificationType\x18\t \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x1a\n\x12rotateOnExpiration\x18\n \x01(\x08\"\xd8\x01\n\x16SharedFolderPermission\x12\x17\n\x0fsharedFolderUid\x18\x01 \x01(\x0c\x12\x12\n\nresharable\x18\x02 \x01(\x08\x12\x10\n\x08\x65\x64itable\x18\x03 \x01(\x08\x12\x10\n\x08revision\x18\x04 \x01(\x03\x12\x12\n\nexpiration\x18\x05 \x01(\x03\x12=\n\x15timerNotificationType\x18\x06 \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x1a\n\x12rotateOnExpiration\x18\x07 \x01(\x08\"\xe8\x02\n\nRecordData\x12\x10\n\x08revision\x18\x01 \x01(\x03\x12\x0f\n\x07version\x18\x02 \x01(\x05\x12\x0e\n\x06shared\x18\x03 \x01(\x08\x12\x1b\n\x13\x65ncryptedRecordData\x18\x04 \x01(\t\x12\x1a\n\x12\x65ncryptedExtraData\x18\x05 \x01(\t\x12\x1a\n\x12\x63lientModifiedTime\x18\x06 \x01(\x03\x12\x15\n\rnonSharedData\x18\x07 \x01(\t\x12-\n\x10linkedRecordData\x18\x08 \x03(\x0b\x32\x13.Records.RecordData\x12\x0e\n\x06\x66ileId\x18\t \x03(\x0c\x12\x10\n\x08\x66ileSize\x18\n \x01(\x03\x12\x15\n\rthumbnailSize\x18\x0b \x01(\x03\x12-\n\rrecordKeyType\x18\x0c \x01(\x0e\x32\x16.Records.RecordKeyType\x12\x11\n\trecordKey\x18\r \x01(\x0c\x12\x11\n\trecordUid\x18\x0e \x01(\x0c\"\xc8\x01\n\x18RecordDataWithAccessInfo\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\'\n\nrecordData\x18\x02 \x01(\x0b\x32\x13.Records.RecordData\x12/\n\x0euserPermission\x18\x03 \x03(\x0b\x32\x17.Records.UserPermission\x12?\n\x16sharedFolderPermission\x18\x04 \x03(\x0b\x32\x1f.Records.SharedFolderPermission\"\x89\x01\n#GetRecordDataWithAccessInfoResponse\x12\x43\n\x18recordDataWithAccessInfo\x18\x01 \x03(\x0b\x32!.Records.RecordDataWithAccessInfo\x12\x1d\n\x15noPermissionRecordUid\x18\x02 \x03(\x0c\"j\n\x12IsObjectShareAdmin\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0f\n\x07isAdmin\x18\x02 \x01(\x08\x12\x36\n\nobjectType\x18\x03 \x01(\x0e\x32\".Records.CheckShareAdminObjectType\"H\n\rAmIShareAdmin\x12\x37\n\x12isObjectShareAdmin\x18\x01 \x03(\x0b\x32\x1b.Records.IsObjectShareAdmin\"\xbc\x01\n\x18RecordShareUpdateRequest\x12.\n\x0f\x61\x64\x64SharedRecord\x18\x01 \x03(\x0b\x32\x15.Records.SharedRecord\x12\x31\n\x12updateSharedRecord\x18\x02 \x03(\x0b\x32\x15.Records.SharedRecord\x12\x31\n\x12removeSharedRecord\x18\x03 \x03(\x0b\x32\x15.Records.SharedRecord\x12\n\n\x02pt\x18\x04 \x01(\t\"\xc4\x02\n\x0cSharedRecord\x12\x12\n\ntoUsername\x18\x01 \x01(\t\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x11\n\trecordKey\x18\x03 \x01(\x0c\x12\x17\n\x0fsharedFolderUid\x18\x04 \x01(\x0c\x12\x0f\n\x07teamUid\x18\x05 \x01(\x0c\x12\x10\n\x08\x65\x64itable\x18\x06 \x01(\x08\x12\x11\n\tshareable\x18\x07 \x01(\x08\x12\x10\n\x08transfer\x18\x08 \x01(\x08\x12\x11\n\tuseEccKey\x18\t \x01(\x08\x12\x17\n\x0fremoveVaultData\x18\n \x01(\x08\x12\x12\n\nexpiration\x18\x0b \x01(\x03\x12=\n\x15timerNotificationType\x18\x0c \x01(\x0e\x32\x1e.Records.TimerNotificationType\x12\x1a\n\x12rotateOnExpiration\x18\r \x01(\x08\"\xd5\x01\n\x19RecordShareUpdateResponse\x12:\n\x15\x61\x64\x64SharedRecordStatus\x18\x01 \x03(\x0b\x32\x1b.Records.SharedRecordStatus\x12=\n\x18updateSharedRecordStatus\x18\x02 \x03(\x0b\x32\x1b.Records.SharedRecordStatus\x12=\n\x18removeSharedRecordStatus\x18\x03 \x03(\x0b\x32\x1b.Records.SharedRecordStatus\"Z\n\x12SharedRecordStatus\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x0e\n\x06status\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x10\n\x08username\x18\x04 \x01(\t\"G\n\x1bGetRecordPermissionsRequest\x12\x12\n\nrecordUids\x18\x01 \x03(\x0c\x12\x14\n\x0cisShareAdmin\x18\x02 \x01(\x08\"T\n\x1cGetRecordPermissionsResponse\x12\x34\n\x11recordPermissions\x18\x01 \x03(\x0b\x32\x19.Records.RecordPermission\"l\n\x10RecordPermission\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\r\n\x05owner\x18\x02 \x01(\x08\x12\x0f\n\x07\x63\x61nEdit\x18\x03 \x01(\x08\x12\x10\n\x08\x63\x61nShare\x18\x04 \x01(\x08\x12\x13\n\x0b\x63\x61nTransfer\x18\x05 \x01(\x08\"h\n\x16GetShareObjectsRequest\x12\x11\n\tstartWith\x18\x01 \x01(\t\x12\x10\n\x08\x63ontains\x18\x02 \x01(\t\x12\x10\n\x08\x66iltered\x18\x03 \x01(\x08\x12\x17\n\x0fsharedFolderUid\x18\x04 \x01(\x0c\"\xe7\x02\n\x17GetShareObjectsResponse\x12.\n\x12shareRelationships\x18\x01 \x03(\x0b\x32\x12.Records.ShareUser\x12,\n\x10shareFamilyUsers\x18\x02 \x03(\x0b\x32\x12.Records.ShareUser\x12\x30\n\x14shareEnterpriseUsers\x18\x03 \x03(\x0b\x32\x12.Records.ShareUser\x12&\n\nshareTeams\x18\x04 \x03(\x0b\x32\x12.Records.ShareTeam\x12(\n\x0cshareMCTeams\x18\x05 \x03(\x0b\x32\x12.Records.ShareTeam\x12\x32\n\x16shareMCEnterpriseUsers\x18\x06 \x03(\x0b\x32\x12.Records.ShareUser\x12\x36\n\x14shareEnterpriseNames\x18\x07 \x03(\x0b\x32\x18.Records.ShareEnterprise\"\xbd\x01\n\tShareUser\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08\x66ullname\x18\x02 \x01(\t\x12\x14\n\x0c\x65nterpriseId\x18\x03 \x01(\x05\x12$\n\x06status\x18\x04 \x01(\x0e\x32\x14.Records.ShareStatus\x12\x14\n\x0cisShareAdmin\x18\x05 \x01(\x08\x12\"\n\x1aisAdminOfSharedFolderOwner\x18\x06 \x01(\x08\x12\x16\n\x0euserAccountUid\x18\x07 \x01(\x0c\"D\n\tShareTeam\x12\x10\n\x08teamname\x18\x01 \x01(\t\x12\x14\n\x0c\x65nterpriseId\x18\x02 \x01(\x05\x12\x0f\n\x07teamUid\x18\x03 \x01(\x0c\"?\n\x0fShareEnterprise\x12\x16\n\x0e\x65nterprisename\x18\x01 \x01(\t\x12\x14\n\x0c\x65nterpriseId\x18\x02 \x01(\x05\"S\n\x1fRecordsOnwershipTransferRequest\x12\x30\n\x0ftransferRecords\x18\x01 \x03(\x0b\x32\x17.Records.TransferRecord\"[\n\x0eTransferRecord\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x11\n\trecordKey\x18\x03 \x01(\x0c\x12\x11\n\tuseEccKey\x18\x04 \x01(\x08\"_\n RecordsOnwershipTransferResponse\x12;\n\x14transferRecordStatus\x18\x01 \x03(\x0b\x32\x1d.Records.TransferRecordStatus\"\\\n\x14TransferRecordStatus\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x11\n\trecordUid\x18\x02 \x01(\x0c\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x0f\n\x07message\x18\x04 \x01(\t\"y\n\x15RecordsUnshareRequest\x12\x34\n\rsharedFolders\x18\x01 \x03(\x0b\x32\x1d.Records.RecordsUnshareFolder\x12*\n\x05users\x18\x02 \x03(\x0b\x32\x1b.Records.RecordsUnshareUser\"\x86\x01\n\x16RecordsUnshareResponse\x12:\n\rsharedFolders\x18\x01 \x03(\x0b\x32#.Records.RecordsUnshareFolderStatus\x12\x30\n\x05users\x18\x02 \x03(\x0b\x32!.Records.RecordsUnshareUserStatus\"B\n\x14RecordsUnshareFolder\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x17\n\x0fsharedFolderUid\x18\x02 \x01(\x0c\";\n\x12RecordsUnshareUser\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x12\n\naccountUid\x18\x02 \x01(\x0c\"H\n\x1aRecordsUnshareFolderStatus\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x17\n\x0fsharedFolderUid\x18\x02 \x01(\x0c\"A\n\x18RecordsUnshareUserStatus\x12\x11\n\trecordUid\x18\x01 \x01(\x0c\x12\x12\n\naccountUid\x18\x02 \x01(\x0c\"[\n\x1aTimedAccessCallbackPayload\x12=\n\x15timeLimitedAccessType\x18\x01 \x01(\x0e\x32\x1e.Records.TimeLimitedAccessType\"\xfd\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=\n\x15timeLimitedAccessType\x18\x05 \x01(\x0e\x32\x1e.Records.TimeLimitedAccessType\x12\x12\n\nexpiration\x18\x06 \x01(\x03\x12=\n\x15timerNotificationType\x18\x07 \x01(\x0e\x32\x1e.Records.TimerNotificationType\"7\n\x17TimeLimitedAccessStatus\x12\x0b\n\x03uid\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xe3\x01\n\x19TimeLimitedAccessResponse\x12\x10\n\x08revision\x18\x01 \x01(\x03\x12:\n\x10userAccessStatus\x18\x02 \x03(\x0b\x32 .Records.TimeLimitedAccessStatus\x12:\n\x10teamAccessStatus\x18\x03 \x03(\x0b\x32 .Records.TimeLimitedAccessStatus\x12<\n\x12recordAccessStatus\x18\x04 \x03(\x0b\x32 .Records.TimeLimitedAccessStatus*h\n\x0fRecordTypeScope\x12\x0f\n\x0bRT_STANDARD\x10\x00\x12\x0b\n\x07RT_USER\x10\x01\x12\x11\n\rRT_ENTERPRISE\x10\x02\x12\n\n\x06RT_PAM\x10\x03\x12\x18\n\x14RT_PAM_CONFIGURATION\x10\x04*\xd1\x01\n\rRecordKeyType\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\x12\x1d\n\x19\x45NCRYPTED_BY_ROOT_KEY_CBC\x10\x05\x12\x1d\n\x19\x45NCRYPTED_BY_ROOT_KEY_GCM\x10\x06*P\n\x10RecordFolderType\x12\x0f\n\x0buser_folder\x10\x00\x12\x11\n\rshared_folder\x10\x01\x12\x18\n\x14shared_folder_folder\x10\x02*\xec\x02\n\x12RecordModifyResult\x12\x0e\n\nRS_SUCCESS\x10\x00\x12\x12\n\x0eRS_OUT_OF_SYNC\x10\x01\x12\x14\n\x10RS_ACCESS_DENIED\x10\x02\x12\x13\n\x0fRS_SHARE_DENIED\x10\x03\x12\x14\n\x10RS_RECORD_EXISTS\x10\x04\x12\x1e\n\x1aRS_OLD_RECORD_VERSION_TYPE\x10\x05\x12\x1e\n\x1aRS_NEW_RECORD_VERSION_TYPE\x10\x06\x12\x16\n\x12RS_FILES_NOT_MATCH\x10\x07\x12\x1b\n\x17RS_RECORD_NOT_SHAREABLE\x10\x08\x12\x1f\n\x1bRS_ATTACHMENT_NOT_SHAREABLE\x10\t\x12\x19\n\x15RS_FILE_LIMIT_REACHED\x10\n\x12\x1a\n\x16RS_SIZE_EXCEEDED_LIMIT\x10\x0b\x12$\n RS_ONLY_OWNER_CAN_MODIFY_SCRIPTS\x10\x0c*-\n\rFileAddResult\x12\x0e\n\nFA_SUCCESS\x10\x00\x12\x0c\n\x08\x46\x41_ERROR\x10\x01*C\n\rFileGetResult\x12\x0e\n\nFG_SUCCESS\x10\x00\x12\x0c\n\x08\x46G_ERROR\x10\x01\x12\x14\n\x10\x46G_ACCESS_DENIED\x10\x02*J\n\x14RecordDetailsInclude\x12\x13\n\x0f\x44\x41TA_PLUS_SHARE\x10\x00\x12\r\n\tDATA_ONLY\x10\x01\x12\x0e\n\nSHARE_ONLY\x10\x02*b\n\x19\x43heckShareAdminObjectType\x12\x19\n\x15\x43HECK_SA_INVALID_TYPE\x10\x00\x12\x12\n\x0e\x43HECK_SA_ON_SF\x10\x01\x12\x16\n\x12\x43HECK_SA_ON_RECORD\x10\x02*1\n\x0bShareStatus\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\t\n\x05\x42LOCK\x10\x01\x12\x0b\n\x07INVITED\x10\x02*:\n\x15RecordTransactionType\x12\x0f\n\x0bRTT_GENERAL\x10\x00\x12\x10\n\x0cRTT_ROTATION\x10\x01*\xdc\x02\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\x12\x1f\n\x1bUSER_ACCESS_TO_SHAREDFOLDER\x10\x04\x12\x1f\n\x1bTEAM_ACCESS_TO_SHAREDFOLDER\x10\x05\x12\x1b\n\x17RECORD_ACCESS_TO_FOLDER\x10\x06\x12\x19\n\x15USER_ACCESS_TO_FOLDER\x10\x07\x12\x19\n\x15TEAM_ACCESS_TO_FOLDER\x10\x08\x12!\n\x1dUSER_OR_TEAM_ACCESS_TO_FOLDER\x10\t*\\\n\x15TimerNotificationType\x12\x14\n\x10NOTIFICATION_OFF\x10\x00\x12\x10\n\x0cNOTIFY_OWNER\x10\x01\x12\x1b\n\x17NOTIFY_PRIVILEGED_USERS\x10\x02\x42#\n\x18\x63om.keepersecurity.protoB\x07Recordsb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -32,30 +32,30 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\030com.keepersecurity.protoB\007Records' - _globals['_RECORDTYPESCOPE']._serialized_start=9490 - _globals['_RECORDTYPESCOPE']._serialized_end=9594 - _globals['_RECORDKEYTYPE']._serialized_start=9597 - _globals['_RECORDKEYTYPE']._serialized_end=9806 - _globals['_RECORDFOLDERTYPE']._serialized_start=9808 - _globals['_RECORDFOLDERTYPE']._serialized_end=9888 - _globals['_RECORDMODIFYRESULT']._serialized_start=9891 - _globals['_RECORDMODIFYRESULT']._serialized_end=10255 - _globals['_FILEADDRESULT']._serialized_start=10257 - _globals['_FILEADDRESULT']._serialized_end=10302 - _globals['_FILEGETRESULT']._serialized_start=10304 - _globals['_FILEGETRESULT']._serialized_end=10371 - _globals['_RECORDDETAILSINCLUDE']._serialized_start=10373 - _globals['_RECORDDETAILSINCLUDE']._serialized_end=10447 - _globals['_CHECKSHAREADMINOBJECTTYPE']._serialized_start=10449 - _globals['_CHECKSHAREADMINOBJECTTYPE']._serialized_end=10547 - _globals['_SHARESTATUS']._serialized_start=10549 - _globals['_SHARESTATUS']._serialized_end=10598 - _globals['_RECORDTRANSACTIONTYPE']._serialized_start=10600 - _globals['_RECORDTRANSACTIONTYPE']._serialized_end=10658 - _globals['_TIMELIMITEDACCESSTYPE']._serialized_start=10661 - _globals['_TIMELIMITEDACCESSTYPE']._serialized_end=11009 - _globals['_TIMERNOTIFICATIONTYPE']._serialized_start=11011 - _globals['_TIMERNOTIFICATIONTYPE']._serialized_end=11103 + _globals['_RECORDTYPESCOPE']._serialized_start=9539 + _globals['_RECORDTYPESCOPE']._serialized_end=9643 + _globals['_RECORDKEYTYPE']._serialized_start=9646 + _globals['_RECORDKEYTYPE']._serialized_end=9855 + _globals['_RECORDFOLDERTYPE']._serialized_start=9857 + _globals['_RECORDFOLDERTYPE']._serialized_end=9937 + _globals['_RECORDMODIFYRESULT']._serialized_start=9940 + _globals['_RECORDMODIFYRESULT']._serialized_end=10304 + _globals['_FILEADDRESULT']._serialized_start=10306 + _globals['_FILEADDRESULT']._serialized_end=10351 + _globals['_FILEGETRESULT']._serialized_start=10353 + _globals['_FILEGETRESULT']._serialized_end=10420 + _globals['_RECORDDETAILSINCLUDE']._serialized_start=10422 + _globals['_RECORDDETAILSINCLUDE']._serialized_end=10496 + _globals['_CHECKSHAREADMINOBJECTTYPE']._serialized_start=10498 + _globals['_CHECKSHAREADMINOBJECTTYPE']._serialized_end=10596 + _globals['_SHARESTATUS']._serialized_start=10598 + _globals['_SHARESTATUS']._serialized_end=10647 + _globals['_RECORDTRANSACTIONTYPE']._serialized_start=10649 + _globals['_RECORDTRANSACTIONTYPE']._serialized_end=10707 + _globals['_TIMELIMITEDACCESSTYPE']._serialized_start=10710 + _globals['_TIMELIMITEDACCESSTYPE']._serialized_end=11058 + _globals['_TIMERNOTIFICATIONTYPE']._serialized_start=11060 + _globals['_TIMERNOTIFICATIONTYPE']._serialized_end=11152 _globals['_RECORDTYPE']._serialized_start=25 _globals['_RECORDTYPE']._serialized_end=117 _globals['_RECORDTYPESREQUEST']._serialized_start=119 @@ -69,135 +69,135 @@ _globals['_RECORD']._serialized_start=496 _globals['_RECORD']._serialized_end=705 _globals['_FOLDERRECORDKEY']._serialized_start=707 - _globals['_FOLDERRECORDKEY']._serialized_end=784 - _globals['_FOLDER']._serialized_start=786 - _globals['_FOLDER']._serialized_end=883 - _globals['_TEAM']._serialized_start=886 - _globals['_TEAM']._serialized_end=1035 - _globals['_RECORDSGETRESPONSE']._serialized_start=1038 - _globals['_RECORDSGETRESPONSE']._serialized_end=1210 - _globals['_RECORDLINK']._serialized_start=1212 - _globals['_RECORDLINK']._serialized_end=1264 - _globals['_RECORDAUDIT']._serialized_start=1266 - _globals['_RECORDAUDIT']._serialized_end=1310 - _globals['_SECURITYDATA']._serialized_start=1312 - _globals['_SECURITYDATA']._serialized_end=1340 - _globals['_SECURITYSCOREDATA']._serialized_start=1342 - _globals['_SECURITYSCOREDATA']._serialized_end=1375 - _globals['_RECORDADD']._serialized_start=1378 - _globals['_RECORDADD']._serialized_end=1766 - _globals['_RECORDSADDREQUEST']._serialized_start=1769 - _globals['_RECORDSADDREQUEST']._serialized_end=1902 - _globals['_RECORDUPDATE']._serialized_start=1905 - _globals['_RECORDUPDATE']._serialized_end=2239 - _globals['_RECORDSUPDATEREQUEST']._serialized_start=2242 - _globals['_RECORDSUPDATEREQUEST']._serialized_end=2381 - _globals['_RECORDFILEFORCONVERSION']._serialized_start=2384 - _globals['_RECORDFILEFORCONVERSION']._serialized_end=2526 - _globals['_RECORDFOLDERFORCONVERSION']._serialized_start=2528 - _globals['_RECORDFOLDERFORCONVERSION']._serialized_end=2602 - _globals['_RECORDCONVERTTOV3']._serialized_start=2605 - _globals['_RECORDCONVERTTOV3']._serialized_end=2879 - _globals['_RECORDSCONVERTTOV3REQUEST']._serialized_start=2881 - _globals['_RECORDSCONVERTTOV3REQUEST']._serialized_end=2974 - _globals['_RECORDSREMOVEREQUEST']._serialized_start=2976 - _globals['_RECORDSREMOVEREQUEST']._serialized_end=3015 - _globals['_RECORDREVERT']._serialized_start=3017 - _globals['_RECORDREVERT']._serialized_end=3079 - _globals['_RECORDSREVERTREQUEST']._serialized_start=3081 - _globals['_RECORDSREVERTREQUEST']._serialized_end=3143 - _globals['_RECORDLINKERROR']._serialized_start=3145 - _globals['_RECORDLINKERROR']._serialized_end=3244 - _globals['_RECORDMODIFYSTATUS']._serialized_start=3247 - _globals['_RECORDMODIFYSTATUS']._serialized_end=3396 - _globals['_RECORDSMODIFYRESPONSE']._serialized_start=3398 - _globals['_RECORDSMODIFYRESPONSE']._serialized_end=3485 - _globals['_RECORDADDAUDITDATA']._serialized_start=3487 - _globals['_RECORDADDAUDITDATA']._serialized_end=3576 - _globals['_ADDAUDITDATAREQUEST']._serialized_start=3578 - _globals['_ADDAUDITDATAREQUEST']._serialized_end=3645 - _globals['_FILE']._serialized_start=3647 - _globals['_FILE']._serialized_end=3763 - _globals['_FILESADDREQUEST']._serialized_start=3765 - _globals['_FILESADDREQUEST']._serialized_end=3833 - _globals['_FILEADDSTATUS']._serialized_start=3836 - _globals['_FILEADDSTATUS']._serialized_end=4003 - _globals['_FILESADDRESPONSE']._serialized_start=4005 - _globals['_FILESADDRESPONSE']._serialized_end=4080 - _globals['_FILESGETREQUEST']._serialized_start=4082 - _globals['_FILESGETREQUEST']._serialized_end=4184 - _globals['_FILEGETSTATUS']._serialized_start=4187 - _globals['_FILEGETSTATUS']._serialized_end=4349 - _globals['_FILESGETRESPONSE']._serialized_start=4351 - _globals['_FILESGETRESPONSE']._serialized_end=4408 - _globals['_APPLICATIONADDREQUEST']._serialized_start=4411 - _globals['_APPLICATIONADDREQUEST']._serialized_end=4552 - _globals['_GETRECORDDATAWITHACCESSINFOREQUEST']._serialized_start=4555 - _globals['_GETRECORDDATAWITHACCESSINFOREQUEST']._serialized_end=4691 - _globals['_USERPERMISSION']._serialized_start=4694 - _globals['_USERPERMISSION']._serialized_end=4956 - _globals['_SHAREDFOLDERPERMISSION']._serialized_start=4959 - _globals['_SHAREDFOLDERPERMISSION']._serialized_end=5175 - _globals['_RECORDDATA']._serialized_start=5178 - _globals['_RECORDDATA']._serialized_end=5538 - _globals['_RECORDDATAWITHACCESSINFO']._serialized_start=5541 - _globals['_RECORDDATAWITHACCESSINFO']._serialized_end=5741 - _globals['_GETRECORDDATAWITHACCESSINFORESPONSE']._serialized_start=5744 - _globals['_GETRECORDDATAWITHACCESSINFORESPONSE']._serialized_end=5881 - _globals['_ISOBJECTSHAREADMIN']._serialized_start=5883 - _globals['_ISOBJECTSHAREADMIN']._serialized_end=5989 - _globals['_AMISHAREADMIN']._serialized_start=5991 - _globals['_AMISHAREADMIN']._serialized_end=6063 - _globals['_RECORDSHAREUPDATEREQUEST']._serialized_start=6066 - _globals['_RECORDSHAREUPDATEREQUEST']._serialized_end=6254 - _globals['_SHAREDRECORD']._serialized_start=6257 - _globals['_SHAREDRECORD']._serialized_end=6581 - _globals['_RECORDSHAREUPDATERESPONSE']._serialized_start=6584 - _globals['_RECORDSHAREUPDATERESPONSE']._serialized_end=6797 - _globals['_SHAREDRECORDSTATUS']._serialized_start=6799 - _globals['_SHAREDRECORDSTATUS']._serialized_end=6889 - _globals['_GETRECORDPERMISSIONSREQUEST']._serialized_start=6891 - _globals['_GETRECORDPERMISSIONSREQUEST']._serialized_end=6962 - _globals['_GETRECORDPERMISSIONSRESPONSE']._serialized_start=6964 - _globals['_GETRECORDPERMISSIONSRESPONSE']._serialized_end=7048 - _globals['_RECORDPERMISSION']._serialized_start=7050 - _globals['_RECORDPERMISSION']._serialized_end=7158 - _globals['_GETSHAREOBJECTSREQUEST']._serialized_start=7160 - _globals['_GETSHAREOBJECTSREQUEST']._serialized_end=7264 - _globals['_GETSHAREOBJECTSRESPONSE']._serialized_start=7267 - _globals['_GETSHAREOBJECTSRESPONSE']._serialized_end=7626 - _globals['_SHAREUSER']._serialized_start=7629 - _globals['_SHAREUSER']._serialized_end=7818 - _globals['_SHARETEAM']._serialized_start=7820 - _globals['_SHARETEAM']._serialized_end=7888 - _globals['_SHAREENTERPRISE']._serialized_start=7890 - _globals['_SHAREENTERPRISE']._serialized_end=7953 - _globals['_RECORDSONWERSHIPTRANSFERREQUEST']._serialized_start=7955 - _globals['_RECORDSONWERSHIPTRANSFERREQUEST']._serialized_end=8038 - _globals['_TRANSFERRECORD']._serialized_start=8040 - _globals['_TRANSFERRECORD']._serialized_end=8131 - _globals['_RECORDSONWERSHIPTRANSFERRESPONSE']._serialized_start=8133 - _globals['_RECORDSONWERSHIPTRANSFERRESPONSE']._serialized_end=8228 - _globals['_TRANSFERRECORDSTATUS']._serialized_start=8230 - _globals['_TRANSFERRECORDSTATUS']._serialized_end=8322 - _globals['_RECORDSUNSHAREREQUEST']._serialized_start=8324 - _globals['_RECORDSUNSHAREREQUEST']._serialized_end=8445 - _globals['_RECORDSUNSHARERESPONSE']._serialized_start=8448 - _globals['_RECORDSUNSHARERESPONSE']._serialized_end=8582 - _globals['_RECORDSUNSHAREFOLDER']._serialized_start=8584 - _globals['_RECORDSUNSHAREFOLDER']._serialized_end=8650 - _globals['_RECORDSUNSHAREUSER']._serialized_start=8652 - _globals['_RECORDSUNSHAREUSER']._serialized_end=8711 - _globals['_RECORDSUNSHAREFOLDERSTATUS']._serialized_start=8713 - _globals['_RECORDSUNSHAREFOLDERSTATUS']._serialized_end=8785 - _globals['_RECORDSUNSHAREUSERSTATUS']._serialized_start=8787 - _globals['_RECORDSUNSHAREUSERSTATUS']._serialized_end=8852 - _globals['_TIMEDACCESSCALLBACKPAYLOAD']._serialized_start=8854 - _globals['_TIMEDACCESSCALLBACKPAYLOAD']._serialized_end=8945 - _globals['_TIMELIMITEDACCESSREQUEST']._serialized_start=8948 - _globals['_TIMELIMITEDACCESSREQUEST']._serialized_end=9201 - _globals['_TIMELIMITEDACCESSSTATUS']._serialized_start=9203 - _globals['_TIMELIMITEDACCESSSTATUS']._serialized_end=9258 - _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_start=9261 - _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_end=9488 + _globals['_FOLDERRECORDKEY']._serialized_end=833 + _globals['_FOLDER']._serialized_start=835 + _globals['_FOLDER']._serialized_end=932 + _globals['_TEAM']._serialized_start=935 + _globals['_TEAM']._serialized_end=1084 + _globals['_RECORDSGETRESPONSE']._serialized_start=1087 + _globals['_RECORDSGETRESPONSE']._serialized_end=1259 + _globals['_RECORDLINK']._serialized_start=1261 + _globals['_RECORDLINK']._serialized_end=1313 + _globals['_RECORDAUDIT']._serialized_start=1315 + _globals['_RECORDAUDIT']._serialized_end=1359 + _globals['_SECURITYDATA']._serialized_start=1361 + _globals['_SECURITYDATA']._serialized_end=1389 + _globals['_SECURITYSCOREDATA']._serialized_start=1391 + _globals['_SECURITYSCOREDATA']._serialized_end=1424 + _globals['_RECORDADD']._serialized_start=1427 + _globals['_RECORDADD']._serialized_end=1815 + _globals['_RECORDSADDREQUEST']._serialized_start=1818 + _globals['_RECORDSADDREQUEST']._serialized_end=1951 + _globals['_RECORDUPDATE']._serialized_start=1954 + _globals['_RECORDUPDATE']._serialized_end=2288 + _globals['_RECORDSUPDATEREQUEST']._serialized_start=2291 + _globals['_RECORDSUPDATEREQUEST']._serialized_end=2430 + _globals['_RECORDFILEFORCONVERSION']._serialized_start=2433 + _globals['_RECORDFILEFORCONVERSION']._serialized_end=2575 + _globals['_RECORDFOLDERFORCONVERSION']._serialized_start=2577 + _globals['_RECORDFOLDERFORCONVERSION']._serialized_end=2651 + _globals['_RECORDCONVERTTOV3']._serialized_start=2654 + _globals['_RECORDCONVERTTOV3']._serialized_end=2928 + _globals['_RECORDSCONVERTTOV3REQUEST']._serialized_start=2930 + _globals['_RECORDSCONVERTTOV3REQUEST']._serialized_end=3023 + _globals['_RECORDSREMOVEREQUEST']._serialized_start=3025 + _globals['_RECORDSREMOVEREQUEST']._serialized_end=3064 + _globals['_RECORDREVERT']._serialized_start=3066 + _globals['_RECORDREVERT']._serialized_end=3128 + _globals['_RECORDSREVERTREQUEST']._serialized_start=3130 + _globals['_RECORDSREVERTREQUEST']._serialized_end=3192 + _globals['_RECORDLINKERROR']._serialized_start=3194 + _globals['_RECORDLINKERROR']._serialized_end=3293 + _globals['_RECORDMODIFYSTATUS']._serialized_start=3296 + _globals['_RECORDMODIFYSTATUS']._serialized_end=3445 + _globals['_RECORDSMODIFYRESPONSE']._serialized_start=3447 + _globals['_RECORDSMODIFYRESPONSE']._serialized_end=3534 + _globals['_RECORDADDAUDITDATA']._serialized_start=3536 + _globals['_RECORDADDAUDITDATA']._serialized_end=3625 + _globals['_ADDAUDITDATAREQUEST']._serialized_start=3627 + _globals['_ADDAUDITDATAREQUEST']._serialized_end=3694 + _globals['_FILE']._serialized_start=3696 + _globals['_FILE']._serialized_end=3812 + _globals['_FILESADDREQUEST']._serialized_start=3814 + _globals['_FILESADDREQUEST']._serialized_end=3882 + _globals['_FILEADDSTATUS']._serialized_start=3885 + _globals['_FILEADDSTATUS']._serialized_end=4052 + _globals['_FILESADDRESPONSE']._serialized_start=4054 + _globals['_FILESADDRESPONSE']._serialized_end=4129 + _globals['_FILESGETREQUEST']._serialized_start=4131 + _globals['_FILESGETREQUEST']._serialized_end=4233 + _globals['_FILEGETSTATUS']._serialized_start=4236 + _globals['_FILEGETSTATUS']._serialized_end=4398 + _globals['_FILESGETRESPONSE']._serialized_start=4400 + _globals['_FILESGETRESPONSE']._serialized_end=4457 + _globals['_APPLICATIONADDREQUEST']._serialized_start=4460 + _globals['_APPLICATIONADDREQUEST']._serialized_end=4601 + _globals['_GETRECORDDATAWITHACCESSINFOREQUEST']._serialized_start=4604 + _globals['_GETRECORDDATAWITHACCESSINFOREQUEST']._serialized_end=4740 + _globals['_USERPERMISSION']._serialized_start=4743 + _globals['_USERPERMISSION']._serialized_end=5005 + _globals['_SHAREDFOLDERPERMISSION']._serialized_start=5008 + _globals['_SHAREDFOLDERPERMISSION']._serialized_end=5224 + _globals['_RECORDDATA']._serialized_start=5227 + _globals['_RECORDDATA']._serialized_end=5587 + _globals['_RECORDDATAWITHACCESSINFO']._serialized_start=5590 + _globals['_RECORDDATAWITHACCESSINFO']._serialized_end=5790 + _globals['_GETRECORDDATAWITHACCESSINFORESPONSE']._serialized_start=5793 + _globals['_GETRECORDDATAWITHACCESSINFORESPONSE']._serialized_end=5930 + _globals['_ISOBJECTSHAREADMIN']._serialized_start=5932 + _globals['_ISOBJECTSHAREADMIN']._serialized_end=6038 + _globals['_AMISHAREADMIN']._serialized_start=6040 + _globals['_AMISHAREADMIN']._serialized_end=6112 + _globals['_RECORDSHAREUPDATEREQUEST']._serialized_start=6115 + _globals['_RECORDSHAREUPDATEREQUEST']._serialized_end=6303 + _globals['_SHAREDRECORD']._serialized_start=6306 + _globals['_SHAREDRECORD']._serialized_end=6630 + _globals['_RECORDSHAREUPDATERESPONSE']._serialized_start=6633 + _globals['_RECORDSHAREUPDATERESPONSE']._serialized_end=6846 + _globals['_SHAREDRECORDSTATUS']._serialized_start=6848 + _globals['_SHAREDRECORDSTATUS']._serialized_end=6938 + _globals['_GETRECORDPERMISSIONSREQUEST']._serialized_start=6940 + _globals['_GETRECORDPERMISSIONSREQUEST']._serialized_end=7011 + _globals['_GETRECORDPERMISSIONSRESPONSE']._serialized_start=7013 + _globals['_GETRECORDPERMISSIONSRESPONSE']._serialized_end=7097 + _globals['_RECORDPERMISSION']._serialized_start=7099 + _globals['_RECORDPERMISSION']._serialized_end=7207 + _globals['_GETSHAREOBJECTSREQUEST']._serialized_start=7209 + _globals['_GETSHAREOBJECTSREQUEST']._serialized_end=7313 + _globals['_GETSHAREOBJECTSRESPONSE']._serialized_start=7316 + _globals['_GETSHAREOBJECTSRESPONSE']._serialized_end=7675 + _globals['_SHAREUSER']._serialized_start=7678 + _globals['_SHAREUSER']._serialized_end=7867 + _globals['_SHARETEAM']._serialized_start=7869 + _globals['_SHARETEAM']._serialized_end=7937 + _globals['_SHAREENTERPRISE']._serialized_start=7939 + _globals['_SHAREENTERPRISE']._serialized_end=8002 + _globals['_RECORDSONWERSHIPTRANSFERREQUEST']._serialized_start=8004 + _globals['_RECORDSONWERSHIPTRANSFERREQUEST']._serialized_end=8087 + _globals['_TRANSFERRECORD']._serialized_start=8089 + _globals['_TRANSFERRECORD']._serialized_end=8180 + _globals['_RECORDSONWERSHIPTRANSFERRESPONSE']._serialized_start=8182 + _globals['_RECORDSONWERSHIPTRANSFERRESPONSE']._serialized_end=8277 + _globals['_TRANSFERRECORDSTATUS']._serialized_start=8279 + _globals['_TRANSFERRECORDSTATUS']._serialized_end=8371 + _globals['_RECORDSUNSHAREREQUEST']._serialized_start=8373 + _globals['_RECORDSUNSHAREREQUEST']._serialized_end=8494 + _globals['_RECORDSUNSHARERESPONSE']._serialized_start=8497 + _globals['_RECORDSUNSHARERESPONSE']._serialized_end=8631 + _globals['_RECORDSUNSHAREFOLDER']._serialized_start=8633 + _globals['_RECORDSUNSHAREFOLDER']._serialized_end=8699 + _globals['_RECORDSUNSHAREUSER']._serialized_start=8701 + _globals['_RECORDSUNSHAREUSER']._serialized_end=8760 + _globals['_RECORDSUNSHAREFOLDERSTATUS']._serialized_start=8762 + _globals['_RECORDSUNSHAREFOLDERSTATUS']._serialized_end=8834 + _globals['_RECORDSUNSHAREUSERSTATUS']._serialized_start=8836 + _globals['_RECORDSUNSHAREUSERSTATUS']._serialized_end=8901 + _globals['_TIMEDACCESSCALLBACKPAYLOAD']._serialized_start=8903 + _globals['_TIMEDACCESSCALLBACKPAYLOAD']._serialized_end=8994 + _globals['_TIMELIMITEDACCESSREQUEST']._serialized_start=8997 + _globals['_TIMELIMITEDACCESSREQUEST']._serialized_end=9250 + _globals['_TIMELIMITEDACCESSSTATUS']._serialized_start=9252 + _globals['_TIMELIMITEDACCESSSTATUS']._serialized_end=9307 + _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_start=9310 + _globals['_TIMELIMITEDACCESSRESPONSE']._serialized_end=9537 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/record_pb2.pyi b/keepersdk-package/src/keepersdk/proto/record_pb2.pyi index 19b58230..0fb64232 100644 --- a/keepersdk-package/src/keepersdk/proto/record_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/record_pb2.pyi @@ -231,14 +231,16 @@ class Record(_message.Message): def __init__(self, record_uid: _Optional[bytes] = ..., record_key: _Optional[bytes] = ..., record_key_type: _Optional[_Union[RecordKeyType, str]] = ..., data: _Optional[bytes] = ..., extra: _Optional[bytes] = ..., version: _Optional[int] = ..., client_modified_time: _Optional[int] = ..., revision: _Optional[int] = ..., file_ids: _Optional[_Iterable[bytes]] = ...) -> None: ... class FolderRecordKey(_message.Message): - __slots__ = ("folder_uid", "record_uid", "record_key") + __slots__ = ("folder_uid", "record_uid", "record_key", "record_key_type") FOLDER_UID_FIELD_NUMBER: _ClassVar[int] RECORD_UID_FIELD_NUMBER: _ClassVar[int] RECORD_KEY_FIELD_NUMBER: _ClassVar[int] + RECORD_KEY_TYPE_FIELD_NUMBER: _ClassVar[int] folder_uid: bytes record_uid: bytes record_key: bytes - def __init__(self, folder_uid: _Optional[bytes] = ..., record_uid: _Optional[bytes] = ..., record_key: _Optional[bytes] = ...) -> None: ... + record_key_type: RecordKeyType + def __init__(self, folder_uid: _Optional[bytes] = ..., record_uid: _Optional[bytes] = ..., record_key: _Optional[bytes] = ..., record_key_type: _Optional[_Union[RecordKeyType, str]] = ...) -> None: ... class Folder(_message.Message): __slots__ = ("folder_uid", "folder_key", "folder_key_type") diff --git a/keepersdk-package/src/keepersdk/proto/remove_pb2.py b/keepersdk-package/src/keepersdk/proto/remove_pb2.py index f5a48130..5a5fd304 100644 --- a/keepersdk-package/src/keepersdk/proto/remove_pb2.py +++ b/keepersdk-package/src/keepersdk/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.3 """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, + 3, + '', + 'remove.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -16,7 +25,7 @@ from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cremove.proto\x12\x10\x66older.v3.remove\x1a\x1cgoogle/api/annotations.proto\"v\n\rRecordRemoval\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_uid\x18\x02 \x01(\x0c\x12=\n\x0eoperation_type\x18\x03 \x01(\x0e\x32%.folder.v3.remove.RecordOperationType\"b\n\rFolderRemoval\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12=\n\x0eoperation_type\x18\x02 \x01(\x0e\x32%.folder.v3.remove.FolderOperationType\"\x93\x01\n\x13RemoveRecordRequest\x12.\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1e.folder.v3.remove.RemoveAction\x12\x30\n\x07records\x18\x02 \x03(\x0b\x32\x1f.folder.v3.remove.RecordRemoval\x12\x1a\n\x12\x63onfirmation_token\x18\x03 \x01(\x0c\"\x93\x01\n\x13RemoveFolderRequest\x12.\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1e.folder.v3.remove.RemoveAction\x12\x30\n\x07\x66olders\x18\x02 \x03(\x0b\x32\x1f.folder.v3.remove.FolderRemoval\x12\x1a\n\x12\x63onfirmation_token\x18\x03 \x01(\x0c\"\x8e\x01\n\x0eRemoveResponse\x12\x1a\n\x12\x63onfirmation_token\x18\x01 \x01(\x0c\x12\x18\n\x10token_expires_at\x18\x02 \x01(\x03\x12/\n\x07results\x18\x03 \x03(\x0b\x32\x1e.folder.v3.remove.RemoveResult\x12\x15\n\rerror_message\x18\x04 \x01(\t\"\xba\x01\n\x0cRemoveResult\x12\x10\n\x08item_uid\x18\x01 \x01(\x0c\x12\x12\n\nfolder_uid\x18\x02 \x01(\x0c\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.folder.v3.remove.RemoveStatus\x12(\n\x06impact\x18\x04 \x01(\x0b\x32\x18.folder.v3.remove.Impact\x12*\n\x05\x65rror\x18\x05 \x01(\x0b\x32\x1b.folder.v3.remove.ItemError\"\xb7\x01\n\x06Impact\x12\x15\n\rfolders_count\x18\x01 \x01(\x05\x12\x15\n\rrecords_count\x18\x02 \x01(\x05\x12\x1c\n\x14\x61\x66\x66\x65\x63ted_users_count\x18\x03 \x01(\x05\x12\x1c\n\x14\x61\x66\x66\x65\x63ted_teams_count\x18\x04 \x01(\x05\x12\x31\n\x0brecord_info\x18\x05 \x03(\x0b\x32\x1c.folder.v3.remove.RecordInfo\x12\x10\n\x08warnings\x18\x06 \x03(\t\"9\n\nRecordInfo\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x17\n\x0flocations_count\x18\x02 \x01(\x05\"M\n\tItemError\x12/\n\x04\x63ode\x18\x01 \x01(\x0e\x32!.folder.v3.remove.RemoveErrorCode\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xa7\x01\n\x13RemovalTokenPayload\x12<\n\x11item_fingerprints\x18\x01 \x03(\x0b\x32!.folder.v3.remove.ItemFingerprint\x12\x0f\n\x07user_id\x18\x02 \x01(\x05\x12\x11\n\tdevice_id\x18\x03 \x01(\x03\x12\x13\n\x0bsession_uid\x18\x04 \x01(\x0c\x12\x19\n\x11\x65xpires_at_millis\x18\x05 \x01(\x03\"\x94\x01\n\x0fItemFingerprint\x12\x30\n\x06record\x18\x01 \x01(\x0b\x32\x1e.folder.v3.remove.RecordTargetH\x00\x12\x30\n\x06\x66older\x18\x02 \x01(\x0b\x32\x1e.folder.v3.remove.FolderTargetH\x00\x12\x13\n\x0b\x66ingerprint\x18\n \x01(\x0c\x42\x08\n\x06target\"u\n\x0cRecordTarget\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_uid\x18\x02 \x01(\x0c\x12=\n\x0eoperation_type\x18\x03 \x01(\x0e\x32%.folder.v3.remove.RecordOperationType\"a\n\x0c\x46olderTarget\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12=\n\x0eoperation_type\x18\x02 \x01(\x0e\x32%.folder.v3.remove.FolderOperationType*D\n\x0cRemoveAction\x12\x19\n\x15REMOVE_ACTION_PREVIEW\x10\x00\x12\x19\n\x15REMOVE_ACTION_CONFIRM\x10\x01*~\n\x13RecordOperationType\x12\x1c\n\x18RECORD_OPERATION_UNKNOWN\x10\x00\x12\x16\n\x12UNLINK_FROM_FOLDER\x10\x01\x12\x18\n\x14MOVE_TO_FOLDER_TRASH\x10\x02\x12\x17\n\x13MOVE_TO_OWNER_TRASH\x10\x03*\x91\x01\n\x13\x46olderOperationType\x12\x1c\n\x18\x46OLDER_OPERATION_UNKNOWN\x10\x00\x12\x1f\n\x1b\x46OLDER_MOVE_TO_FOLDER_TRASH\x10\x01\x12\x1e\n\x1a\x46OLDER_MOVE_TO_OWNER_TRASH\x10\x02\x12\x1b\n\x17\x46OLDER_DELETE_PERMANENT\x10\x03*\xcb\x01\n\x0fRemoveErrorCode\x12\x18\n\x14REMOVE_ERROR_UNKNOWN\x10\x00\x12\x1a\n\x16REMOVE_ERROR_NOT_FOUND\x10\x01\x12\x1e\n\x1aREMOVE_ERROR_ACCESS_DENIED\x10\x02\x12 \n\x1cREMOVE_ERROR_TRASHCAN_FOLDER\x10\x03\x12\x1c\n\x18REMOVE_ERROR_ROOT_FOLDER\x10\x04\x12\"\n\x1eREMOVE_ERROR_DESCENDANT_DENIED\x10\x05*\xec\x01\n\x0cRemoveStatus\x12\x19\n\x15REMOVE_STATUS_UNKNOWN\x10\x00\x12\x19\n\x15REMOVE_STATUS_SUCCESS\x10\x01\x12\x1f\n\x1bREMOVE_STATUS_STALE_PREVIEW\x10\x02\x12\x1f\n\x1bREMOVE_STATUS_TOKEN_EXPIRED\x10\x03\x12\x1f\n\x1bREMOVE_STATUS_TOKEN_INVALID\x10\x04\x12\x1f\n\x1bREMOVE_STATUS_ACCESS_DENIED\x10\x05\x12\"\n\x1eREMOVE_STATUS_VALIDATION_ERROR\x10\x06\x32\xad\x02\n\rRemoveService\x12\x8c\x01\n\x0cRemoveRecord\x12%.folder.v3.remove.RemoveRecordRequest\x1a .folder.v3.remove.RemoveResponse\"3\x82\xd3\xe4\x93\x02-\"(/api/rest/vault/folders/v3/remove_record:\x01*\x12\x8c\x01\n\x0cRemoveFolder\x12%.folder.v3.remove.RemoveFolderRequest\x1a .folder.v3.remove.RemoveResponse\"3\x82\xd3\xe4\x93\x02-\"(/api/rest/vault/folders/v3/remove_folder:\x01*B1\n-com.keepersecurity.proto.api.folder.v3.removeP\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cremove.proto\x12\x10\x66older.v3.remove\x1a\x1cgoogle/api/annotations.proto\"v\n\rRecordRemoval\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_uid\x18\x02 \x01(\x0c\x12=\n\x0eoperation_type\x18\x03 \x01(\x0e\x32%.folder.v3.remove.RecordOperationType\"b\n\rFolderRemoval\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12=\n\x0eoperation_type\x18\x02 \x01(\x0e\x32%.folder.v3.remove.FolderOperationType\"\x93\x01\n\x13RemoveRecordRequest\x12.\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1e.folder.v3.remove.RemoveAction\x12\x30\n\x07records\x18\x02 \x03(\x0b\x32\x1f.folder.v3.remove.RecordRemoval\x12\x1a\n\x12\x63onfirmation_token\x18\x03 \x01(\x0c\"\x93\x01\n\x13RemoveFolderRequest\x12.\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1e.folder.v3.remove.RemoveAction\x12\x30\n\x07\x66olders\x18\x02 \x03(\x0b\x32\x1f.folder.v3.remove.FolderRemoval\x12\x1a\n\x12\x63onfirmation_token\x18\x03 \x01(\x0c\"\x8e\x01\n\x0eRemoveResponse\x12\x1a\n\x12\x63onfirmation_token\x18\x01 \x01(\x0c\x12\x18\n\x10token_expires_at\x18\x02 \x01(\x03\x12/\n\x07results\x18\x03 \x03(\x0b\x32\x1e.folder.v3.remove.RemoveResult\x12\x15\n\rerror_message\x18\x04 \x01(\t\"\xba\x01\n\x0cRemoveResult\x12\x10\n\x08item_uid\x18\x01 \x01(\x0c\x12\x12\n\nfolder_uid\x18\x02 \x01(\x0c\x12.\n\x06status\x18\x03 \x01(\x0e\x32\x1e.folder.v3.remove.RemoveStatus\x12(\n\x06impact\x18\x04 \x01(\x0b\x32\x18.folder.v3.remove.Impact\x12*\n\x05\x65rror\x18\x05 \x01(\x0b\x32\x1b.folder.v3.remove.ItemError\"\xb7\x01\n\x06Impact\x12\x15\n\rfolders_count\x18\x01 \x01(\x05\x12\x15\n\rrecords_count\x18\x02 \x01(\x05\x12\x1c\n\x14\x61\x66\x66\x65\x63ted_users_count\x18\x03 \x01(\x05\x12\x1c\n\x14\x61\x66\x66\x65\x63ted_teams_count\x18\x04 \x01(\x05\x12\x31\n\x0brecord_info\x18\x05 \x03(\x0b\x32\x1c.folder.v3.remove.RecordInfo\x12\x10\n\x08warnings\x18\x06 \x03(\t\"9\n\nRecordInfo\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x17\n\x0flocations_count\x18\x02 \x01(\x05\"M\n\tItemError\x12/\n\x04\x63ode\x18\x01 \x01(\x0e\x32!.folder.v3.remove.RemoveErrorCode\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xa7\x01\n\x13RemovalTokenPayload\x12<\n\x11item_fingerprints\x18\x01 \x03(\x0b\x32!.folder.v3.remove.ItemFingerprint\x12\x0f\n\x07user_id\x18\x02 \x01(\x05\x12\x11\n\tdevice_id\x18\x03 \x01(\x03\x12\x13\n\x0bsession_uid\x18\x04 \x01(\x0c\x12\x19\n\x11\x65xpires_at_millis\x18\x05 \x01(\x03\"\x94\x01\n\x0fItemFingerprint\x12\x30\n\x06record\x18\x01 \x01(\x0b\x32\x1e.folder.v3.remove.RecordTargetH\x00\x12\x30\n\x06\x66older\x18\x02 \x01(\x0b\x32\x1e.folder.v3.remove.FolderTargetH\x00\x12\x13\n\x0b\x66ingerprint\x18\n \x01(\x0c\x42\x08\n\x06target\"u\n\x0cRecordTarget\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x12\n\nrecord_uid\x18\x02 \x01(\x0c\x12=\n\x0eoperation_type\x18\x03 \x01(\x0e\x32%.folder.v3.remove.RecordOperationType\"a\n\x0c\x46olderTarget\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12=\n\x0eoperation_type\x18\x02 \x01(\x0e\x32%.folder.v3.remove.FolderOperationType\"\x9f\x01\n\rRestoreResult\x12\x10\n\x08item_uid\x18\x01 \x01(\x0c\x12\x34\n\titem_type\x18\x02 \x01(\x0e\x32!.folder.v3.remove.RestoreItemType\x12/\n\x06status\x18\x03 \x01(\x0e\x32\x1f.folder.v3.remove.RestoreStatus\x12\x15\n\rerror_message\x18\x04 \x01(\t\"b\n\x17TrashcanRestoreResponse\x12\x30\n\x07results\x18\x01 \x03(\x0b\x32\x1f.folder.v3.remove.RestoreResult\x12\x15\n\rerror_message\x18\x02 \x01(\t\"\\\n\rRestoreRecord\x12\x12\n\nrecord_uid\x18\x01 \x01(\x0c\x12\x1c\n\x14\x65ncrypted_record_key\x18\x02 \x01(\x0c\x12\x19\n\x11source_folder_uid\x18\x03 \x01(\x0c\"A\n\rRestoreFolder\x12\x12\n\nfolder_uid\x18\x01 \x01(\x0c\x12\x1c\n\x14\x65ncrypted_folder_key\x18\x02 \x01(\x0c\"\x97\x01\n\x16TrashcanRestoreRequest\x12\x30\n\x07records\x18\x01 \x03(\x0b\x32\x1f.folder.v3.remove.RestoreRecord\x12\x30\n\x07\x66olders\x18\x02 \x03(\x0b\x32\x1f.folder.v3.remove.RestoreFolder\x12\x19\n\x11target_folder_uid\x18\x03 \x01(\x0c*D\n\x0cRemoveAction\x12\x19\n\x15REMOVE_ACTION_PREVIEW\x10\x00\x12\x19\n\x15REMOVE_ACTION_CONFIRM\x10\x01*~\n\x13RecordOperationType\x12\x1c\n\x18RECORD_OPERATION_UNKNOWN\x10\x00\x12\x16\n\x12UNLINK_FROM_FOLDER\x10\x01\x12\x18\n\x14MOVE_TO_FOLDER_TRASH\x10\x02\x12\x17\n\x13MOVE_TO_OWNER_TRASH\x10\x03*\x91\x01\n\x13\x46olderOperationType\x12\x1c\n\x18\x46OLDER_OPERATION_UNKNOWN\x10\x00\x12\x1f\n\x1b\x46OLDER_MOVE_TO_FOLDER_TRASH\x10\x01\x12\x1e\n\x1a\x46OLDER_MOVE_TO_OWNER_TRASH\x10\x02\x12\x1b\n\x17\x46OLDER_DELETE_PERMANENT\x10\x03*\xcb\x01\n\x0fRemoveErrorCode\x12\x18\n\x14REMOVE_ERROR_UNKNOWN\x10\x00\x12\x1a\n\x16REMOVE_ERROR_NOT_FOUND\x10\x01\x12\x1e\n\x1aREMOVE_ERROR_ACCESS_DENIED\x10\x02\x12 \n\x1cREMOVE_ERROR_TRASHCAN_FOLDER\x10\x03\x12\x1c\n\x18REMOVE_ERROR_ROOT_FOLDER\x10\x04\x12\"\n\x1eREMOVE_ERROR_DESCENDANT_DENIED\x10\x05*\xec\x01\n\x0cRemoveStatus\x12\x19\n\x15REMOVE_STATUS_UNKNOWN\x10\x00\x12\x19\n\x15REMOVE_STATUS_SUCCESS\x10\x01\x12\x1f\n\x1bREMOVE_STATUS_STALE_PREVIEW\x10\x02\x12\x1f\n\x1bREMOVE_STATUS_TOKEN_EXPIRED\x10\x03\x12\x1f\n\x1bREMOVE_STATUS_TOKEN_INVALID\x10\x04\x12\x1f\n\x1bREMOVE_STATUS_ACCESS_DENIED\x10\x05\x12\"\n\x1eREMOVE_STATUS_VALIDATION_ERROR\x10\x06*\xb7\x01\n\rRestoreStatus\x12\x1a\n\x16RESTORE_STATUS_UNKNOWN\x10\x00\x12\x0e\n\nRS_SUCCESS\x10\x01\x12\x16\n\x12RS_NOT_IN_TRASHCAN\x10\x02\x12\x14\n\x10RS_ACCESS_DENIED\x10\x03\x12\x1e\n\x1aRS_TARGET_FOLDER_NOT_FOUND\x10\x04\x12\x1f\n\x1bRS_ALREADY_EXISTS_IN_TARGET\x10\x05\x12\x0b\n\x07RS_FAIL\x10\x06*]\n\x0fRestoreItemType\x12\x18\n\x14RESTORE_ITEM_UNKNOWN\x10\x00\x12\x17\n\x13RESTORE_ITEM_RECORD\x10\x01\x12\x17\n\x13RESTORE_ITEM_FOLDER\x10\x02\x32\xce\x03\n\rRemoveService\x12\x8c\x01\n\x0cRemoveRecord\x12%.folder.v3.remove.RemoveRecordRequest\x1a .folder.v3.remove.RemoveResponse\"3\x82\xd3\xe4\x93\x02-\"(/api/rest/vault/folders/v3/remove_record:\x01*\x12\x8c\x01\n\x0cRemoveFolder\x12%.folder.v3.remove.RemoveFolderRequest\x1a .folder.v3.remove.RemoveResponse\"3\x82\xd3\xe4\x93\x02-\"(/api/rest/vault/folders/v3/remove_folder:\x01*\x12\x9e\x01\n\x0fTrashcanRestore\x12(.folder.v3.remove.TrashcanRestoreRequest\x1a).folder.v3.remove.TrashcanRestoreResponse\"6\x82\xd3\xe4\x93\x02\x30\"+/api/rest/vault/folders/v3/trashcan/restore:\x01*B1\n-com.keepersecurity.proto.api.folder.v3.removeP\x01\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -28,16 +37,22 @@ _globals['_REMOVESERVICE'].methods_by_name['RemoveRecord']._serialized_options = b'\202\323\344\223\002-\"(/api/rest/vault/folders/v3/remove_record:\001*' _globals['_REMOVESERVICE'].methods_by_name['RemoveFolder']._loaded_options = None _globals['_REMOVESERVICE'].methods_by_name['RemoveFolder']._serialized_options = b'\202\323\344\223\002-\"(/api/rest/vault/folders/v3/remove_folder:\001*' - _globals['_REMOVEACTION']._serialized_start=1781 - _globals['_REMOVEACTION']._serialized_end=1849 - _globals['_RECORDOPERATIONTYPE']._serialized_start=1851 - _globals['_RECORDOPERATIONTYPE']._serialized_end=1977 - _globals['_FOLDEROPERATIONTYPE']._serialized_start=1980 - _globals['_FOLDEROPERATIONTYPE']._serialized_end=2125 - _globals['_REMOVEERRORCODE']._serialized_start=2128 - _globals['_REMOVEERRORCODE']._serialized_end=2331 - _globals['_REMOVESTATUS']._serialized_start=2334 - _globals['_REMOVESTATUS']._serialized_end=2570 + _globals['_REMOVESERVICE'].methods_by_name['TrashcanRestore']._loaded_options = None + _globals['_REMOVESERVICE'].methods_by_name['TrashcanRestore']._serialized_options = b'\202\323\344\223\0020\"+/api/rest/vault/folders/v3/trashcan/restore:\001*' + _globals['_REMOVEACTION']._serialized_start=2358 + _globals['_REMOVEACTION']._serialized_end=2426 + _globals['_RECORDOPERATIONTYPE']._serialized_start=2428 + _globals['_RECORDOPERATIONTYPE']._serialized_end=2554 + _globals['_FOLDEROPERATIONTYPE']._serialized_start=2557 + _globals['_FOLDEROPERATIONTYPE']._serialized_end=2702 + _globals['_REMOVEERRORCODE']._serialized_start=2705 + _globals['_REMOVEERRORCODE']._serialized_end=2908 + _globals['_REMOVESTATUS']._serialized_start=2911 + _globals['_REMOVESTATUS']._serialized_end=3147 + _globals['_RESTORESTATUS']._serialized_start=3150 + _globals['_RESTORESTATUS']._serialized_end=3333 + _globals['_RESTOREITEMTYPE']._serialized_start=3335 + _globals['_RESTOREITEMTYPE']._serialized_end=3428 _globals['_RECORDREMOVAL']._serialized_start=64 _globals['_RECORDREMOVAL']._serialized_end=182 _globals['_FOLDERREMOVAL']._serialized_start=184 @@ -64,6 +79,16 @@ _globals['_RECORDTARGET']._serialized_end=1680 _globals['_FOLDERTARGET']._serialized_start=1682 _globals['_FOLDERTARGET']._serialized_end=1779 - _globals['_REMOVESERVICE']._serialized_start=2573 - _globals['_REMOVESERVICE']._serialized_end=2874 + _globals['_RESTORERESULT']._serialized_start=1782 + _globals['_RESTORERESULT']._serialized_end=1941 + _globals['_TRASHCANRESTORERESPONSE']._serialized_start=1943 + _globals['_TRASHCANRESTORERESPONSE']._serialized_end=2041 + _globals['_RESTORERECORD']._serialized_start=2043 + _globals['_RESTORERECORD']._serialized_end=2135 + _globals['_RESTOREFOLDER']._serialized_start=2137 + _globals['_RESTOREFOLDER']._serialized_end=2202 + _globals['_TRASHCANRESTOREREQUEST']._serialized_start=2205 + _globals['_TRASHCANRESTOREREQUEST']._serialized_end=2356 + _globals['_REMOVESERVICE']._serialized_start=3431 + _globals['_REMOVESERVICE']._serialized_end=3893 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/remove_pb2.pyi b/keepersdk-package/src/keepersdk/proto/remove_pb2.pyi index c88bbfd0..bd21c9ae 100644 --- a/keepersdk-package/src/keepersdk/proto/remove_pb2.pyi +++ b/keepersdk-package/src/keepersdk/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 @@ -45,6 +44,22 @@ class RemoveStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): REMOVE_STATUS_TOKEN_INVALID: _ClassVar[RemoveStatus] REMOVE_STATUS_ACCESS_DENIED: _ClassVar[RemoveStatus] REMOVE_STATUS_VALIDATION_ERROR: _ClassVar[RemoveStatus] + +class RestoreStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + RESTORE_STATUS_UNKNOWN: _ClassVar[RestoreStatus] + RS_SUCCESS: _ClassVar[RestoreStatus] + RS_NOT_IN_TRASHCAN: _ClassVar[RestoreStatus] + RS_ACCESS_DENIED: _ClassVar[RestoreStatus] + RS_TARGET_FOLDER_NOT_FOUND: _ClassVar[RestoreStatus] + RS_ALREADY_EXISTS_IN_TARGET: _ClassVar[RestoreStatus] + RS_FAIL: _ClassVar[RestoreStatus] + +class RestoreItemType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + RESTORE_ITEM_UNKNOWN: _ClassVar[RestoreItemType] + RESTORE_ITEM_RECORD: _ClassVar[RestoreItemType] + RESTORE_ITEM_FOLDER: _ClassVar[RestoreItemType] REMOVE_ACTION_PREVIEW: RemoveAction REMOVE_ACTION_CONFIRM: RemoveAction RECORD_OPERATION_UNKNOWN: RecordOperationType @@ -68,6 +83,16 @@ REMOVE_STATUS_TOKEN_EXPIRED: RemoveStatus REMOVE_STATUS_TOKEN_INVALID: RemoveStatus REMOVE_STATUS_ACCESS_DENIED: RemoveStatus REMOVE_STATUS_VALIDATION_ERROR: RemoveStatus +RESTORE_STATUS_UNKNOWN: RestoreStatus +RS_SUCCESS: RestoreStatus +RS_NOT_IN_TRASHCAN: RestoreStatus +RS_ACCESS_DENIED: RestoreStatus +RS_TARGET_FOLDER_NOT_FOUND: RestoreStatus +RS_ALREADY_EXISTS_IN_TARGET: RestoreStatus +RS_FAIL: RestoreStatus +RESTORE_ITEM_UNKNOWN: RestoreItemType +RESTORE_ITEM_RECORD: RestoreItemType +RESTORE_ITEM_FOLDER: RestoreItemType class RecordRemoval(_message.Message): __slots__ = ("folder_uid", "record_uid", "operation_type") @@ -206,3 +231,51 @@ class FolderTarget(_message.Message): folder_uid: bytes operation_type: FolderOperationType def __init__(self, folder_uid: _Optional[bytes] = ..., operation_type: _Optional[_Union[FolderOperationType, str]] = ...) -> None: ... + +class RestoreResult(_message.Message): + __slots__ = ("item_uid", "item_type", "status", "error_message") + ITEM_UID_FIELD_NUMBER: _ClassVar[int] + ITEM_TYPE_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + ERROR_MESSAGE_FIELD_NUMBER: _ClassVar[int] + item_uid: bytes + item_type: RestoreItemType + status: RestoreStatus + error_message: str + def __init__(self, item_uid: _Optional[bytes] = ..., item_type: _Optional[_Union[RestoreItemType, str]] = ..., status: _Optional[_Union[RestoreStatus, str]] = ..., error_message: _Optional[str] = ...) -> None: ... + +class TrashcanRestoreResponse(_message.Message): + __slots__ = ("results", "error_message") + RESULTS_FIELD_NUMBER: _ClassVar[int] + ERROR_MESSAGE_FIELD_NUMBER: _ClassVar[int] + results: _containers.RepeatedCompositeFieldContainer[RestoreResult] + error_message: str + def __init__(self, results: _Optional[_Iterable[_Union[RestoreResult, _Mapping]]] = ..., error_message: _Optional[str] = ...) -> None: ... + +class RestoreRecord(_message.Message): + __slots__ = ("record_uid", "encrypted_record_key", "source_folder_uid") + RECORD_UID_FIELD_NUMBER: _ClassVar[int] + ENCRYPTED_RECORD_KEY_FIELD_NUMBER: _ClassVar[int] + SOURCE_FOLDER_UID_FIELD_NUMBER: _ClassVar[int] + record_uid: bytes + encrypted_record_key: bytes + source_folder_uid: bytes + def __init__(self, record_uid: _Optional[bytes] = ..., encrypted_record_key: _Optional[bytes] = ..., source_folder_uid: _Optional[bytes] = ...) -> None: ... + +class RestoreFolder(_message.Message): + __slots__ = ("folder_uid", "encrypted_folder_key") + FOLDER_UID_FIELD_NUMBER: _ClassVar[int] + ENCRYPTED_FOLDER_KEY_FIELD_NUMBER: _ClassVar[int] + folder_uid: bytes + encrypted_folder_key: bytes + def __init__(self, folder_uid: _Optional[bytes] = ..., encrypted_folder_key: _Optional[bytes] = ...) -> None: ... + +class TrashcanRestoreRequest(_message.Message): + __slots__ = ("records", "folders", "target_folder_uid") + RECORDS_FIELD_NUMBER: _ClassVar[int] + FOLDERS_FIELD_NUMBER: _ClassVar[int] + TARGET_FOLDER_UID_FIELD_NUMBER: _ClassVar[int] + records: _containers.RepeatedCompositeFieldContainer[RestoreRecord] + folders: _containers.RepeatedCompositeFieldContainer[RestoreFolder] + target_folder_uid: bytes + def __init__(self, records: _Optional[_Iterable[_Union[RestoreRecord, _Mapping]]] = ..., folders: _Optional[_Iterable[_Union[RestoreFolder, _Mapping]]] = ..., target_folder_uid: _Optional[bytes] = ...) -> None: ... diff --git a/keepersdk-package/src/keepersdk/proto/router_pb2.py b/keepersdk-package/src/keepersdk/proto/router_pb2.py index 56a2d647..1fcf0c1e 100644 --- a/keepersdk-package/src/keepersdk/proto/router_pb2.py +++ b/keepersdk-package/src/keepersdk/proto/router_pb2.py @@ -27,7 +27,7 @@ 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\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\"\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\"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\"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\"[\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') +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\"\xbd\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\x12\x15\n\x08\x64\x65viceId\x18\r \x01(\x03H\x00\x88\x01\x01\x42\x0b\n\t_deviceId\"\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\"\xfe\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\x12\x0f\n\x07scripts\x18\n \x03(\x0c\"\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\"p\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\x12\x16\n\x0e\x63hallengeToken\x18\x04 \x01(\x0c\"~\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\"h\n%Router2FAGetWebAuthnChallengeResponse\x12\x11\n\tchallenge\x18\x01 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x02 \x03(\t\x12\x16\n\x0e\x63hallengeToken\x18\x03 \x01(\x0c\"[\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\"\xd8\x01\n\x16UserAccessLoweredEvent\x12\x35\n\teventType\x18\x01 \x01(\x0e\x32\".Router.UserAccessLoweredEventType\x12\x19\n\x11\x65nterpriseUserIds\x18\x02 \x03(\x03\x12\x12\n\nrecordUids\x18\x03 \x03(\x0c\x12\x15\n\x08\x64\x65viceId\x18\x04 \x01(\x03H\x00\x88\x01\x01\x12\x1e\n\x11\x65nforcementTypeId\x18\x05 \x01(\x05H\x01\x88\x01\x01\x42\x0b\n\t_deviceIdB\x14\n\x12_enforcementTypeId\"P\n\x1eUserAccessLoweredEventsRequest\x12.\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1e.Router.UserAccessLoweredEvent*\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*\xa7\x01\n\x1aUserAccessLoweredEventType\x12\x14\n\x10UALE_UNSPECIFIED\x10\x00\x12\x16\n\x12UALE_DEVICE_LOGOUT\x10\x01\x12 \n\x1cUALE_USER_LOGOUT_ALL_DEVICES\x10\x02\x12\x1c\n\x18UALE_ENFORCEMENT_REMOVED\x10\x03\x12\x1b\n\x17UALE_RECORD_ACCESS_LOST\x10\x04\x42\"\n\x18\x63om.keepersecurity.protoB\x06Routerb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -35,88 +35,100 @@ 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=4420 - _globals['_ROUTERRESPONSECODE']._serialized_end=4700 - _globals['_ROUTERROTATIONSTATUS']._serialized_start=4702 - _globals['_ROUTERROTATIONSTATUS']._serialized_end=4809 - _globals['_USERRECORDACCESSLEVEL']._serialized_start=4811 - _globals['_USERRECORDACCESSLEVEL']._serialized_end=4936 - _globals['_SERVICETYPE']._serialized_start=4938 - _globals['_SERVICETYPE']._serialized_end=4984 + _globals['_ROUTERRESPONSECODE']._serialized_start=5297 + _globals['_ROUTERRESPONSECODE']._serialized_end=5577 + _globals['_ROUTERROTATIONSTATUS']._serialized_start=5579 + _globals['_ROUTERROTATIONSTATUS']._serialized_end=5686 + _globals['_USERRECORDACCESSLEVEL']._serialized_start=5688 + _globals['_USERRECORDACCESSLEVEL']._serialized_end=5813 + _globals['_SERVICETYPE']._serialized_start=5815 + _globals['_SERVICETYPE']._serialized_end=5861 + _globals['_USERACCESSLOWEREDEVENTTYPE']._serialized_start=5864 + _globals['_USERACCESSLOWEREDEVENTTYPE']._serialized_end=6031 _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=1792 - _globals['_USERRECORDACCESSREQUEST']._serialized_start=1794 - _globals['_USERRECORDACCESSREQUEST']._serialized_end=1854 - _globals['_USERRECORDACCESSRESPONSE']._serialized_start=1856 - _globals['_USERRECORDACCESSRESPONSE']._serialized_end=1953 - _globals['_USERRECORDACCESSREQUESTS']._serialized_start=1955 - _globals['_USERRECORDACCESSREQUESTS']._serialized_end=2032 - _globals['_USERRECORDACCESSRESPONSES']._serialized_start=2034 - _globals['_USERRECORDACCESSRESPONSES']._serialized_end=2114 - _globals['_USERSHAREDFOLDERACCESSREQUEST']._serialized_start=2116 - _globals['_USERSHAREDFOLDERACCESSREQUEST']._serialized_end=2188 - _globals['_USERSHAREDFOLDERACCESSRESPONSE']._serialized_start=2190 - _globals['_USERSHAREDFOLDERACCESSRESPONSE']._serialized_end=2295 - _globals['_USERSHAREDFOLDERACCESSRESPONSES']._serialized_start=2297 - _globals['_USERSHAREDFOLDERACCESSRESPONSES']._serialized_end=2389 - _globals['_ROTATIONSCHEDULE']._serialized_start=2391 - _globals['_ROTATIONSCHEDULE']._serialized_end=2447 - _globals['_APICALLBACKREQUEST']._serialized_start=2450 - _globals['_APICALLBACKREQUEST']._serialized_end=2594 - _globals['_APICALLBACKSCHEDULE']._serialized_start=2596 - _globals['_APICALLBACKSCHEDULE']._serialized_end=2649 - _globals['_ROUTERSCHEDULEDACTIONS']._serialized_start=2651 - _globals['_ROUTERSCHEDULEDACTIONS']._serialized_end=2715 - _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_start=2717 - _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_end=2806 - _globals['_CONNECTIONPARAMETERS']._serialized_start=2809 - _globals['_CONNECTIONPARAMETERS']._serialized_end=2942 - _globals['_VALIDATECONNECTIONSREQUEST']._serialized_start=2944 - _globals['_VALIDATECONNECTIONSREQUEST']._serialized_end=3023 - _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_start=3025 - _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_end=3099 - _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_start=3101 - _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_end=3194 - _globals['_GETENFORCEMENTREQUEST']._serialized_start=3196 - _globals['_GETENFORCEMENTREQUEST']._serialized_end=3245 - _globals['_ENFORCEMENTTYPE']._serialized_start=3247 - _globals['_ENFORCEMENTTYPE']._serialized_end=3306 - _globals['_GETENFORCEMENTRESPONSE']._serialized_start=3308 - _globals['_GETENFORCEMENTRESPONSE']._serialized_end=3420 - _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_start=3422 - _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_end=3501 - _globals['_GETPEDMADMININFORESPONSE']._serialized_start=3503 - _globals['_GETPEDMADMININFORESPONSE']._serialized_end=3575 - _globals['_PAMNETWORKSETTINGS']._serialized_start=3577 - _globals['_PAMNETWORKSETTINGS']._serialized_end=3622 - _globals['_PAMNETWORKCONFIGURATIONREQUEST']._serialized_start=3625 - _globals['_PAMNETWORKCONFIGURATIONREQUEST']._serialized_end=3853 - _globals['_PAMDISCOVERYRULESSETREQUEST']._serialized_start=3855 - _globals['_PAMDISCOVERYRULESSETREQUEST']._serialized_end=3937 - _globals['_ROUTER2FAVALIDATEREQUEST']._serialized_start=3939 - _globals['_ROUTER2FAVALIDATEREQUEST']._serialized_end=4027 - _globals['_ROUTER2FASENDPUSHREQUEST']._serialized_start=4029 - _globals['_ROUTER2FASENDPUSHREQUEST']._serialized_end=4155 - _globals['_ROUTER2FAGETWEBAUTHNCHALLENGEREQUEST']._serialized_start=4157 - _globals['_ROUTER2FAGETWEBAUTHNCHALLENGEREQUEST']._serialized_end=4242 - _globals['_ROUTER2FAGETWEBAUTHNCHALLENGERESPONSE']._serialized_start=4244 - _globals['_ROUTER2FAGETWEBAUTHNCHALLENGERESPONSE']._serialized_end=4324 - _globals['_CREATEEPHEMERALSECRETREQUEST']._serialized_start=4326 - _globals['_CREATEEPHEMERALSECRETREQUEST']._serialized_end=4417 + _globals['_ROUTERUSERAUTH']._serialized_end=679 + _globals['_ROUTERDEVICEAUTH']._serialized_start=682 + _globals['_ROUTERDEVICEAUTH']._serialized_end=967 + _globals['_ROUTERRECORDROTATION']._serialized_start=970 + _globals['_ROUTERRECORDROTATION']._serialized_end=1101 + _globals['_ROUTERRECORDROTATIONSREQUEST']._serialized_start=1103 + _globals['_ROUTERRECORDROTATIONSREQUEST']._serialized_end=1172 + _globals['_ROUTERRECORDROTATIONSRESPONSE']._serialized_start=1174 + _globals['_ROUTERRECORDROTATIONSRESPONSE']._serialized_end=1271 + _globals['_ROUTERROTATIONINFO']._serialized_start=1274 + _globals['_ROUTERROTATIONINFO']._serialized_end=1528 + _globals['_ROUTERRECORDROTATIONREQUEST']._serialized_start=1531 + _globals['_ROUTERRECORDROTATIONREQUEST']._serialized_end=1959 + _globals['_USERRECORDACCESSREQUEST']._serialized_start=1961 + _globals['_USERRECORDACCESSREQUEST']._serialized_end=2021 + _globals['_USERRECORDACCESSRESPONSE']._serialized_start=2023 + _globals['_USERRECORDACCESSRESPONSE']._serialized_end=2142 + _globals['_USERRECORDACCESSREQUESTS']._serialized_start=2144 + _globals['_USERRECORDACCESSREQUESTS']._serialized_end=2221 + _globals['_USERRECORDACCESSRESPONSES']._serialized_start=2223 + _globals['_USERRECORDACCESSRESPONSES']._serialized_end=2303 + _globals['_USERSHAREDFOLDERACCESSREQUEST']._serialized_start=2305 + _globals['_USERSHAREDFOLDERACCESSREQUEST']._serialized_end=2377 + _globals['_USERSHAREDFOLDERACCESSRESPONSE']._serialized_start=2379 + _globals['_USERSHAREDFOLDERACCESSRESPONSE']._serialized_end=2484 + _globals['_USERSHAREDFOLDERACCESSRESPONSES']._serialized_start=2486 + _globals['_USERSHAREDFOLDERACCESSRESPONSES']._serialized_end=2578 + _globals['_USERFOLDERPERMISSIONSREQUEST']._serialized_start=2580 + _globals['_USERFOLDERPERMISSIONSREQUEST']._serialized_end=2645 + _globals['_USERFOLDERPERMISSIONSRESPONSE']._serialized_start=2647 + _globals['_USERFOLDERPERMISSIONSRESPONSE']._serialized_end=2745 + _globals['_USERFOLDERPERMISSIONSRESPONSES']._serialized_start=2747 + _globals['_USERFOLDERPERMISSIONSRESPONSES']._serialized_end=2837 + _globals['_ROTATIONSCHEDULE']._serialized_start=2839 + _globals['_ROTATIONSCHEDULE']._serialized_end=2895 + _globals['_APICALLBACKREQUEST']._serialized_start=2898 + _globals['_APICALLBACKREQUEST']._serialized_end=3042 + _globals['_APICALLBACKSCHEDULE']._serialized_start=3044 + _globals['_APICALLBACKSCHEDULE']._serialized_end=3097 + _globals['_ROUTERSCHEDULEDACTIONS']._serialized_start=3099 + _globals['_ROUTERSCHEDULEDACTIONS']._serialized_end=3163 + _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_start=3165 + _globals['_ROUTERRECORDSROTATIONREQUEST']._serialized_end=3254 + _globals['_CONNECTIONPARAMETERS']._serialized_start=3257 + _globals['_CONNECTIONPARAMETERS']._serialized_end=3390 + _globals['_VALIDATECONNECTIONSREQUEST']._serialized_start=3392 + _globals['_VALIDATECONNECTIONSREQUEST']._serialized_end=3471 + _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_start=3473 + _globals['_CONNECTIONVALIDATIONFAILURE']._serialized_end=3547 + _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_start=3549 + _globals['_VALIDATECONNECTIONSRESPONSE']._serialized_end=3642 + _globals['_GETENFORCEMENTREQUEST']._serialized_start=3644 + _globals['_GETENFORCEMENTREQUEST']._serialized_end=3693 + _globals['_ENFORCEMENTTYPE']._serialized_start=3695 + _globals['_ENFORCEMENTTYPE']._serialized_end=3754 + _globals['_GETENFORCEMENTRESPONSE']._serialized_start=3756 + _globals['_GETENFORCEMENTRESPONSE']._serialized_end=3868 + _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_start=3870 + _globals['_PEDMTOTPVALIDATEREQUEST']._serialized_end=3949 + _globals['_GETPEDMADMININFORESPONSE']._serialized_start=3951 + _globals['_GETPEDMADMININFORESPONSE']._serialized_end=4023 + _globals['_PAMNETWORKSETTINGS']._serialized_start=4025 + _globals['_PAMNETWORKSETTINGS']._serialized_end=4150 + _globals['_PAMNETWORKCONFIGURATIONREQUEST']._serialized_start=4153 + _globals['_PAMNETWORKCONFIGURATIONREQUEST']._serialized_end=4381 + _globals['_PAMDISCOVERYRULESSETREQUEST']._serialized_start=4383 + _globals['_PAMDISCOVERYRULESSETREQUEST']._serialized_end=4465 + _globals['_ROUTER2FAVALIDATEREQUEST']._serialized_start=4467 + _globals['_ROUTER2FAVALIDATEREQUEST']._serialized_end=4579 + _globals['_ROUTER2FASENDPUSHREQUEST']._serialized_start=4581 + _globals['_ROUTER2FASENDPUSHREQUEST']._serialized_end=4707 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGEREQUEST']._serialized_start=4709 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGEREQUEST']._serialized_end=4794 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGERESPONSE']._serialized_start=4796 + _globals['_ROUTER2FAGETWEBAUTHNCHALLENGERESPONSE']._serialized_end=4900 + _globals['_CREATEEPHEMERALSECRETREQUEST']._serialized_start=4902 + _globals['_CREATEEPHEMERALSECRETREQUEST']._serialized_end=4993 + _globals['_USERACCESSLOWEREDEVENT']._serialized_start=4996 + _globals['_USERACCESSLOWEREDEVENT']._serialized_end=5212 + _globals['_USERACCESSLOWEREDEVENTSREQUEST']._serialized_start=5214 + _globals['_USERACCESSLOWEREDEVENTSREQUEST']._serialized_end=5294 # @@protoc_insertion_point(module_scope) diff --git a/keepersdk-package/src/keepersdk/proto/router_pb2.pyi b/keepersdk-package/src/keepersdk/proto/router_pb2.pyi index 92d676da..830944e2 100644 --- a/keepersdk-package/src/keepersdk/proto/router_pb2.pyi +++ b/keepersdk-package/src/keepersdk/proto/router_pb2.pyi @@ -1,6 +1,6 @@ -import pam_pb2 as _pam_pb2 -import APIRequest_pb2 as _APIRequest_pb2 -import folder_pb2 as _folder_pb2 +from . import pam_pb2 as _pam_pb2 +from . import APIRequest_pb2 as _APIRequest_pb2 +from . 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 @@ -43,6 +43,14 @@ class ServiceType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): UNSPECIFIED: _ClassVar[ServiceType] KA: _ClassVar[ServiceType] BI: _ClassVar[ServiceType] + +class UserAccessLoweredEventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + UALE_UNSPECIFIED: _ClassVar[UserAccessLoweredEventType] + UALE_DEVICE_LOGOUT: _ClassVar[UserAccessLoweredEventType] + UALE_USER_LOGOUT_ALL_DEVICES: _ClassVar[UserAccessLoweredEventType] + UALE_ENFORCEMENT_REMOVED: _ClassVar[UserAccessLoweredEventType] + UALE_RECORD_ACCESS_LOST: _ClassVar[UserAccessLoweredEventType] RRC_OK: RouterResponseCode RRC_GENERAL_ERROR: RouterResponseCode RRC_NOT_ALLOWED: RouterResponseCode @@ -66,6 +74,11 @@ RRAL_OWNER: UserRecordAccessLevel UNSPECIFIED: ServiceType KA: ServiceType BI: ServiceType +UALE_UNSPECIFIED: UserAccessLoweredEventType +UALE_DEVICE_LOGOUT: UserAccessLoweredEventType +UALE_USER_LOGOUT_ALL_DEVICES: UserAccessLoweredEventType +UALE_ENFORCEMENT_REMOVED: UserAccessLoweredEventType +UALE_RECORD_ACCESS_LOST: UserAccessLoweredEventType class RouterResponse(_message.Message): __slots__ = ("responseCode", "errorMessage", "encryptedPayload") @@ -94,7 +107,7 @@ class RouterControllerMessage(_message.Message): 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") + __slots__ = ("transmissionKey", "sessionToken", "userId", "enterpriseUserId", "deviceName", "deviceToken", "clientVersionId", "needUsername", "username", "mspEnterpriseId", "isPedmAdmin", "mcEnterpriseId", "deviceId") TRANSMISSIONKEY_FIELD_NUMBER: _ClassVar[int] SESSIONTOKEN_FIELD_NUMBER: _ClassVar[int] USERID_FIELD_NUMBER: _ClassVar[int] @@ -107,6 +120,7 @@ class RouterUserAuth(_message.Message): MSPENTERPRISEID_FIELD_NUMBER: _ClassVar[int] ISPEDMADMIN_FIELD_NUMBER: _ClassVar[int] MCENTERPRISEID_FIELD_NUMBER: _ClassVar[int] + DEVICEID_FIELD_NUMBER: _ClassVar[int] transmissionKey: bytes sessionToken: bytes userId: int @@ -119,7 +133,8 @@ 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: bool = ..., username: _Optional[str] = ..., mspEnterpriseId: _Optional[int] = ..., isPedmAdmin: bool = ..., mcEnterpriseId: _Optional[int] = ...) -> None: ... + deviceId: int + def __init__(self, transmissionKey: _Optional[bytes] = ..., sessionToken: _Optional[bytes] = ..., userId: _Optional[int] = ..., enterpriseUserId: _Optional[int] = ..., deviceName: _Optional[str] = ..., deviceToken: _Optional[bytes] = ..., clientVersionId: _Optional[int] = ..., needUsername: bool = ..., username: _Optional[str] = ..., mspEnterpriseId: _Optional[int] = ..., isPedmAdmin: bool = ..., mcEnterpriseId: _Optional[int] = ..., deviceId: _Optional[int] = ...) -> None: ... class RouterDeviceAuth(_message.Message): __slots__ = ("clientId", "clientVersion", "signature", "enterpriseId", "nodeId", "deviceName", "deviceToken", "controllerName", "controllerUid", "ownerUser", "challenge", "ownerId", "maxInstanceCount") @@ -182,7 +197,7 @@ class RouterRecordRotationsResponse(_message.Message): 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") + __slots__ = ("status", "configurationUid", "resourceUid", "nodeId", "controllerUid", "controllerName", "scriptName", "pwdComplexity", "disabled", "scripts") STATUS_FIELD_NUMBER: _ClassVar[int] CONFIGURATIONUID_FIELD_NUMBER: _ClassVar[int] RESOURCEUID_FIELD_NUMBER: _ClassVar[int] @@ -192,6 +207,7 @@ class RouterRotationInfo(_message.Message): SCRIPTNAME_FIELD_NUMBER: _ClassVar[int] PWDCOMPLEXITY_FIELD_NUMBER: _ClassVar[int] DISABLED_FIELD_NUMBER: _ClassVar[int] + SCRIPTS_FIELD_NUMBER: _ClassVar[int] status: RouterRotationStatus configurationUid: bytes resourceUid: bytes @@ -201,10 +217,11 @@ class RouterRotationInfo(_message.Message): scriptName: str pwdComplexity: str disabled: bool - def __init__(self, status: _Optional[_Union[RouterRotationStatus, str]] = ..., configurationUid: _Optional[bytes] = ..., resourceUid: _Optional[bytes] = ..., nodeId: _Optional[int] = ..., controllerUid: _Optional[bytes] = ..., controllerName: _Optional[str] = ..., scriptName: _Optional[str] = ..., pwdComplexity: _Optional[str] = ..., disabled: bool = ...) -> None: ... + scripts: _containers.RepeatedScalarFieldContainer[bytes] + 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 = ..., scripts: _Optional[_Iterable[bytes]] = ...) -> 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 +234,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 +248,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: bool = ..., remoteAddress: _Optional[str] = ..., clientVersionId: _Optional[int] = ..., noop: 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 +261,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",) @@ -281,6 +304,28 @@ class UserSharedFolderAccessResponses(_message.Message): 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] @@ -400,10 +445,14 @@ class GetPEDMAdminInfoResponse(_message.Message): 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") @@ -428,14 +477,16 @@ class PAMDiscoveryRulesSetRequest(_message.Message): def __init__(self, networkUid: _Optional[bytes] = ..., rules: _Optional[bytes] = ..., rulesKey: _Optional[bytes] = ...) -> None: ... class Router2FAValidateRequest(_message.Message): - __slots__ = ("transmissionKey", "sessionToken", "value") + __slots__ = ("transmissionKey", "sessionToken", "value", "challengeToken") TRANSMISSIONKEY_FIELD_NUMBER: _ClassVar[int] SESSIONTOKEN_FIELD_NUMBER: _ClassVar[int] VALUE_FIELD_NUMBER: _ClassVar[int] + CHALLENGETOKEN_FIELD_NUMBER: _ClassVar[int] transmissionKey: bytes sessionToken: bytes value: str - def __init__(self, transmissionKey: _Optional[bytes] = ..., sessionToken: _Optional[bytes] = ..., value: _Optional[str] = ...) -> None: ... + challengeToken: bytes + def __init__(self, transmissionKey: _Optional[bytes] = ..., sessionToken: _Optional[bytes] = ..., value: _Optional[str] = ..., challengeToken: _Optional[bytes] = ...) -> None: ... class Router2FASendPushRequest(_message.Message): __slots__ = ("transmissionKey", "sessionToken", "pushType") @@ -456,12 +507,14 @@ class Router2FAGetWebAuthnChallengeRequest(_message.Message): def __init__(self, transmissionKey: _Optional[bytes] = ..., sessionToken: _Optional[bytes] = ...) -> None: ... class Router2FAGetWebAuthnChallengeResponse(_message.Message): - __slots__ = ("challenge", "capabilities") + __slots__ = ("challenge", "capabilities", "challengeToken") CHALLENGE_FIELD_NUMBER: _ClassVar[int] CAPABILITIES_FIELD_NUMBER: _ClassVar[int] + CHALLENGETOKEN_FIELD_NUMBER: _ClassVar[int] challenge: str capabilities: _containers.RepeatedScalarFieldContainer[str] - def __init__(self, challenge: _Optional[str] = ..., capabilities: _Optional[_Iterable[str]] = ...) -> None: ... + challengeToken: bytes + def __init__(self, challenge: _Optional[str] = ..., capabilities: _Optional[_Iterable[str]] = ..., challengeToken: _Optional[bytes] = ...) -> None: ... class CreateEphemeralSecretRequest(_message.Message): __slots__ = ("encryptedSecret", "secretKeyHash", "ttl") @@ -472,3 +525,23 @@ class CreateEphemeralSecretRequest(_message.Message): secretKeyHash: bytes ttl: int def __init__(self, encryptedSecret: _Optional[bytes] = ..., secretKeyHash: _Optional[bytes] = ..., ttl: _Optional[int] = ...) -> None: ... + +class UserAccessLoweredEvent(_message.Message): + __slots__ = ("eventType", "enterpriseUserIds", "recordUids", "deviceId", "enforcementTypeId") + EVENTTYPE_FIELD_NUMBER: _ClassVar[int] + ENTERPRISEUSERIDS_FIELD_NUMBER: _ClassVar[int] + RECORDUIDS_FIELD_NUMBER: _ClassVar[int] + DEVICEID_FIELD_NUMBER: _ClassVar[int] + ENFORCEMENTTYPEID_FIELD_NUMBER: _ClassVar[int] + eventType: UserAccessLoweredEventType + enterpriseUserIds: _containers.RepeatedScalarFieldContainer[int] + recordUids: _containers.RepeatedScalarFieldContainer[bytes] + deviceId: int + enforcementTypeId: int + def __init__(self, eventType: _Optional[_Union[UserAccessLoweredEventType, str]] = ..., enterpriseUserIds: _Optional[_Iterable[int]] = ..., recordUids: _Optional[_Iterable[bytes]] = ..., deviceId: _Optional[int] = ..., enforcementTypeId: _Optional[int] = ...) -> None: ... + +class UserAccessLoweredEventsRequest(_message.Message): + __slots__ = ("events",) + EVENTS_FIELD_NUMBER: _ClassVar[int] + events: _containers.RepeatedCompositeFieldContainer[UserAccessLoweredEvent] + def __init__(self, events: _Optional[_Iterable[_Union[UserAccessLoweredEvent, _Mapping]]] = ...) -> None: ... diff --git a/keepersdk-package/src/keepersdk/vault/ksm_management.py b/keepersdk-package/src/keepersdk/vault/ksm_management.py index 566a0573..3c83f3ae 100644 --- a/keepersdk-package/src/keepersdk/vault/ksm_management.py +++ b/keepersdk-package/src/keepersdk/vault/ksm_management.py @@ -8,6 +8,8 @@ from urllib import parse from . import ksm, record_management, shares_management, share_management_utils, vault_online, vault_record, vault_types +from . import nsf_management, nsf_sharing, vault_extensions +from .nsf_management import NsfError from .. import utils, crypto, constants from ..enterprise import enterprise_data from ..proto.APIRequest_pb2 import ( @@ -16,10 +18,13 @@ AppShareAdd, AddAppSharesRequest, RemoveAppSharesRequest ) from ..errors import KeeperApiError -from ..proto.enterprise_pb2 import GENERAL +from ..proto.enterprise_pb2 import ( + GENERAL, + DISCOVERY_AND_ROTATION_CONTROLLER, + KCM_CONTROLLER, +) from ..proto import record_pb2 from ..proto.record_pb2 import ApplicationAddRequest, RecordUpdate, RecordsUpdateRequest -from . import vault_extensions URL_GET_SUMMARY_API = 'vault/get_applications_summary' URL_GET_APP_INFO_API = 'vault/get_app_info' @@ -34,6 +39,13 @@ CLIENT_SHORT_ID_LENGTH = 8 +# Client types shown by secrets-manager-app get (GENERAL KSM + PAM/KCM gateways). +_DISPLAY_APP_CLIENT_TYPES = frozenset({ + GENERAL, + DISCOVERY_AND_ROTATION_CONTROLLER, + KCM_CONTROLLER, +}) + MILLISECONDS_PER_SECOND = 1000 CLIENT_ID_COUNTER_BYTES = b'KEEPER_SECRETS_MANAGER_CLIENT_ID' @@ -78,7 +90,9 @@ def get_secrets_manager_app(vault: vault_online.VaultOnline, uid_or_name: str) - raise ValueError('No Secrets Manager Applications returned.') app_info = app_infos[0] - client_devices = [x for x in app_info.clients if x.appClientType == GENERAL] + client_devices = [ + x for x in app_info.clients if x.appClientType in _DISPLAY_APP_CLIENT_TYPES + ] client_list = [] for c in client_devices: client_id = utils.base64_url_encode(c.clientId) @@ -97,13 +111,18 @@ def get_secrets_manager_app(vault: vault_online.VaultOnline, uid_or_name: str) - shared_secrets = [] for share in getattr(app_info, 'shares', []): - shared_secrets.append(handle_share_type(share, ksm_app, vault)) + info = handle_share_type(share, ksm_app, vault) + if info is not None: + shared_secrets.append(info) records_count = len([ s for s in getattr(app_info, 'shares', []) if ApplicationShareType.Name(s.shareType) == 'SHARE_TYPE_RECORD' ]) - folders_count = len(shared_secrets) - records_count + folders_count = len([ + s for s in getattr(app_info, 'shares', []) + if ApplicationShareType.Name(s.shareType) == 'SHARE_TYPE_FOLDER' + ]) return ksm.SecretsManagerApp( name=ksm_app.title, @@ -298,26 +317,32 @@ def _get_app_user_permissions(vault: vault_online.VaultOnline, uid: str) -> List def _separate_shared_items(vault: vault_online.VaultOnline, shared_secrets): - """Separate shared secrets into records and folders.""" + """Separate shared secrets into classic records and shared folders. + + NSF folder/record shares are skipped here: cascading user-permission + updates use classic share APIs that do not apply to NSF. + """ shared_recs = [] shared_folders = [] - + for share in shared_secrets: uid_str = utils.base64_url_encode(share.secretUid) share_type = ApplicationShareType.Name(share.shareType) - - if share_type == ApplicationShareType.SHARE_TYPE_RECORD: - shared_recs.append(uid_str) - elif share_type == ApplicationShareType.SHARE_TYPE_FOLDER: - shared_folders.append(uid_str) - + + if share_type == 'SHARE_TYPE_RECORD': + if uid_str in vault.vault_data._records: + shared_recs.append(uid_str) + elif share_type == 'SHARE_TYPE_FOLDER': + if uid_str in vault.vault_data._shared_folders: + shared_folders.append(uid_str) + if shared_recs: share_management_utils.get_record_shares( - vault=vault, - record_uids=shared_recs, + vault=vault, + record_uids=shared_recs, is_share_admin=False ) - + return shared_recs, shared_folders @@ -561,15 +586,28 @@ def handle_share_type(share, ksm_app, vault: vault_online.VaultOnline): editable_status = share.editable if share_type == 'SHARE_TYPE_RECORD': - return ksm.SharedSecretsInfo(type='RECORD', uid=uid_str, name=ksm_app.title, permissions=editable_status) - - elif share_type == 'SHARE_TYPE_FOLDER': - cached_sf = next((f for f in vault.vault_data.folders() if f.folder_uid == uid_str), None) - if cached_sf: - return ksm.SharedSecretsInfo(type='FOLDER', uid=uid_str, name=cached_sf.name, permissions=editable_status) - - else: - return None + return ksm.SharedSecretsInfo( + type='RECORD', uid=uid_str, name=ksm_app.title, permissions=editable_status + ) + + if share_type == 'SHARE_TYPE_FOLDER': + folder_name = uid_str + cached_sf = next( + (f for f in vault.vault_data.folders() if f.folder_uid == uid_str), None + ) + if cached_sf is not None: + folder_name = cached_sf.name or uid_str + else: + nsf = vault.nsf_data + if nsf is not None: + nsf_folder = nsf.get_folder(uid_str) + if nsf_folder is not None: + folder_name = nsf_folder.name or uid_str + return ksm.SharedSecretsInfo( + type='FOLDER', uid=uid_str, name=folder_name, permissions=editable_status + ) + + return None class KSMClientManagement: @@ -585,7 +623,8 @@ def add_client_to_ksm_app( first_access_expire_duration_ms: int, access_expire_in_ms: Optional[int], master_key: bytes, - server: str) -> Dict: + server: str, + client_type: int) -> Dict: """Generate a single client device and return token info and output string.""" # Generate secret and client ID @@ -605,7 +644,8 @@ def add_client_to_ksm_app( client_id=client_id, client_name=client_name, count=count, - index=index + index=index, + client_type=client_type ) # Generate token with server prefix @@ -650,7 +690,8 @@ def _create_client_request( client_id: bytes, client_name: str, count: int, - index: int) -> Device: + index: int, + client_type: int) -> Device: """Create and send client request to server.""" request = AddAppClientRequest() @@ -658,7 +699,7 @@ def _create_client_request( request.encryptedAppKey = encrypted_master_key request.lockIp = not unlock_ip request.firstAccessExpireOn = first_access_expire_duration_ms - request.appClientType = GENERAL + request.appClientType = client_type request.clientId = client_id if access_expire_in_ms: @@ -822,25 +863,80 @@ class KSMShareManagement: @staticmethod def add_secrets_to_ksm_app(vault: vault_online.VaultOnline, enterprise:enterprise_data.EnterpriseData, app_uid: str, master_key: bytes, secret_uids: List[str], is_editable: bool = False) -> List: - """Share secrets with a KSM application.""" + """Share secrets with a KSM application. - app_shares, added_secret_info = KSMShareManagement._process_all_secrets( - vault, secret_uids, master_key, is_editable - ) + Classic records/shared folders use vault/app_share_add. + NSF folders use folders/v3/access_update with AT_APPLICATION. + """ + app_shares = [] + added_secret_info = [] + nsf_folder_uids = [] + + for secret_uid in secret_uids: + kind = KSMShareManagement._classify_secret(vault, secret_uid) + if kind is None: + KSMShareManagement._log_invalid_secret_warning(secret_uid) + continue + + channel, resolved_uid, type_label = kind + if channel == 'nsf_folder': + nsf_sharing.grant_nsf_folder_to_application( + vault, resolved_uid, app_uid, + is_editable=is_editable, request_sync=False) + added_secret_info.append((resolved_uid, type_label)) + nsf_folder_uids.append(resolved_uid) + continue + + share_info = KSMShareManagement._process_secret( + vault, resolved_uid, master_key, is_editable + ) + if share_info: + app_shares.append(share_info['app_share']) + added_secret_info.append(share_info['secret_info']) if not added_secret_info: raise ValueError("No valid secrets found to share.") - KSMShareManagement._send_share_request( - vault, app_uid, app_shares - ) + if app_shares: + KSMShareManagement._send_share_request(vault, app_uid, app_shares) vault.sync_down() - _update_shares_user_permissions(vault, enterprise, app_uid, removed=False) + if app_shares: + _update_shares_user_permissions(vault, enterprise, app_uid, removed=False) return added_secret_info + @staticmethod + def _classify_secret( + vault: vault_online.VaultOnline, secret_uid: str + ) -> Optional[Tuple[str, str, str]]: + """Return (channel, resolved_uid, type_label) or None. + + channel is one of: 'classic', 'nsf_folder', 'nsf_record'. + """ + if secret_uid in vault.vault_data._records: + return 'classic', secret_uid, 'Record' + if secret_uid in vault.vault_data._shared_folders: + return 'classic', secret_uid, 'Shared Folder' + + if vault.nsf_data is None: + return None + + folder_uid = nsf_management.resolve_nsf_folder_uid(vault, secret_uid) + if folder_uid is None and vault.nsf_data.get_folder(secret_uid) is not None: + folder_uid = secret_uid + if folder_uid: + return 'nsf_folder', folder_uid, 'NSF Folder' + + record_uid = nsf_management.resolve_nsf_record_uid(vault, secret_uid) + if record_uid is None and vault.nsf_data.get_record(secret_uid) is not None: + record_uid = secret_uid + if record_uid: + return 'nsf_record', record_uid, 'NSF Record' + + return None + @staticmethod def _process_all_secrets(vault: vault_online.VaultOnline, secret_uids: List[str], master_key: bytes, is_editable: bool) -> Tuple[List, List]: @@ -862,41 +958,84 @@ def _process_all_secrets(vault: vault_online.VaultOnline, secret_uids: List[str] @staticmethod def _process_secret(vault: vault_online.VaultOnline, secret_uid: str, master_key: bytes, is_editable: bool) -> Optional[Dict]: - """Process a single secret and create share request.""" + """Process a single classic/NSF-record secret into an app_share_add payload.""" secret_info = KSMShareManagement._get_secret_info(vault, secret_uid) if not secret_info: return None - share_key_decrypted, share_type, secret_type_name = secret_info + share_key_decrypted, share_type, secret_type_name, resolved_uid = secret_info if not share_key_decrypted: logging.warning(f"Could not retrieve key for secret {secret_uid}") return None app_share = KSMShareManagement._build_app_share( - secret_uid, share_key_decrypted, master_key, share_type, is_editable + resolved_uid, share_key_decrypted, master_key, share_type, is_editable ) return { 'app_share': app_share, - 'secret_info': (secret_uid, secret_type_name) + 'secret_info': (resolved_uid, secret_type_name) } @staticmethod def _get_secret_info(vault: vault_online.VaultOnline, secret_uid: str) -> Optional[Tuple]: - """Get secret information (key, type, name) for a given UID.""" + """Resolve secret key/type for classic or NSF record UID (or NSF name). + + NSF folders are not handled here — they use AT_APPLICATION access_update. + Returns (share_key, share_type, type_label, resolved_uid) or None. + """ is_record = secret_uid in vault.vault_data._records is_shared_folder = secret_uid in vault.vault_data._shared_folders if is_record: - return KSMShareManagement._get_record_secret_info(vault, secret_uid) - elif is_shared_folder: - return KSMShareManagement._get_folder_secret_info(vault, secret_uid) - else: - KSMShareManagement._log_invalid_secret_warning(secret_uid) + info = KSMShareManagement._get_record_secret_info(vault, secret_uid) + return (*info, secret_uid) if info else None + if is_shared_folder: + info = KSMShareManagement._get_folder_secret_info(vault, secret_uid) + return (*info, secret_uid) + + nsf_info = KSMShareManagement._get_nsf_record_secret_info(vault, secret_uid) + if nsf_info: + return nsf_info + + KSMShareManagement._log_invalid_secret_warning(secret_uid) + return None + + @staticmethod + def _get_nsf_record_secret_info( + vault: vault_online.VaultOnline, secret_uid: str + ) -> Optional[Tuple]: + """Resolve an NSF record (by UID or exact name) for app_share_add.""" + if vault.nsf_data is None: return None + # Skip NSF folders here — those must use AT_APPLICATION. + folder_uid = nsf_management.resolve_nsf_folder_uid(vault, secret_uid) + if folder_uid is None and vault.nsf_data.get_folder(secret_uid) is not None: + folder_uid = secret_uid + if folder_uid: + return None + + record_uid = nsf_management.resolve_nsf_record_uid(vault, secret_uid) + if record_uid is None and vault.nsf_data.get_record(secret_uid) is not None: + record_uid = secret_uid + if not record_uid: + return None + + try: + record_key = nsf_management._get_record_key(vault, record_uid) + except NsfError as e: + logging.warning('Could not resolve NSF record key for %s: %s', secret_uid, e) + return None + return ( + record_key, + ApplicationShareType.SHARE_TYPE_RECORD, + 'NSF Record', + record_uid, + ) + @staticmethod def _get_record_secret_info(vault: vault_online.VaultOnline, secret_uid: str) -> Optional[Tuple]: """Get secret info for a record.""" @@ -923,8 +1062,10 @@ def _get_folder_secret_info(vault: vault_online.VaultOnline, secret_uid: str) -> def _log_invalid_secret_warning(secret_uid: str) -> None: """Log warning for invalid secret UID.""" logging.warning( - f"UID='{secret_uid}' is not a Record nor Shared Folder. " - "Only individual records or Shared Folders can be added to the application. " + "UID='%s' is not a classic/NSF record or folder. " + "Share individual records, classic Shared Folders, or NSF folders/records. " + "Run sync-down (or sync-down --force) and try again.", + secret_uid, ) @staticmethod @@ -958,16 +1099,29 @@ def _build_share_request(app_uid: str, app_shares: List) -> AddAppSharesRequest: @staticmethod def remove_secrets_from_ksm_app(vault: vault_online.VaultOnline, app_uid: str, secret_uids: List[str]) -> None: - """Send remove share request to server.""" - request = RemoveAppSharesRequest() - request.appRecordUid = utils.base64_url_decode(app_uid) - request.shares.extend(utils.base64_url_decode(uid) for uid in secret_uids) - vault.keeper_auth.execute_auth_rest(rest_endpoint=SHARE_REMOVE_URL, request=request) + """Remove classic app shares and/or NSF AT_APPLICATION folder access.""" + classic_uids = [] + for uid in secret_uids: + kind = KSMShareManagement._classify_secret(vault, uid) + if kind and kind[0] == 'nsf_folder': + nsf_sharing.revoke_nsf_folder_from_application( + vault, kind[1], app_uid, request_sync=False) + else: + classic_uids.append(uid) + + if classic_uids: + request = RemoveAppSharesRequest() + request.appRecordUid = utils.base64_url_decode(app_uid) + request.shares.extend(utils.base64_url_decode(uid) for uid in classic_uids) + vault.keeper_auth.execute_auth_rest( + rest_endpoint=SHARE_REMOVE_URL, request=request) + + vault.sync_down() @staticmethod def update_secrets_in_ksm_app(vault: vault_online.VaultOnline, app_uid: str, secret_uids: List[str], is_editable: bool) -> List[str]: - """Update editable vs read-only on secrets already shared with the app (remove + re-add).""" + """Update editable vs read-only on secrets already shared with the app.""" if not secret_uids: raise ValueError('At least one secret UID is required') @@ -984,8 +1138,19 @@ def update_secrets_in_ksm_app(vault: vault_online.VaultOnline, app_uid: str, sec for share in getattr(app_infos[0], 'shares', []) } - uids_to_update = [] + updated_uids = [] + classic_uids_to_re_add = [] + for uid in secret_uids: + kind = KSMShareManagement._classify_secret(vault, uid) + if kind and kind[0] == 'nsf_folder': + folder_uid = kind[1] + nsf_sharing.update_nsf_folder_application_access( + vault, folder_uid, app_uid, + is_editable=is_editable, request_sync=False) + updated_uids.append(folder_uid) + continue + if uid not in existing_shares: logging.warning( 'Secret "%s" is not currently shared with this application. ' @@ -996,23 +1161,33 @@ def update_secrets_in_ksm_app(vault: vault_online.VaultOnline, app_uid: str, sec perm = 'editable' if is_editable else 'read-only' logging.info('Secret "%s" is already %s. No change needed.', uid, perm) continue - uids_to_update.append(uid) - - if not uids_to_update: + classic_uids_to_re_add.append(uid) + + if classic_uids_to_re_add: + request = RemoveAppSharesRequest() + request.appRecordUid = utils.base64_url_decode(app_uid) + request.shares.extend( + utils.base64_url_decode(uid) for uid in classic_uids_to_re_add) + vault.keeper_auth.execute_auth_rest( + rest_endpoint=SHARE_REMOVE_URL, request=request) + + app_shares = [] + for uid in classic_uids_to_re_add: + share_info = KSMShareManagement._process_secret( + vault, uid, master_key, is_editable) + if share_info: + app_shares.append(share_info['app_share']) + updated_uids.append(uid) + + if not app_shares and not updated_uids: + raise ValueError( + 'No valid secrets found to update. Run sync-down and try again.') + if app_shares: + KSMShareManagement._send_share_request(vault, app_uid, app_shares) + + if not updated_uids: logging.warning('No share permissions to update.') return [] - KSMShareManagement.remove_secrets_from_ksm_app(vault, app_uid, uids_to_update) - - app_shares = [] - for uid in uids_to_update: - share_info = KSMShareManagement._process_secret(vault, uid, master_key, is_editable) - if share_info: - app_shares.append(share_info['app_share']) - - if not app_shares: - raise ValueError('No valid secrets found to update. Run sync-down and try again.') - - KSMShareManagement._send_share_request(vault, app_uid, app_shares) vault.sync_down() - return uids_to_update \ No newline at end of file + return updated_uids diff --git a/keepersdk-package/src/keepersdk/vault/nsf_crypto.py b/keepersdk-package/src/keepersdk/vault/nsf_crypto.py index 43cd3e57..ec960ab2 100644 --- a/keepersdk-package/src/keepersdk/vault/nsf_crypto.py +++ b/keepersdk-package/src/keepersdk/vault/nsf_crypto.py @@ -1,7 +1,8 @@ from __future__ import annotations import json -from typing import Dict, List, Optional +from dataclasses import dataclass +from typing import Any, Dict, List, Mapping, Optional from .. import crypto, utils from ..authentication import keeper_auth @@ -11,6 +12,15 @@ _FOLDER_KEY_ENCRYPTION = folder_pb2.FolderKeyEncryptionType _ENCRYPTED_KEY_TYPE = folder_pb2.EncryptedKeyType +_ACCESS_TYPE = folder_pb2.AccessType + + +@dataclass(frozen=True) +class TeamKeyMaterial: + """Decrypted team keys used to unwrap team-shared NSF folder keys.""" + team_key: bytes + rsa_private_key: Optional[Any] = None + ec_private_key: Optional[Any] = None def try_decrypt_symmetric(encrypted_key: bytes, symmetric_key: bytes) -> Optional[bytes]: @@ -42,30 +52,85 @@ def try_decrypt_with_user_keys(encrypted_key: bytes, auth_context: keeper_auth.A return None +def try_decrypt_with_typed_key( + encrypted_key: bytes, + key_type: int, + *, + aes_key: Optional[bytes] = None, + rsa_key: Optional[Any] = None, + ecc_key: Optional[Any] = None) -> Optional[bytes]: + """Decrypt using the algorithm indicated by *key_type* (Vault decryptFolderKeyByType).""" + try: + if key_type == int(_ENCRYPTED_KEY_TYPE.encrypted_by_data_key_gcm): + if aes_key is not None: + return crypto.decrypt_aes_v2(encrypted_key, aes_key) + elif key_type == int(_ENCRYPTED_KEY_TYPE.encrypted_by_data_key): + if aes_key is not None: + return crypto.decrypt_aes_v1(encrypted_key, aes_key) + elif key_type == int(_ENCRYPTED_KEY_TYPE.encrypted_by_public_key): + if rsa_key is not None: + return crypto.decrypt_rsa(encrypted_key, rsa_key) + elif key_type == int(_ENCRYPTED_KEY_TYPE.encrypted_by_public_key_ecc): + if ecc_key is not None: + return crypto.decrypt_ec(encrypted_key, ecc_key) + except Exception: + return None + return None + + def try_decrypt_from_folder_access( folder_uid: str, storage: INSFStorage, - auth_context: keeper_auth.AuthContext) -> Optional[bytes]: + auth_context: keeper_auth.AuthContext, + teams: Optional[Mapping[str, TeamKeyMaterial]] = None) -> Optional[bytes]: + """Unwrap folder key from folderAccesses (user or team), mirroring Web Vault.""" + teams = teams or {} for fa in storage.folder_accesses.get_links_by_subject(folder_uid): if not fa.folder_key_encrypted: continue try: enc_key = utils.base64_url_decode(fa.folder_key_encrypted) key_type = fa.folder_key_type - if key_type == int(_ENCRYPTED_KEY_TYPE.encrypted_by_data_key_gcm): - return crypto.decrypt_aes_v2(enc_key, auth_context.data_key) - if key_type == int(_ENCRYPTED_KEY_TYPE.encrypted_by_data_key): - return crypto.decrypt_aes_v1(enc_key, auth_context.data_key) - if key_type == int(_ENCRYPTED_KEY_TYPE.encrypted_by_public_key): - if auth_context.rsa_private_key is not None: - return crypto.decrypt_rsa(enc_key, auth_context.rsa_private_key) - elif key_type == int(_ENCRYPTED_KEY_TYPE.encrypted_by_public_key_ecc): - if auth_context.ec_private_key is not None: - return crypto.decrypt_ec(enc_key, auth_context.ec_private_key) - else: - result = try_decrypt_with_user_keys(enc_key, auth_context) - if result is not None: - return result + access_uid = fa.access_type_uid + use_team = ( + fa.access_type == int(_ACCESS_TYPE.AT_TEAM) + or (access_uid in teams) + ) + + folder_key: Optional[bytes] = None + if use_team and access_uid in teams: + team = teams[access_uid] + folder_key = try_decrypt_with_typed_key( + enc_key, key_type, + aes_key=team.team_key, + rsa_key=team.rsa_private_key, + ecc_key=team.ec_private_key, + ) + if folder_key is None: + folder_key = try_decrypt_symmetric(enc_key, team.team_key) + if folder_key is None and team.rsa_private_key is not None: + try: + folder_key = crypto.decrypt_rsa(enc_key, team.rsa_private_key) + except Exception: + pass + if folder_key is None and team.ec_private_key is not None: + try: + folder_key = crypto.decrypt_ec(enc_key, team.ec_private_key) + except Exception: + pass + + if folder_key is None: + folder_key = try_decrypt_with_typed_key( + enc_key, key_type, + aes_key=auth_context.data_key, + rsa_key=auth_context.rsa_private_key, + ecc_key=auth_context.ec_private_key, + ) + if folder_key is None: + folder_key = try_decrypt_with_user_keys(enc_key, auth_context) + + if folder_key is not None and len(folder_key) == 32: + return folder_key except Exception: continue return None @@ -74,18 +139,28 @@ def try_decrypt_from_folder_access( def try_decrypt_folder_key( fk: nsf.NSFFolderKey, auth_context: keeper_auth.AuthContext, - decrypted_folder_keys: Dict[str, bytes]) -> Optional[bytes]: + decrypted_folder_keys: Dict[str, bytes], + teams: Optional[Mapping[str, TeamKeyMaterial]] = None) -> Optional[bytes]: + """ + Attempt decrypt from a FolderKey link. + + Returns None for ENCRYPTED_BY_TEAM_KEY (caller must use folderAccesses). + For PARENT_KEY without a decrypted parent, returns None so caller can fall back. + """ + enc_key_type = fk.encrypted_by + if enc_key_type == int(_FOLDER_KEY_ENCRYPTION.ENCRYPTED_BY_TEAM_KEY): + return None # key lives in folderAccesses if not fk.folder_key: return None try: - enc_key_type = fk.encrypted_by encrypted_key = utils.base64_url_decode(fk.folder_key) if enc_key_type == int(_FOLDER_KEY_ENCRYPTION.ENCRYPTED_BY_USER_KEY): return try_decrypt_with_user_keys(encrypted_key, auth_context) if enc_key_type == int(_FOLDER_KEY_ENCRYPTION.ENCRYPTED_BY_PARENT_KEY): - if not fk.parent_uid: + parent_uid = fk.parent_uid + if not parent_uid: return None - parent_key = decrypted_folder_keys.get(fk.parent_uid) + parent_key = decrypted_folder_keys.get(parent_uid) if parent_key is None: return None return try_decrypt_symmetric(encrypted_key, parent_key) @@ -114,14 +189,49 @@ def try_decrypt_folder_entity_key( return None +def _folder_needs_access_fallback( + folder_uid: str, + keys_by_folder: Mapping[str, List[nsf.NSFFolderKey]], + decrypted_keys: Mapping[str, bytes]) -> bool: + """True when FolderKey links require folderAccesses (TEAM_KEY or failed PARENT/USER).""" + if folder_uid in decrypted_keys: + return False + folder_keys = keys_by_folder.get(folder_uid, []) + if not folder_keys: + # No FolderKey links — still try folderAccesses (bare sync rows). + return True + for fk in folder_keys: + if fk.encrypted_by == int(_FOLDER_KEY_ENCRYPTION.ENCRYPTED_BY_TEAM_KEY): + return True + if fk.encrypted_by == int(_FOLDER_KEY_ENCRYPTION.ENCRYPTED_BY_PARENT_KEY): + parent_uid = fk.parent_uid + if not parent_uid or parent_uid not in decrypted_keys: + # Parent missing or not yet unwrapped — try accesses (Vault fallback). + return True + # Parent key already available; PARENT_KEY path will handle this folder. + continue + if fk.encrypted_by == int(_FOLDER_KEY_ENCRYPTION.ENCRYPTED_BY_USER_KEY): + return True # USER_KEY already tried; fall back to accesses + return False + + def decrypt_folder_keys( storage: INSFStorage, - auth_context: keeper_auth.AuthContext) -> Dict[str, bytes]: + auth_context: keeper_auth.AuthContext, + teams: Optional[Mapping[str, TeamKeyMaterial]] = None) -> Dict[str, bytes]: + """Decrypt NSF folder keys. Pass *teams* for team-shared folder unwrap. + + Mirrors Commander / Web Vault: folderAccesses unwrap runs inside the progress + loop so TEAM_KEY parents unlock first, then ENCRYPTED_BY_PARENT_KEY children + continue on the next pass (team-shared NSF sub-folders). + """ + teams = teams or {} decrypted_keys: Dict[str, bytes] = {} keys_by_folder: Dict[str, List[nsf.NSFFolderKey]] = {} for fk in storage.folder_keys.get_all_links(): keys_by_folder.setdefault(fk.folder_uid, []).append(fk) folder_rows = list(storage.folders.get_all_entities()) + candidates = set(keys_by_folder.keys()) | {row.folder_uid for row in folder_rows} progress = True while progress: @@ -130,7 +240,7 @@ def decrypt_folder_keys( if folder_uid in decrypted_keys: continue for fk in folder_keys: - key = try_decrypt_folder_key(fk, auth_context, decrypted_keys) + key = try_decrypt_folder_key(fk, auth_context, decrypted_keys, teams) if key is not None: decrypted_keys[folder_uid] = key progress = True @@ -143,17 +253,17 @@ def decrypt_folder_keys( decrypted_keys[row.folder_uid] = key progress = True - for folder_uid in keys_by_folder: - if folder_uid not in decrypted_keys: - key = try_decrypt_from_folder_access(folder_uid, storage, auth_context) + # folderAccesses inside the loop (TEAM_KEY / missing parent / USER miss). + # After a team parent unlocks here, the next iteration unwraps PARENT_KEY children. + for folder_uid in candidates: + if folder_uid in decrypted_keys: + continue + if not _folder_needs_access_fallback(folder_uid, keys_by_folder, decrypted_keys): + continue + key = try_decrypt_from_folder_access(folder_uid, storage, auth_context, teams) if key is not None: decrypted_keys[folder_uid] = key - - for row in storage.folders.get_all_entities(): - if row.folder_uid not in decrypted_keys: - key = try_decrypt_from_folder_access(row.folder_uid, storage, auth_context) - if key is not None: - decrypted_keys[row.folder_uid] = key + progress = True return decrypted_keys diff --git a/keepersdk-package/src/keepersdk/vault/nsf_data.py b/keepersdk-package/src/keepersdk/vault/nsf_data.py index 6e053fec..619beb73 100644 --- a/keepersdk-package/src/keepersdk/vault/nsf_data.py +++ b/keepersdk-package/src/keepersdk/vault/nsf_data.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import Dict, Iterable, List, Optional, Set +from typing import Dict, Iterable, List, Mapping, Optional, Set from ..authentication import keeper_auth from . import nsf_crypto, nsf_storage_types as nsf @@ -103,13 +103,17 @@ def rebuild_data(self, changes: Optional[NSFRebuildTask] = None) -> None: del changes self.rebuild_nsf(self._auth_context) - def rebuild_nsf(self, auth_context: Optional[keeper_auth.AuthContext]) -> None: + def rebuild_nsf( + self, + auth_context: Optional[keeper_auth.AuthContext], + teams: Optional[Mapping[str, nsf_crypto.TeamKeyMaterial]] = None) -> None: self._folders.clear() self._records.clear() if auth_context is None: return - decrypted_folder_keys = nsf_crypto.decrypt_folder_keys(self._storage, auth_context) + decrypted_folder_keys = nsf_crypto.decrypt_folder_keys( + self._storage, auth_context, teams=teams) decrypted_record_keys = nsf_crypto.decrypt_record_keys( self._storage, decrypted_folder_keys, auth_context) diff --git a/keepersdk-package/src/keepersdk/vault/nsf_management.py b/keepersdk-package/src/keepersdk/vault/nsf_management.py index 090d11a1..b1890225 100644 --- a/keepersdk-package/src/keepersdk/vault/nsf_management.py +++ b/keepersdk-package/src/keepersdk/vault/nsf_management.py @@ -3,7 +3,7 @@ import json import os from dataclasses import dataclass -from typing import Any, Dict, Iterable, List, Mapping, Optional +from typing import Any, Dict, Iterable, List, Mapping, Optional, Tuple, Union from .. import crypto, utils from ..errors import KeeperApiError @@ -14,6 +14,9 @@ ROOT_FOLDER_UID = 'AAAAAAAAAAAAAAAAAPmtNA' """Sentinel UID the server uses for the NSF root folder.""" +NSF_RECORD_ADD_BATCH_LIMIT = 1000 +"""Maximum number of NSF records per ``vault/records/v3/add`` request.""" + class NsfError(ValueError): """Raised when NSF operations cannot proceed (missing cache, bad identifier, etc.).""" @@ -37,6 +40,17 @@ class NsfModifyResult: revision: int = 0 +@dataclass(frozen=True) +class NsfRecordAddSpec: + """Specification for creating one NSF record in a batch add request.""" + title: str + record_type: str + folder_uid: Optional[str] = None + fields: Optional[Mapping[str, Any]] = None + notes: Optional[str] = None + record_data: Optional[Mapping[str, Any]] = None + + @dataclass class NsfFolderModifyResult: folder_uid: str @@ -465,7 +479,8 @@ def _get_folder_key(vault: VaultOnline, folder_uid: str) -> bytes: return folder.folder_key auth_context = vault.keeper_auth.auth_context - decrypted = nsf_crypto.decrypt_folder_keys(view.storage, auth_context) + teams = vault.vault_data.get_nsf_team_key_materials() + decrypted = nsf_crypto.decrypt_folder_keys(view.storage, auth_context, teams=teams) key = decrypted.get(folder_uid) if key is None: label = folder.name if folder and folder.name and folder.name != '(NSF Folder)' else folder_uid @@ -534,94 +549,256 @@ def _build_record_add_message( return ra -def _build_legacy_record_add_message( - record_uid: str, - record_key: bytes, - data: Dict[str, Any], - auth_data_key: bytes, - folder_uid: Optional[str], - folder_key: Optional[bytes]) -> record_pb2.RecordAdd: - ra = record_pb2.RecordAdd() - ra.record_uid = utils.base64_url_decode(record_uid) - ra.client_modified_time = utils.current_milli_time() - json_bytes = vault_extensions.get_padded_json_bytes(data) - if folder_uid and folder_key: - ra.folder_uid = utils.base64_url_decode(folder_uid) - ra.record_key = crypto.encrypt_aes_v2(record_key, folder_key) - else: - ra.record_key = crypto.encrypt_aes_v2(record_key, auth_data_key) - ra.data = crypto.encrypt_aes_v2(json_bytes, record_key) - return ra +def _normalize_record_add_spec( + spec: Union[NsfRecordAddSpec, Mapping[str, Any]]) -> NsfRecordAddSpec: + if isinstance(spec, NsfRecordAddSpec): + return spec + record_type = spec.get('record_type') or spec.get('type') + if not record_type: + raise NsfError('record_type is required for each batch record spec') + title = spec.get('title') + if not title: + raise NsfError('title is required for each batch record spec') + return NsfRecordAddSpec( + title=title, + record_type=record_type, + folder_uid=spec.get('folder_uid') or spec.get('folder'), + fields=spec.get('fields'), + notes=spec.get('notes'), + record_data=spec.get('record_data'), + ) + + +def _resolve_nsf_folder_for_add(vault: VaultOnline, folder_uid: str) -> str: + resolved = resolve_nsf_folder_uid(vault, folder_uid) or folder_uid + if not is_nsf_folder(vault, resolved): + raise NsfError(f'NSF folder not found: {folder_uid}') + return resolved def _parse_modify_response( response: record_pb2.RecordsModifyResponse, record_uid: str) -> NsfModifyResult: + results = _parse_batch_modify_response(response, [record_uid]) + return results[0] + + +def _parse_batch_modify_response( + response: record_pb2.RecordsModifyResponse, + record_uids: List[str]) -> List[NsfModifyResult]: if not response.records: raise KeeperApiError('no_results', 'No results from record modify response') - for row in response.records: - if utils.base64_url_encode(row.record_uid) == record_uid: - status_name = record_pb2.RecordModifyResult.Name(row.status) - return NsfModifyResult( - record_uid=record_uid, - success=row.status == record_pb2.RS_SUCCESS, - status=status_name, - message=row.message, - revision=getattr(response, 'revision', 0), - ) - raise KeeperApiError('no_results', f'Record {record_uid} not present in modify response') + if len(response.records) != len(record_uids): + raise KeeperApiError( + 'no_results', + f'Expected {len(record_uids)} record results, received {len(response.records)}') + revision = getattr(response, 'revision', 0) + results: List[NsfModifyResult] = [] + for idx, row in enumerate(response.records): + status_name = record_pb2.RecordModifyResult.Name(row.status) + results.append(NsfModifyResult( + record_uid=record_uids[idx], + success=row.status == record_pb2.RS_SUCCESS, + status=status_name, + message=row.message, + revision=revision, + )) + return results -def create_nsf_record( +def _prepare_nsf_record_add_messages( + vault: VaultOnline, + specs: List[NsfRecordAddSpec], + folder_key_cache: Optional[Dict[str, bytes]] = None, +) -> Tuple[List[str], List[record_endpoints_pb2.RecordAdd]]: + auth = vault.keeper_auth + data_key = auth.auth_context.data_key + cache = folder_key_cache if folder_key_cache is not None else {} + record_uids: List[str] = [] + messages: List[record_endpoints_pb2.RecordAdd] = [] + + for spec in specs: + folder_uid = None + if spec.folder_uid: + folder_uid = _resolve_nsf_folder_for_add(vault, spec.folder_uid) + data = _build_record_data( + spec.record_type, spec.title, spec.fields, spec.notes, spec.record_data) + record_uid = utils.generate_uid() + record_key = os.urandom(32) + folder_key = None + if folder_uid: + if folder_uid not in cache: + cache[folder_uid] = _get_folder_key(vault, folder_uid) + folder_key = cache[folder_uid] + messages.append(_build_record_add_message( + record_uid, record_key, data, data_key, folder_uid, folder_key)) + record_uids.append(record_uid) + return record_uids, messages + + +def _execute_nsf_records_add( + vault: VaultOnline, + record_adds: List[record_endpoints_pb2.RecordAdd], +) -> record_pb2.RecordsModifyResponse: + """Create NSF records via ``vault/records/v3/add`` endpoint.""" + if not record_adds or len(record_adds) > NSF_RECORD_ADD_BATCH_LIMIT: + raise ValueError(f'Provide 1..{NSF_RECORD_ADD_BATCH_LIMIT} records') + + auth = vault.keeper_auth + rq = record_endpoints_pb2.RecordsAddRequest() + rq.clientTime = utils.current_milli_time() + rq.records.extend(record_adds) + response = auth.execute_auth_rest( + 'vault/records/v3/add', rq, response_type=record_pb2.RecordsModifyResponse) + if response is None: + raise KeeperApiError('no_results', 'No results from NSF record add') + return response + + +def create_nsf_records_batch( vault: VaultOnline, + record_specs: Iterable[Union[NsfRecordAddSpec, Mapping[str, Any]]], + *, + request_sync: bool = True, + folder_key_cache: Optional[Dict[str, bytes]] = None) -> List[NsfModifyResult]: + """Create up to 1000 NSF records in a single ``vault/records/v3/add`` request.""" + specs = [_normalize_record_add_spec(spec) for spec in record_specs] + if not specs: + raise ValueError('At least one record spec is required') + if len(specs) > NSF_RECORD_ADD_BATCH_LIMIT: + raise ValueError(f'Maximum {NSF_RECORD_ADD_BATCH_LIMIT} records at a time') + + record_uids, record_adds = _prepare_nsf_record_add_messages( + vault, specs, folder_key_cache=folder_key_cache) + response = _execute_nsf_records_add(vault, record_adds) + results = _parse_batch_modify_response(response, record_uids) + if request_sync: + vault.sync_requested = True + vault.run_pending_jobs() + return results + + +def create_nsf_records( + vault: VaultOnline, + record_specs: Iterable[Union[NsfRecordAddSpec, Mapping[str, Any]]], + *, + request_sync: bool = True) -> List[NsfModifyResult]: + """Create NSF records, chunking into batches of up to 1000 per API request.""" + specs = [_normalize_record_add_spec(spec) for spec in record_specs] + if not specs: + raise ValueError('At least one record spec is required') + + all_results: List[NsfModifyResult] = [] + folder_key_cache: Dict[str, bytes] = {} + for batch_start in range(0, len(specs), NSF_RECORD_ADD_BATCH_LIMIT): + batch = specs[batch_start:batch_start + NSF_RECORD_ADD_BATCH_LIMIT] + is_last = batch_start + len(batch) >= len(specs) + all_results.extend(create_nsf_records_batch( + vault, + batch, + request_sync=request_sync and is_last, + folder_key_cache=folder_key_cache, + )) + return all_results + + +def create_nsf_pam_configuration( + vault: VaultOnline, + record: Any, + folder_uid: str, *, - title: str, - record_type: str, - folder_uid: Optional[str] = None, - fields: Optional[Mapping[str, Any]] = None, - notes: Optional[str] = None, - record_data: Optional[Mapping[str, Any]] = None, request_sync: bool = True) -> NsfModifyResult: - """Create an NSF record.""" - if folder_uid: - resolved = resolve_nsf_folder_uid(vault, folder_uid) or folder_uid - if not is_nsf_folder(vault, resolved): - raise NsfError(f'NSF folder not found: {folder_uid}') - folder_uid = resolved + """Create a PAM configuration in an NSF folder via vault/records/v3/add_pam_configuration. + + Matches Commander ``pam_configuration_create_record_nsf``: encrypts the record + key with the NSF folder key (and owner data key) and posts to the PAM-specific + v3 add endpoint instead of generic ``vault/records/v3/add``. + """ + if not folder_uid: + raise NsfError('NSF folder UID is required to create a PAM configuration') + + resolved = resolve_nsf_folder_uid(vault, folder_uid) or folder_uid + if not is_nsf_folder(vault, resolved): + raise NsfError(f'NSF folder not found: {folder_uid}') + folder_uid = resolved + + if not getattr(record, 'record_uid', None): + record.record_uid = utils.generate_uid() + record_uid = record.record_uid + record_key = utils.generate_aes_key() - data = _build_record_data(record_type, title, fields, notes, record_data) - record_uid = utils.generate_uid() - record_key = os.urandom(32) auth = vault.keeper_auth - folder_key = _get_folder_key(vault, folder_uid) if folder_uid else None + data_key = auth.auth_context.data_key + folder_key = _get_folder_key(vault, folder_uid) + + schema = vault.vault_data.get_record_type_by_name(record.record_type) + record_data = vault_extensions.extract_typed_record_data(record, schema) + client_time = utils.current_milli_time() + json_bytes = vault_extensions.get_padded_json_bytes(record_data) + + ra = record_endpoints_pb2.RecordAdd() + ra.recordUid = utils.base64_url_decode(record_uid) + ra.clientModifiedTime = client_time + ra.folderUid = utils.base64_url_decode(folder_uid) + ra.recordKey = crypto.encrypt_aes_v2(record_key, folder_key) + ra.recordKeyEncryptedBy = folder_pb2.ENCRYPTED_BY_PARENT_KEY + ra.recordKeyEncryptedByOwnerKey = crypto.encrypt_aes_v2(record_key, data_key) + ra.recordKeyType = folder_pb2.encrypted_by_data_key_gcm + ra.data = crypto.encrypt_aes_v2(json_bytes, record_key) + + if auth.auth_context.enterprise_ec_public_key: + audit_data = vault_extensions.extract_audit_data(record) + if audit_data: + ra.audit.version = 0 + ra.audit.data = crypto.encrypt_ec( + json.dumps(audit_data).encode('utf-8'), + auth.auth_context.enterprise_ec_public_key) - ra = _build_record_add_message( - record_uid, record_key, data, auth.auth_context.data_key, folder_uid, folder_key) rq = record_endpoints_pb2.RecordsAddRequest() - rq.clientTime = utils.current_milli_time() + rq.clientTime = client_time rq.records.append(ra) response = auth.execute_auth_rest( - 'vault/records/v3/add', rq, response_type=record_pb2.RecordsModifyResponse) - if response is None: - legacy_ra = _build_legacy_record_add_message( - record_uid, record_key, data, auth.auth_context.data_key, folder_uid, folder_key) - legacy_rq = record_pb2.RecordsAddRequest() - legacy_rq.client_time = utils.current_milli_time() - legacy_rq.records.append(legacy_ra) - response = auth.execute_auth_rest( - 'vault/records_add', legacy_rq, response_type=record_pb2.RecordsModifyResponse) + 'vault/records/v3/add_pam_configuration', + rq, + response_type=record_pb2.RecordsModifyResponse) assert response is not None result = _parse_modify_response(response, record_uid) if not result.success: - raise KeeperApiError(result.status, result.message) + raise KeeperApiError(result.status, result.message or 'Failed to create PAM configuration record') if request_sync: vault.sync_requested = True vault.run_pending_jobs() return result +def create_nsf_record( + vault: VaultOnline, + *, + title: str, + record_type: str, + folder_uid: Optional[str] = None, + fields: Optional[Mapping[str, Any]] = None, + notes: Optional[str] = None, + record_data: Optional[Mapping[str, Any]] = None, + request_sync: bool = True) -> NsfModifyResult: + """Create an NSF record.""" + spec = NsfRecordAddSpec( + title=title, + record_type=record_type, + folder_uid=folder_uid, + fields=fields, + notes=notes, + record_data=record_data, + ) + results = create_nsf_records_batch(vault, [spec], request_sync=request_sync) + result = results[0] + if not result.success: + raise KeeperApiError(result.status, result.message) + return result + + def update_nsf_record( vault: VaultOnline, record_uid: str, @@ -694,6 +871,95 @@ def update_nsf_record( return result +def update_nsf_typed_record( + vault: VaultOnline, + record: 'vault_record.TypedRecord', + *, + request_sync: bool = True) -> NsfModifyResult: + """Update an NSF typed record, including record-link adds/removes for file/script refs. + + In sync with the classic ``record_management.update_record`` flow so PAM + rotation scripts (and other fileRef/script attachments) work on NSF records. + """ + from . import vault_record as vr + + if not isinstance(record, vr.TypedRecord) or not record.record_uid: + raise NsfError('TypedRecord with record_uid is required') + record_uid = resolve_nsf_record_uid(vault, record.record_uid) or record.record_uid + if not is_nsf_record(vault, record_uid): + raise NsfError(f'NSF record not found: {record.record_uid}') + record.record_uid = record_uid + + record_key = _get_record_key(vault, record_uid) + storage_row = _nsf_view(vault).storage.records.get_entity(record_uid) + revision = storage_row.revision if storage_row else 0 + + existing = vr.TypedRecord() + existing.record_uid = record_uid + try: + meta = load_nsf_record_metadata(vault, record_uid) + existing.load_record_data({ + 'type': meta.get('type') or '', + 'title': meta.get('title') or record_uid, + 'notes': meta.get('notes') or '', + 'fields': meta.get('fields') or [], + 'custom': meta.get('custom') or [], + }) + except NsfError: + pass + + data = vault_extensions.extract_typed_record_data(record, None) + ru = record_pb2.RecordUpdate() + ru.record_uid = utils.base64_url_decode(record_uid) + ru.client_modified_time = utils.current_milli_time() + ru.revision = revision + ru.data = crypto.encrypt_aes_v2(vault_extensions.get_padded_json_bytes(data), record_key) + + existing_refs = vault_extensions.extract_typed_record_refs(existing) + refs = vault_extensions.extract_typed_record_refs(record) + for ref_uid in refs.difference(existing_refs): + ref_key = None + if record.linked_keys and ref_uid in record.linked_keys: + ref_key = record.linked_keys[ref_uid] + if not ref_key: + try: + ref_key = vault.vault_data.get_record_key(ref_uid) + except Exception: + ref_key = None + if not ref_key and vault.nsf_data: + entry = vault.nsf_data.get_record(ref_uid) + if entry: + ref_key = entry.record_key + if not ref_key: + continue + link = record_pb2.RecordLink() + link.record_uid = utils.base64_url_decode(ref_uid) + link.record_key = crypto.encrypt_aes_v2(ref_key, record_key) + ru.record_links_add.append(link) + for ref_uid in existing_refs.difference(refs): + ru.record_links_remove.append(utils.base64_url_decode(ref_uid)) + + rq = record_pb2.RecordsUpdateRequest() + rq.client_time = utils.current_milli_time() + rq.records.append(ru) + + auth = vault.keeper_auth + response = auth.execute_auth_rest( + 'vault/records/v3/update', rq, response_type=record_pb2.RecordsModifyResponse) + if response is None: + response = auth.execute_auth_rest( + 'vault/records_update', rq, response_type=record_pb2.RecordsModifyResponse) + assert response is not None + + result = _parse_modify_response(response, record_uid) + if not result.success: + raise KeeperApiError(result.status, result.message) + if request_sync: + vault.sync_requested = True + vault.run_pending_jobs() + return result + + def get_nsf_record_details( vault: VaultOnline, record_uids: Iterable[str]) -> Dict[str, Any]: diff --git a/keepersdk-package/src/keepersdk/vault/nsf_sharing.py b/keepersdk-package/src/keepersdk/vault/nsf_sharing.py index 38ffb98b..7a0dae29 100644 --- a/keepersdk-package/src/keepersdk/vault/nsf_sharing.py +++ b/keepersdk-package/src/keepersdk/vault/nsf_sharing.py @@ -5,7 +5,7 @@ from dataclasses import dataclass, field from typing import Any, Dict, List, Optional, Set, Tuple -from .. import utils +from .. import crypto, utils from ..errors import KeeperApiError from ..proto import folder_pb2, record_pb2, record_sharing_pb2 from . import nsf_common @@ -252,6 +252,141 @@ def grant_nsf_folder_access( return result +def _nsf_app_role_for_editable(is_editable: bool) -> str: + """Map KSM editable flag to NSF folder role used for AT_APPLICATION shares.""" + return 'content-manager' if is_editable else 'viewer' + + +def grant_nsf_folder_to_application( + vault: VaultOnline, + folder_identifier: str, + app_uid: str, + *, + is_editable: bool = False, + request_sync: bool = True) -> Dict[str, Any]: + """Share an NSF folder with a KSM application via AT_APPLICATION. + """ + folder_uid = resolve_nsf_folder_uid(vault, folder_identifier) or folder_identifier + if not is_nsf_folder(vault, folder_uid): + raise NsfError(f'NSF folder not found: {folder_identifier}') + _ensure_folder_share_permission(vault, folder_uid) + _prepare_folder_for_access_change(vault, folder_uid) + + app_key = vault.vault_data.get_record_key(app_uid) + if not app_key: + raise NsfError(f'Could not resolve record key for application {app_uid}') + + role = _nsf_app_role_for_editable(is_editable) + access_role = nsf_common.resolve_nsf_role(role) + target_role_name = folder_pb2.AccessRoleType.Name(access_role) + app_uid_bytes = utils.base64_url_decode(app_uid) + + existing_role = _check_existing_nsf_folder_access( + vault, folder_uid, app_uid_bytes, 'AT_APPLICATION') + if existing_role is not None: + if existing_role == target_role_name: + return { + 'folder_uid': folder_uid, + 'accessor': app_uid, + 'access_type': 'AT_APPLICATION', + 'status': 'SUCCESS', + 'message': f'Application already has {role} access', + 'success': True, + 'action_taken': 'already_had_access', + } + return update_nsf_folder_application_access( + vault, folder_uid, app_uid, is_editable=is_editable, request_sync=request_sync) + + ad = folder_pb2.FolderAccessData() + ad.folderUid = utils.base64_url_decode(folder_uid) + ad.accessTypeUid = app_uid_bytes + ad.accessType = folder_pb2.AT_APPLICATION + ad.accessRoleType = access_role + ad.permissions.CopyFrom(nsf_common.get_folder_permissions_for_role(access_role)) + + folder_key = _get_folder_key(vault, folder_uid) + ek = folder_pb2.EncryptedDataKey() + ek.encryptedKey = crypto.encrypt_aes_v2(folder_key, app_key) + ek.encryptedKeyType = folder_pb2.encrypted_by_data_key_gcm + ad.folderKey.CopyFrom(ek) + + response = _folder_access_update(vault, adds=[ad]) + result = nsf_common.parse_folder_access_result( + response, folder_uid, app_uid, 'Application access granted successfully') + result['access_type'] = 'AT_APPLICATION' + result.setdefault( + 'action_taken', 'granted' if result['success'] else 'grant_failed') + if not result['success']: + raise KeeperApiError(result['status'], result['message']) + _request_sync(vault, request_sync) + return result + + +def update_nsf_folder_application_access( + vault: VaultOnline, + folder_identifier: str, + app_uid: str, + *, + is_editable: bool = False, + request_sync: bool = True) -> Dict[str, Any]: + """Update AT_APPLICATION role for an NSF folder already shared with a KSM app.""" + folder_uid = resolve_nsf_folder_uid(vault, folder_identifier) or folder_identifier + if not is_nsf_folder(vault, folder_uid): + raise NsfError(f'NSF folder not found: {folder_identifier}') + _ensure_folder_share_permission(vault, folder_uid) + _prepare_folder_for_access_change(vault, folder_uid) + + role = _nsf_app_role_for_editable(is_editable) + access_role = nsf_common.resolve_nsf_role(role) + app_uid_bytes = utils.base64_url_decode(app_uid) + + ad = folder_pb2.FolderAccessData() + ad.folderUid = utils.base64_url_decode(folder_uid) + ad.accessTypeUid = app_uid_bytes + ad.accessType = folder_pb2.AT_APPLICATION + ad.accessRoleType = access_role + ad.permissions.CopyFrom(nsf_common.get_folder_permissions_for_role(access_role)) + + response = _folder_access_update(vault, updates=[ad]) + result = nsf_common.parse_folder_access_result( + response, folder_uid, app_uid, 'Application access updated successfully') + result['access_type'] = 'AT_APPLICATION' + result.setdefault( + 'action_taken', 'updated' if result['success'] else 'update_failed') + if not result['success']: + raise KeeperApiError(result['status'], result['message']) + _request_sync(vault, request_sync) + return result + + +def revoke_nsf_folder_from_application( + vault: VaultOnline, + folder_identifier: str, + app_uid: str, + *, + request_sync: bool = True) -> Dict[str, Any]: + """Revoke AT_APPLICATION access for a KSM app on an NSF folder.""" + folder_uid = resolve_nsf_folder_uid(vault, folder_identifier) or folder_identifier + if not is_nsf_folder(vault, folder_uid): + raise NsfError(f'NSF folder not found: {folder_identifier}') + _ensure_folder_share_permission(vault, folder_uid) + _prepare_folder_for_access_change(vault, folder_uid) + + ad = folder_pb2.FolderAccessData() + ad.folderUid = utils.base64_url_decode(folder_uid) + ad.accessTypeUid = utils.base64_url_decode(app_uid) + ad.accessType = folder_pb2.AT_APPLICATION + + response = _folder_access_update(vault, removes=[ad]) + result = nsf_common.parse_folder_access_result( + response, folder_uid, app_uid, 'Application access revoked successfully') + result['access_type'] = 'AT_APPLICATION' + if not result['success']: + raise KeeperApiError(result['status'], result['message']) + _request_sync(vault, request_sync) + return result + + def update_nsf_folder_access( vault: VaultOnline, folder_identifier: str, diff --git a/keepersdk-package/src/keepersdk/vault/nsf_sync.py b/keepersdk-package/src/keepersdk/vault/nsf_sync.py index 78e03c2d..2304297e 100644 --- a/keepersdk-package/src/keepersdk/vault/nsf_sync.py +++ b/keepersdk-package/src/keepersdk/vault/nsf_sync.py @@ -319,16 +319,19 @@ def _store_optional_extras_proto( storage.breach_watch_security_data.put_entities(bws) if task: task.add_records((r.record_uid for r in bws)) - chunk_payload: Dict[str, Any] = { - CHUNK_RECORD_ROTATION: list(nsf_msg.recordRotationData), - CHUNK_RAW_DAG: list(nsf_msg.rawDagData), - } - _replace_json_lists(storage, chunk_payload) + # Only replace when the sync page includes data. Proto3 repeated fields cannot + # distinguish "omitted" from "empty", and wiping on empty drops NSF rotations. + if nsf_msg.recordRotationData: + _replace_chunk_group(storage, CHUNK_RECORD_ROTATION, list(nsf_msg.recordRotationData)) + if nsf_msg.rawDagData: + _replace_chunk_group(storage, CHUNK_RAW_DAG, list(nsf_msg.rawDagData)) def _replace_json_lists(storage: INSFStorage, d: Mapping[str, Any]) -> None: - _replace_chunk_group(storage, CHUNK_RECORD_ROTATION, d.get(CHUNK_RECORD_ROTATION)) - _replace_chunk_group(storage, CHUNK_RAW_DAG, d.get(CHUNK_RAW_DAG)) + if CHUNK_RECORD_ROTATION in d and d.get(CHUNK_RECORD_ROTATION): + _replace_chunk_group(storage, CHUNK_RECORD_ROTATION, d.get(CHUNK_RECORD_ROTATION)) + if CHUNK_RAW_DAG in d and d.get(CHUNK_RAW_DAG): + _replace_chunk_group(storage, CHUNK_RAW_DAG, d.get(CHUNK_RAW_DAG)) def _replace_chunk_group(storage: INSFStorage, group: str, items: Any) -> None: diff --git a/keepersdk-package/src/keepersdk/vault/record_types.py b/keepersdk-package/src/keepersdk/vault/record_types.py index bb17de6d..490145ca 100644 --- a/keepersdk-package/src/keepersdk/vault/record_types.py +++ b/keepersdk-package/src/keepersdk/vault/record_types.py @@ -44,7 +44,8 @@ class FieldType: FieldType('recordRef', '', 'reference to other record'), FieldType('pamResources', {'controllerUid': '', 'folderUid': '', 'resourceRef': []}, 'PAM resources'), - FieldType('schedule', {'type': '', 'utcTime': '', 'month': '', }, 'schedule information'), + FieldType('schedule', {'type': '', 'utcTime': '', 'month': '', 'cron': '', 'tz': ''}, + 'schedule information'), FieldType('passkey', {'privateKey': {}, 'credentialId': '', 'signCount': 0, 'userId': '', 'relyingParty': '', 'username': '', 'createdDate': 0}, 'passwordless login passkey'), FieldType('script', {'fileRef': '', 'command': '', 'recordRef': [], }, 'Post rotation script'), @@ -87,7 +88,7 @@ class RecordField: def coly_field_types(): for ft in FieldTypes.values(): if ft.name not in RecordFields: - RecordFields[ft.name] = RecordField(ft.name, ft.name, Multiple.Never) + RecordFields[ft.name] = RecordField(ft.name, ft.name, Multiple.Optional) coly_field_types() class ITypedField(abc.ABC): diff --git a/keepersdk-package/src/keepersdk/vault/sync_down.py b/keepersdk-package/src/keepersdk/vault/sync_down.py index ec3aa638..550c284d 100644 --- a/keepersdk-package/src/keepersdk/vault/sync_down.py +++ b/keepersdk-package/src/keepersdk/vault/sync_down.py @@ -36,6 +36,9 @@ def decrypt_keeper_key(auth_context: keeper_auth.AuthContext, encrypted: bytes, class SyncDownResult: vault: vault_data.RebuildTask nsf: Optional[nsf_data.NSFRebuildTask] = None + # Classic recordRotations + NSF recordRotationData from this sync. + record_rotations: List[SyncDown_pb2.RecordRotation] = dataclasses.field(default_factory=list) + rotations_cleared: bool = False def sync_down_request(auth: keeper_auth.KeeperAuth, @@ -53,6 +56,8 @@ def sync_down_request(auth: keeper_auth.KeeperAuth, token = user_settings.continuation_token task: Optional[vault_data.RebuildTask] = None nsf_task: Optional[nsf_data.NSFRebuildTask] = None + record_rotations: List[SyncDown_pb2.RecordRotation] = [] + rotations_cleared = False done = False rq = SyncDown_pb2.SyncDownRequest() while not done: @@ -65,15 +70,22 @@ def sync_down_request(auth: keeper_auth.KeeperAuth, sync_record_types = True storage.clear() nsf_task = nsf_data.NSFRebuildTask(True) + rotations_cleared = True + record_rotations.clear() logger.info('Syncing...') if task is None: task = vault_data.RebuildTask(response.cacheStatus == SyncDown_pb2.CLEAR) + if len(response.recordRotations) > 0: + record_rotations.extend(response.recordRotations) + nsf_storage = storage.nsf if nsf_storage is not None: if nsf_task is None: nsf_task = nsf_data.NSFRebuildTask(False) nsf_sync.try_apply_nsf_from_sync_down_proto(response, nsf_storage, nsf_task) + if response.HasField('keeperDriveData') and response.keeperDriveData.recordRotationData: + record_rotations.extend(response.keeperDriveData.recordRotationData) if len(response.removedRecords) > 0: record_uids = [utils.base64_url_encode(x) for x in response.removedRecords] @@ -599,4 +611,9 @@ def to_record_type(rt: record_pb2.RecordType) -> Optional[StorageRecordType]: old_notifications = old_notifications[:to_delete] storage.notifications.delete_uids([x[0] for x in old_notifications]) - return SyncDownResult(vault=task, nsf=nsf_task) + return SyncDownResult( + vault=task, + nsf=nsf_task, + record_rotations=record_rotations, + rotations_cleared=rotations_cleared, + ) diff --git a/keepersdk-package/src/keepersdk/vault/vault_data.py b/keepersdk-package/src/keepersdk/vault/vault_data.py index 4e4a2851..8a117c2f 100644 --- a/keepersdk-package/src/keepersdk/vault/vault_data.py +++ b/keepersdk-package/src/keepersdk/vault/vault_data.py @@ -260,6 +260,18 @@ def get_team_key(self, team_uid: str) -> Optional[bytes]: if t: return t.team_key + def get_nsf_team_key_materials(self) -> Dict[str, 'nsf_crypto.TeamKeyMaterial']: + """Decrypted team keys for NSF folderAccesses unwrap (team-shared folders).""" + from . import nsf_crypto + return { + uid: nsf_crypto.TeamKeyMaterial( + team_key=t.team_key, + rsa_private_key=t.rsa_private_key, + ec_private_key=t.ec_private_key, + ) + for uid, t in self._teams.items() + } + def teams(self) -> Iterable[vault_types.TeamInfo]: return (x.info for x in self._teams.values()) diff --git a/keepersdk-package/src/keepersdk/vault/vault_extensions.py b/keepersdk-package/src/keepersdk/vault/vault_extensions.py index 7fe695fc..356fc47e 100644 --- a/keepersdk-package/src/keepersdk/vault/vault_extensions.py +++ b/keepersdk-package/src/keepersdk/vault/vault_extensions.py @@ -236,11 +236,20 @@ def extract_audit_data(record: Union[vault_record.KeeperRecord, vault_record.Typ def extract_typed_record_refs(record: vault_record.TypedRecord) -> Set[str]: refs = set() for field in itertools.chain(record.fields, record.custom): - if field.type in {'fileRef', 'addressRef', 'cardRef'}: + if field.type in {'fileRef', 'addressRef', 'cardRef', 'recordRef'}: if isinstance(field.value, list): for ref in field.value: if isinstance(ref, str): refs.add(ref) + elif field.type == 'script': + if not isinstance(field.value, list): + continue + for script in field.value: + if not isinstance(script, dict): + continue + file_ref = script.get('fileRef') + if isinstance(file_ref, str) and file_ref: + refs.add(file_ref) return refs diff --git a/keepersdk-package/src/keepersdk/vault/vault_online.py b/keepersdk-package/src/keepersdk/vault/vault_online.py index 6d2504da..b11765b0 100644 --- a/keepersdk-package/src/keepersdk/vault/vault_online.py +++ b/keepersdk-package/src/keepersdk/vault/vault_online.py @@ -6,6 +6,7 @@ from . import vault_data, vault_storage, nsf_data, nsf_vault_storage from .. import utils from ..authentication import keeper_auth +from ..plugins.pam import pam_storage, pam_types class VaultOnline(vault_plugins.IVaultData, keeper_auth.IKeeperAuth): @@ -24,6 +25,8 @@ def __init__(self, auth: keeper_auth.KeeperAuth, storage: vault_storage.IVaultSt self._background_future: Optional[concurrent.futures.Future] = None self._sync_record_types = True self.sync_requested = False + self._record_rotation_cache: Dict[str, pam_types.PamRecordRotationInfo] = {} + self._pending_rotation_replace = False self.auto_sync = True # call setter @property @@ -46,6 +49,19 @@ def nsf_data(self) -> Optional[nsf_data.NSFData]: def lock(self)-> threading.Lock: return self._lock + @property + def record_rotation_cache(self) -> Dict[str, pam_types.PamRecordRotationInfo]: + return self._record_rotation_cache + + def get_record_rotation(self, record_uid: str) -> Optional[pam_types.PamRecordRotationInfo]: + return self._record_rotation_cache.get(record_uid) + + def consume_rotations_cleared(self) -> bool: + """Return and clear the 'last sync cleared rotations' flag for PAM sqlite replace.""" + cleared = self._pending_rotation_replace + self._pending_rotation_replace = False + return cleared + def close(self): self.auto_sync = False self._executor.shutdown(wait=False) @@ -104,9 +120,31 @@ def on_notification_received(self, event: Dict[str, Any]) -> Optional[bool]: return False return None + def _ingest_record_rotations(self, result: sync_down.SyncDownResult) -> None: + if result.rotations_cleared: + self._record_rotation_cache.clear() + self._pending_rotation_replace = True + for rr in result.record_rotations: + row = pam_storage.pam_record_rotation_from_proto(rr) + if not row.record_uid: + continue + self._record_rotation_cache[row.record_uid] = pam_types.PamRecordRotationInfo( + record_uid=row.record_uid, + revision=row.revision, + configuration_uid=row.configuration_uid, + schedule=row.schedule, + pwd_complexity=row.pwd_complexity, + disabled=row.disabled, + resource_uid=row.resource_uid, + last_rotation=row.last_rotation, + last_rotation_status=row.last_rotation_status, + ) + def sync_down(self, force=False): if force: self._vault_data.storage.clear() + self._record_rotation_cache.clear() + self._pending_rotation_replace = True result = sync_down.sync_down_request(self._keeper_auth, self._vault_data.storage, sync_record_types=self._sync_record_types, @@ -116,7 +154,12 @@ def sync_down(self, force=False): self._sync_record_types = False self._vault_data.rebuild_data(result.vault) if self._nsf_data is not None: - self._nsf_data.rebuild_nsf(self._keeper_auth.auth_context) + # Teams are decrypted during vault rebuild; NSF team-shared folders + # need those keys to unwrap folderAccesses (ENCRYPTED_BY_TEAM_KEY). + self._nsf_data.rebuild_nsf( + self._keeper_auth.auth_context, + teams=self._vault_data.get_nsf_team_key_materials(), + ) def _background_task(self): if self._keeper_auth.auth_context.enterprise_ec_public_key: diff --git a/keepersdk-package/unit_tests/test_device_management.py b/keepersdk-package/unit_tests/test_device_management.py index 7e7b012f..5fc8b9a1 100644 --- a/keepersdk-package/unit_tests/test_device_management.py +++ b/keepersdk-package/unit_tests/test_device_management.py @@ -133,6 +133,19 @@ def test_list_admin_devices_rejects_bool_user_id(self): with self.assertRaises(ValueError): device_management.list_admin_devices(auth, [True]) + def test_list_admin_devices_feature_unavailable(self): + from keepersdk import errors + + auth = MagicMock() + auth.execute_auth_rest.side_effect = errors.KeeperApiError( + 'invalid_path_or_method', + 'An error has occurred. (bad_path)', + ) + with self.assertRaisesRegex( + ValueError, device_management.DEVICE_FEATURE_UNAVAILABLE_MESSAGE + ): + device_management.list_admin_devices(auth, [12345]) + def test_logout_admin_user_devices(self): auth = MagicMock() list_rs = _admin_list_response(12345, _device('Laptop', 100)) @@ -301,6 +314,41 @@ def test_ambiguous_device_name_lists_matches(self): with self.assertRaisesRegex(ValueError, 'No matching devices found'): device_management.unlock_user_devices(auth, ['Web Vault Chrome']) + def _admin_action_test(self, fn, action_type, user_id=12345, ident='1', device_name='Laptop'): + auth = MagicMock() + list_rs = _admin_list_response(user_id, _device(device_name, 100)) + action_rs = DeviceManagement_pb2.DeviceAdminActionResponse() + ar = action_rs.deviceAdminActionResults.add() + ar.deviceActionStatus = DeviceManagement_pb2.SUCCESS + ar.encryptedDeviceToken.append(b'\x01\x02') + auth.execute_auth_rest.side_effect = [list_rs, action_rs] + names = fn(auth, user_id, [ident]) + self.assertEqual(names, [device_name]) + admin_action = auth.execute_auth_rest.call_args_list[1].kwargs.get('request').deviceAdminAction[0] + self.assertEqual(admin_action.deviceActionType, action_type) + self.assertEqual(admin_action.enterpriseUserId, user_id) + + def test_lock_admin_user_devices(self): + self._admin_action_test( + device_management.lock_admin_user_devices, DeviceManagement_pb2.DA_LOCK + ) + + def test_unlock_admin_user_devices(self): + self._admin_action_test( + device_management.unlock_admin_user_devices, DeviceManagement_pb2.DA_UNLOCK + ) + + def test_account_lock_admin_user_devices(self): + self._admin_action_test( + device_management.account_lock_admin_user_devices, + DeviceManagement_pb2.DA_DEVICE_ACCOUNT_LOCK, + ) + + def test_account_unlock_admin_user_devices(self): + self._admin_action_test( + device_management.account_unlock_admin_user_devices, + DeviceManagement_pb2.DA_DEVICE_ACCOUNT_UNLOCK, + ) if __name__ == '__main__': unittest.main() diff --git a/keepersdk-package/unit_tests/test_enterprise_team_management.py b/keepersdk-package/unit_tests/test_enterprise_team_management.py new file mode 100644 index 00000000..76f7b91b --- /dev/null +++ b/keepersdk-package/unit_tests/test_enterprise_team_management.py @@ -0,0 +1,252 @@ +import unittest +from unittest.mock import MagicMock + +from keepersdk.enterprise import enterprise_team_management, enterprise_types + + +def _enterprise_data( + teams=None, + users=None, + roles=None, + team_users=None, + role_teams=None, + queued_team_users=None, + nodes=None, +): + enterprise_data = MagicMock() + enterprise_data.teams = MagicMock() + enterprise_data.users = MagicMock() + enterprise_data.roles = MagicMock() + enterprise_data.team_users = MagicMock() + enterprise_data.role_teams = MagicMock() + enterprise_data.queued_team_users = MagicMock() + enterprise_data.nodes = MagicMock() + enterprise_data.root_node = enterprise_types.Node(node_id=1, parent_id=0, name='Root') + enterprise_data.enterprise_info = MagicMock() + enterprise_data.enterprise_info.enterprise_name = 'Metronlabs' + + team_map = {t.team_uid: t for t in (teams or [])} + user_map = {u.enterprise_user_id: u for u in (users or [])} + role_map = {r.role_id: r for r in (roles or [])} + node_map = {n.node_id: n for n in (nodes or [])} + + enterprise_data.teams.get_entity.side_effect = lambda uid: team_map.get(uid) + enterprise_data.teams.get_all_entities.return_value = list(team_map.values()) + enterprise_data.users.get_entity.side_effect = lambda uid: user_map.get(uid) + enterprise_data.roles.get_entity.side_effect = lambda role_id: role_map.get(role_id) + enterprise_data.nodes.get_entity.side_effect = lambda node_id: node_map.get(node_id) + enterprise_data.team_users.get_links_by_subject.return_value = team_users or [] + enterprise_data.team_users.get_all_links.return_value = team_users or [] + enterprise_data.role_teams.get_links_by_object.return_value = role_teams or [] + enterprise_data.role_teams.get_all_links.return_value = role_teams or [] + enterprise_data.queued_team_users.get_links_by_subject.return_value = queued_team_users or [] + return enterprise_data + + +class EnterpriseTeamManagementTests(unittest.TestCase): + def test_resolve_enterprise_team_by_uid(self): + team = enterprise_types.Team(team_uid='uid-1', name='Testing Team', node_id=10) + enterprise_data = _enterprise_data(teams=[team]) + + resolved = enterprise_team_management.resolve_enterprise_team(enterprise_data, 'uid-1') + self.assertEqual(resolved.team_uid, 'uid-1') + + def test_resolve_enterprise_team_by_name(self): + team = enterprise_types.Team(team_uid='uid-1', name='Testing Team', node_id=10) + enterprise_data = _enterprise_data(teams=[team]) + + resolved = enterprise_team_management.resolve_enterprise_team(enterprise_data, 'testing team') + self.assertEqual(resolved.name, 'Testing Team') + + def test_resolve_enterprise_team_multiple_matches(self): + teams = [ + enterprise_types.Team(team_uid='uid-1', name='Testing Team', node_id=10), + enterprise_types.Team(team_uid='uid-2', name='testing team', node_id=10), + ] + enterprise_data = _enterprise_data(teams=teams) + + with self.assertRaises(enterprise_team_management.EnterpriseTeamManagementError): + enterprise_team_management.resolve_enterprise_team(enterprise_data, 'Testing Team') + + def test_get_team_includes_roles_and_users(self): + team = enterprise_types.Team( + team_uid='uid-1', + name='Testing Team', + node_id=10, + restrict_edit=True, + ) + user = enterprise_types.User( + enterprise_user_id=100, + username='user@example.com', + node_id=10, + status='active', + full_name='Test User', + ) + role = enterprise_types.Role(role_id=200, name='Role Name', node_id=10) + node = enterprise_types.Node(node_id=10, parent_id=1, name='TestNode') + team_user = enterprise_types.TeamUser(team_uid='uid-1', enterprise_user_id=100) + role_team = enterprise_types.RoleTeam(role_id=200, team_uid='uid-1') + + enterprise_data = _enterprise_data( + teams=[team], + users=[user], + roles=[role], + nodes=[node, enterprise_types.Node(node_id=1, parent_id=0, name='Root')], + team_users=[team_user], + role_teams=[role_team], + ) + + info = enterprise_team_management.get_team( + 'Testing Team', + enterprise_data=enterprise_data, + is_enterprise_admin=True, + ) + + self.assertEqual(info.team_name, 'Testing Team') + self.assertEqual(info.node_name, 'Root\\TestNode') + self.assertTrue(info.restrict_edit) + self.assertEqual(len(info.team_users), 1) + self.assertEqual(info.team_users[0].username, 'user@example.com') + self.assertEqual(len(info.team_roles), 1) + self.assertEqual(info.team_roles[0].role_name, 'Role Name') + + def test_list_teams_with_pattern(self): + teams = [ + enterprise_types.Team(team_uid='uid-1', name='Testing Team', node_id=10), + enterprise_types.Team(team_uid='uid-2', name='Developers', node_id=10), + ] + node = enterprise_types.Node(node_id=10, parent_id=1, name='TestNode') + enterprise_data = _enterprise_data( + teams=teams, + nodes=[node, enterprise_types.Node(node_id=1, parent_id=0, name='Root')], + ) + + summaries = enterprise_team_management.list_teams(enterprise_data, pattern='testing') + self.assertEqual(len(summaries), 1) + self.assertEqual(summaries[0].team_name, 'Testing Team') + + def test_get_team_members(self): + auth = MagicMock() + response = MagicMock() + user = MagicMock() + user.enterpriseUserId = 100 + user.email = 'user@example.com' + user.enterpriseUsername = 'User Name' + user.isShareAdmin = True + response.enterpriseUser = [user] + auth.execute_auth_rest.return_value = response + + members = enterprise_team_management.get_team_members(auth, 'dGVhbQ') + self.assertEqual(len(members), 1) + self.assertEqual(members[0].email, 'user@example.com') + self.assertTrue(members[0].is_share_admin) + + def test_resolve_team_prefers_vault_cache(self): + vault_data_obj = MagicMock() + vault_team = MagicMock() + vault_team.team_uid = 'vault-uid' + vault_team.name = 'Vault Team' + vault_data_obj.get_team.return_value = vault_team + vault_data_obj.teams.return_value = [vault_team] + + resolved = enterprise_team_management.resolve_team( + 'vault-uid', + vault_data_obj=vault_data_obj, + enterprise_data=_enterprise_data(), + is_enterprise_admin=True, + ) + self.assertEqual(resolved.team_uid, 'vault-uid') + self.assertIsNotNone(resolved.vault_team) + + def test_resolve_team_from_share_objects(self): + vault = MagicMock() + with unittest.mock.patch( + 'keepersdk.enterprise.enterprise_team_management.share_management_utils.get_share_objects', + return_value={ + 'teams': { + 'IuiVKCcPSjW1BZ-85o9bwA': { + 'name': 'Testing Team', + 'enterprise_id': 123, + } + } + }, + ): + resolved = enterprise_team_management.resolve_team( + 'Testing Team', + vault=vault, + include_share_objects=True, + ) + self.assertEqual(resolved.team_uid, 'IuiVKCcPSjW1BZ-85o9bwA') + self.assertIsNotNone(resolved.share_team) + self.assertEqual(resolved.share_team.name, 'Testing Team') + + def test_resolve_team_skips_share_objects_by_default(self): + vault = MagicMock() + with unittest.mock.patch( + 'keepersdk.enterprise.enterprise_team_management.share_management_utils.get_share_objects', + ) as get_share_objects: + resolved = enterprise_team_management.resolve_team( + 'record-uid-not-a-team', + vault=vault, + ) + get_share_objects.assert_not_called() + self.assertFalse(resolved.found) + + def test_get_team_marks_non_member_for_share_reference(self): + auth = MagicMock() + auth.auth_context.username = 'user@example.com' + auth.execute_auth_rest.return_value = MagicMock(enterpriseUser=[]) + + vault = MagicMock() + with unittest.mock.patch( + 'keepersdk.enterprise.enterprise_team_management.share_management_utils.get_share_objects', + return_value={ + 'teams': { + 'team-uid': {'name': 'Developers', 'enterprise_id': 123}, + } + }, + ): + info = enterprise_team_management.get_team( + 'Developers', + auth=auth, + vault=vault, + include_share_objects=True, + fetch_live_members=True, + ) + + self.assertFalse(info.is_member) + self.assertEqual(info.access_level, 'share_reference') + + def test_get_team_marks_member_when_listed_in_team_members(self): + auth = MagicMock() + auth.auth_context.username = 'user@example.com' + member = MagicMock() + member.enterpriseUserId = 100 + member.email = 'user@example.com' + member.enterpriseUsername = 'user@example.com' + member.isShareAdmin = False + auth.execute_auth_rest.return_value = MagicMock(enterpriseUser=[member]) + + vault = MagicMock() + with unittest.mock.patch( + 'keepersdk.enterprise.enterprise_team_management.share_management_utils.get_share_objects', + return_value={ + 'teams': { + 'team-uid': {'name': 'Testing Team', 'enterprise_id': 123}, + } + }, + ): + info = enterprise_team_management.get_team( + 'Testing Team', + auth=auth, + vault=vault, + include_share_objects=True, + fetch_live_members=True, + ) + + self.assertTrue(info.is_member) + self.assertEqual(len(info.members), 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/keepersdk-package/unit_tests/test_ksm_management.py b/keepersdk-package/unit_tests/test_ksm_management.py index 7bc0d9c8..b6f46413 100644 --- a/keepersdk-package/unit_tests/test_ksm_management.py +++ b/keepersdk-package/unit_tests/test_ksm_management.py @@ -64,12 +64,12 @@ def setUp(self): self.mock_app = self.patcher_app.start() self.patcher_type = patch('keepersdk.proto.APIRequest_pb2.ApplicationShareType.Name', side_effect=lambda x: 'SHARE_TYPE_RECORD' if x == 1 else 'SHARE_TYPE_FOLDER' if x == 2 else 'UNKNOWN') self.mock_type = self.patcher_type.start() - self.patcher_enterprise = patch('keepersdk.vault.ksm_management.GENERAL', 1) - self.mock_enterprise = self.patcher_enterprise.start() self.patcher_short = patch('keepersdk.vault.ksm_management.shorten_client_id', return_value='shortid') self.mock_short = self.patcher_short.start() self.patcher_folders = patch('keepersdk.vault.ksm_management.vault_online.VaultOnline.vault_data', create=True) self.mock_folders = self.patcher_folders.start() + self.vault.vault_data.folders.return_value = [] + self.vault.nsf_data = None def tearDown(self): self.patcher_encode.stop() @@ -78,7 +78,6 @@ def tearDown(self): self.patcher_shared.stop() self.patcher_app.stop() self.patcher_type.stop() - self.patcher_enterprise.stop() self.patcher_short.stop() self.patcher_folders.stop() @@ -107,6 +106,37 @@ def test_app_found_with_folder_share(self): result = ksm_management.get_secrets_manager_app(self.vault, 'uid1') self.assertEqual(result['folders'], 1) self.assertEqual(result['records'], 0) + self.assertEqual(result['shared_secrets'][0]['type'], 'FOLDER') + self.assertEqual(result['shared_secrets'][0]['name'], 'encoded_uid1') + + def test_gateway_controller_client_is_included(self): + app_info = MagicMock() + client = MagicMock( + appClientType=2, id='DISCOVERY_AND_ROTATION_CONTROLLER', + createdOn=1710000000000, accessExpireOn=0, firstAccess=0, lastAccess=0, + lockIp=False, ipAddress='', clientId=b'gwclient', + ) + app_info.clients = [client] + app_info.shares = [] + with patch('keepersdk.vault.ksm_management.get_app_info', return_value=[app_info]): + result = ksm_management.get_secrets_manager_app(self.vault, 'uid1') + self.assertEqual(len(result['client_devices']), 1) + self.assertEqual(result['client_devices'][0]['name'], 'DISCOVERY_AND_ROTATION_CONTROLLER') + + def test_nsf_folder_share_resolves_name(self): + from types import SimpleNamespace + app_info = MagicMock() + app_info.clients = [] + share = MagicMock(secretUid=b'nsffolder', shareType=2, editable=True) + app_info.shares = [share] + nsf_folder = SimpleNamespace(folder_uid='encoded_uid1', name='PAM NSF Root') + self.vault.nsf_data = MagicMock() + self.vault.nsf_data.get_folder.return_value = nsf_folder + with patch('keepersdk.vault.ksm_management.get_app_info', return_value=[app_info]): + result = ksm_management.get_secrets_manager_app(self.vault, 'uid1') + self.assertEqual(result['folders'], 1) + self.assertEqual(result['shared_secrets'][0]['name'], 'PAM NSF Root') + self.vault.nsf_data.get_folder.assert_called_once_with('encoded_uid1') def test_app_not_found_raises(self): self.vault.vault_data.records.return_value = [] @@ -272,5 +302,51 @@ def test_remove_app_with_clients_force(self): self.assertEqual(uid, 'appuid') +class ClassifyNsfSecretTestCase(unittest.TestCase): + def setUp(self): + self.vault = MagicMock() + self.vault.vault_data._records = {} + self.vault.vault_data._shared_folders = {} + self.vault.nsf_data = MagicMock() + + def test_nsf_folder_classified_for_application_access(self): + self.vault.nsf_data.get_folder.return_value = MagicMock() + with patch( + 'keepersdk.vault.ksm_management.nsf_management.resolve_nsf_folder_uid', + return_value='nsfFolderUid', + ): + result = ksm_management.KSMShareManagement._classify_secret( + self.vault, 'nsfFolderUid' + ) + self.assertEqual(result, ('nsf_folder', 'nsfFolderUid', 'NSF Folder')) + + def test_nsf_record_returns_record_share_info(self): + self.vault.nsf_data.get_folder.return_value = None + self.vault.nsf_data.get_record.return_value = MagicMock() + with patch( + 'keepersdk.vault.ksm_management.nsf_management.resolve_nsf_folder_uid', + return_value=None, + ), patch( + 'keepersdk.vault.ksm_management.nsf_management.resolve_nsf_record_uid', + return_value='nsfRecordUid', + ), patch( + 'keepersdk.vault.ksm_management.nsf_management._get_record_key', + return_value=b'record-key-32-bytes!!!!!!!!!!!!!?', + ): + result = ksm_management.KSMShareManagement._get_secret_info( + self.vault, 'nsfRecordUid' + ) + self.assertIsNotNone(result) + key, share_type, label, uid = result + self.assertEqual(key, b'record-key-32-bytes!!!!!!!!!!!!!?') + self.assertEqual(label, 'NSF Record') + self.assertEqual(uid, 'nsfRecordUid') + + def test_unknown_uid_returns_none(self): + self.vault.nsf_data = None + result = ksm_management.KSMShareManagement._classify_secret(self.vault, 'missing') + self.assertIsNone(result) + + if __name__ == "__main__": unittest.main() \ No newline at end of file diff --git a/keepersdk-package/unit_tests/test_login.py b/keepersdk-package/unit_tests/test_login.py index f8050821..fbfdba7f 100644 --- a/keepersdk-package/unit_tests/test_login.py +++ b/keepersdk-package/unit_tests/test_login.py @@ -14,6 +14,7 @@ class TestLogin(TestCase): StopAtDeviceApproval = False StopAtTwoFactor = False StopAtPassword = False + StopAtDeviceAccountLocked = False @staticmethod def mock_execute_rest(keeper_endpoint, rest_endpoint, request=None, response_type=None, session_token=None, payload_version=None): @@ -32,6 +33,9 @@ def mock_execute_rest(keeper_endpoint, rest_endpoint, request=None, response_typ lrq: APIRequest_pb2.StartLoginRequest = request lrs = response_type() lrs.encryptedLoginToken = data_vault.EncryptedLoginToken + if TestLogin.StopAtDeviceAccountLocked: + lrs.loginState = APIRequest_pb2.DEVICE_ACCOUNT_LOCKED + return lrs if TestLogin.StopAtDeviceApproval: lrs.loginState = APIRequest_pb2.DEVICE_APPROVAL_REQUIRED elif TestLogin.StopAtTwoFactor: @@ -169,6 +173,7 @@ def reset_stops(): TestLogin.StopAtDeviceApproval = False TestLogin.StopAtTwoFactor = False TestLogin.StopAtPassword = False + TestLogin.StopAtDeviceAccountLocked = False def test_success_flow(self): TestLogin.reset_stops() @@ -353,3 +358,24 @@ def test_invalid_password(self): self.assertIsInstance(step, login_auth.LoginStepPassword) with self.assertRaises(errors.KeeperApiError): step.verify_password('wrong password') + + def test_device_account_locked(self): + TestLogin.reset_stops() + TestLogin.StopAtDeviceAccountLocked = True + + auth = self.get_auth_sync() + config = auth.keeper_endpoint.get_configuration_storage().get() + device_count_before = len(list(config.devices().list())) + + auth.login(data_vault.UserName) + + step = auth.login_step + self.assertIsInstance(step, login_auth.LoginStepError) + self.assertEqual(step.code, 'device_account_locked') + config = auth.keeper_endpoint.get_configuration_storage().get() + self.assertEqual(len(list(config.devices().list())), device_count_before) + register_calls = [ + c for c in auth.keeper_endpoint.execute_rest.call_args_list + if c.args and c.args[0] == 'authentication/register_device' + ] + self.assertEqual(register_calls, []) diff --git a/keepersdk-package/unit_tests/test_nsf_folder_key_decrypt.py b/keepersdk-package/unit_tests/test_nsf_folder_key_decrypt.py new file mode 100644 index 00000000..d6a930a6 --- /dev/null +++ b/keepersdk-package/unit_tests/test_nsf_folder_key_decrypt.py @@ -0,0 +1,269 @@ +"""Unit tests for NSF folder key / name decrypt (team + shared paths).""" + +import json +import unittest +from unittest.mock import Mock + +from keepersdk import crypto, utils +from keepersdk.proto import folder_pb2 +from keepersdk.vault import nsf_crypto, nsf_storage_types as nsf, memory_nsf_storage + + +def _auth(data_key=None): + ctx = Mock() + ctx.data_key = data_key or utils.generate_aes_key() + ctx.rsa_private_key = None + ctx.ec_private_key = None + return ctx + + +def _put_folder(storage, name, folder_key, parent_uid=''): + folder_uid = utils.generate_uid() + data_b64 = utils.base64_url_encode( + crypto.encrypt_aes_v2(json.dumps({'name': name}).encode('utf-8'), folder_key) + ) + storage.folders.put_entities([ + nsf.NSFFolder( + folder_uid=folder_uid, + parent_uid=parent_uid, + data=data_b64, + ), + ]) + return folder_uid + + +class TestNsfFolderKeyDecrypt(unittest.TestCase): + + def test_user_key_owner_path(self): + storage = memory_nsf_storage.InMemoryNSFStorage() + auth = _auth() + folder_key = utils.generate_aes_key() + folder_uid = _put_folder(storage, 'Owner Folder', folder_key) + storage.folder_keys.put_links([ + nsf.NSFFolderKey( + folder_uid=folder_uid, + parent_uid='', + folder_key=utils.base64_url_encode( + crypto.encrypt_aes_v2(folder_key, auth.data_key) + ), + encrypted_by=int(folder_pb2.ENCRYPTED_BY_USER_KEY), + ), + ]) + + keys = nsf_crypto.decrypt_folder_keys(storage, auth) + name = nsf_crypto.decrypt_folder_name( + storage.folders.get_entity(folder_uid).data, keys[folder_uid] + ) + self.assertEqual(name, 'Owner Folder') + + def test_team_key_via_folder_access(self): + storage = memory_nsf_storage.InMemoryNSFStorage() + auth = _auth() + team_uid = utils.generate_uid() + team_aes = utils.generate_aes_key() + folder_key = utils.generate_aes_key() + folder_uid = _put_folder(storage, 'Team Shared NSF', folder_key) + storage.folder_keys.put_links([ + nsf.NSFFolderKey( + folder_uid=folder_uid, + parent_uid='', + folder_key='', + encrypted_by=int(folder_pb2.ENCRYPTED_BY_TEAM_KEY), + ), + ]) + storage.folder_accesses.put_links([ + nsf.NSFFolderAccess( + folder_uid=folder_uid, + access_type_uid=team_uid, + access_type=int(folder_pb2.AT_TEAM), + folder_key_encrypted=utils.base64_url_encode( + crypto.encrypt_aes_v2(folder_key, team_aes) + ), + folder_key_type=int(folder_pb2.encrypted_by_data_key_gcm), + ), + ]) + teams = { + team_uid: nsf_crypto.TeamKeyMaterial(team_key=team_aes), + } + + keys = nsf_crypto.decrypt_folder_keys(storage, auth, teams=teams) + self.assertIn(folder_uid, keys) + name = nsf_crypto.decrypt_folder_name( + storage.folders.get_entity(folder_uid).data, keys[folder_uid] + ) + self.assertEqual(name, 'Team Shared NSF') + + def test_team_key_fails_without_team_materials(self): + storage = memory_nsf_storage.InMemoryNSFStorage() + auth = _auth() + team_uid = utils.generate_uid() + team_aes = utils.generate_aes_key() + folder_key = utils.generate_aes_key() + folder_uid = _put_folder(storage, 'Hidden', folder_key) + storage.folder_keys.put_links([ + nsf.NSFFolderKey( + folder_uid=folder_uid, + parent_uid='', + folder_key='', + encrypted_by=int(folder_pb2.ENCRYPTED_BY_TEAM_KEY), + ), + ]) + storage.folder_accesses.put_links([ + nsf.NSFFolderAccess( + folder_uid=folder_uid, + access_type_uid=team_uid, + access_type=int(folder_pb2.AT_TEAM), + folder_key_encrypted=utils.base64_url_encode( + crypto.encrypt_aes_v2(folder_key, team_aes) + ), + folder_key_type=int(folder_pb2.encrypted_by_data_key_gcm), + ), + ]) + + keys = nsf_crypto.decrypt_folder_keys(storage, auth, teams={}) + self.assertNotIn(folder_uid, keys) + + def test_parent_key_without_parent_falls_back_to_team_access(self): + storage = memory_nsf_storage.InMemoryNSFStorage() + auth = _auth() + team_uid = utils.generate_uid() + team_aes = utils.generate_aes_key() + folder_key = utils.generate_aes_key() + parent_uid = utils.generate_uid() + folder_uid = _put_folder(storage, 'Child Shared', folder_key, parent_uid=parent_uid) + storage.folder_keys.put_links([ + nsf.NSFFolderKey( + folder_uid=folder_uid, + parent_uid=parent_uid, + folder_key=utils.base64_url_encode( + crypto.encrypt_aes_v2(folder_key, utils.generate_aes_key()) + ), + encrypted_by=int(folder_pb2.ENCRYPTED_BY_PARENT_KEY), + ), + ]) + storage.folder_accesses.put_links([ + nsf.NSFFolderAccess( + folder_uid=folder_uid, + access_type_uid=team_uid, + access_type=int(folder_pb2.AT_TEAM), + folder_key_encrypted=utils.base64_url_encode( + crypto.encrypt_aes_v2(folder_key, team_aes) + ), + folder_key_type=int(folder_pb2.encrypted_by_data_key_gcm), + ), + ]) + teams = {team_uid: nsf_crypto.TeamKeyMaterial(team_key=team_aes)} + + keys = nsf_crypto.decrypt_folder_keys(storage, auth, teams=teams) + name = nsf_crypto.decrypt_folder_name( + storage.folders.get_entity(folder_uid).data, keys[folder_uid] + ) + self.assertEqual(name, 'Child Shared') + + def test_user_access_fallback(self): + storage = memory_nsf_storage.InMemoryNSFStorage() + auth = _auth() + folder_key = utils.generate_aes_key() + folder_uid = _put_folder(storage, 'Access Shared', folder_key) + storage.folder_keys.put_links([ + nsf.NSFFolderKey( + folder_uid=folder_uid, + parent_uid='', + folder_key=utils.base64_url_encode( + crypto.encrypt_aes_v2(folder_key, utils.generate_aes_key()) + ), + encrypted_by=int(folder_pb2.ENCRYPTED_BY_USER_KEY), + ), + ]) + storage.folder_accesses.put_links([ + nsf.NSFFolderAccess( + folder_uid=folder_uid, + access_type_uid=utils.generate_uid(), + access_type=int(folder_pb2.AT_USER), + folder_key_encrypted=utils.base64_url_encode( + crypto.encrypt_aes_v2(folder_key, auth.data_key) + ), + folder_key_type=int(folder_pb2.encrypted_by_data_key_gcm), + ), + ]) + + keys = nsf_crypto.decrypt_folder_keys(storage, auth) + name = nsf_crypto.decrypt_folder_name( + storage.folders.get_entity(folder_uid).data, keys[folder_uid] + ) + self.assertEqual(name, 'Access Shared') + + def test_team_parent_then_parent_key_child_name(self): + """Team-shared root + PARENT_KEY child with no child folderAccesses key. + + Regression: access unwrap must run inside the progress loop so the child + can unwrap on the next pass after the parent team key is available. + """ + storage = memory_nsf_storage.InMemoryNSFStorage() + auth = _auth() + team_uid = utils.generate_uid() + team_aes = utils.generate_aes_key() + parent_key = utils.generate_aes_key() + child_key = utils.generate_aes_key() + teams = {team_uid: nsf_crypto.TeamKeyMaterial(team_key=team_aes)} + + parent_uid = _put_folder(storage, 'Team Root', parent_key) + child_uid = _put_folder(storage, 'Team Child', child_key, parent_uid=parent_uid) + + # Put child FolderKey first so iteration order would fail a one-shot access pass. + storage.folder_keys.put_links([ + nsf.NSFFolderKey( + folder_uid=child_uid, + parent_uid=parent_uid, + folder_key=utils.base64_url_encode( + crypto.encrypt_aes_v2(child_key, parent_key) + ), + encrypted_by=int(folder_pb2.ENCRYPTED_BY_PARENT_KEY), + ), + nsf.NSFFolderKey( + folder_uid=parent_uid, + parent_uid='', + folder_key='', + encrypted_by=int(folder_pb2.ENCRYPTED_BY_TEAM_KEY), + ), + ]) + storage.folder_accesses.put_links([ + nsf.NSFFolderAccess( + folder_uid=parent_uid, + access_type_uid=team_uid, + access_type=int(folder_pb2.AT_TEAM), + folder_key_encrypted=utils.base64_url_encode( + crypto.encrypt_aes_v2(parent_key, team_aes) + ), + folder_key_type=int(folder_pb2.encrypted_by_data_key_gcm), + ), + # Child has inherited/empty access — no folder_key_encrypted. + nsf.NSFFolderAccess( + folder_uid=child_uid, + access_type_uid=team_uid, + access_type=int(folder_pb2.AT_TEAM), + folder_key_encrypted='', + folder_key_type=0, + inherited=True, + ), + ]) + + keys = nsf_crypto.decrypt_folder_keys(storage, auth, teams=teams) + self.assertIn(parent_uid, keys) + self.assertIn(child_uid, keys) + self.assertEqual( + nsf_crypto.decrypt_folder_name( + storage.folders.get_entity(parent_uid).data, keys[parent_uid] + ), + 'Team Root', + ) + self.assertEqual( + nsf_crypto.decrypt_folder_name( + storage.folders.get_entity(child_uid).data, keys[child_uid] + ), + 'Team Child', + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/keepersdk-package/unit_tests/test_nsf_record_add_batch.py b/keepersdk-package/unit_tests/test_nsf_record_add_batch.py new file mode 100644 index 00000000..5a5b794a --- /dev/null +++ b/keepersdk-package/unit_tests/test_nsf_record_add_batch.py @@ -0,0 +1,148 @@ +import unittest +from typing import List +from unittest.mock import MagicMock, patch + +from keepersdk import utils +from keepersdk.errors import KeeperApiError +from keepersdk.proto import record_pb2 +from keepersdk.vault import nsf_management + + +class TestNsfRecordAddBatch(unittest.TestCase): + def _vault(self): + vault = MagicMock() + vault.keeper_auth.auth_context.data_key = b'0' * 32 + vault.keeper_auth.execute_auth_rest.return_value = None + return vault + + def _success_response(self, record_uids): + response = record_pb2.RecordsModifyResponse() + for uid in record_uids: + row = response.records.add() + row.record_uid = utils.base64_url_decode(uid) + row.status = record_pb2.RS_SUCCESS + row.message = '' + return response + + @patch.object(nsf_management, 'is_nsf_folder', return_value=True) + @patch.object(nsf_management, 'resolve_nsf_folder_uid', side_effect=lambda _v, uid: uid) + @patch.object(nsf_management, '_get_folder_key', return_value=b'1' * 32) + @patch.object( + nsf_management.utils, + 'generate_uid', + side_effect=[utils.generate_uid() for _ in range(5)], + ) + def test_create_nsf_records_batch_single_request(self, *_mocks): + vault = self._vault() + expected_uids: List[str] = [] + + def _execute(rest_endpoint, request, response_type=None): + expected_uids.extend( + utils.base64_url_encode(record.recordUid) for record in request.records) + return self._success_response(expected_uids) + + vault.keeper_auth.execute_auth_rest.side_effect = _execute + + specs = [ + {'title': f'Record {i}', 'record_type': 'login', 'fields': {'login': f'user{i}'}} + for i in range(3) + ] + results = nsf_management.create_nsf_records_batch( + vault, specs, request_sync=False) + + self.assertEqual(len(results), 3) + self.assertTrue(all(result.success for result in results)) + vault.keeper_auth.execute_auth_rest.assert_called_once() + request = vault.keeper_auth.execute_auth_rest.call_args.args[1] + self.assertEqual(len(request.records), 3) + + def test_create_nsf_records_batch_rejects_over_limit(self): + vault = self._vault() + specs = [ + {'title': f'Record {i}', 'record_type': 'login'} + for i in range(nsf_management.NSF_RECORD_ADD_BATCH_LIMIT + 1) + ] + with self.assertRaisesRegex(ValueError, '1000'): + nsf_management.create_nsf_records_batch(vault, specs, request_sync=False) + + @patch.object(nsf_management, 'is_nsf_folder', return_value=True) + @patch.object(nsf_management, 'resolve_nsf_folder_uid', side_effect=lambda _v, uid: uid) + @patch.object(nsf_management, '_get_folder_key', return_value=b'1' * 32) + @patch.object( + nsf_management.utils, + 'generate_uid', + side_effect=[utils.generate_uid() for _ in range(1001)], + ) + def test_create_nsf_records_chunks_large_input(self, generate_uid_mock, *_mocks): + vault = self._vault() + + call_count = {'value': 0} + + def _execute(rest_endpoint, request, response_type=None): + call_count['value'] += 1 + uids = [utils.base64_url_encode(record.recordUid) for record in request.records] + return self._success_response(uids) + + vault.keeper_auth.execute_auth_rest.side_effect = _execute + + specs = [ + {'title': f'Record {i}', 'record_type': 'login'} + for i in range(1001) + ] + results = nsf_management.create_nsf_records(vault, specs, request_sync=False) + + self.assertEqual(len(results), 1001) + self.assertEqual(call_count['value'], 2) + first_batch_size = vault.keeper_auth.execute_auth_rest.call_args_list[0].args[1].records + second_batch_size = vault.keeper_auth.execute_auth_rest.call_args_list[1].args[1].records + self.assertEqual(len(first_batch_size), 1000) + self.assertEqual(len(second_batch_size), 1) + + @patch.object(nsf_management, 'is_nsf_folder', return_value=True) + @patch.object(nsf_management, 'resolve_nsf_folder_uid', side_effect=lambda _v, uid: uid) + @patch.object(nsf_management, '_get_folder_key', return_value=b'1' * 32) + @patch.object(nsf_management.utils, 'generate_uid', return_value='AAAAAAAAAAAAAAAAAAAAAA') + def test_create_nsf_record_raises_on_failure(self, *_mocks): + vault = self._vault() + response = record_pb2.RecordsModifyResponse() + row = response.records.add() + row.record_uid = utils.base64_url_decode('AAAAAAAAAAAAAAAAAAAAAA') + row.status = record_pb2.RS_ACCESS_DENIED + row.message = 'denied' + vault.keeper_auth.execute_auth_rest.return_value = response + + with self.assertRaises(KeeperApiError): + nsf_management.create_nsf_record( + vault, + title='Test', + record_type='login', + request_sync=False, + ) + + @patch.object(nsf_management.utils, 'generate_uid', return_value='AAAAAAAAAAAAAAAAAAAAAA') + def test_v3_none_response_raises_no_results(self, *_mocks): + vault = self._vault() + vault.keeper_auth.execute_auth_rest.return_value = None + + with self.assertRaises(KeeperApiError) as ctx: + nsf_management.create_nsf_records_batch( + vault, + [{'title': 'Test', 'record_type': 'login'}], + request_sync=False, + ) + self.assertEqual(ctx.exception.result_code, 'no_results') + vault.keeper_auth.execute_auth_rest.assert_called_once() + self.assertEqual( + vault.keeper_auth.execute_auth_rest.call_args.args[0], + 'vault/records/v3/add', + ) + + def test_normalize_record_add_spec_requires_title_and_type(self): + with self.assertRaisesRegex(nsf_management.NsfError, 'title'): + nsf_management._normalize_record_add_spec({'record_type': 'login'}) + with self.assertRaisesRegex(nsf_management.NsfError, 'record_type'): + nsf_management._normalize_record_add_spec({'title': 'Test'}) + + +if __name__ == '__main__': + unittest.main() diff --git a/keepersdk-package/unit_tests/test_passphrase_generator.py b/keepersdk-package/unit_tests/test_passphrase_generator.py new file mode 100644 index 00000000..9d98d782 --- /dev/null +++ b/keepersdk-package/unit_tests/test_passphrase_generator.py @@ -0,0 +1,144 @@ +from unittest import TestCase, mock + +from keepersdk import generator + + +class TestKeeperPassphraseGenerator(TestCase): + + def test_default_generates_five_hyphen_separated_words(self): + gen = generator.KeeperPassphraseGenerator() + gen._vocabulary = ['alpha', 'bravo', 'charlie', 'delta', 'echo', 'foxtrot'] + with mock.patch('secrets.randbelow', side_effect=[0, 0, 0, 0, 0, 3]): + result = gen.generate() + self.assertEqual(result, 'Alpha3-Bravo-Charlie-Delta-Echo') + + def test_does_not_shuffle_words_like_diceware(self): + gen = generator.KeeperPassphraseGenerator( + word_count=5, separator=' ', capitalize=False, append_number=False) + gen._vocabulary = ['one', 'two', 'three', 'four', 'five', 'six'] + with mock.patch('secrets.randbelow', side_effect=[0, 0, 0, 0, 0]): + result = gen.generate() + self.assertEqual(result, 'one two three four five') + + def test_capitalize_applies_to_every_word_number_to_first_word_only(self): + gen = generator.KeeperPassphraseGenerator( + word_count=5, separator='-', capitalize=True, append_number=True) + gen._vocabulary = ['alpha', 'bravo', 'charlie', 'delta', 'echo', 'foxtrot'] + with mock.patch('secrets.randbelow', side_effect=[0, 0, 0, 0, 0, 7]): + result = gen.generate() + self.assertEqual(result, 'Alpha7-Bravo-Charlie-Delta-Echo') + + def test_create_from_policy_honors_passphrase_fields(self): + gen = generator.KeeperPassphraseGenerator.create_from_policy({ + 'passphrase-length': 5, + 'passphrase-separator': '-', + 'passphrase-capitalize': True, + 'passphrase-number': True, + }) + gen._vocabulary = ['alpha', 'bravo', 'charlie', 'delta', 'echo', 'foxtrot'] + with mock.patch('secrets.randbelow', side_effect=[0, 0, 0, 0, 0, 4]): + result = gen.generate() + self.assertEqual(result, 'Alpha4-Bravo-Charlie-Delta-Echo') + + def test_generated_words_are_unique(self): + # Use '_' so hyphenated EFF words (yo-yo, felt-tip, ...) do not inflate split(). + gen = generator.KeeperPassphraseGenerator( + word_count=9, separator='_', capitalize=False, append_number=False) + for _ in range(100): + words = gen.generate().split(gen.separator) + self.assertEqual(len(words), 9) + self.assertEqual(len(words), len(set(words))) + + def test_parse_passphrase_gen_parameters(self): + opts, error = generator.parse_passphrase_gen_parameters( + ['passphrase', '7', '_', 'true', 'false']) + self.assertIsNone(error) + self.assertEqual(opts.word_count, 7) + self.assertEqual(opts.separator, '_') + self.assertTrue(opts.capitalize) + self.assertFalse(opts.append_number) + + def test_parse_passphrase_rejects_invalid_separator(self): + _, error = generator.parse_passphrase_gen_parameters( + ['passphrase', '7', '@', 'true', 'true']) + self.assertIn('Invalid passphrase separator', error) + + def test_parse_passphrase_rejects_invalid_boolean(self): + _, error = generator.parse_passphrase_gen_parameters( + ['passphrase', '7', '_', 'tr', 'true']) + self.assertIn('capitalize', error) + + def test_parse_passphrase_rejects_trailing_comma(self): + _, error = generator.parse_passphrase_gen_parameters( + ['passphrase', '9', '_', 'true', '']) + self.assertIn('missing value after comma', error) + + def test_parse_passphrase_rejects_extra_parameters(self): + _, error = generator.parse_passphrase_gen_parameters( + ['passphrase', '7', '_', 'true', 'true', 'test']) + self.assertIn('Unexpected', error) + + def test_parse_passphrase_rejects_out_of_range_word_count(self): + _, error = generator.parse_passphrase_gen_parameters(['passphrase', '12']) + self.assertIn('between 5 and 9', error) + + def test_create_with_options_overrides_policy(self): + policy = { + 'passphrase-length': 5, + 'passphrase-separator': '-', + 'passphrase-capitalize': False, + 'passphrase-number': False, + } + gen = generator.KeeperPassphraseGenerator.create_with_options( + policy, word_count=3, separator='_', capitalize=True, append_number=True) + self.assertEqual(gen.word_count, 5) + self.assertEqual(gen.separator, '_') + self.assertTrue(gen.capitalize) + self.assertTrue(gen.append_number) + + def test_commander_defaults_override_policy_capitalize_and_number(self): + gen = generator.KeeperPassphraseGenerator.create_with_options({ + 'passphrase-capitalize': False, + 'passphrase-number': False, + }) + self.assertTrue(gen.capitalize) + self.assertTrue(gen.append_number) + + def test_policy_separator_uses_vault_order_not_raw_first_char(self): + gen = generator.KeeperPassphraseGenerator.create_with_options({ + 'passphrase-separator': '!._?-', + }) + self.assertEqual(gen.separator, '-') + + def test_invalid_separator_override_is_rejected_by_parser(self): + _, error = generator.parse_passphrase_gen_parameters( + ['passphrase', '7', '~', 'true', 'true']) + self.assertIn('Invalid passphrase separator', error) + + def test_word_count_clamped_to_vault_range(self): + self.assertEqual(generator.clamp_passphrase_word_count(2), 5) + self.assertEqual(generator.clamp_passphrase_word_count(9), 9) + self.assertEqual(generator.clamp_passphrase_word_count(12), 9) + + def test_word_count_clamp_logs_warning(self): + with mock.patch('keepersdk.generator.logging.warning') as mock_warning: + generator.clamp_passphrase_word_count(12) + mock_warning.assert_called_once() + args, _ = mock_warning.call_args + self.assertIn('between', args[0]) + self.assertEqual(args[1:], (5, 9, 9)) + + def test_loads_bundled_diceware_wordlist(self): + words = generator._load_wordlist() + self.assertEqual(len(words), 7776) + self.assertEqual(words[0], 'abacus') + + def test_resolve_gen_password_algorithm_rejects_typos(self): + algorithm, error = generator.resolve_gen_password_algorithm(['passphra']) + self.assertIsNone(algorithm) + self.assertIn('passphrase', error) + + def test_resolve_gen_password_algorithm_accepts_numeric_length(self): + algorithm, error = generator.resolve_gen_password_algorithm(['16']) + self.assertEqual(algorithm, 'rand') + self.assertIsNone(error)