diff --git a/.generator/src/generator/templates/configuration.j2 b/.generator/src/generator/templates/configuration.j2 index b0f3730142..05e490f291 100644 --- a/.generator/src/generator/templates/configuration.j2 +++ b/.generator/src/generator/templates/configuration.j2 @@ -269,6 +269,8 @@ class Configuration: self.api_key["apiKeyAuth"] = os.environ["DD_API_KEY"] if "DD_APP_KEY" in os.environ and not self.api_key.get("appKeyAuth"): self.api_key["appKeyAuth"] = os.environ["DD_APP_KEY"] + if "DD_BEARER_TOKEN" in os.environ and not self.access_token: + self.access_token = os.environ["DD_BEARER_TOKEN"] def __deepcopy__(self, memo): cls = self.__class__ @@ -535,7 +537,7 @@ class Configuration: "key": "Authorization", "value": self.get_basic_auth_token() } -{# {%- elif schema.type == "http" and schema.scheme == "bearer" %} +{%- elif schema.type == "http" and schema.scheme == "bearer" %} if self.access_token is not None: auth["{{name}}"] = { "type": "bearer", @@ -546,7 +548,6 @@ class Configuration: "key": "Authorization", "value": "Bearer " + self.access_token } -#} {%- elif schema.type == "oauth2" %} if self.access_token is not None: auth["AuthZ"] = { diff --git a/src/datadog_api_client/configuration.py b/src/datadog_api_client/configuration.py index 8aa9287f21..8d243d852d 100644 --- a/src/datadog_api_client/configuration.py +++ b/src/datadog_api_client/configuration.py @@ -478,6 +478,8 @@ def __init__( self.api_key["apiKeyAuth"] = os.environ["DD_API_KEY"] if "DD_APP_KEY" in os.environ and not self.api_key.get("appKeyAuth"): self.api_key["appKeyAuth"] = os.environ["DD_APP_KEY"] + if "DD_BEARER_TOKEN" in os.environ and not self.access_token: + self.access_token = os.environ["DD_BEARER_TOKEN"] def __deepcopy__(self, memo): cls = self.__class__ @@ -777,4 +779,11 @@ def auth_settings(self): "appKeyAuth", ), } + if self.access_token is not None: + auth["bearerAuth"] = { + "type": "bearer", + "in": "header", + "key": "Authorization", + "value": "Bearer " + self.access_token, + } return auth diff --git a/tests/test_pat_auth.py b/tests/test_pat_auth.py new file mode 100644 index 0000000000..6614eebad7 --- /dev/null +++ b/tests/test_pat_auth.py @@ -0,0 +1,123 @@ +"""Tests for Personal Access Token (PAT) authentication support.""" + +from unittest.mock import patch + +from datadog_api_client.api_client import ApiClient, Endpoint as _Endpoint +from datadog_api_client.configuration import Configuration + + +class TestBearerTokenConfiguration: + """Test access_token field on Configuration for bearer auth.""" + + def test_bearer_token_stored(self): + config = Configuration(access_token="ddpat_test123") + assert config.access_token == "ddpat_test123" + + def test_bearer_token_default_none(self): + config = Configuration() + assert config.access_token is None + + @patch.dict("os.environ", {"DD_BEARER_TOKEN": "ddpat_from_env"}) + def test_bearer_token_env_var(self): + config = Configuration() + assert config.access_token == "ddpat_from_env" + + @patch.dict("os.environ", {"DD_BEARER_TOKEN": "ddpat_from_env"}) + def test_bearer_token_env_var_no_override(self): + config = Configuration(access_token="ddpat_explicit") + assert config.access_token == "ddpat_explicit" + + +class TestAuthSettingsWithBearerToken: + """Test auth_settings() with access_token configured.""" + + def test_auth_settings_includes_bearer(self): + config = Configuration(access_token="ddpat_test123") + auth = config.auth_settings() + assert "bearerAuth" in auth + assert auth["bearerAuth"]["type"] == "bearer" + assert auth["bearerAuth"]["in"] == "header" + assert auth["bearerAuth"]["key"] == "Authorization" + assert auth["bearerAuth"]["value"] == "Bearer ddpat_test123" + + def test_auth_settings_without_bearer(self): + config = Configuration() + auth = config.auth_settings() + assert "bearerAuth" not in auth + + +class TestUpdateParamsForAuthWithBearerToken: + """Test that update_params_for_auth sends all configured auth headers.""" + + def _make_endpoint(self, config, auth_schemes): + """Helper to create an Endpoint with given auth schemes.""" + api_client = ApiClient(config) + return _Endpoint( + settings={ + "response_type": None, + "auth": auth_schemes, + "endpoint_path": "/api/v2/test", + "operation_id": "test_op", + "http_method": "GET", + "version": "v2", + }, + params_map={}, + headers_map={"accept": ["application/json"]}, + api_client=api_client, + ) + + def test_all_auth_headers_sent_when_all_configured(self): + """When all credentials are set, all auth headers are sent.""" + config = Configuration( + api_key={"apiKeyAuth": "test-api-key", "appKeyAuth": "test-app-key"}, + access_token="ddpat_test_pat", + ) + endpoint = self._make_endpoint(config, ["apiKeyAuth", "appKeyAuth", "bearerAuth"]) + headers = {} + queries = [] + endpoint.update_params_for_auth(headers, queries) + + assert headers["Authorization"] == "Bearer ddpat_test_pat" + assert headers["DD-API-KEY"] == "test-api-key" + assert headers["DD-APPLICATION-KEY"] == "test-app-key" + + def test_bearer_only(self): + """Bearer token works without any API keys configured.""" + config = Configuration(access_token="ddpat_test_pat") + endpoint = self._make_endpoint(config, ["apiKeyAuth", "appKeyAuth", "bearerAuth"]) + headers = {} + queries = [] + endpoint.update_params_for_auth(headers, queries) + + assert headers["Authorization"] == "Bearer ddpat_test_pat" + assert "DD-API-KEY" not in headers + assert "DD-APPLICATION-KEY" not in headers + + def test_api_keys_only(self): + """Regular auth works without bearer token.""" + config = Configuration( + api_key={"apiKeyAuth": "test-api-key", "appKeyAuth": "test-app-key"}, + ) + endpoint = self._make_endpoint(config, ["apiKeyAuth", "appKeyAuth", "bearerAuth"]) + headers = {} + queries = [] + endpoint.update_params_for_auth(headers, queries) + + assert headers["DD-API-KEY"] == "test-api-key" + assert headers["DD-APPLICATION-KEY"] == "test-app-key" + assert "Authorization" not in headers + + def test_bearer_not_sent_when_not_in_endpoint_auth(self): + """access_token set but endpoint doesn't declare bearerAuth.""" + config = Configuration( + api_key={"apiKeyAuth": "test-api-key", "appKeyAuth": "test-app-key"}, + access_token="ddpat_test_pat", + ) + endpoint = self._make_endpoint(config, ["apiKeyAuth", "appKeyAuth"]) + headers = {} + queries = [] + endpoint.update_params_for_auth(headers, queries) + + assert headers["DD-API-KEY"] == "test-api-key" + assert headers["DD-APPLICATION-KEY"] == "test-app-key" + assert "Authorization" not in headers