Skip to content

fix: gate admin user-management routes with adminMiddleware and add route-wiring regression tests#87

Open
diveshpatil9104 wants to merge 1 commit into
OneBusAway:mainfrom
diveshpatil9104:test/admin-middleware-route-wiring
Open

fix: gate admin user-management routes with adminMiddleware and add route-wiring regression tests#87
diveshpatil9104 wants to merge 1 commit into
OneBusAway:mainfrom
diveshpatil9104:test/admin-middleware-route-wiring

Conversation

@diveshpatil9104

@diveshpatil9104 diveshpatil9104 commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

fixes : #85

Summary

Fixes a privilege-escalation bug (#82) where the five /api/v1/admin/users/* routes were wrapped with authMiddleware only — any authenticated driver could list, create, update, and deactivate user accounts. Adds the route-wiring regression tests requested in #85 so the same gap can never silently reappear.

What changed

Modified (1)

  • main.go — Extracted all route registration into newMux(store, tracker, rateLimiter, jwtSecret, startTime) so tests can build the real mux without a live database. Added adminMiddleware to the five user-management routes that were missing it.

New (1)

  • route_wiring_test.go — Two table-driven tests over all 14 /api/v1/admin/* routes:
    • TestAdminRoutes_DriverTokenRejected — a valid driver JWT must receive 403 + "admin access required" on every admin route.
    • TestAdminRoutes_AdminTokenAllowed — a valid admin JWT must not be blocked by either middleware layer on any admin route.

Design decisions

Extract newMux instead of duplicating route registration in tests. Copying the route table into the test would let the test pass even when main.go's wiring is wrong. By calling the same newMux function, the test exercises the actual production wiring — a missing adminMiddleware in main.go fails the test directly.

noopStore with zero-value stubs. The driver-rejection tests never reach handler code (middleware short-circuits first), so store methods are never called. For the admin-allowed tests, stubs return safe zero values — enough to confirm middleware passes the request through without needing a real database.

Both negative and positive paths. Testing only that drivers get 403 is not enough — a double-wrapped or misconfigured middleware could also block admins. TestAdminRoutes_AdminTokenAllowed guards against that.

Testing

  • go fmt ./... — clean
  • go vet ./... — clean
  • go test ./... — all pass (28 new subtests across 2 test functions)

Summary by CodeRabbit

  • Tests

    • Added integration tests to verify admin route access control and permission enforcement.
  • Refactor

    • Improved internal code organization for route registration.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Lost in the diff? Review this PR in Change Stack to follow the change map from intent to exact ranges.

Review Change Stack

📝 Walkthrough

Walkthrough

The PR extracts the server's HTTP route wiring from main() into a new newMux() helper, introducing an appStore interface to represent shared store capabilities. Two integration tests verify that admin routes correctly reject non-admin tokens with 403 and permit admin tokens without middleware blocking.

Changes

Route Wiring Refactoring and Middleware Testing

Layer / File(s) Summary
Route wiring helper and appStore contract
main.go
Introduced appStore interface aggregating store methods required by handlers, and newMux() function that constructs http.ServeMux, applies requireAuth and requireAdmin middleware, and registers all API and health endpoints in one consolidated location.
Main function integration
main.go
Replaced inline mux construction and route registration in main() with a single call to newMux(), passing store, tracker, rate limiter, JWT secret, and start time.
Admin middleware integration tests
route_wiring_test.go
Added noopStore stub implementing the appStore interface with zero-value returns, and two table-driven tests verifying that admin routes reject driver-role tokens with HTTP 403 and permit admin-role tokens without auth/admin middleware blocking.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: fixing privilege escalation by gating admin routes with adminMiddleware and adding regression tests for route wiring.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@diveshpatil9104 diveshpatil9104 marked this pull request as ready for review June 6, 2026 07:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
route_wiring_test.go (1)

88-106: ⚡ Quick win

Deduplicate the admin route matrix to prevent coverage drift.

Both tests carry separate copies of the same 14 routes. A shared table keeps positive/negative-path coverage locked together when routes change.

♻️ Proposed refactor
+type adminRouteCase struct {
+	method string
+	path   string
+}
+
+var adminRouteCases = []adminRouteCase{
+	{"GET", "/api/v1/admin/status"},
+	{"GET", "/api/v1/admin/vehicles"},
+	{"GET", "/api/v1/admin/vehicles/bus-1"},
+	{"POST", "/api/v1/admin/vehicles"},
+	{"DELETE", "/api/v1/admin/vehicles/bus-1"},
+	{"GET", "/api/v1/admin/users"},
+	{"GET", "/api/v1/admin/users/1"},
+	{"POST", "/api/v1/admin/users"},
+	{"PUT", "/api/v1/admin/users/1"},
+	{"DELETE", "/api/v1/admin/users/1"},
+	{"POST", "/api/v1/admin/assignments"},
+	{"DELETE", "/api/v1/admin/users/1/vehicles/bus-1"},
+	{"GET", "/api/v1/admin/users/1/vehicles"},
+	{"GET", "/api/v1/admin/vehicles/bus-1/users"},
+}
+
 func TestAdminRoutes_DriverTokenRejected(t *testing.T) {
@@
-	tests := []struct {
-		method string
-		path   string
-	}{
-		...
-	}
-
-	for _, tc := range tests {
+	for _, tc := range adminRouteCases {
 		t.Run(tc.method+" "+tc.path, func(t *testing.T) {
@@
 func TestAdminRoutes_AdminTokenAllowed(t *testing.T) {
@@
-	tests := []struct {
-		method string
-		path   string
-	}{
-		...
-	}
-
-	for _, tc := range tests {
+	for _, tc := range adminRouteCases {
 		t.Run(tc.method+" "+tc.path, func(t *testing.T) {

Also applies to: 139-157

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@route_wiring_test.go` around lines 88 - 106, The admin route table is
duplicated; extract the list into a single shared variable (e.g., adminRoutes or
adminRouteMatrix) and replace both local copies (the tests variable defined
around the shown block and the duplicate at lines ~139-157) to reference that
single slice so coverage stays in sync when routes change; update any test
functions that currently declare the local tests slice to iterate over
adminRoutes instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@route_wiring_test.go`:
- Around line 88-106: The admin route table is duplicated; extract the list into
a single shared variable (e.g., adminRoutes or adminRouteMatrix) and replace
both local copies (the tests variable defined around the shown block and the
duplicate at lines ~139-157) to reference that single slice so coverage stays in
sync when routes change; update any test functions that currently declare the
local tests slice to iterate over adminRoutes instead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7c43edeb-9862-4bd4-b5fa-58b099780cef

📥 Commits

Reviewing files that changed from the base of the PR and between a40ca64 and aea4cfe.

📒 Files selected for processing (2)
  • main.go
  • route_wiring_test.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add route-wiring regression tests for admin middleware

1 participant