Skip to content

Commit 233b469

Browse files
committed
RSPEED-2466: Replace verbose rh-identity error messages with opaque responses
Error messages in rh-identity auth revealed the full expected JSON structure of the x-rh-identity header, allowing attackers to reconstruct the schema in ~9 iterative requests. Replace all 11 verbose validation errors with generic responses ("Invalid identity data" / "Insufficient entitlements") and move detailed diagnostics to logger.warning() for ops troubleshooting. Signed-off-by: Major Hayden <major@redhat.com>
1 parent 7a4d6d1 commit 233b469

3 files changed

Lines changed: 61 additions & 40 deletions

File tree

src/authentication/rh_identity.py

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,45 +54,53 @@ def _validate_structure(self) -> None:
5454
"identity" not in self.identity_data
5555
or self.identity_data["identity"] is None
5656
):
57-
raise HTTPException(status_code=400, detail="Missing 'identity' field")
57+
logger.warning("Identity validation failed: missing 'identity' field")
58+
raise HTTPException(status_code=400, detail="Invalid identity data")
5859

5960
identity = self.identity_data["identity"]
6061
if "type" not in identity:
61-
raise HTTPException(status_code=400, detail="Missing identity 'type' field")
62+
logger.warning("Identity validation failed: missing 'type' field")
63+
raise HTTPException(status_code=400, detail="Invalid identity data")
6264

