diff --git a/cogsol/core/api.py b/cogsol/core/api.py index eda7098..db89279 100644 --- a/cogsol/core/api.py +++ b/cogsol/core/api.py @@ -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, ) @@ -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" @@ -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, diff --git a/cogsol/core/constants.py b/cogsol/core/constants.py index 3496684..f09e6ed 100644 --- a/cogsol/core/constants.py +++ b/cogsol/core/constants.py @@ -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", @@ -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", ] diff --git a/tests/test_api_key_errors.py b/tests/test_api_key_errors.py index 461f3ec..3895733 100644 --- a/tests/test_api_key_errors.py +++ b/tests/test_api_key_errors.py @@ -38,12 +38,52 @@ 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): @@ -51,6 +91,7 @@ def test_raises_cogsol_api_error(self, monkeypatch): 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: @@ -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: @@ -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: @@ -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;