Skip to content

Commit

Permalink
Improve EOF error messaging during CLI login flows
Browse files Browse the repository at this point in the history
Motivated by observed issues in Compute - users can set up working
Compute endpoints, change their setups slightly around access to STDIN,
and then see cryptic EOF errors when they eventually need to re-auth.

NB: as of the time of this commit, Compute handles this by raising an
error before running login flows if the current environment does not
appear to be an interactive terminal. The goal of this commit is to move
that behavior to a more centralized location.
  • Loading branch information
chris-janidlo committed Oct 24, 2024
1 parent 1871538 commit 001e3f5
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Changed
~~~~~~~

- Improved error messaging around EOF errors when prompting for code during a command
line login flow (:pr:`NUMBER`)
3 changes: 3 additions & 0 deletions docs/authorization/login_flows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ Example Code:
:member-order: bysource
:show-inheritance:


.. autoexception:: CommandLineLoginFlowEOFError

Local Server
------------

Expand Down
6 changes: 5 additions & 1 deletion src/globus_sdk/login_flows/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from .command_line_login_flow_manager import CommandLineLoginFlowManager
from .command_line_login_flow_manager import (
CommandLineLoginFlowEOFError,
CommandLineLoginFlowManager,
)
from .local_server_login_flow_manager import (
LocalServerEnvironmentalLoginError,
LocalServerLoginError,
Expand All @@ -8,6 +11,7 @@

__all__ = (
"CommandLineLoginFlowManager",
"CommandLineLoginFlowEOFError",
"LocalServerLoginError",
"LocalServerEnvironmentalLoginError",
"LocalServerLoginFlowManager",
Expand Down
23 changes: 22 additions & 1 deletion src/globus_sdk/login_flows/command_line_login_flow_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import textwrap
import typing as t
from contextlib import contextmanager

import globus_sdk
from globus_sdk.exc.base import GlobusError
from globus_sdk.gare import GlobusAuthorizationParameters
from globus_sdk.utils import get_nice_hostname

Expand All @@ -13,6 +15,13 @@
from globus_sdk.globus_app import GlobusAppConfig


class CommandLineLoginFlowEOFError(GlobusError):
"""
An error raised when a CommandLineLoginFlowManager reads an EOF when prompting for
a code.
"""


class CommandLineLoginFlowManager(LoginFlowManager):
"""
A login flow manager which drives authorization-code token grants through the
Expand Down Expand Up @@ -128,4 +137,16 @@ def prompt_for_code(self) -> str:
:returns: The authorization code entered by the user.
"""
code_prompt = "Enter the resulting Authorization Code here: "
return input(code_prompt).strip()
with self.handle_input_errors():
return input(code_prompt).strip()

@contextmanager
def handle_input_errors(self) -> t.Iterator[None]:
try:
yield
except EOFError as e:
msg = (
"An EOF was read when an authorization code was expected."
" (Are you running this in an interactive terminal?)"
)
raise CommandLineLoginFlowEOFError(msg) from e
27 changes: 27 additions & 0 deletions tests/functional/login_flows/test_login_flow_manager.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from unittest.mock import Mock, patch

import pytest

from globus_sdk import ConfidentialAppAuthClient, NativeAppAuthClient
from globus_sdk._testing import load_response
from globus_sdk.gare import GlobusAuthorizationParameters
from globus_sdk.login_flows import (
CommandLineLoginFlowManager,
LocalServerLoginFlowManager,
)
from globus_sdk.login_flows.command_line_login_flow_manager import (
CommandLineLoginFlowEOFError,
)


def _mock_input(s):
Expand Down Expand Up @@ -96,6 +101,28 @@ def test_command_line_login_flower_manager_confidential(monkeypatch, capsys):
assert "&session_required_single_domain=org.edu" in captured_output


def test_command_line_login_flow_manager_eof_error(monkeypatch, capsys):
"""
test CommandLineLoginFlowManager with a NativeAppAuthClient
"""
login_client = NativeAppAuthClient("mock_client_id")
load_response(login_client.oauth2_exchange_code_for_tokens)

def broken_input(_s):
raise EOFError()

monkeypatch.setattr("builtins.input", broken_input)

login_flow_manager = CommandLineLoginFlowManager(login_client)
auth_params = GlobusAuthorizationParameters(
required_scopes=["urn:globus:auth:scope:transfer.api.globus.org:all"],
session_required_identities=["user@org.edu"],
)

with pytest.raises(CommandLineLoginFlowEOFError):
login_flow_manager.run_login_flow(auth_params)


class MockRedirectServer:
def __init__(self, *args, **kwargs):
self.socket = Mock()
Expand Down

0 comments on commit 001e3f5

Please sign in to comment.