6365
identity_type = identity["type"]
6466
if identity_type == "User":
6567
if "user" not in identity:
66-
raise HTTPException(
67-
status_code=400, detail="Missing 'user' field for User type"
68+
logger.warning(
69+
"Identity validation failed: missing 'user' field for User type"
6870
)
71+
raise HTTPException(status_code=400, detail="Invalid identity data")
6972
user = identity["user"]
7073
if "user_id" not in user:
71-
raise HTTPException(
72-
status_code=400, detail="Missing 'user_id' in user data"
74+
logger.warning(
75+
"Identity validation failed: missing 'user_id' in user data"
7376
)
77+
raise HTTPException(status_code=400, detail="Invalid identity data")
7478
if "username" not in user:
75-
raise HTTPException(
76-
status_code=400, detail="Missing 'username' in user data"
79+
logger.warning(
80+
"Identity validation failed: missing 'username' in user data"
7781
)
82+
raise HTTPException(status_code=400, detail="Invalid identity data")
7883
elif identity_type == "System":
7984
if "system" not in identity:
80-
raise HTTPException(
81-
status_code=400, detail="Missing 'system' field for System type"
85+
logger.warning(
86+
"Identity validation failed: missing 'system' field for System type"
8287
)
88+
raise HTTPException(status_code=400, detail="Invalid identity data")
8389
system = identity["system"]
8490
if "cn" not in system:
85-
raise HTTPException(
86-
status_code=400, detail="Missing 'cn' in system data"
91+
logger.warning(
92+
"Identity validation failed: missing 'cn' in system data"
8793
)
94+
raise HTTPException(status_code=400, detail="Invalid identity data")
8895
if "account_number" not in identity:
89-
raise HTTPException(
90-
status_code=400, detail="Missing 'account_number' for System type"
96+
logger.warning(
97+
"Identity validation failed: "
98+
"missing 'account_number' for System type"
9199
)
100+
raise HTTPException(status_code=400, detail="Invalid identity data")
92101
else:
93-
raise HTTPException(
94-
status_code=400, detail=f"Unsupported identity type: {identity_type}"
95-
)
102+
logger.warning("Identity validation failed: unsupported identity type")
103+
raise HTTPException(status_code=400, detail="Invalid identity data")
96104

97105
def _get_identity_type(self) -> str:
98106
"""Get the identity type (User or System).
@@ -169,10 +177,13 @@ def validate_entitlements(self) -> None:
169177

170178
missing = [s for s in self.required_entitlements if not self.has_entitlement(s)]
171179
if missing:
172-
entitlement_word = "entitlement" if len(missing) == 1 else "entitlements"
180+
logger.warning(
181+
"Entitlement validation failed: missing required entitlements: %s",
182+
", ".join(missing),
183+
)
173184
raise HTTPException(
174185
status_code=403,
175-
detail=f"Missing required {entitlement_word}: {', '.join(missing)}",
186+
detail="Insufficient entitlements",
176187
)
177188

178189

tests/e2e/features/authorized_rh_identity.feature

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Feature: Authorized endpoint API tests for the rh-identity authentication module
2828
{"placeholder":"abc"}
2929
"""
3030
Then The status code of the response is 400
31-
And The body of the response contains Missing 'identity' field
31+
And The body of the response contains Invalid identity data
3232

3333
Scenario: Request succeeds with valid User identity and required entitlements
3434
Given The system is in default state
@@ -79,7 +79,7 @@ Feature: Authorized endpoint API tests for the rh-identity authentication module
7979
{"placeholder":"abc"}
8080
"""
8181
Then The status code of the response is 403
82-
And The body of the response contains Missing required entitlement
82+
And The body of the response contains Insufficient entitlements
8383

8484
Scenario: Request fails when entitlement exists but is_entitled is false
8585
Given The system is in default state
@@ -99,7 +99,7 @@ Feature: Authorized endpoint API tests for the rh-identity authentication module
9999
{"placeholder":"abc"}
100100
"""
101101
Then The status code of the response is 403
102-
And The body of the response contains Missing required entitlement
102+
And The body of the response contains Insufficient entitlements
103103

104104
Scenario: Request fails when User identity is missing user_id
105105
Given The system is in default state
@@ -119,7 +119,7 @@ Feature: Authorized endpoint API tests for the rh-identity authentication module
119119
{"placeholder":"abc"}
120120
"""
121121
Then The status code of the response is 400
122-
And The body of the response contains Missing 'user_id' in user data
122+
And The body of the response contains Invalid identity data
123123

124124
Scenario: Request fails when User identity is missing username
125125
Given The system is in default state
@@ -139,7 +139,7 @@ Feature: Authorized endpoint API tests for the rh-identity authentication module
139139
{"placeholder":"abc"}
140140
"""
141141
Then The status code of the response is 400
142-
And The body of the response contains Missing 'username' in user data
142+
And The body of the response contains Invalid identity data
143143

144144
Scenario: Request fails when System identity is missing cn
145145
Given The system is in default state
@@ -160,4 +160,4 @@ Feature: Authorized endpoint API tests for the rh-identity authentication module
160160
{"placeholder":"abc"}
161161
"""
162162
Then The status code of the response is 400
163-
And The body of the response contains Missing 'cn' in system data
163+
And The body of the response contains Invalid identity data

tests/unit/authentication/test_rh_identity.py

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,12 @@ def test_has_entitlements(
195195
(
196196
["openshift"],
197197
True,
198-
"Missing required entitlement: openshift",
198+
"Insufficient entitlements",
199199
), # Single missing
200200
(
201201
["rhel", "ansible", "openshift"],
202202
True,
203-
"Missing required entitlement: openshift",
203+
"Insufficient entitlements",
204204
), # Multiple with one missing
205205
],
206206
)
@@ -236,11 +236,11 @@ def test_validate_entitlements(
236236
@pytest.mark.parametrize(
237237
"missing_field,expected_error",
238238
[
239-
({"identity": None}, "Missing 'identity' field"),
240-
({"identity": {"org_id": "123"}}, "Missing identity 'type' field"),
239+
({"identity": None}, "Invalid identity data"),
240+
({"identity": {"org_id": "123"}}, "Invalid identity data"),
241241
(
242242
{"identity": {"type": "User", "org_id": "123"}},
243-
"Missing 'user' field for User type",
243+
"Invalid identity data",
244244
),
245245
(
246246
{
@@ -250,7 +250,7 @@ def test_validate_entitlements(
250250
"user": {"username": "test"},
251251
}
252252
},
253-
"Missing 'user_id' in user data",
253+
"Invalid identity data",
254254
),
255255
(
256256
{
@@ -260,15 +260,15 @@ def test_validate_entitlements(
260260
"user": {"user_id": "123"},
261261
}
262262
},
263-
"Missing 'username' in user data",
263+
"Invalid identity data",
264264
),
265265
(
266266
{"identity": {"type": "System", "org_id": "123"}},
267-
"Missing 'system' field for System type",
267+
"Invalid identity data",
268268
),
269269
(
270270
{"identity": {"type": "System", "org_id": "123", "system": {}}},
271-
"Missing 'cn' in system data",
271+
"Invalid identity data",
272272
),
273273
(
274274
{
@@ -278,29 +278,39 @@ def test_validate_entitlements(
278278
"system": {"cn": "test"},
279279
}
280280
},
281-
"Missing 'account_number' for System type",
281+
"Invalid identity data",
282282
),
283283
],
284284
)
285285
def test_validation_failures(
286-
self, missing_field: dict, expected_error: str
286+
self,
287+
missing_field: dict,
288+
expected_error: str,
289+
mocker: MockerFixture,
287290
) -> None:
288291
"""Test validation failures for various missing fields."""
292+
mock_warning = mocker.patch("authentication.rh_identity.logger.warning")
293+
289294
with pytest.raises(HTTPException) as exc_info:
290295
RHIdentityData(missing_field)
291296

292297
assert exc_info.value.status_code == 400
293298
assert expected_error in str(exc_info.value.detail)
299+
mock_warning.assert_called_once()
300+
assert "Identity validation failed" in mock_warning.call_args[0][0]
294301

295-
def test_unsupported_identity_type(self) -> None:
302+
def test_unsupported_identity_type(self, mocker: MockerFixture) -> None:
296303
"""Test validation fails with unsupported identity type."""
304+
mock_warning = mocker.patch("authentication.rh_identity.logger.warning")
297305
invalid_data = {"identity": {"type": "Unknown", "org_id": "123"}}
298306

299307
with pytest.raises(HTTPException) as exc_info:
300308
RHIdentityData(invalid_data)
301309

302310
assert exc_info.value.status_code == 400
303-
assert "Unsupported identity type: Unknown" in str(exc_info.value.detail)
311+
assert "Invalid identity data" in str(exc_info.value.detail)
312+
mock_warning.assert_called_once()
313+
assert "Identity validation failed" in mock_warning.call_args[0][0]
304314

305315

306316
class TestRHIdentityAuthDependency:
@@ -406,12 +416,12 @@ async def test_invalid_json(self) -> None:
406416
(
407417
["openshift"],
408418
True,
409-
"Missing required entitlement: openshift",
419+
"Insufficient entitlements",
410420
), # Single missing
411421
(
412422
["rhel", "ansible", "openshift"],
413423
True,
414-
"Missing required entitlement: openshift",
424+
"Insufficient entitlements",
415425
), # Multiple with one missing
416426
],
417427
)

0 commit comments

Comments
 (0)