Skip to content

Commit d290173

Browse files
committed
fix(skore-hub-project/login): Ignore token URI when using API key
1 parent 2f7bd86 commit d290173

File tree

2 files changed

+76
-13
lines changed

2 files changed

+76
-13
lines changed

skore-hub-project/src/skore_hub_project/authentication/uri.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,36 @@ def persist(uri: str) -> None:
2424

2525

2626
def URI() -> str:
27-
"""URI used for ``skore hub`` authentication."""
27+
"""
28+
URI used for ``skore hub`` authentication.
29+
30+
Notes
31+
-----
32+
It is discovered by following the rules in order:
33+
34+
1. If you have setup an API key using the envar ``SKORE_HUB_API_KEY``, returns the
35+
value of the envar ``SKORE_HUB_URI``, or ``https://api.skore.probabl.ai``.
36+
37+
2. If you have an active token, extract the content of the envar ``SKORE_HUB_URI``
38+
and the URI associated with the token:
39+
2.1. If both the URIs from the environment and the one associated with the token
40+
are empty, returns ``https://api.skore.probabl.ai``.
41+
2.2. If the URI from the environment is empty and the one associated with the
42+
token isn't, returns the URI associated wit the token.
43+
2.2. If the URI associated with the token is empty, and the one from the
44+
environment isn't, returns the URI from the environment.
45+
2.2. If both the URIs associated with the token and the one from the
46+
environment are not empty:
47+
2.2.1. If both are equal, returns one of them.
48+
2.2.2. If both aren't equal, raises a conflicting exception.
49+
"""
50+
uri_from_environment = environ.get(ENVARNAME)
51+
52+
if environ.get("SKORE_HUB_API_KEY"):
53+
return uri_from_environment or DEFAULT
54+
2855
filepath = Filepath()
2956
uri_from_persistence = filepath.read_text() if filepath.exists() else None
30-
uri_from_environment = environ.get(ENVARNAME)
3157

