- Shipped
- August 27, 2026 at 8:39 PM UTC
- Author
- Kamo
- Commit
- 1600dc1
Task 8. SecurityService is the authorization boundary; InvoicingService is ClusterIP with no ingress, so this relay is the only way in. InvoicingServiceClient is TimecardServiceClient renamed. Timeouts are explicit and non-negotiable — connect 5s, read 95s: a bare RestTemplate has NONE, and this service issues every session on the platform from the same thread pool, so one wedged downstream would take authentication down with it. The read budget is generous because a connection test makes a real outbound call to a payment processor. Downstream status and body pass through VERBATIM; a 502 carrying Stripe's own sentence is a thing a person needs to read. BillingProviderRelayController is mapped at /api/security/billing/provider, two tiers: MANAGE_BILLING_PROVIDER (277) save, test, disconnect MANAGE_SUBSCRIPTION_SETTINGS (103) catalog, matrix, get, health, runs The connection TEST takes the WRITE right, not the read one, deliberately: a test is an outbound call made with stored credentials, and letting anyone who can view the page trigger it hands them a way to probe the org's payment processor. The catalog is gated rather than open because it enumerates which billing systems the platform integrates with. SecurityService runs anyRequest().permitAll() with hand-rolled per-handler auth, so a handler that forgets its guard is not weakly controlled — it is uncontrolled and reachable from the public internet. Every handler here resolves orgId/memberId from the SESSION and refuses before relaying. PREFIX CORRECTION. The plan specified the controller at /api/security/billing/provider AND the kamo-internal route file at **************** Those do not match, and a relay prefix with no covering catch-all 404s in the browser while every service is healthy and every build is green — the exact failure the plan itself warns has happened twice on this platform. The route ships as **************** instead, following the payroll precedent (route at .../payroll/, controller at **************** and kamo-internal now carries a test that reads this file's @RequestMapping and fails if no route file covers it. mvn test: Tests run: 1697, Failures: 0, Errors: 0, Skipped: 1 — BUILD SUCCESS Run in an ISOLATED tree: git archive of origin/main plus these two files, its own target/ and its own -Dmaven.repo.local. This checkout is shared and currently carries dozens of other sessions' modified and STAGED files; building in place would have shared target/ and produced phantom errors, and its result would not have been about my change. Nothing of theirs was touched (`git commit --only` on these two paths; `git show --stat HEAD` lists exactly two files). Compiled against a shared-library jar built into a PRIVATE maven repo, never `mvn install`. Note that SecurityService@origin/main does NOT compile against kamo-shared-library@origin/main at all — it needs **************** and **************** which exist only in another session's UNCOMMITTED shared-lib working tree. That is pre-existing and unrelated to this change, but it is the reason the jar used here was built from the shared-lib worktree rather than from its origin/main.