Skip to content

feat(forge): rbac sync — auto-register permission codes from RequirePermission scans (Tier-A #2)#15

Merged
dedeez14 merged 1 commit intomainfrom
devin/1777089650-rbac-sync
Apr 25, 2026
Merged

feat(forge): rbac sync — auto-register permission codes from RequirePermission scans (Tier-A #2)#15
dedeez14 merged 1 commit intomainfrom
devin/1777089650-rbac-sync

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented Apr 25, 2026

Summary

Adds forge rbac sync — a static-analysis pass that scans every .go file under the module for RequirePermission(code, …) and RequireAnyPermission([]string{code, code, …}, …) middleware references and upserts the discovered codes into the permissions table. Eliminates the "permission drift" failure mode where a route enforces a code the database has never heard of (or vice versa).

Why

Tier-A #2 from the framework-improvement plan. Today, when a developer adds middleware.RequirePermission("orders.refund", …) to a new route, they also have to remember to insert a matching row into permissions and grant it to the appropriate role(s). When they forget, the route silently 403s for everyone. The reverse happens too — codes get added to the DB and never enforced anywhere. A boot-time auto-registry collapses both classes into "did forge rbac sync run after deploy?" — easy to put in CI.

How

  • cmd/forge/rbac.go — AST scanner using go/parser + go/ast extracts string literals from the first argument of RequirePermission calls and from the elements of the slice literal passed to RequireAnyPermission. Tolerates parse errors on individual files (broken generator output won't abort the scan). Skips vendor/, node_modules/, dot-prefixed directories. Handles both pkg.RequirePermission(...) and bare RequirePermission(...) (after dot-import).
  • Resource and action columns are derived from the framework's <resource>.<action> convention (orders.refundresource=orders, action=refund). For codes without a dot, action is empty.
  • Upsert uses ON CONFLICT (code) DO NOTHING so re-runs are safe and human-curated descriptions added in the admin UI are never overwritten.
  • cmd/forge/main.gorbac registered alongside doctor, gen, migrate, etc.
  • cmd/forge/rbac_test.go — synthetic Go fixtures covering both middleware forms, vendor skipping, test-file inclusion, bare-ident calls, plus splitCode table.

Subcommands:

forge rbac sync                    # apply to $GOFORGE_DATABASE_DSN
forge rbac sync --dry-run          # diff without writing
forge rbac sync --print-only       # just list discovered codes
forge rbac sync --root ./api       # scan a different module

Local run on the framework itself discovers menu.manage and rbac.manage from the router.

Test plan

  • make lint clean (golangci-lint run ./...)
  • make test clean (go test -race ./cmd/forge/...)
  • Manual: go run ./cmd/forge rbac sync --print-only prints the two codes currently enforced
  • N/A — no migrations in this PR

Risk

  • The scanner is purely additive — it never deletes rows from permissions, never touches role_permissions, and never overwrites descriptions. Worst case: a removed RequirePermission leaves an orphan row (harmless). A --prune flag can be added later if that becomes annoying.
  • Codes inside dynamic call paths (e.g. RequirePermission(somevar, …)) are silently ignored. Framework convention is to always pass a string literal so this is a fine policy.
  • Rollback: revert this PR; no schema changes.

Checklist

  • Doc comments on every exported / unexported function
  • N/A — no user-visible runtime behaviour change (CLI-only)
  • No secrets / debug logs / fmt.Println left behind

Link to Devin session: https://app.devin.ai/sessions/8fdfc20358514c97a766adca630a2527
Requested by: @dedeez14


Open in Devin Review

…ermission scans

Adds 'forge rbac sync' which AST-scans every .go file under the
module root for RequirePermission(code, …) and RequireAnyPermission(
[]string{code, code, …}, …) middleware references and upserts the
discovered codes into the permissions table.

The point: eliminate 'permission drift' where a route enforces a
code the database has never heard of (or vice versa). Run it as
part of the CI/CD pipeline after migrations complete.

Resource and action columns are derived from the first dot-segment /
remainder convention (orders.refund -> resource=orders, action=refund).
Descriptions are left empty so human-curated descriptions added in
the admin UI are never overwritten - the upsert uses
ON CONFLICT (code) DO NOTHING.

Subcommands:
  forge rbac sync               # apply to GOFORGE_DATABASE_DSN
  forge rbac sync --dry-run     # diff without writing
  forge rbac sync --print-only  # only list discovered codes
  forge rbac sync --root ./api  # scan a different module

Skips vendor/, node_modules/, dot-prefixed dirs. Tolerates single-
file parse errors so broken generator output doesn't abort the scan.

go test -race ./cmd/forge/... clean; golangci-lint clean. Local run
on the framework itself discovers menu.manage and rbac.manage from
internal/infrastructure/server/router.go.

Co-Authored-By: dede febriansyah <febriansyahd65@gmail.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional findings.

Open in Devin Review

@dedeez14 dedeez14 merged commit c739c5b into main Apr 25, 2026
3 checks passed
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.

1 participant