Skip to content

Commit 9bc92a2

Browse files
committed
CRED-2146: Add PAT auth support to Python API client - templates and tests
1 parent 9f2b67a commit 9bc92a2

File tree

2 files changed

+133
-2
lines changed

2 files changed

+133
-2
lines changed

.generator/src/generator/templates/configuration.j2

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ class Configuration:
269269
self.api_key["apiKeyAuth"] = os.environ["DD_API_KEY"]
270270
if "DD_APP_KEY" in os.environ and not self.api_key.get("appKeyAuth"):
271271
self.api_key["appKeyAuth"] = os.environ["DD_APP_KEY"]
272+
if "DD_BEARER_TOKEN" in os.environ and not self.access_token:
273+
self.access_token = os.environ["DD_BEARER_TOKEN"]
272274

273275
def __deepcopy__(self, memo):
274276
cls = self.__class__
@@ -535,7 +537,7 @@ class Configuration:
535537
"key": "Authorization",
536538
"value": self.get_basic_auth_token()
537539
}
538-
{# {%- elif schema.type == "http" and schema.scheme == "bearer" %}
540+
{%- elif schema.type == "http" and schema.scheme == "bearer" %}
539541
if self.access_token is not None:
540542
auth["{{name}}"] = {
541543
"type": "bearer",
@@ -546,7 +548,6 @@ class Configuration:
546548
"key": "Authorization",
547549
"value": "Bearer " + self.access_token
548550
}
549-
#}
550551
{%- elif schema.type == "oauth2" %}
551552
if self.access_token is not None:
552553
auth["AuthZ"] = {

tests/test_pat_auth.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""Tests for Personal Access Token (PAT) authentication support."""
2+
3+
import pytest
4+
from datetime import datetime, timedelta
5+
from unittest.mock import patch
6+
7+
from datadog_api_client.api_client import ApiClient, Endpoint as _Endpoint
8+
from datadog_api_client.configuration import Configuration
9+
from datadog_api_client.delegated_auth import (
10+
DelegatedTokenCredentials,
11+
DelegatedTokenConfig,
12+
DelegatedTokenProvider,
13+
)
14+
15+
16+
class TestBearerTokenConfiguration:
17+
"""Test access_token field on Configuration for bearer auth."""
18+
19+
def test_bearer_token_stored(self):
20+
config = Configuration(access_token="ddpat_test123")
21+
assert config.access_token == "ddpat_test123"
22+
23+
def test_bearer_token_default_none(self):
24+
config = Configuration()
25+
assert config.access_token is None
26+
27+
@patch.dict("os.environ", {"DD_BEARER_TOKEN": "ddpat_from_env"})
28+
def test_bearer_token_env_var(self):
29+
config = Configuration()
30+
assert config.access_token == "ddpat_from_env"
31+
32+
@patch.dict("os.environ", {"DD_BEARER_TOKEN": "ddpat_from_env"})
33+
def test_bearer_token_env_var_no_override(self):
34+
config = Configuration(access_token="ddpat_explicit")
35+
assert config.access_token == "ddpat_explicit"
36+
37+
38+
class TestAuthSettingsWithBearerToken:
39+
"""Test auth_settings() with access_token configured."""
40+
41+
def test_auth_settings_includes_bearer(self):
42+
config = Configuration(access_token="ddpat_test123")
43+
auth = config.auth_settings()
44+
assert "bearerAuth" in auth
45+
assert auth["bearerAuth"]["type"] == "bearer"
46+
assert auth["bearerAuth"]["in"] == "header"
47+
assert auth["bearerAuth"]["key"] == "Authorization"
48+
assert auth["bearerAuth"]["value"] == "Bearer ddpat_test123"
49+
50+
def test_auth_settings_without_bearer(self):
51+
config = Configuration()
52+
auth = config.auth_settings()
53+
assert "bearerAuth" not in auth
54+
55+
56+
class TestUpdateParamsForAuthWithBearerToken:
57+
"""Test that update_params_for_auth sends all configured auth headers."""
58+
59+
def _make_endpoint(self, config, auth_schemes):
60+
"""Helper to create an Endpoint with given auth schemes."""
61+
api_client = ApiClient(config)
62+
return _Endpoint(
63+
settings={
64+
"response_type": None,
65+
"auth": auth_schemes,
66+
"endpoint_path": "/api/v2/test",
67+
"operation_id": "test_op",
68+
"http_method": "GET",
69+
"version": "v2",
70+
},
71+
params_map={},
72+
headers_map={"accept": ["application/json"]},
73+
api_client=api_client,
74+
)
75+
76+
def test_all_auth_headers_sent_when_all_configured(self):
77+
"""When all credentials are set, all auth headers are sent."""
78+
config = Configuration(
79+
api_key={"apiKeyAuth": "test-api-key", "appKeyAuth": "test-app-key"},
80+
access_token="ddpat_test_pat",
81+
)
82+
endpoint = self._make_endpoint(config, ["apiKeyAuth", "appKeyAuth", "bearerAuth"])
83+
headers = {}
84+
queries = []
85+
endpoint.update_params_for_auth(headers, queries)
86+
87+
assert headers["Authorization"] == "Bearer ddpat_test_pat"
88+
assert headers["DD-API-KEY"] == "test-api-key"
89+
assert headers["DD-APPLICATION-KEY"] == "test-app-key"
90+
91+
def test_bearer_only(self):
92+
"""Bearer token works without any API keys configured."""
93+
config = Configuration(access_token="ddpat_test_pat")
94+
endpoint = self._make_endpoint(config, ["apiKeyAuth", "appKeyAuth", "bearerAuth"])
95+
headers = {}
96+
queries = []
97+
endpoint.update_params_for_auth(headers, queries)
98+
99+
assert headers["Authorization"] == "Bearer ddpat_test_pat"
100+
assert "DD-API-KEY" not in headers
101+
assert "DD-APPLICATION-KEY" not in headers
102+
103+
def test_api_keys_only(self):
104+
"""Regular auth works without bearer token."""
105+
config = Configuration(
106+
api_key={"apiKeyAuth": "test-api-key", "appKeyAuth": "test-app-key"},
107+
)
108+
endpoint = self._make_endpoint(config, ["apiKeyAuth", "appKeyAuth", "bearerAuth"])
109+
headers = {}
110+
queries = []
111+
endpoint.update_params_for_auth(headers, queries)
112+
113+
assert headers["DD-API-KEY"] == "test-api-key"
114+
assert headers["DD-APPLICATION-KEY"] == "test-app-key"
115+
assert "Authorization" not in headers
116+
117+
def test_bearer_not_sent_when_not_in_endpoint_auth(self):
118+
"""access_token set but endpoint doesn't declare bearerAuth."""
119+
config = Configuration(
120+
api_key={"apiKeyAuth": "test-api-key", "appKeyAuth": "test-app-key"},
121+
access_token="ddpat_test_pat",
122+
)
123+
endpoint = self._make_endpoint(config, ["apiKeyAuth", "appKeyAuth"])
124+
headers = {}
125+
queries = []
126+
endpoint.update_params_for_auth(headers, queries)
127+
128+
assert headers["DD-API-KEY"] == "test-api-key"
129+
assert headers["DD-APPLICATION-KEY"] == "test-app-key"
130+
assert "Authorization" not in headers

0 commit comments

Comments
 (0)