Skip to content

Commit cbcd752

Browse files
arne-aignxclaude
andcommitted
fix(auth): address SonarCloud cognitive complexity and string duplication
Extract cookie auth field validation into _validate_cookie_auth helper to reduce validate_auth_dependencies cognitive complexity from 17 to 4. Extract repeated _fetch_jwks patch path into _FETCH_JWKS_PATH constant. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4122f75 commit cbcd752

2 files changed

Lines changed: 28 additions & 20 deletions

File tree

src/aignostics_foundry_core/api/auth.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -97,36 +97,45 @@ def __init__(self, **kwargs: Any) -> None: # noqa: ANN401
9797
ctx = get_context()
9898
super().__init__(_env_prefix=f"{ctx.env_prefix}AUTH_", _env_file=ctx.env_file, **kwargs) # pyright: ignore[reportCallIssue]
9999

100-
@model_validator(mode="after")
101-
def validate_auth_dependencies(self) -> "AuthSettings":
102-
"""Validate cross-field auth dependencies.
103-
104-
Returns:
105-
AuthSettings: The validated settings instance.
100+
def _validate_cookie_auth(self) -> None:
101+
"""Validate cookie auth required fields when cookie auth is active.
106102
107103
Raises:
108-
ValueError: If any cross-field dependency is violated.
104+
ValueError: If any required cookie auth field is missing or invalid.
109105
"""
110106
cookie_active = self.cookie_enabled or self.enabled
111-
112-
if cookie_active and self.session_secret is None:
107+
if not cookie_active:
108+
return
109+
if self.session_secret is None:
113110
msg = "AUTH_SESSION_SECRET must not be None when cookie auth is enabled"
114111
raise ValueError(msg)
115-
if cookie_active and self.client_secret is None:
112+
if self.client_secret is None:
116113
msg = "AUTH_CLIENT_SECRET must not be None when cookie auth is enabled"
117114
raise ValueError(msg)
118-
if cookie_active and not self.domain:
115+
if not self.domain:
119116
msg = "AUTH_DOMAIN must not be empty when cookie auth is enabled"
120117
raise ValueError(msg)
121-
if cookie_active and not self.client_id:
118+
if not self.client_id:
122119
msg = "AUTH_CLIENT_ID must not be empty when cookie auth is enabled"
123120
raise ValueError(msg)
124-
if cookie_active and not self.internal_org_id:
121+
if not self.internal_org_id:
125122
msg = "AUTH_INTERNAL_ORG_ID must not be empty when cookie auth is enabled"
126123
raise ValueError(msg)
127-
if cookie_active and not self.role_claim:
124+
if not self.role_claim:
128125
msg = "AUTH_ROLE_CLAIM must not be empty when cookie auth is enabled"
129126
raise ValueError(msg)
127+
128+
@model_validator(mode="after")
129+
def validate_auth_dependencies(self) -> "AuthSettings":
130+
"""Validate cross-field auth dependencies.
131+
132+
Returns:
133+
AuthSettings: The validated settings instance.
134+
135+
Raises:
136+
ValueError: If any cross-field dependency is violated.
137+
"""
138+
self._validate_cookie_auth()
130139
if self.jwt_enabled and not self.domain:
131140
msg = "AUTH_DOMAIN must not be empty when AUTH_JWT_ENABLED is True"
132141
raise ValueError(msg)

tests/aignostics_foundry_core/api/auth_test.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
_TEST_JWT_AUDIENCE = "https://api.example.com"
4343
_TEST_BEARER_TOKEN = "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3QifQ.test.test" # noqa: S105
4444
_TEST_KID = "test-kid"
45+
_FETCH_JWKS_PATH = "aignostics_foundry_core.api.auth._fetch_jwks"
4546

4647

4748
@pytest.mark.unit
@@ -397,7 +398,7 @@ async def test_validate_jwt_returns_none_when_kid_not_in_jwks(self) -> None:
397398
jwks_without_kid: dict = {"keys": []}
398399
settings = AuthSettings(jwt_enabled=True, domain=_TEST_DOMAIN, jwt_audience=_TEST_JWT_AUDIENCE)
399400

400-
with patch("aignostics_foundry_core.api.auth._fetch_jwks", AsyncMock(return_value=jwks_without_kid)):
401+
with patch(_FETCH_JWKS_PATH, AsyncMock(return_value=jwks_without_kid)):
401402
import jwt
402403

403404
with patch.object(jwt, "get_unverified_header", return_value={"kid": _TEST_KID, "alg": "RS256"}):
@@ -409,9 +410,7 @@ async def test_validate_jwt_returns_none_on_fetch_failure(self) -> None:
409410
"""_validate_jwt returns None when JWKS fetch raises an exception."""
410411
settings = AuthSettings(jwt_enabled=True, domain=_TEST_DOMAIN, jwt_audience=_TEST_JWT_AUDIENCE)
411412

412-
with patch(
413-
"aignostics_foundry_core.api.auth._fetch_jwks", AsyncMock(side_effect=RuntimeError("network error"))
414-
):
413+
with patch(_FETCH_JWKS_PATH, AsyncMock(side_effect=RuntimeError("network error"))):
415414
result = await _validate_jwt(_TEST_BEARER_TOKEN, settings)
416415

417416
assert result is None
@@ -424,7 +423,7 @@ async def test_validate_jwt_returns_none_for_invalid_token(self, mock_jwks: dict
424423
settings = AuthSettings(jwt_enabled=True, domain=_TEST_DOMAIN, jwt_audience=_TEST_JWT_AUDIENCE)
425424

426425
with (
427-
patch("aignostics_foundry_core.api.auth._fetch_jwks", AsyncMock(return_value=mock_jwks)),
426+
patch(_FETCH_JWKS_PATH, AsyncMock(return_value=mock_jwks)),
428427
patch.object(jwt, "get_unverified_header", return_value={"kid": _TEST_KID, "alg": "RS256"}),
429428
patch.object(RSAAlgorithm, "from_jwk", return_value=MagicMock()),
430429
patch.object(jwt, "decode", side_effect=jwt.ExpiredSignatureError("expired")),
@@ -442,7 +441,7 @@ async def test_validate_jwt_returns_payload_for_valid_token(self, mock_jwks: dic
442441
settings = AuthSettings(jwt_enabled=True, domain=_TEST_DOMAIN, jwt_audience=_TEST_JWT_AUDIENCE)
443442

444443
with (
445-
patch("aignostics_foundry_core.api.auth._fetch_jwks", AsyncMock(return_value=mock_jwks)),
444+
patch(_FETCH_JWKS_PATH, AsyncMock(return_value=mock_jwks)),
446445
patch.object(jwt, "get_unverified_header", return_value={"kid": _TEST_KID, "alg": "RS256"}),
447446
patch.object(RSAAlgorithm, "from_jwk", return_value=MagicMock()),
448447
patch.object(jwt, "decode", return_value=expected_payload),

0 commit comments

Comments
 (0)