3258
if (
3359
uri_from_persistence

skore-hub-project/tests/unit/client/test_client.py

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from pytest import mark, raises
1212

1313
from skore_hub_project.authentication import token, uri
14-
from skore_hub_project.authentication.uri import DEFAULT as URI
1514
from skore_hub_project.client.client import Client, HUBClient
1615

1716
DATETIME_MIN = datetime.min.replace(tzinfo=timezone.utc).isoformat()
@@ -123,7 +122,7 @@ def sleep(timeout):
123122
class TestHUBClient:
124123
def test_request_with_api_key(self, monkeypatch, respx_mock):
125124
monkeypatch.setenv("SKORE_HUB_API_KEY", "<api-key>")
126-
respx_mock.get(urljoin(URI, "foo")).mock(Response(200))
125+
respx_mock.get(urljoin(uri.DEFAULT, "foo")).mock(Response(200))
127126

128127
with HUBClient() as client:
129128
client.get("foo")
@@ -132,7 +131,7 @@ def test_request_with_api_key(self, monkeypatch, respx_mock):
132131
assert respx_mock.calls.last.request.headers["X-API-Key"] == "<api-key>"
133132

134133
def test_request_with_token(self, respx_mock):
135-
respx_mock.get(urljoin(URI, "foo")).mock(Response(200))
134+
respx_mock.get(urljoin(uri.DEFAULT, "foo")).mock(Response(200))
136135

137136
assert not token.Filepath().exists()
138137

@@ -150,12 +149,12 @@ def test_request_with_token(self, respx_mock):
150149
assert respx_mock.calls.last.request.headers["authorization"] == "Bearer A"
151150

152151
def test_request_with_token_and_uri(self, respx_mock):
153-
respx_mock.get(urljoin(URI, "foo")).mock(Response(200))
152+
respx_mock.get(urljoin(uri.DEFAULT, "foo")).mock(Response(200))
154153

155154
assert not token.Filepath().exists()
156155

157156
token.persist("A", "B", DATETIME_MAX)
158-
uri.persist(URI)
157+
uri.persist(uri.DEFAULT)
159158

160159
assert token.Filepath().exists()
161160
assert token.access(refresh=False) == "A"
@@ -165,10 +164,48 @@ def test_request_with_token_and_uri(self, respx_mock):
165164

166165
assert token.Filepath().exists()
167166
assert token.access(refresh=False) == "A"
168-
assert uri.URI() == URI
167+
assert uri.URI() == uri.DEFAULT
169168
assert "X-API-Key" not in respx_mock.calls.last.request.headers
170169
assert respx_mock.calls.last.request.headers["authorization"] == "Bearer A"
171170

171+
def test_request_with_api_key_and_token_and_uri(self, monkeypatch, respx_mock):
172+
TOKEN_URI = "https://token.com"
173+
API_KEY_URI = "https://apikey.com"
174+
175+
assert not uri.Filepath().exists()
176+
177+
# simulate user with token
178+
uri.persist(TOKEN_URI)
179+
180+
assert uri.URI() == TOKEN_URI
181+
182+
# simulate user with token and API key
183+
monkeypatch.setenv("SKORE_HUB_API_KEY", "<api-key>")
184+
respx_mock.get(urljoin(uri.DEFAULT, "foo")).mock(Response(200))
185+
186+
assert uri.URI() == uri.DEFAULT
187+
188+
with HUBClient() as client:
189+
client.get("foo")
190+
191+
assert respx_mock.calls.last.request.url == urljoin(uri.DEFAULT, "foo")
192+
assert "authorization" not in respx_mock.calls.last.request.headers
193+
assert respx_mock.calls.last.request.headers["X-API-Key"] == "<api-key>"
194+
195+
# simulate user with token, API key and URI in environment
196+
monkeypatch.setenv("SKORE_HUB_API_KEY", "<api-key>")
197+
monkeypatch.setenv("SKORE_HUB_URI", API_KEY_URI)
198+
respx_mock.get(urljoin(API_KEY_URI, "foo")).mock(Response(200))
199+
200+
assert uri.URI() == API_KEY_URI
201+
202+
with HUBClient() as client:
203+
client.get("foo")
204+
205+
assert respx_mock.calls.last.request.url == urljoin(API_KEY_URI, "foo")
206+
assert "authorization" not in respx_mock.calls.last.request.headers
207+
assert respx_mock.calls.last.request.headers["X-API-Key"] == "<api-key>"
208+
172209
@mark.respx(assert_all_mocked=False)
173210
def test_request_with_invalid_token_raises(self, respx_mock):
174211
with raises(token.TokenError, match="not logged in"), HUBClient() as client:
@@ -179,7 +216,7 @@ def test_request_with_invalid_token_raises(self, respx_mock):
179216
@mark.respx(assert_all_mocked=False)
180217
def test_request_with_conflicting_uri_raises(self, respx_mock, monkeypatch):
181218
token.persist("A", "B", DATETIME_MAX)
182-
uri.persist(URI)
219+
uri.persist(uri.DEFAULT)
183220
monkeypatch.setenv(uri.ENVARNAME, "https://my-conflicting-uri")
184221

185222
with (
@@ -193,7 +230,7 @@ def test_request_with_conflicting_uri_raises(self, respx_mock, monkeypatch):
193230
assert not respx_mock.calls
194231

195232
def test_request_with_expired_token(self, tmp_path, respx_mock):
196-
respx_mock.get(urljoin(URI, "foo")).mock(Response(200))
233+
respx_mock.get(urljoin(uri.DEFAULT, "foo")).mock(Response(200))
197234
respx_mock.post(REFRESH_URL).mock(
198235
Response(
199236
200,
@@ -221,7 +258,7 @@ def test_request_with_expired_token(self, tmp_path, respx_mock):
221258
assert respx_mock.calls.last.request.headers["authorization"] == "Bearer D"
222259

223260
def test_request_raises(self, tmp_path, respx_mock):
224-
respx_mock.get(urljoin(URI, "foo")).mock(Response(404))
261+
respx_mock.get(urljoin(uri.DEFAULT, "foo")).mock(Response(404))
225262

226263
assert not token.Filepath().exists()
227264

@@ -244,7 +281,7 @@ def test_request_without_package_version(self, respx_mock):
244281
assert version("skore-hub-project") == "0.0.0+unknown"
245282
assert PACKAGE_VERSION is None
246283

247-
respx_mock.get(urljoin(URI, "foo")).mock(Response(200))
284+
respx_mock.get(urljoin(uri.DEFAULT, "foo")).mock(Response(200))
248285

249286
with HUBClient(authenticated=False) as client:
250287
client.get("foo")
@@ -253,7 +290,7 @@ def test_request_without_package_version(self, respx_mock):
253290

254291
def test_request_with_package_version(self, monkeypatch, respx_mock):
255292
monkeypatch.setattr("skore_hub_project.client.client.PACKAGE_VERSION", "1.0.0")
256-
respx_mock.get(urljoin(URI, "foo")).mock(Response(200))
293+
respx_mock.get(urljoin(uri.DEFAULT, "foo")).mock(Response(200))
257294

258295
with HUBClient(authenticated=False) as client:
259296
client.get("foo")

0 commit comments

Comments
 (0)