From 8d1609dd52524b5ffb5075990023326bb9193709 Mon Sep 17 00:00:00 2001 From: Kyle Corbitt Date: Thu, 3 Aug 2023 09:34:06 -0700 Subject: [PATCH] Add admin role Allow privileged users to administer the system. --- .../migration.sql | 5 +++ prisma/schema.prisma | 18 +++++++--- .../OutputsTable/OutputCell/OutputCell.tsx | 2 +- src/utils/accessControl.ts | 33 ++++++++++++------- 4 files changed, 41 insertions(+), 17 deletions(-) create mode 100644 prisma/migrations/20230803163242_add_admin_role/migration.sql diff --git a/prisma/migrations/20230803163242_add_admin_role/migration.sql b/prisma/migrations/20230803163242_add_admin_role/migration.sql new file mode 100644 index 0000000..8d5b6a7 --- /dev/null +++ b/prisma/migrations/20230803163242_add_admin_role/migration.sql @@ -0,0 +1,5 @@ +-- CreateEnum +CREATE TYPE "UserRole" AS ENUM ('ADMIN', 'USER'); + +-- AlterTable +ALTER TABLE "User" ADD COLUMN "role" "UserRole" NOT NULL DEFAULT 'USER'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 00aa762..16d4c1c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -249,12 +249,20 @@ model Session { user User @relation(fields: [userId], references: [id], onDelete: Cascade) } +enum UserRole { + ADMIN + USER +} + model User { - id String @id @default(uuid()) @db.Uuid - name String? - email String? @unique - emailVerified DateTime? - image String? + id String @id @default(uuid()) @db.Uuid + name String? + email String? @unique + emailVerified DateTime? + image String? + + role UserRole @default(USER) + accounts Account[] sessions Session[] organizationUsers OrganizationUser[] diff --git a/src/components/OutputsTable/OutputCell/OutputCell.tsx b/src/components/OutputsTable/OutputCell/OutputCell.tsx index 63b8adf..de7eff1 100644 --- a/src/components/OutputsTable/OutputCell/OutputCell.tsx +++ b/src/components/OutputsTable/OutputCell/OutputCell.tsx @@ -88,7 +88,7 @@ export default function OutputCell({ )} ), - [hardRefetching, hardRefetch, mostRecentResponse, scenario], + [hardRefetching, hardRefetch, mostRecentResponse, scenario, cell], ); if (!vars) return null; diff --git a/src/utils/accessControl.ts b/src/utils/accessControl.ts index 420c95f..bf19351 100644 --- a/src/utils/accessControl.ts +++ b/src/utils/accessControl.ts @@ -3,6 +3,14 @@ import { TRPCError } from "@trpc/server"; import { type TRPCContext } from "~/server/api/trpc"; import { prisma } from "~/server/db"; +const isAdmin = async (userId: string) => { + const user = await prisma.user.findFirst({ + where: { id: userId, role: "ADMIN" }, + }); + + return !!user; +}; + // No-op method for protected routes that really should be accessible to anyone. export const requireNothing = (ctx: TRPCContext) => { ctx.markAccessControlRun(); @@ -18,21 +26,24 @@ export const requireCanViewExperiment = async (experimentId: string, ctx: TRPCCo }; export const canModifyExperiment = async (experimentId: string, userId: string) => { - const experiment = await prisma.experiment.findFirst({ - where: { - id: experimentId, - organization: { - organizationUsers: { - some: { - role: { in: [OrganizationUserRole.ADMIN, OrganizationUserRole.MEMBER] }, - userId, + const [adminUser, experiment] = await Promise.all([ + isAdmin(userId), + prisma.experiment.findFirst({ + where: { + id: experimentId, + organization: { + organizationUsers: { + some: { + role: { in: [OrganizationUserRole.ADMIN, OrganizationUserRole.MEMBER] }, + userId, + }, }, }, }, - }, - }); + }), + ]); - return !!experiment; + return adminUser || !!experiment; }; export const requireCanModifyExperiment = async (experimentId: string, ctx: TRPCContext) => {