diff --git a/examples/sdk_examples/action_report/action_report.py b/examples/sdk_examples/action_report/action_report.py index 3fcf0f75..53dccad3 100644 --- a/examples/sdk_examples/action_report/action_report.py +++ b/examples/sdk_examples/action_report/action_report.py @@ -112,14 +112,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -154,17 +154,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/aging_report/aging_report.py b/examples/sdk_examples/aging_report/aging_report.py index c3ad3c67..9b082977 100644 --- a/examples/sdk_examples/aging_report/aging_report.py +++ b/examples/sdk_examples/aging_report/aging_report.py @@ -117,14 +117,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -159,17 +159,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/audit_alert/list_alerts.py b/examples/sdk_examples/audit_alert/list_alerts.py index 1a27241a..000e4bbe 100644 --- a/examples/sdk_examples/audit_alert/list_alerts.py +++ b/examples/sdk_examples/audit_alert/list_alerts.py @@ -104,14 +104,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -146,17 +146,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/audit_alert/view_alert.py b/examples/sdk_examples/audit_alert/view_alert.py index 3132a90f..c52e8f5d 100644 --- a/examples/sdk_examples/audit_alert/view_alert.py +++ b/examples/sdk_examples/audit_alert/view_alert.py @@ -104,14 +104,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -146,17 +146,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/audit_report/audit_log.py b/examples/sdk_examples/audit_report/audit_log.py index 453d69c1..fe84d1ce 100644 --- a/examples/sdk_examples/audit_report/audit_log.py +++ b/examples/sdk_examples/audit_report/audit_log.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/audit_report/audit_summary.py b/examples/sdk_examples/audit_report/audit_summary.py index 1eecc5fc..e4776d2a 100644 --- a/examples/sdk_examples/audit_report/audit_summary.py +++ b/examples/sdk_examples/audit_report/audit_summary.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/auth/login.py b/examples/sdk_examples/auth/login.py index b6e3118d..1110ac99 100644 --- a/examples/sdk_examples/auth/login.py +++ b/examples/sdk_examples/auth/login.py @@ -103,14 +103,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -145,17 +145,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/auth/logout.py b/examples/sdk_examples/auth/logout.py index c83f8b5a..63806d4e 100644 --- a/examples/sdk_examples/auth/logout.py +++ b/examples/sdk_examples/auth/logout.py @@ -104,14 +104,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -146,17 +146,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/auth/whoami.py b/examples/sdk_examples/auth/whoami.py index 2014fa44..9cd5413c 100644 --- a/examples/sdk_examples/auth/whoami.py +++ b/examples/sdk_examples/auth/whoami.py @@ -103,14 +103,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -145,17 +145,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/breachwatch/breach_status.py b/examples/sdk_examples/breachwatch/breach_status.py index 535a93d4..a0f0a917 100644 --- a/examples/sdk_examples/breachwatch/breach_status.py +++ b/examples/sdk_examples/breachwatch/breach_status.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/breachwatch/breachwatch_report.py b/examples/sdk_examples/breachwatch/breachwatch_report.py index a21a238a..e499e025 100644 --- a/examples/sdk_examples/breachwatch/breachwatch_report.py +++ b/examples/sdk_examples/breachwatch/breachwatch_report.py @@ -118,14 +118,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -160,17 +160,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/breachwatch/list_breaches.py b/examples/sdk_examples/breachwatch/list_breaches.py index 18b00b4e..f5c1745d 100644 --- a/examples/sdk_examples/breachwatch/list_breaches.py +++ b/examples/sdk_examples/breachwatch/list_breaches.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/breachwatch/scan_password.py b/examples/sdk_examples/breachwatch/scan_password.py index cbaf7d18..3e846df7 100644 --- a/examples/sdk_examples/breachwatch/scan_password.py +++ b/examples/sdk_examples/breachwatch/scan_password.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/breachwatch/scan_records.py b/examples/sdk_examples/breachwatch/scan_records.py index 811a206e..b0f0a373 100644 --- a/examples/sdk_examples/breachwatch/scan_records.py +++ b/examples/sdk_examples/breachwatch/scan_records.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/compliance/compliance_record_access_report.py b/examples/sdk_examples/compliance/compliance_record_access_report.py index 914cb480..c24da7b1 100644 --- a/examples/sdk_examples/compliance/compliance_record_access_report.py +++ b/examples/sdk_examples/compliance/compliance_record_access_report.py @@ -120,14 +120,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -162,17 +162,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/compliance/compliance_report.py b/examples/sdk_examples/compliance/compliance_report.py index 2ee03178..9461baf4 100644 --- a/examples/sdk_examples/compliance/compliance_report.py +++ b/examples/sdk_examples/compliance/compliance_report.py @@ -118,14 +118,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -160,17 +160,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/compliance/compliance_shared_folder_report.py b/examples/sdk_examples/compliance/compliance_shared_folder_report.py index d22dfae9..f483d562 100644 --- a/examples/sdk_examples/compliance/compliance_shared_folder_report.py +++ b/examples/sdk_examples/compliance/compliance_shared_folder_report.py @@ -118,14 +118,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -160,17 +160,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/compliance/compliance_summary_report.py b/examples/sdk_examples/compliance/compliance_summary_report.py index 5b1523a5..4d2f02c1 100644 --- a/examples/sdk_examples/compliance/compliance_summary_report.py +++ b/examples/sdk_examples/compliance/compliance_summary_report.py @@ -120,14 +120,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -162,17 +162,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/compliance/compliance_team_report.py b/examples/sdk_examples/compliance/compliance_team_report.py index 7e9e0faf..8e0676b5 100644 --- a/examples/sdk_examples/compliance/compliance_team_report.py +++ b/examples/sdk_examples/compliance/compliance_team_report.py @@ -119,14 +119,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -161,17 +161,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_info/enterprise_info_node.py b/examples/sdk_examples/enterprise_info/enterprise_info_node.py index 7290b115..ae65ae53 100644 --- a/examples/sdk_examples/enterprise_info/enterprise_info_node.py +++ b/examples/sdk_examples/enterprise_info/enterprise_info_node.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_info/enterprise_info_role.py b/examples/sdk_examples/enterprise_info/enterprise_info_role.py index ba482be3..670e1077 100644 --- a/examples/sdk_examples/enterprise_info/enterprise_info_role.py +++ b/examples/sdk_examples/enterprise_info/enterprise_info_role.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_info/enterprise_info_team.py b/examples/sdk_examples/enterprise_info/enterprise_info_team.py index a7271381..503033fa 100644 --- a/examples/sdk_examples/enterprise_info/enterprise_info_team.py +++ b/examples/sdk_examples/enterprise_info/enterprise_info_team.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_info/enterprise_info_tree.py b/examples/sdk_examples/enterprise_info/enterprise_info_tree.py index cf846f30..ba4c5632 100644 --- a/examples/sdk_examples/enterprise_info/enterprise_info_tree.py +++ b/examples/sdk_examples/enterprise_info/enterprise_info_tree.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_info/enterprise_info_user.py b/examples/sdk_examples/enterprise_info/enterprise_info_user.py index 585fef23..cc905c6f 100644 --- a/examples/sdk_examples/enterprise_info/enterprise_info_user.py +++ b/examples/sdk_examples/enterprise_info/enterprise_info_user.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_node/enterprise_node_view.py b/examples/sdk_examples/enterprise_node/enterprise_node_view.py index d79bb08f..bb8ed43e 100644 --- a/examples/sdk_examples/enterprise_node/enterprise_node_view.py +++ b/examples/sdk_examples/enterprise_node/enterprise_node_view.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_role/enterprise_role_membership.py b/examples/sdk_examples/enterprise_role/enterprise_role_membership.py index 57252138..c9593eea 100644 --- a/examples/sdk_examples/enterprise_role/enterprise_role_membership.py +++ b/examples/sdk_examples/enterprise_role/enterprise_role_membership.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_role/enterprise_role_view.py b/examples/sdk_examples/enterprise_role/enterprise_role_view.py index 59141e4b..7acdccee 100644 --- a/examples/sdk_examples/enterprise_role/enterprise_role_view.py +++ b/examples/sdk_examples/enterprise_role/enterprise_role_view.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_team/enterprise_team_membership.py b/examples/sdk_examples/enterprise_team/enterprise_team_membership.py index e7e89233..3f715a0c 100644 --- a/examples/sdk_examples/enterprise_team/enterprise_team_membership.py +++ b/examples/sdk_examples/enterprise_team/enterprise_team_membership.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_team/enterprise_team_view.py b/examples/sdk_examples/enterprise_team/enterprise_team_view.py index e85d06a3..63ba9126 100644 --- a/examples/sdk_examples/enterprise_team/enterprise_team_view.py +++ b/examples/sdk_examples/enterprise_team/enterprise_team_view.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_user/device_approve.py b/examples/sdk_examples/enterprise_user/device_approve.py index a5055a82..a0c9b689 100644 --- a/examples/sdk_examples/enterprise_user/device_approve.py +++ b/examples/sdk_examples/enterprise_user/device_approve.py @@ -112,14 +112,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -154,17 +154,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_user/enterprise_user_view.py b/examples/sdk_examples/enterprise_user/enterprise_user_view.py index aac07b53..bc2799ef 100644 --- a/examples/sdk_examples/enterprise_user/enterprise_user_view.py +++ b/examples/sdk_examples/enterprise_user/enterprise_user_view.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/enterprise_user/transfer_user.py b/examples/sdk_examples/enterprise_user/transfer_user.py index a7592a3b..9b1413dc 100644 --- a/examples/sdk_examples/enterprise_user/transfer_user.py +++ b/examples/sdk_examples/enterprise_user/transfer_user.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/external_shares_report/ext_shares_report.py b/examples/sdk_examples/external_shares_report/ext_shares_report.py index 995b46a1..4baaf8ca 100644 --- a/examples/sdk_examples/external_shares_report/ext_shares_report.py +++ b/examples/sdk_examples/external_shares_report/ext_shares_report.py @@ -118,14 +118,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -160,17 +160,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/folder/add_folder.py b/examples/sdk_examples/folder/add_folder.py index e62eeffb..98c6eee7 100644 --- a/examples/sdk_examples/folder/add_folder.py +++ b/examples/sdk_examples/folder/add_folder.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/folder/delete_folder.py b/examples/sdk_examples/folder/delete_folder.py index 0b8c15e6..acca7b6c 100644 --- a/examples/sdk_examples/folder/delete_folder.py +++ b/examples/sdk_examples/folder/delete_folder.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/folder/list_folders.py b/examples/sdk_examples/folder/list_folders.py index 32f33c1e..8f7f0e98 100644 --- a/examples/sdk_examples/folder/list_folders.py +++ b/examples/sdk_examples/folder/list_folders.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/folder/list_shared_folders.py b/examples/sdk_examples/folder/list_shared_folders.py index a7b2e3bd..0053db03 100644 --- a/examples/sdk_examples/folder/list_shared_folders.py +++ b/examples/sdk_examples/folder/list_shared_folders.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/folder/move_folder.py b/examples/sdk_examples/folder/move_folder.py index e9416aa0..17123186 100644 --- a/examples/sdk_examples/folder/move_folder.py +++ b/examples/sdk_examples/folder/move_folder.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/folder/update_folder.py b/examples/sdk_examples/folder/update_folder.py index 9a293490..01b8ac71 100644 --- a/examples/sdk_examples/folder/update_folder.py +++ b/examples/sdk_examples/folder/update_folder.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/import_export/export_vault.py b/examples/sdk_examples/import_export/export_vault.py index 29525192..8167d99d 100644 --- a/examples/sdk_examples/import_export/export_vault.py +++ b/examples/sdk_examples/import_export/export_vault.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/import_export/vault_summary.py b/examples/sdk_examples/import_export/vault_summary.py index e7df78f4..67c3a43a 100644 --- a/examples/sdk_examples/import_export/vault_summary.py +++ b/examples/sdk_examples/import_export/vault_summary.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/miscellaneous/copy_field.py b/examples/sdk_examples/miscellaneous/copy_field.py index 2b27db89..7c6ece13 100644 --- a/examples/sdk_examples/miscellaneous/copy_field.py +++ b/examples/sdk_examples/miscellaneous/copy_field.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/miscellaneous/get_totp.py b/examples/sdk_examples/miscellaneous/get_totp.py index 9c14251f..e7b8c84d 100644 --- a/examples/sdk_examples/miscellaneous/get_totp.py +++ b/examples/sdk_examples/miscellaneous/get_totp.py @@ -104,14 +104,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -146,17 +146,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/miscellaneous/list_teams.py b/examples/sdk_examples/miscellaneous/list_teams.py index 692b9818..a2a186c6 100644 --- a/examples/sdk_examples/miscellaneous/list_teams.py +++ b/examples/sdk_examples/miscellaneous/list_teams.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/miscellaneous/password_strength.py b/examples/sdk_examples/miscellaneous/password_strength.py index fd07a43d..c6141a34 100644 --- a/examples/sdk_examples/miscellaneous/password_strength.py +++ b/examples/sdk_examples/miscellaneous/password_strength.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/one_time_share/list_shares.py b/examples/sdk_examples/one_time_share/list_shares.py index 7beb69d0..a9b626ec 100644 --- a/examples/sdk_examples/one_time_share/list_shares.py +++ b/examples/sdk_examples/one_time_share/list_shares.py @@ -107,14 +107,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -149,17 +149,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/record_types/list_record_types.py b/examples/sdk_examples/record_types/list_record_types.py index 94c710dd..f22d2ed2 100644 --- a/examples/sdk_examples/record_types/list_record_types.py +++ b/examples/sdk_examples/record_types/list_record_types.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/add_record.py b/examples/sdk_examples/records/add_record.py index ec61e6c4..ce0b6e3a 100644 --- a/examples/sdk_examples/records/add_record.py +++ b/examples/sdk_examples/records/add_record.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/delete_attachment.py b/examples/sdk_examples/records/delete_attachment.py index 273cea1b..da034870 100644 --- a/examples/sdk_examples/records/delete_attachment.py +++ b/examples/sdk_examples/records/delete_attachment.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/delete_record.py b/examples/sdk_examples/records/delete_record.py index 770f708d..887e663b 100644 --- a/examples/sdk_examples/records/delete_record.py +++ b/examples/sdk_examples/records/delete_record.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/download_attachment.py b/examples/sdk_examples/records/download_attachment.py index d5f459d9..4c491046 100644 --- a/examples/sdk_examples/records/download_attachment.py +++ b/examples/sdk_examples/records/download_attachment.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/file_report.py b/examples/sdk_examples/records/file_report.py index 6dd0ddc7..1e84f3b6 100644 --- a/examples/sdk_examples/records/file_report.py +++ b/examples/sdk_examples/records/file_report.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/find_duplicate.py b/examples/sdk_examples/records/find_duplicate.py index 56207f68..ac603785 100644 --- a/examples/sdk_examples/records/find_duplicate.py +++ b/examples/sdk_examples/records/find_duplicate.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/get_record.py b/examples/sdk_examples/records/get_record.py index 1aed0039..be0a5f75 100644 --- a/examples/sdk_examples/records/get_record.py +++ b/examples/sdk_examples/records/get_record.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/list_records.py b/examples/sdk_examples/records/list_records.py index e78bb84d..1679d3bf 100644 --- a/examples/sdk_examples/records/list_records.py +++ b/examples/sdk_examples/records/list_records.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/record_history.py b/examples/sdk_examples/records/record_history.py index 44b5c8f2..b68d767e 100644 --- a/examples/sdk_examples/records/record_history.py +++ b/examples/sdk_examples/records/record_history.py @@ -107,14 +107,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -149,17 +149,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/search_record.py b/examples/sdk_examples/records/search_record.py index cfd16725..12de67ae 100644 --- a/examples/sdk_examples/records/search_record.py +++ b/examples/sdk_examples/records/search_record.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/update_record.py b/examples/sdk_examples/records/update_record.py index eed8459f..0ed58473 100644 --- a/examples/sdk_examples/records/update_record.py +++ b/examples/sdk_examples/records/update_record.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/records/upload_attachment.py b/examples/sdk_examples/records/upload_attachment.py index f6d56fb1..0a700541 100644 --- a/examples/sdk_examples/records/upload_attachment.py +++ b/examples/sdk_examples/records/upload_attachment.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/secrets_manager/app_clients.py b/examples/sdk_examples/secrets_manager/app_clients.py index 03ccd025..e3df4095 100644 --- a/examples/sdk_examples/secrets_manager/app_clients.py +++ b/examples/sdk_examples/secrets_manager/app_clients.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/secrets_manager/app_secrets.py b/examples/sdk_examples/secrets_manager/app_secrets.py index e249b187..cd62b111 100644 --- a/examples/sdk_examples/secrets_manager/app_secrets.py +++ b/examples/sdk_examples/secrets_manager/app_secrets.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/secrets_manager/create_app.py b/examples/sdk_examples/secrets_manager/create_app.py index 75e563d1..a1cf25dc 100644 --- a/examples/sdk_examples/secrets_manager/create_app.py +++ b/examples/sdk_examples/secrets_manager/create_app.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/secrets_manager/get_app.py b/examples/sdk_examples/secrets_manager/get_app.py index 3c4df012..e75d99ed 100644 --- a/examples/sdk_examples/secrets_manager/get_app.py +++ b/examples/sdk_examples/secrets_manager/get_app.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/secrets_manager/list_apps.py b/examples/sdk_examples/secrets_manager/list_apps.py index e40c3edc..69569d8c 100644 --- a/examples/sdk_examples/secrets_manager/list_apps.py +++ b/examples/sdk_examples/secrets_manager/list_apps.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/secrets_manager/share_app.py b/examples/sdk_examples/secrets_manager/share_app.py index 5c1d4b1f..ae412567 100644 --- a/examples/sdk_examples/secrets_manager/share_app.py +++ b/examples/sdk_examples/secrets_manager/share_app.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/security_audit/security_audit_report.py b/examples/sdk_examples/security_audit/security_audit_report.py index e22b15b4..c8db0618 100644 --- a/examples/sdk_examples/security_audit/security_audit_report.py +++ b/examples/sdk_examples/security_audit/security_audit_report.py @@ -127,14 +127,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -169,17 +169,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/share_report/share_report.py b/examples/sdk_examples/share_report/share_report.py index 3db1b811..ca6a3d83 100644 --- a/examples/sdk_examples/share_report/share_report.py +++ b/examples/sdk_examples/share_report/share_report.py @@ -116,14 +116,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -158,17 +158,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/shared_records_report/shared_records_report.py b/examples/sdk_examples/shared_records_report/shared_records_report.py index 9a6984f8..27b3138e 100644 --- a/examples/sdk_examples/shared_records_report/shared_records_report.py +++ b/examples/sdk_examples/shared_records_report/shared_records_report.py @@ -115,14 +115,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -157,17 +157,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/sharing_commands/share_folder.py b/examples/sdk_examples/sharing_commands/share_folder.py index d6d0b22e..c761e559 100644 --- a/examples/sdk_examples/sharing_commands/share_folder.py +++ b/examples/sdk_examples/sharing_commands/share_folder.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/sharing_commands/share_record.py b/examples/sdk_examples/sharing_commands/share_record.py index 8707933b..af6eb4d3 100644 --- a/examples/sdk_examples/sharing_commands/share_record.py +++ b/examples/sdk_examples/sharing_commands/share_record.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/trash/list_trash.py b/examples/sdk_examples/trash/list_trash.py index 3a03f8b1..995253a6 100644 --- a/examples/sdk_examples/trash/list_trash.py +++ b/examples/sdk_examples/trash/list_trash.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/trash/restore_records.py b/examples/sdk_examples/trash/restore_records.py index 15fbfd17..0461a2cc 100644 --- a/examples/sdk_examples/trash/restore_records.py +++ b/examples/sdk_examples/trash/restore_records.py @@ -105,14 +105,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -147,17 +147,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/trash/view_trash_record.py b/examples/sdk_examples/trash/view_trash_record.py index 27888a68..cec61893 100644 --- a/examples/sdk_examples/trash/view_trash_record.py +++ b/examples/sdk_examples/trash/view_trash_record.py @@ -106,14 +106,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -148,17 +148,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [ diff --git a/examples/sdk_examples/user_report/user_report.py b/examples/sdk_examples/user_report/user_report.py index 3b873c0f..3daf91f7 100644 --- a/examples/sdk_examples/user_report/user_report.py +++ b/examples/sdk_examples/user_report/user_report.py @@ -112,14 +112,14 @@ def run(self) -> Optional[keeper_auth.KeeperAuth]: step = login_auth_context.login_step if isinstance(step, login_auth.LoginStepDeviceApproval): self._handle_device_approval(step) - elif isinstance(step, login_auth.LoginStepPassword): - self._handle_password(step) elif isinstance(step, login_auth.LoginStepTwoFactor): self._handle_two_factor(step) - elif isinstance(step, login_auth.LoginStepSsoDataKey): - self._handle_sso_data_key(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 @@ -154,17 +154,61 @@ def _ensure_server(self) -> str: def _handle_device_approval( self, step: login_auth.LoginStepDeviceApproval ) -> None: - step.send_push(login_auth.DeviceApprovalChannel.KeeperPush) - print( - "Device approval request sent. Login to existing vault/console or " - "ask admin to approve this device and then press return/enter to resume" - ) - input() - step.resume() + """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 = getpass.getpass("Enter password: ") - step.verify_password(password) + """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 = [