Skip to content

Commit 20f5ae6

Browse files
authored
Route local API by api_type before server-specific strategies (#2129)
## Summary Move the `APIType.LOCAL` check to the top of `build_auth_strategy` so a local config can carry any `server` identity (e.g. `Server.REXEL`) purely for labelling, without misrouting to that server's cloud strategy. ## Why Local configs are built via `create_local_server_config`. Previously, if a caller set `server=Server.REXEL` on a local config, the server-specific Rexel branch matched *before* the generic `api_type == APIType.LOCAL` branch and routed to the Rexel OAuth strategy — which rejects a local token with `TypeError`. The only `server` value that worked for local was one with no earlier branch (the `Server.SOMFY_DEVELOPER_MODE` default). Routing on `api_type` first removes that coupling: `server`/`name`/`manufacturer` become free-form labels for local connections. ## Safety Every server in `SUPPORTED_SERVERS` is `APIType.CLOUD`; the only source of `APIType.LOCAL` is `create_local_server_config` (or a hand-built `ServerConfig`). So moving the `LOCAL` check first cannot steal routing from any cloud server. ## Tests - Adds `test_build_auth_strategy_local_token_with_cloud_server`: a local config with `server=Server.REXEL` must still select `LocalTokenAuthStrategy`. - Full `tests/test_auth.py` suite passes (70 tests).
1 parent c0a5c50 commit 20f5ae6

2 files changed

Lines changed: 41 additions & 12 deletions

File tree

pyoverkiz/auth/factory.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@ def build_auth_strategy(
3939
"""Build the correct auth strategy for the given server and credentials."""
4040
server: Server | None = server_config.server
4141

42+
# Local API routing keys off api_type, not server, so a local config can
43+
# carry any server identity (e.g. Server.REXEL) purely for labelling.
44+
if server_config.api_type == APIType.LOCAL:
45+
if isinstance(credentials, LocalTokenCredentials):
46+
return LocalTokenAuthStrategy(
47+
credentials, session, server_config, ssl_context
48+
)
49+
return BearerTokenAuthStrategy(
50+
_ensure_credentials(credentials, TokenCredentials),
51+
session,
52+
server_config,
53+
ssl_context,
54+
)
55+
4256
if server == Server.SOMFY_EUROPE:
4357
return SomfyAuthStrategy(
4458
_ensure_credentials(credentials, UsernamePasswordCredentials),
@@ -79,18 +93,6 @@ def build_auth_strategy(
7993
ssl_context,
8094
)
8195

82-
if server_config.api_type == APIType.LOCAL:
83-
if isinstance(credentials, LocalTokenCredentials):
84-
return LocalTokenAuthStrategy(
85-
credentials, session, server_config, ssl_context
86-
)
87-
return BearerTokenAuthStrategy(
88-
_ensure_credentials(credentials, TokenCredentials),
89-
session,
90-
server_config,
91-
ssl_context,
92-
)
93-
9496
if isinstance(credentials, TokenCredentials) and not isinstance(
9597
credentials, LocalTokenCredentials
9698
):

tests/test_auth.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,33 @@ async def test_build_auth_strategy_local_token(self):
340340

341341
assert isinstance(strategy, LocalTokenAuthStrategy)
342342

343+
@pytest.mark.asyncio
344+
async def test_build_auth_strategy_local_token_with_cloud_server(self):
345+
"""A local config keeps local routing even when server is a cloud id.
346+
347+
Local API routing keys off api_type, so a config labelled with a
348+
cloud-mapped server (e.g. Server.REXEL) must still select the local
349+
token strategy rather than the Rexel OAuth strategy.
350+
"""
351+
server_config = ServerConfig(
352+
server=Server.REXEL,
353+
name="Rexel Energeasy Connect (local)",
354+
endpoint="https://gateway.local",
355+
manufacturer="Rexel",
356+
api_type=APIType.LOCAL,
357+
)
358+
credentials = LocalTokenCredentials("local_token")
359+
session = AsyncMock(spec=ClientSession)
360+
361+
strategy = build_auth_strategy(
362+
server_config=server_config,
363+
credentials=credentials,
364+
session=session,
365+
ssl_context=True,
366+
)
367+
368+
assert isinstance(strategy, LocalTokenAuthStrategy)
369+
343370
@pytest.mark.asyncio
344371
async def test_build_auth_strategy_local_bearer(self):
345372
"""Test building local bearer token auth strategy."""

0 commit comments

Comments
 (0)