Consolidate all X-API-Key auth into gitea_saas/auth.py (#24) #27
No reviewers
Labels
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: pbwraith/gitea-saas-core#27
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "issue-24"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #24.
What
Issue #24 found several front-ends each hand-rolling the same X-API-Key check — one of them (
intake_factory.py) originally with a non-constant-time==andhmacnot imported, and (post-#9)dashboard.pyrepeating both defects. The security half is already fixed by the merged auth PRs; this change completes the consistency work: ONE auth path.New
gitea_saas/auth.pyowns:API_KEY_HEADER— the only"X-API-Key"literal in the package.PUBLIC_PATHS— the single exemption list (/health,/healthz,/api/factory/health) —/dashboardis NOT in it.key_is_valid()— the onlyhmac.compare_digeston the API-key path, fail-closed on unset/empty/non-str configured key.UNAUTHORIZED_BODY+unauthorized_response()— the canonical 401 body, byte-identical on every front-end's wire.Every front-end delegates;
config.pyre-exportsAPI_KEY_HEADER;stripe_client.py:88(webhook signature, different secret) is intentionally untouched.Rebase on !26 (bounce finding, PR #27 review 2026-08-26)
Rebased this branch onto !26's head — the #25
/dashboardguard (verified green: 160 passed, 1 skipped, CONFLICTS=0; this PR's diff currently includes that single commit and shrinks to the #24 commits only once !26 merges). The measured conflict (gitea_saas/dashboard.py,tests/test_dashboard.py) was resolved deliberately per the finding:/dashboardstays guarded (never unguarded) and now flows directly throughgitea_saas.auth.key_is_valid+unauthorized_response— the dashboard's local_guardclosure is deleted, so there is still exactly one auth path.test_dashboard_page_guard_blocks_anonymous_access(!26's self-check) is kept: removing the guard from/dashboardfails it.test_dashboard_wrong_key_same_length_rejected(HTML page) kept; bare-mount + composed-app exposure tests updated and passing.Acceptance criteria
git grep -c compare_digest gitea_saas/— 1 for the API-key path (auth.py:70) plusstripe_client.py:88(webhook signature, exempt). Asserted bytest_single_compare_digest_on_api_key_path.git grep -n '"X-API-Key"' gitea_saas/— exactly 1 line (auth.py:28), down from 3. Asserted bytest_single_x_api_key_literal.API_KEY_NOT_CONFIGUREDremoved).test_401_body_identical_across_all_front_ends).git diff --checkclean.Plus the owner-requested composition test
test_composed_app_exposure(every front-end on one app; liveness public, everything else 401-without-key / 200-with-key).Testing
python3 -m pytest -q→ 173 passed, 1 skippedgit diff --check→ cleanCloses #24
Implemented and pushed for review.
issue-24(basemain)5968f27— "Consolidate all X-API-Key auth into gitea_saas/auth.py (#24)"tests/test_auth.pypins all five acceptance criteria;git diff --checkclean.Acceptance summary:
compare_digest(gitea_saas/auth.py:70);stripe_client.py:88is webhook-signature verification, explicitly out of scope and untouched.git grep -n '"X-API-Key"' gitea_saas/→ exactly 1 line (auth.py:28), down from 3.server,delivery,intake_factory,dashboard)./dashboardleft to issue #25).Bounced -- not on quality, and not merged: this PR is on the sweep's
escalation list (auth) and the repo is held for an owner decision. But there is
a concrete, measured hazard that must not be lost, so it is recorded here and
labelled so the rework is reachable.
Finding: this PR and !26 conflict, and the resolution can revert a p1 fix
Both report
mergeable=true. That flag is unreliable in both directions, so Imerged them into a throwaway tree:
Why that specific conflict is dangerous
This branch predates
!26, so its side of the conflict still describes/dashboardas public. Onorigin/issue-24:and on
origin/issue-25(the fix):The docstring hunk conflicts the same way -- this branch's side reads
"
GET /dashboard-- HTML operator page (NO auth ...)".So taking this branch's side of the conflict -- the ordinary reflex when the
incoming branch is the larger, newer-looking change -- removes
@_guardfrom/dashboardand reinstates the unauthenticated billing dashboard that #25exists to close. Worse, it would likely look fine: this branch's tests were
written against a world where
/dashboardis public, so they would pass.What to do
!26first (it is verified green: 160 passed, 1 skipped, CONFLICTS=0).gitea_saas/dashboard.py,take the guarded route and then express it through this PR's own
consolidation --
/dashboardmust end up going throughgitea_saas.auth.key_is_valid/unauthorized_response, not through thelocal
_guardclosure and not unguarded.removing the guard from
/dashboardmust still failtest_dashboard_page_guard_blocks_anonymous_access. If it does not, theconsolidation has quietly dropped the route from the auth path.
/dashboardmust NOT be added toauth.PUBLIC_PATHS.Not assessed
I did not gate this PR on its merits -- the consolidation itself (one
X-API-Keyliteral, onehmac.compare_digest, one canonical 401 body) isexactly the right direction, but it is an auth-design change and that decision
is the owner's, not this sweep's.
5968f27490to3819b49498[worker-bounce-fix attempt 1]
Finding-by-finding (bounce review 2026-08-26):
08febd5, the verified-green #25 guard, 160 passed/1 skipped). This PR's diff currently includes that one commit and shrinks to the #24 commits only once !26 merges — both landing orders are then conflict-free and the guard can never be dropped.gitea_saas/dashboard.py,tests/test_dashboard.py) was resolved exactly per the finding:/dashboardstays GUARDED and now flows directly throughgitea_saas.auth.key_is_valid+unauthorized_response; the dashboard's local_guardclosure is deleted — one auth path, no closure, not unguarded.test_dashboard_page_guard_blocks_anonymous_access(from !26) is kept and passes: removing the guard from/dashboardfails it. Also kept:test_dashboard_wrong_key_same_length_rejected./dashboardis NOT inauth.PUBLIC_PATHS(verified:/health,/healthz,/api/factory/healthonly).Resolution semantics: unset configured key now refuses BOTH dashboard endpoints with the canonical 401 (the !26 local guard's 503-on-unset was normalized to the single #24 refusal shape, per the finding's 'through gitea_saas.auth' requirement); bare-mount and composed-app exposure tests updated accordingly.
Evidence: commits
64ab847(#24 consolidation, rebased + resolved) and3819b49(guard-through-auth resolution);python3 -m pytest -q→ 173 passed, 1 skipped (fine: 171+1 on the old base + !26's suite);git diff --checkclean; head pushed via force-with-lease, PR re-verified open/unmerged with 11 changed files.Escalated to the owner — not merged by the sweep
This PR is on the sweep's escalation list and I am deliberately leaving it for a
human. The standing rule: a PR that touches auth, credentials,
execute_action,the action allow-list, or the adversarial corpus is escalated rather than merged.
gitea-saas-coreis additionally a standing owner escalation in its own right —it holds a key authorizing debits, a Stripe secret, and a repo-creating Gitea
token, so the blast radius of an auth mistake here is money and repo control, not
a failed test.
This is not a defect finding. I have not reviewed the diff on its merits and
nothing here says the change is wrong. It is a "who is allowed to merge this"
call, not a "is this correct" call.
Deliberately not labelling this
bounced: that label means "rework wanted"and would put it in the worker rework queue, which is the opposite of what this
needs.
I also could not have run a gate this sweep even if the change were mergeable —
see
gitea-worker-ops#40:wsl.exeexec fails on the review box withWsl/Service/0x8007274c(reproduced twice, 31s each) whilewsl -l -vstillreports the distro
Running.What the owner needs to do: review and merge, or hand it back with a finding.
Gate evidence -- the "I could not run a gate" caveat on my earlier escalation is now stale
My 2026-08-26 escalation said I could not gate this because
wsl.exeexec wasfailing on the review box (
gitea-worker-ops#40). That has cleared -- probedwsl -e echo alive->alive, exit 0. So I have now run the real gate.The escalation itself is unchanged and this PR is still not merged by the
sweep. This comment adds evidence only; it is not a defect finding and not an
approval.
Gate:
pytest -qunderC:\Users\Videostation\AppData\Local\Programs\Python\Python312\python.exe(pytest 8.3.2), in a throwaway detached worktree, against the merge result
(
git merge origin/maininto the PR branch), never the branch alone.The suite is safe to run here:
ARCHITECTURE.md:72states the tests arehermetic (mock Stripe + mock Gitea + tmp SQLite), and the run touches no live
service, no Stripe key and no real Gitea token.
Count arithmetic checks exactly:
158 + 15 = 173, and the diff adds 15def test_undertests/and removes 0. So no coverage was deleted --worth stating explicitly, because a consolidation PR that collapses three auth
implementations into one is exactly the shape where tests quietly disappear.
The thing the owner most needs to know: !27 already contains !26
These two PRs look like they must collide -- both edit
gitea_saas/dashboard.pyand
tests/test_dashboard.py. They do not. Measured rather than inferred:issue-25(!26) is a direct ancestor ofissue-24(!27); !27 is stacked ontop of it. Confirmed from the other direction by composing them in a throwaway
tree in both orders:
Both orders give 173, the same as !27 alone -- not
158 + 2 + 15 = 175--because !26's two tests
(
test_dashboard_page_guard_blocks_anonymous_access,test_dashboard_wrong_key_same_length_rejected) are already present on thisbranch.
Consequence for whoever merges: merging !27 alone delivers both changes.
There is no ordering hazard and no rebase needed either way. I am deliberately
not closing !26 as subsumed -- that is a call on an escalated auth PR, so it is
yours, not mine.
Owner-authorized auth merge — exact current-base result verified
Merged head
3819b49498030f49b3ecc842f9ef5e1f29d5985eonly after independently constructing its synthetic merge onto current base8588033d73999f06c937f059194f3c3cab405138in an isolated worktree.Evidence on that exact merge result:
git diff --check <base>was clean;python3 -m pytest -q→ 218 passed, 1 skipped in 55.21s;gitea_saas/auth.pyowns the single API-key comparison viahmac.compare_digest, the header constant, canonical 401 body, and explicit public-health route set;/dashboardand/api/dashboard/summaryboth fail closed through that shared path; missing, wrong, same-length wrong, and unset configured keys are covered;/health,/healthz, and/api/factory/health;PR #27 contains PR #26 head
08febd56ca1ea4e23efb0f99887a76efa52ddf13as an ancestor, including its dashboard guard and mutation-sensitive tests. PR #26 is therefore being closed as subsumed rather than merged again. Linked auth issues #24 and #25 are resolved.The PR head/base/mergeability were re-read immediately before merge and matched the tested SHAs. Owner authorization was given in the governor session on 2026-08-29.