Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion cogsol/core/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
from jwt import decode

from cogsol.core.constants import (
AUTH_SCOPE_IDS,
get_cognitive_api_base_url,
get_cogsol_env,
get_content_api_base_url,
)

Expand Down Expand Up @@ -133,6 +135,14 @@ def _refresh_bearer_token(self) -> None:
client_id = os.environ.get("COGSOL_AUTH_CLIENT_ID")
client_secret = os.environ.get("COGSOL_AUTH_SECRET")

cogsol_env = get_cogsol_env()
scope_id = AUTH_SCOPE_IDS.get(cogsol_env)
if scope_id is None:
raise CogSolAPIError(
f"Unknown COGSOL_ENV value: {cogsol_env!r}. "
"Expected one of: development, testing, implantation, production."
)

if not client_secret:
raise CogSolAPIError(
"Missing authentication configuration: COGSOL_AUTH_SECRET is not set.\n"
Expand All @@ -141,7 +151,7 @@ def _refresh_bearer_token(self) -> None:
)

authority = "https://pyxiscognitivesweden.b2clogin.com/pyxiscognitivesweden.onmicrosoft.com/B2C_1A_CS_signup_signin_Sweden_MigrationOIDC"
scopes = [f"https://pyxiscognitivesweden.onmicrosoft.com/{client_id}/.default"]
scopes = [f"https://pyxiscognitivesweden.onmicrosoft.com/{scope_id}/.default"]

app = msal.ConfidentialClientApplication(
client_id,
Expand Down
9 changes: 9 additions & 0 deletions cogsol/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ def get_content_api_base_url() -> str:
return _get_env_var(COGSOL_CONTENT_API_BASE_VAR) or get_default_content_api_base_url()


AUTH_SCOPE_IDS: Final[dict[str, str]] = {
"development": "02d621e7-40ef-4a46-a110-4214e1234abf",
"testing": "ce62b03a-7eca-4d4a-9c7a-2f1e001fce29",
"implantation": "9efa4bc6-2b2b-4208-8c88-7a218c7061d6",
"production": "92c0d1cc-127b-4ec5-9be4-960c13c7aecc",
}


__all__ = [
"COGSOL_ENV_VAR",
"COGSOL_API_BASE_VAR",
Expand All @@ -76,4 +84,5 @@ def get_content_api_base_url() -> str:
"get_default_content_api_base_url",
"get_cognitive_api_base_url",
"get_content_api_base_url",
"AUTH_SCOPE_IDS",
]
44 changes: 44 additions & 0 deletions tests/test_api_key_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,60 @@ def _bare_client() -> CogSolClient:
# ---------------------------------------------------------------------------


class TestInvalidCogsolEnv:
"""_refresh_bearer_token must raise CogSolAPIError when COGSOL_ENV is
missing or set to an unrecognised value."""

def test_raises_when_env_not_set(self, monkeypatch):
monkeypatch.setenv("COGSOL_AUTH_CLIENT_ID", "test-client-id")
monkeypatch.delenv("COGSOL_ENV", raising=False)

with pytest.raises(CogSolAPIError):
_bare_client()._refresh_bearer_token()

def test_raises_when_env_is_unknown(self, monkeypatch):
monkeypatch.setenv("COGSOL_AUTH_CLIENT_ID", "test-client-id")
monkeypatch.setenv("COGSOL_ENV", "staging")

with pytest.raises(CogSolAPIError):
_bare_client()._refresh_bearer_token()

def test_error_mentions_invalid_env_value(self, monkeypatch):
monkeypatch.setenv("COGSOL_AUTH_CLIENT_ID", "test-client-id")
monkeypatch.setenv("COGSOL_ENV", "staging")

with pytest.raises(CogSolAPIError) as exc_info:
_bare_client()._refresh_bearer_token()

assert "staging" in str(exc_info.value)

def test_error_lists_valid_env_values(self, monkeypatch):
monkeypatch.setenv("COGSOL_AUTH_CLIENT_ID", "test-client-id")
monkeypatch.setenv("COGSOL_ENV", "bad-value")

with pytest.raises(CogSolAPIError) as exc_info:
_bare_client()._refresh_bearer_token()

error_msg = str(exc_info.value)
for valid_env in ("development", "testing", "implantation", "production"):
assert valid_env in error_msg


class TestMissingAuthSecret:
"""_refresh_bearer_token must raise CogSolAPIError with a helpful message
when COGSOL_AUTH_CLIENT_ID is set but COGSOL_AUTH_SECRET is missing."""

def test_raises_cogsol_api_error(self, monkeypatch):
monkeypatch.setenv("COGSOL_AUTH_CLIENT_ID", "test-client-id")
monkeypatch.setenv("COGSOL_ENV", "development")
monkeypatch.delenv("COGSOL_AUTH_SECRET", raising=False)

with pytest.raises(CogSolAPIError):
_bare_client()._refresh_bearer_token()

def test_error_mentions_missing_secret_var(self, monkeypatch):
monkeypatch.setenv("COGSOL_AUTH_CLIENT_ID", "test-client-id")
monkeypatch.setenv("COGSOL_ENV", "development")
monkeypatch.delenv("COGSOL_AUTH_SECRET", raising=False)

with pytest.raises(CogSolAPIError) as exc_info:
Expand All @@ -60,6 +101,7 @@ def test_error_mentions_missing_secret_var(self, monkeypatch):

def test_error_includes_onboarding_url(self, monkeypatch):
monkeypatch.setenv("COGSOL_AUTH_CLIENT_ID", "test-client-id")
monkeypatch.setenv("COGSOL_ENV", "development")
monkeypatch.delenv("COGSOL_AUTH_SECRET", raising=False)

with pytest.raises(CogSolAPIError) as exc_info:
Expand All @@ -69,6 +111,7 @@ def test_error_includes_onboarding_url(self, monkeypatch):

def test_error_mentions_implantation_portal(self, monkeypatch):
monkeypatch.setenv("COGSOL_AUTH_CLIENT_ID", "test-client-id")
monkeypatch.setenv("COGSOL_ENV", "development")
monkeypatch.delenv("COGSOL_AUTH_SECRET", raising=False)

with pytest.raises(CogSolAPIError) as exc_info:
Expand All @@ -80,6 +123,7 @@ def test_no_error_when_secret_is_present(self, monkeypatch):
"""With a valid secret the error must NOT be raised (msal call may fail,
but that is a different error path)."""
monkeypatch.setenv("COGSOL_AUTH_CLIENT_ID", "test-client-id")
monkeypatch.setenv("COGSOL_ENV", "development")
monkeypatch.setenv("COGSOL_AUTH_SECRET", "some-secret")

# The error about missing secret should not be raised;
Expand Down
Loading