Enforce user ACL on datapoint write for authenticated non-admin users#55
Enforce user ACL on datapoint write for authenticated non-admin users#55Micsi wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
openbridgeserver/obs/api/v1/datapoints.py
Line 288 in 8a02c62
When access == "user", this check passes user straight into _check_user_access, but under API-key auth user is the key's display name (see obs/api/auth.py:get_current_user) rather than the owner username in api_keys.owner. That makes legitimate API-key callers fail authorization on assigned user pages unless the key name exactly matches the username, introducing a 403 regression for non-admin automation clients after this ACL change.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| user_row = await db.fetchone("SELECT is_admin FROM users WHERE username = ?", (user,)) | ||
| is_admin = bool(user_row and user_row["is_admin"]) |
There was a problem hiding this comment.
Derive admin status from API key owner
This admin check uses username = user, but in API-key authentication user is the API key's name (from get_current_user), not the key owner username stored in api_keys.owner (create_api_key). As a result, admin-owned API keys whose display name differs from the admin username are misclassified as non-admin and now get 401 when X-Page-Id is omitted, which is a regression from prior authenticated-write behavior and breaks headless integrations that use API keys.
Useful? React with 👍 / 👎.
| user_row = await db.fetchone("SELECT is_admin FROM users WHERE username = ?", (user,)) | ||
| is_admin = bool(user_row and user_row["is_admin"]) |
There was a problem hiding this comment.
Use immutable principal for admin authorization
This branch treats any authenticated identity string that matches a users.username row as admin, but for API-key auth the identity is caller-controlled key metadata (api_keys.name), not a verified username. Because /api/v1/auth/apikeys lets non-admin users create keys with arbitrary names, naming a key after an admin account causes is_admin to resolve true here and bypasses the new non-admin X-Page-Id requirement, so the intended ACL hardening can be bypassed.
Useful? React with 👍 / 👎.
Motivation
X-Page-Idheader and thereby skip the per-pageuserACL, enabling unauthorized writes to datapoints on pages where the user is not assigned.Description
await db.fetchone("SELECT is_admin FROM users WHERE username = ?", (user,))and treat admins as allowed to write without a page header.X-Page-Idheader and enforce the page ACL by callingawait _resolve_page_access(...)andawait _check_user_access(...)when the page access resolves touser.public/protected/readonlypage types, and kept changes localized toobs/api/v1/datapoints.pyonly.Testing
python -m compileall obs/api/v1/datapoints.py, which succeeded.pytest -q tests/integration/test_datapoints.py::test_write_value_and_read_backbut the test run failed in this environment due to a missing test dependency (pytest_asyncio).Codex Task