feat(auth): centralized RBAC — roles, permissions, and route protection#4230
Open
Another-DevX wants to merge 12 commits into
Open
feat(auth): centralized RBAC — roles, permissions, and route protection#4230Another-DevX wants to merge 12 commits into
Another-DevX wants to merge 12 commits into
Conversation
|
Someone is attempting to deploy a commit to the Ava Labs Team on Vercel. A member of the Team first needs to authorize it. |
46e0f50 to
2edc509
Compare
Contributor
Permission Test ScriptAutomated permission/role testing script for route-level access control. Requirements
SetupCreate a BASE_URL=http://localhost:3000
DEVREL_EMAIL=your-superadmin@example.com
DUMMY_EMAIL=your-dummy@example.com
DUMMY_USER_ID=the-dummy-account-db-idThe script uses two accounts:
Running the scriptnode scripts/permission_test.jsThe script will prompt for OTP codes as needed. Since OTPs are generated How it works
Test logicEach route is tested from two angles:
UI routes ( |
d004098 to
5e85866
Compare
…all callsites - Remove User.custom_attributes merge from JWT callback; roles now from UserRole rows only - Delete lib/auth/permissions.ts; all helpers inlined as hasPermission() calls - Remove hasAnyRole and withAuthRole; add isHackathonJudge and canEvaluateHackathon to roles.ts - Add platform:admin permission to superadmin and devrel for strictly devrel-only gates - Add builder_insights:write permission to builder_insights role - Migrate 13 API routes and 14 UI files from .includes(role) to hasPermission() - Update docs/auth/roles-and-permissions.md to reflect new data flow and permission map Signed-off-by: Anotherdev <anotherdev@MacBook-Pro-de-Anotherdev.local>
- Add Team1-Leader, Team1-member, T1-Technical roles with builder_insights:read - Add showcase:export action type and permission to hackathonCreator - Fix export showcase gate (was platform:admin, now showcase:export) - Update ShowCaseCard and /api/projects/export to use showcase:export Signed-off-by: Anotherdev <anotherdev@MacBook-Pro-de-Anotherdev.local>
Signed-off-by: Anotherdev <joseluismanco37@gmail.com>
- judge: replace judge:write with judge:assign; only devrel/superadmin/ team1-admin can add or remove judges from hackathons - events/[id]: add ownership check in PUT/PATCH — hackathonCreator can only edit their own hackathons unless actor has hackathon:manage - console-migrate: restrict to platform:admin (was badge:manage, which included badge_admin unintentionally) - UI pages /events/new and /hackathons/new: relax guard from platform:admin to hackathon:write so hackathonCreator can reach them - admin-panel page: add ownership check matching the API guard - judges page: change guard from platform:admin to judge:assign - admin/user-roles: add Zod validation, expires_at > now check, anti-escalation (cannot grant roles with wider permissions than own), and structured JSON audit logs on assign/revoke - proxy.ts: replace CORS wildcard with NEXTAUTH_URL allowlist - routeManifest: add /api/evaluate and /api/evaluate/* (judge:read) - rolePermissions: add builder_insights to Resource union, drop | string, add assign action, replace action:* with action:manage in superadmin/devrel, memoize getPermissionsFromRoles - evaluate/*: fix 401 → 403 on permission failure - schema: add @@index([user_id, expires_at]) on UserRole - docs: fix merge claim, usertole typo, update role/action tables, remove custom_attributes from judges API response Signed-off-by: Anotherdev <joseluismanco37@gmail.com>
- Rename Resource type "hackathon" → "event" across all RBAC consumers (rolePermissions.ts, routeManifest.ts, roles.ts, all route handlers, edit pages, hackathon/event list components, UserButton) - Fix routeManifest: /api/projects/export now maps to resource "showcase" instead of "hackathon" (aligns with actual handler check showcase:export) - Docs: add "How to Extend the System" section to roles-and-permissions.md covering how to add new roles, resources, and actions with code examples Signed-off-by: Anotherdev <joseluismanco37@gmail.com>
…issions Signed-off-by: Anotherdev <joseluismanco37@gmail.com>
Replace getServerSession(AuthOptions) + manual permission checks with
withAuthPermission / withAuth from lib/protectedRoute:
- create: withAuthPermission({ resource: notification, action: write })
also removes unnecessary getUserById DB call
- get: withAuth (session-only check)
- read: withAuth (session-only check)
Signed-off-by: Anotherdev <joseluismanco37@gmail.com>
7898a10 to
0de5d1d
Compare
…honCreator
Add explicit action override support to RouteConfig so the proxy
does not always infer the action from the HTTP method. Set
action: "export" on the /api/projects/export manifest entry and
replace withAuth + manual hasPermission check in the handler
with withAuthPermission({ resource: "showcase", action: "export" }).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the scattered
custom_attributes.includes(role)pattern with a centralizedRole-Based Access Control system. All authorization now flows through a single
permission model backed by a
UserRoleDB table instead of a string array onUser.Implementation
Permission model (
lib/auth/rolePermissions.ts)Single source of truth for all roles. Each role maps to a list of
{ resource, action }pairs. Resources follow anamespace:subnamespaceconvention (
badge:nft, etc.). Actions areread | write | delete | manage | admin | export | assign.manageimplies all CRUD actions;resource "*"matches any resource.getPermissionsFromRoles()is memoized per process to avoid rebuilding sets onevery request.
Route manifest (
lib/auth/routeManifest.ts)Declarative map of URL prefixes to required resources. The middleware in
proxy.tsreads this map to enforce auth at the edge before any handler runs.Mixed routes (e.g.
POST /api/eventsis public, butPUTrequires auth) arehandled at the handler level via
withAuthPermission/withAuthResourceHOFs.Database
New
UserRoletable (user_id,role,expires_at,granted_by) replacescustom_attributes: String[]onUser. The JWT callback now reads rolesexclusively from
UserRolerows. Migration includes a backfill that populatesUserRolefrom existingcustom_attributesvalues so no user loses access.Composite index on
(user_id, expires_at)covers the query in the JWT callback.Callsite migration
lib/auth/permissions.ts; replacedhasAnyRole/withAuthRolewithhasPermission()User.custom_attributesmerge from JWT callbackisHackathonJudgeandcanEvaluateHackathonhelpers tolib/auth/roles.ts.includes(role)tohasPermission()hasShowcaseRole,hasHackathonEditorRole, etc.) kept but marked@deprecatedRole additions & permission corrections
Team1-Leader,Team1-member,T1-Technicalwithbuilder_insights:readshowcase:exportaction;hackathonCreatornow getsshowcase:export/api/projects/exportandShowCaseCardguard updated toshowcase:exportplatform:adminadded tosuperadminanddevrelfor strictly internal gatesbadge/console-migraterestricted toplatform:adminPUT/PATCH /api/events/[id]— creators can only edit their own hackathonsadmin/user-roles: Zod validation,expires_at > nowcheck, anti-escalation guard, structured JSON audit logsproxy.tsusesNEXTAUTH_URLallowlist instead of wildcard*evaluate/*returns403on permission failures (was401)custom_attributesexcluded from judge API responsesDocs
docs/auth/roles-and-permissions.md— role→permission table, action semantics,route manifest usage, and Admin API reference.