fix(api): distinguish Nextcloud 5xx from auth failure; tighten body parse (#824)
Address PR #825 review round 2: - _validate_nextcloud_credentials now only maps OCS HTTP 401/403 to a 401 "invalid credential"; any other non-200 (5xx, 503 maintenance mode) surfaces as 502 "Nextcloud returned a server error" so ops don't chase a phantom bad password when Nextcloud is actually down. - The client-facing 401 message is now a parameter, so delete_app_password keeps its "Invalid credentials" wording without unwrapping/rebuilding the helper's JSONResponse. - Body parsing catches (ValueError, UnicodeDecodeError) instead of bare Exception, and guards body.get behind isinstance(body, dict) — no longer swallows RuntimeError/AttributeError or a non-object JSON body. - Add a unit test asserting 500/503 -> 502. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3c55d03ecf
commit
be360edab0
@@ -429,6 +429,33 @@ async def test_provision_app_password_ocs_v1_failure_payload_returns_401(mocker)
|
||||
assert "Invalid app password" in response.json()["error"]
|
||||
|
||||
|
||||
async def test_provision_app_password_nextcloud_5xx_returns_502(mocker):
|
||||
"""A Nextcloud server error / maintenance mode (5xx) surfaces as 502, not a
|
||||
misleading 401 that blames the user's password."""
|
||||
mocker.patch(
|
||||
"nextcloud_mcp_server.api.passwords.get_settings",
|
||||
return_value=MagicMock(
|
||||
nextcloud_host="http://localhost:8080",
|
||||
nextcloud_verify_ssl=True,
|
||||
nextcloud_ca_bundle=None,
|
||||
),
|
||||
)
|
||||
for status_code in (500, 503):
|
||||
passwords._rate_limit_attempts.clear()
|
||||
_mock_ocs_client(mocker, status_code=status_code, json_payload={})
|
||||
client = TestClient(_provision_only_app())
|
||||
response = client.post(
|
||||
"/api/v1/users/testuser/app-password",
|
||||
headers={
|
||||
"Authorization": create_basic_auth_header(
|
||||
"testuser", "aaaaa-bbbbb-ccccc-ddddd-eeeee"
|
||||
)
|
||||
},
|
||||
)
|
||||
assert response.status_code == 502, f"HTTP {status_code} should map to 502"
|
||||
assert "server error" in response.json()["error"].lower()
|
||||
|
||||
|
||||
async def test_provision_app_password_nondict_data_does_not_500(mocker):
|
||||
"""Regression for #824: a non-dict ``ocs.data`` under HTTP 200 (list, or
|
||||
``null``) is handled gracefully (401), not as an unhandled 500/
|
||||
|
||||
Reference in New Issue
Block a user