67 lines
1.7 KiB
TypeScript
67 lines
1.7 KiB
TypeScript
import { TRPCError } from "@trpc/server";
|
|
import type { TRPCContext } from "../trpc.js";
|
|
|
|
export type OwnedResourceReadContext = Pick<TRPCContext, "db" | "dbUser">;
|
|
|
|
export function canManageOwnedResourceReads(ctx: { dbUser: { systemRole: string } | null }): boolean {
|
|
const role = ctx.dbUser?.systemRole;
|
|
return role === "ADMIN" || role === "MANAGER";
|
|
}
|
|
|
|
export async function findOwnedReadResourceId(
|
|
ctx: OwnedResourceReadContext,
|
|
): Promise<string | null> {
|
|
if (!ctx.dbUser?.id) {
|
|
return null;
|
|
}
|
|
|
|
if (!ctx.db.resource || typeof ctx.db.resource.findFirst !== "function") {
|
|
return null;
|
|
}
|
|
|
|
const resource = await ctx.db.resource.findFirst({
|
|
where: { userId: ctx.dbUser.id },
|
|
select: { id: true },
|
|
});
|
|
|
|
return resource?.id ?? null;
|
|
}
|
|
|
|
export async function assertCanReadOwnedResource(
|
|
ctx: OwnedResourceReadContext,
|
|
resourceId: string,
|
|
forbiddenMessage: string,
|
|
): Promise<void> {
|
|
if (canManageOwnedResourceReads(ctx)) {
|
|
return;
|
|
}
|
|
|
|
const ownedResourceId = await findOwnedReadResourceId(ctx);
|
|
if (!ownedResourceId || ownedResourceId !== resourceId) {
|
|
throw new TRPCError({
|
|
code: "FORBIDDEN",
|
|
message: forbiddenMessage,
|
|
});
|
|
}
|
|
}
|
|
|
|
export async function resolveOwnedResourceReadFilter(
|
|
ctx: OwnedResourceReadContext,
|
|
requestedResourceId: string | undefined,
|
|
forbiddenMessage: string,
|
|
): Promise<string | null | undefined> {
|
|
if (canManageOwnedResourceReads(ctx)) {
|
|
return requestedResourceId;
|
|
}
|
|
|
|
const ownedResourceId = await findOwnedReadResourceId(ctx);
|
|
if (requestedResourceId && requestedResourceId !== ownedResourceId) {
|
|
throw new TRPCError({
|
|
code: "FORBIDDEN",
|
|
message: forbiddenMessage,
|
|
});
|
|
}
|
|
|
|
return ownedResourceId;
|
|
}
|