Skip to content

Commit 9b6d515

Browse files
committed
RSPEED-2326: feat(auth): store RH Identity data in request.state
Store RHIdentityData instance in request.state.rh_identity_data during authentication, enabling downstream endpoints to access org_id and system_id for telemetry without modifying AuthTuple. Signed-off-by: Major Hayden <major@redhat.com>
1 parent d5db1ad commit 9b6d515

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

src/authentication/rh_identity.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ async def __call__(self, request: Request) -> AuthTuple:
247247
# Validate entitlements if configured
248248
rh_identity.validate_entitlements()
249249

250+
# Store identity data in request.state for downstream access
251+
request.state.rh_identity_data = rh_identity
252+
250253
# Extract user data
251254
user_id = rh_identity.get_user_id()
252255
username = rh_identity.get_username()

tests/unit/authentication/test_rh_identity.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def create_auth_header(identity_data: dict) -> str:
9393
return base64.b64encode(json_str.encode("utf-8")).decode("utf-8")
9494

9595

96-
def create_request_with_header(header_value: Optional[str]) -> Request:
96+
def create_request_with_header(header_value: Optional[str]) -> Mock:
9797
"""Helper to create mock Request with x-rh-identity header.
9898
9999
Create a mock FastAPI Request with an `x-rh-identity` header for tests.
@@ -109,6 +109,7 @@ def create_request_with_header(header_value: Optional[str]) -> Request:
109109
"""
110110
request = Mock(spec=Request)
111111
request.headers = {"x-rh-identity": header_value} if header_value else {}
112+
request.state = Mock()
112113
return request
113114

114115

@@ -142,8 +143,12 @@ def test_get_org_id(
142143

143144
def test_get_org_id_missing(self, user_identity_data: dict) -> None:
144145
"""Test org_id returns empty string when not present."""
145-
user_identity_data["identity"].pop("org_id")
146-
rh_identity = RHIdentityData(user_identity_data)
146+
identity_data = {
147+
**user_identity_data,
148+
"identity": {**user_identity_data["identity"]},
149+
}
150+
identity_data["identity"].pop("org_id", None)
151+
rh_identity = RHIdentityData(identity_data)
147152
assert rh_identity.get_org_id() == ""
148153

149154
@pytest.mark.parametrize(
@@ -326,6 +331,31 @@ async def test_system_authentication_success(
326331
assert skip_check is False
327332
assert token == NO_USER_TOKEN
328333

334+
@pytest.mark.asyncio
335+
@pytest.mark.parametrize(
336+
"fixture_name,expected_user_id",
337+
[
338+
("user_identity_data", "abc123"),
339+
("system_identity_data", "c87dcb4c-8af1-40dd-878e-60c744edddd0"),
340+
],
341+
)
342+
async def test_rh_identity_stored_in_request_state(
343+
self, fixture_name: str, expected_user_id: str, request: pytest.FixtureRequest
344+
) -> None:
345+
"""Test RH Identity data is stored in request.state for downstream access."""
346+
identity_data = request.getfixturevalue(fixture_name)
347+
auth_dep = RHIdentityAuthDependency()
348+
header_value = create_auth_header(identity_data)
349+
mock_request = create_request_with_header(header_value)
350+
351+
await auth_dep(mock_request)
352+
353+
assert hasattr(mock_request.state, "rh_identity_data")
354+
rh_identity = mock_request.state.rh_identity_data
355+
assert isinstance(rh_identity, RHIdentityData)
356+
assert rh_identity.get_user_id() == expected_user_id
357+
assert rh_identity.get_org_id() == "321"
358+
329359
@pytest.mark.asyncio
330360
async def test_missing_header(self) -> None:
331361
"""Test authentication fails when header is missing."""

0 commit comments

Comments
 (0)