prisma-rls
Version:
Prisma client extension for row-level security on any database
196 lines (195 loc) • 13.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createRlsExtension = void 0;
const extension_1 = require("@prisma/client/extension");
const library_1 = require("@prisma/client/runtime/library");
const errors_1 = require("./errors");
const logic_1 = require("./logic");
const utils_1 = require("./utils");
const createRlsExtension = ({ dmmf, permissionsConfig, context, authorizationError, checkRequiredBelongsTo }) => {
const fieldsMap = (0, utils_1.buildFieldsMap)(dmmf);
if (!authorizationError) {
authorizationError = new errors_1.AuthorizationError();
}
return extension_1.Prisma.defineExtension((prismaClient) => {
return prismaClient.$extends({
name: "prisma-rls",
query: {
$allModels: {
async $allOperations(rawAllOperationsArgs) {
const allOperationsArgs = rawAllOperationsArgs;
const { model: modelName, operation: operationName, args, query } = allOperationsArgs;
const modelPermissions = permissionsConfig[modelName];
const modelResolver = new logic_1.ModelResolver(permissionsConfig, context, fieldsMap, authorizationError, !!checkRequiredBelongsTo);
switch (operationName) {
case "findUnique":
case "findUniqueOrThrow": {
if (!modelPermissions.read && operationName === "findUnique") {
return Promise.resolve(null);
}
else if (!modelPermissions.read && operationName === "findUniqueOrThrow") {
throw new library_1.PrismaClientKnownRequestError(`No ${modelName} found`, {
code: "P2025",
clientVersion: extension_1.Prisma.prismaVersion.client,
});
}
const relationsMetadata = [];
const [selectAndInclude, where] = await Promise.all([
modelResolver.resolveSelectAndInclude(modelName, args.select, args.include, relationsMetadata, { path: "$" }),
modelResolver.resolveWhereUnique(modelPermissions.read, modelName, args.where),
]);
const result = await query({ ...args, ...selectAndInclude, where });
if (checkRequiredBelongsTo) {
return modelResolver.performRelationProcessing((0, utils_1.getTransactionClient)(prismaClient, allOperationsArgs), result, relationsMetadata);
}
return result;
}
case "findFirst":
case "findFirstOrThrow":
case "findMany": {
if (!modelPermissions.read && operationName === "findFirst") {
return Promise.resolve(null);
}
else if (!modelPermissions.read && operationName === "findFirstOrThrow") {
throw new library_1.PrismaClientKnownRequestError(`No ${modelName} found`, {
code: "P2025",
clientVersion: extension_1.Prisma.prismaVersion.client,
});
}
else if (!modelPermissions.read && operationName === "findMany") {
return Promise.resolve([]);
}
const relationsMetadata = [];
const recursiveContext = { path: operationName === "findMany" ? "$.*" : "$" };
const [selectAndInclude, where] = await Promise.all([
modelResolver.resolveSelectAndInclude(modelName, args.select, args.include, relationsMetadata, recursiveContext),
modelResolver.resolveWhere(modelPermissions.read, modelName, args.where),
]);
const result = await query({ ...args, ...selectAndInclude, where });
if (checkRequiredBelongsTo) {
return modelResolver.performRelationProcessing((0, utils_1.getTransactionClient)(prismaClient, allOperationsArgs), result, relationsMetadata);
}
return result;
}
case "aggregate":
case "count":
case "groupBy": {
if (!modelPermissions.read && operationName === "aggregate") {
return query({ ...args, where: (0, utils_1.generateImpossibleWhere)(fieldsMap[modelName]) });
}
else if (!modelPermissions.read && operationName === "count") {
return query({ ...args, where: (0, utils_1.generateImpossibleWhere)(fieldsMap[modelName]) });
}
else if (!modelPermissions.read && operationName === "groupBy") {
return Promise.resolve([]);
}
return query({ ...args, where: await modelResolver.resolveWhere(modelPermissions.read, modelName, args.where) });
}
case "create": {
if (!modelPermissions.create) {
throw authorizationError;
}
const relationsMetadata = [];
const [selectAndInclude, data] = await Promise.all([
modelResolver.resolveSelectAndInclude(modelName, args.select, args.include, relationsMetadata, { path: "$" }),
modelResolver.resolveCreate(modelName, args.data),
]);
const result = await query({ ...args, ...selectAndInclude, data });
if (checkRequiredBelongsTo) {
return modelResolver.performRelationProcessing((0, utils_1.getTransactionClient)(prismaClient, allOperationsArgs), result, relationsMetadata);
}
return result;
}
case "createMany": {
if (!modelPermissions.create) {
throw authorizationError;
}
return query(args);
}
case "createManyAndReturn": {
if (!modelPermissions.create) {
throw authorizationError;
}
return query(args);
}
case "update": {
if (!modelPermissions.update) {
throw authorizationError;
}
const relationsMetadata = [];
const [selectAndInclude, data, where] = await Promise.all([
modelResolver.resolveSelectAndInclude(modelName, args.select, args.include, relationsMetadata, { path: "$" }),
modelResolver.resolveUpdate(modelName, args.data),
modelResolver.resolveWhereUnique(modelPermissions.update, modelName, args.where),
]);
const result = await query({ ...args, ...selectAndInclude, data, where });
if (checkRequiredBelongsTo) {
return modelResolver.performRelationProcessing((0, utils_1.getTransactionClient)(prismaClient, allOperationsArgs), result, relationsMetadata);
}
return result;
}
case "updateMany": {
if (!modelPermissions.update) {
throw authorizationError;
}
const relationsMetadata = [];
const result = await query({ ...args, where: await modelResolver.resolveWhere(modelPermissions.update, modelName, args.where) });
if (checkRequiredBelongsTo) {
return modelResolver.performRelationProcessing((0, utils_1.getTransactionClient)(prismaClient, allOperationsArgs), result, relationsMetadata);
}
return result;
}
case "upsert": {
if (!modelPermissions.create || !modelPermissions.update) {
throw authorizationError;
}
const relationsMetadata = [];
const [selectAndInclude, create, update, where] = await Promise.all([
modelResolver.resolveSelectAndInclude(modelName, args.select, args.include, relationsMetadata, { path: "$" }),
modelResolver.resolveCreate(modelName, args.create),
modelResolver.resolveUpdate(modelName, args.update),
modelResolver.resolveWhereUnique(modelPermissions.update, modelName, args.where),
]);
const result = await query({ ...args, ...selectAndInclude, create, update, where });
if (checkRequiredBelongsTo) {
return modelResolver.performRelationProcessing((0, utils_1.getTransactionClient)(prismaClient, allOperationsArgs), result, relationsMetadata);
}
return result;
}
case "delete": {
if (!modelPermissions.delete) {
throw authorizationError;
}
const relationsMetadata = [];
const [selectAndInclude, where] = await Promise.all([
modelResolver.resolveSelectAndInclude(modelName, args.select, args.include, relationsMetadata, { path: "$" }),
modelResolver.resolveWhereUnique(modelPermissions.delete, modelName, args.where),
]);
const result = await query({ ...args, ...selectAndInclude, where });
if (checkRequiredBelongsTo) {
return modelResolver.performRelationProcessing((0, utils_1.getTransactionClient)(prismaClient, allOperationsArgs), result, relationsMetadata);
}
return result;
}
case "deleteMany": {
if (!modelPermissions.delete) {
throw authorizationError;
}
const relationsMetadata = [];
const result = await query({ ...args, where: await modelResolver.resolveWhere(modelPermissions.delete, modelName, args.where) });
if (checkRequiredBelongsTo) {
return modelResolver.performRelationProcessing((0, utils_1.getTransactionClient)(prismaClient, allOperationsArgs), result, relationsMetadata);
}
return result;
}
default: {
return query(args);
}
}
},
},
},
});
});
};
exports.createRlsExtension = createRlsExtension;