Skip to content

Instantly share code, notes, and snippets.

@ahoward
Last active April 29, 2026 16:27
Show Gist options
  • Select an option

  • Save ahoward/69252d48bb94aae9e2fefe45e065220e to your computer and use it in GitHub Desktop.

Select an option

Save ahoward/69252d48bb94aae9e2fefe45e065220e to your computer and use it in GitHub Desktop.
scoped roles vs flat roles — why can(user, action, scope) beats roles[]
// ─── OLD WAY — flat roles on the user ────────────────────────────────────────
if (user.roles.includes('manager')) {
// manager of what? no idea. this passes for managers of ANY property.
await create_lot(property_id, params)
}
if (user.roles.includes('patrol') || user.roles.includes('admin')) {
// patrol where? could be a different property entirely.
await create_scan(lot_id, plate)
}
// a bug waiting to happen:
// alice is manager of property A. she hits the API with property B's id.
// old check passes. she just created a lot on someone else's property.
// ─── NEW WAY — roles scoped to the thing ─────────────────────────────────────
if (can(user, 'create_lot', property_id)) {
await create_lot(property_id, params)
}
if (can(user, 'create_scan', property_id)) {
await create_scan(lot_id, plate)
}
// alice is manager of property A, patrol at property B.
// can(alice, 'create_lot', 'property-a') → true
// can(alice, 'create_lot', 'property-b') → false ← caught
// can(alice, 'create_scan', 'property-b') → true
// can(alice, 'create_scan', 'property-a') → false ← caught
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment