fix(api): block cross-user delete and address review feedback (#824)

Review of PR #825 surfaced an auth bypass introduced by adding loginName
support to delete_app_password: with the OCS-resolved UID discarded, a user
could authenticate as their own loginName (via the request body) while
targeting another user's path and delete the victim's stored app password.
Add the same UID-mismatch guard provisioning already has, so the
authenticated account must own the path UID (403 otherwise).

Also:
- integration test: build the BasicAuth header via base64 instead of
  httpx.BasicAuth._auth_header (private attribute); mark the throwaway test
  credential NOSONAR(S2068).
- unit tests: cover the httpx.RequestError -> 502 branch, the standard OCS v2
  success shape (meta.statuscode 200), and the cross-user delete 403 guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-06-01 13:04:42 +02:00
co-authored by Claude Opus 4.8
parent a11a4e709c
commit 3c55d03ecf
3 changed files with 142 additions and 4 deletions
@@ -22,6 +22,8 @@ capitals (`Admin`) and spaces (`Test User`) — which is exactly the path that
used to 500.
"""
import base64
import httpx
import pytest
@@ -30,12 +32,14 @@ LOGIN_FLOW_API_BASE_URL = "http://localhost:8004"
pytestmark = [pytest.mark.integration, pytest.mark.login_flow]
# A syntactically valid app password (matches APP_PASSWORD_PATTERN) that is not
# a real credential for any account — so the OCS validation always fails.
_WRONG_APP_PASSWORD = "aaaaa-bbbbb-ccccc-ddddd-eeeee"
# a real credential for any account — so the OCS validation always fails. This
# is a throwaway test fixture, not a real secret.
_WRONG_APP_PASSWORD = "aaaaa-bbbbb-ccccc-ddddd-eeeee" # NOSONAR(S2068)
def _basic_auth_header(username: str, password: str) -> str:
return httpx.BasicAuth(username, password)._auth_header
credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
return f"Basic {credentials}"
@pytest.mark.parametrize(