Implement RBAC with permissions, roles, and route guards
- permission, role, role_permission, user_role_assignment tables - 42 system permissions across 13 domains - 6 default roles: Admin, Manager, Sales Associate, Technician, Instructor, Viewer - Permission inheritance: admin implies edit implies view - requirePermission() Fastify decorator on ALL routes - System permissions and roles seeded per company - Test helpers and API test runner seed RBAC data - All 42 API tests pass with permissions enforced
This commit is contained in:
54
packages/backend/src/db/schema/rbac.ts
Normal file
54
packages/backend/src/db/schema/rbac.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { pgTable, uuid, varchar, text, timestamp, boolean, uniqueIndex } from 'drizzle-orm/pg-core'
|
||||
import { companies } from './stores.js'
|
||||
import { users } from './users.js'
|
||||
|
||||
export const permissions = pgTable('permission', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
slug: varchar('slug', { length: 100 }).notNull().unique(),
|
||||
domain: varchar('domain', { length: 50 }).notNull(),
|
||||
action: varchar('action', { length: 50 }).notNull(),
|
||||
description: varchar('description', { length: 255 }).notNull(),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
})
|
||||
|
||||
export const roles = pgTable('role', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
companyId: uuid('company_id')
|
||||
.notNull()
|
||||
.references(() => companies.id),
|
||||
name: varchar('name', { length: 100 }).notNull(),
|
||||
slug: varchar('slug', { length: 100 }).notNull(),
|
||||
description: text('description'),
|
||||
isSystem: boolean('is_system').notNull().default(false),
|
||||
isActive: boolean('is_active').notNull().default(true),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
})
|
||||
|
||||
export const rolePermissions = pgTable('role_permission', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
roleId: uuid('role_id')
|
||||
.notNull()
|
||||
.references(() => roles.id),
|
||||
permissionId: uuid('permission_id')
|
||||
.notNull()
|
||||
.references(() => permissions.id),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
})
|
||||
|
||||
export const userRoles = pgTable('user_role_assignment', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: uuid('user_id')
|
||||
.notNull()
|
||||
.references(() => users.id),
|
||||
roleId: uuid('role_id')
|
||||
.notNull()
|
||||
.references(() => roles.id),
|
||||
assignedBy: uuid('assigned_by'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
})
|
||||
|
||||
export type Permission = typeof permissions.$inferSelect
|
||||
export type Role = typeof roles.$inferSelect
|
||||
export type RolePermission = typeof rolePermissions.$inferSelect
|
||||
export type UserRole = typeof userRoles.$inferSelect
|
||||
Reference in New Issue
Block a user