Skip to content

Commit 2305d75

Browse files
authored
fix: šŸ› don't load credentials when config does not match the discovered environment (#122)
* feat: improve compatibility with Python 3.14 `httpcore` that is dependency of the `httpx` causes `AttributeError` for `httpcore<1.0.8`. (https://github.com/encode/httpcore/blob/master/CHANGELOG.md#version-108-april-11th-2025) Support for `httpcore>=1.0` was added in `httpc==0.25.1`. (https://github.com/encode/httpx/blob/master/CHANGELOG.md#0251-3rd-november-2023) Constraining httpx for python 3.14 allows to use resolve httpx to versions can use `httpcore` that fixes the issue for python 3.14 * fix: šŸ› don't load credentials when config does not match the discovered environment This can happen when the discovery endpoint is set explicitly or via env variables. Such credentials do not work with the target environment anyway.
1 parent aa7e702 commit 2305d75

File tree

3 files changed

+50
-14
lines changed

3 files changed

+50
-14
lines changed

ā€Žsrc/h2o_discovery/__init__.pyā€Ž

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,7 @@ def discover(
6060
discovery = load.load_discovery(
6161
client.Client(uri=uri, timeout=http_timeout, ssl_context=ssl_context)
6262
)
63-
credentials = load.load_credentials(
64-
clients=discovery.clients, config_tokens=cfg.tokens
65-
)
66-
63+
credentials = load.load_credentials(discovery=discovery, cfg=cfg)
6764
return dataclasses.replace(discovery, credentials=credentials)
6865

6966

@@ -110,10 +107,7 @@ async def discover_async(
110107
discovery = await load.load_discovery_async(
111108
client.AsyncClient(uri=uri, timeout=http_timeout, ssl_context=ssl_context)
112109
)
113-
credentials = load.load_credentials(
114-
clients=discovery.clients, config_tokens=cfg.tokens
115-
)
116-
110+
credentials = load.load_credentials(discovery=discovery, cfg=cfg)
117111
return dataclasses.replace(discovery, credentials=credentials)
118112

119113

ā€Žsrc/h2o_discovery/_internal/load.pyā€Ž

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
from typing import Iterable
66
from typing import List
77
from typing import Mapping
8-
from typing import Optional
98

109
import httpx
1110

1211
from h2o_discovery import error
1312
from h2o_discovery import model
1413
from h2o_discovery._internal import client
14+
from h2o_discovery._internal import config
1515

1616

1717
def load_discovery(cl: client.Client) -> model.Discovery:
@@ -112,17 +112,20 @@ async def _list_components_async(cl: client.AsyncClient) -> List[model.Component
112112

113113

114114
def load_credentials(
115-
clients: Mapping[str, model.Client], config_tokens: Optional[Mapping[str, str]]
115+
discovery: model.Discovery, cfg: config.Config
116116
) -> Mapping[str, model.Credentials]:
117117
"""Loads client credentials from the environment or tokens loaded from the
118118
config.
119119
"""
120+
h2o_cloud_environment = discovery.environment.h2o_cloud_environment.rstrip("/")
121+
config_matches = cfg.endpoint and h2o_cloud_environment == cfg.endpoint.rstrip("/")
120122
tokens: Mapping[str, str] = {}
121-
if config_tokens is not None:
122-
tokens = config_tokens
123+
if config_matches and cfg.tokens is not None:
124+
# Load tokens from the config only when the environments match.
125+
tokens = cfg.tokens
123126

124127
out = {}
125-
for name, cl in clients.items():
128+
for name, cl in discovery.clients.items():
126129
env_name = f"H2O_CLOUD_CLIENT_{name.upper()}_TOKEN"
127130
token = os.environ.get(env_name) or tokens.get(cl.oauth2_client_id)
128131
if token:

ā€Žtests/_internal/load/test_load_credentials.pyā€Ž

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
from h2o_discovery import model
2+
from h2o_discovery._internal import config
23
from h2o_discovery._internal import load
34

5+
_ENVIRONMENT = model.Environment(
6+
h2o_cloud_environment="https://example.com",
7+
issuer_url="https://example.com",
8+
h2o_cloud_platform_oauth2_scope=["scope1", "scope2"],
9+
)
10+
411

512
def test_load_credentials(monkeypatch):
613
# Given
@@ -24,8 +31,11 @@ def test_load_credentials(monkeypatch):
2431
tokens = {"config-client-id": "config-token"}
2532
monkeypatch.setenv("H2O_CLOUD_CLIENT_ENV_CLIENT_TOKEN", "env-token")
2633

34+
discovery = model.Discovery(clients=clients, environment=_ENVIRONMENT, services={})
35+
cfg = config.Config(tokens=tokens, endpoint=_ENVIRONMENT.h2o_cloud_environment)
36+
2737
# When
28-
result = load.load_credentials(clients=clients, config_tokens=tokens)
38+
result = load.load_credentials(discovery=discovery, cfg=cfg)
2939

3040
# Then
3141
assert result == {
@@ -34,3 +44,32 @@ def test_load_credentials(monkeypatch):
3444
client="config_client", refresh_token="config-token"
3545
),
3646
}
47+
48+
49+
def test_load_credentials_not_loaded_from_config_for_different_environment(monkeypatch):
50+
# Given
51+
clients = {
52+
"env_client": model.Client(
53+
name="clients/env_client",
54+
display_name="Env Client",
55+
oauth2_client_id="env-client-id",
56+
),
57+
"config_client": model.Client(
58+
name="clients/config_client",
59+
display_name="Config Client",
60+
oauth2_client_id="config-client-id",
61+
),
62+
}
63+
tokens = {"config-client-id": "config-token"}
64+
monkeypatch.setenv("H2O_CLOUD_CLIENT_ENV_CLIENT_TOKEN", "env-token")
65+
66+
discovery = model.Discovery(clients=clients, environment=_ENVIRONMENT, services={})
67+
cfg = config.Config(tokens=tokens, endpoint="https://other.com")
68+
69+
# When
70+
result = load.load_credentials(discovery=discovery, cfg=cfg)
71+
72+
# Then
73+
assert result == {
74+
"env_client": model.Credentials(client="env_client", refresh_token="env-token")
75+
}

0 commit comments

Comments
Ā (0)