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:
Ryan Moon
2026-03-28 17:00:42 -05:00
parent dd03fb79ef
commit 4a1fc608f0
13 changed files with 679 additions and 79 deletions

View File

@@ -0,0 +1,43 @@
-- Permissions and role-based access control
CREATE TABLE IF NOT EXISTS "permission" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"slug" varchar(100) NOT NULL UNIQUE,
"domain" varchar(50) NOT NULL,
"action" varchar(50) NOT NULL,
"description" varchar(255) NOT NULL,
"created_at" timestamp with time zone NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS "role" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"company_id" uuid NOT NULL REFERENCES "company"("id"),
"name" varchar(100) NOT NULL,
"slug" varchar(100) NOT NULL,
"description" text,
"is_system" boolean NOT NULL DEFAULT false,
"is_active" boolean NOT NULL DEFAULT true,
"created_at" timestamp with time zone NOT NULL DEFAULT now(),
"updated_at" timestamp with time zone NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX "role_company_slug" ON "role" ("company_id", "slug");
CREATE TABLE IF NOT EXISTS "role_permission" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"role_id" uuid NOT NULL REFERENCES "role"("id"),
"permission_id" uuid NOT NULL REFERENCES "permission"("id"),
"created_at" timestamp with time zone NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX "role_permission_unique" ON "role_permission" ("role_id", "permission_id");
CREATE TABLE IF NOT EXISTS "user_role_assignment" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"user_id" uuid NOT NULL REFERENCES "user"("id"),
"role_id" uuid NOT NULL REFERENCES "role"("id"),
"assigned_by" uuid,
"created_at" timestamp with time zone NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX "user_role_assignment_unique" ON "user_role_assignment" ("user_id", "role_id");

View File

@@ -92,6 +92,13 @@
"when": 1774720000000,
"tag": "0012_file_storage",
"breakpoints": true
},
{
"idx": 13,
"version": "7",
"when": 1774730000000,
"tag": "0013_rbac",
"breakpoints": true
}
]
}