@permitio/permit-prisma
Version:
Prisma extension for integrating Permit.io authorization (RBAC, ABAC, ReBAC) into your Prisma application.
174 lines (173 loc) • 10.3 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPermitClientExtension = void 0;
const extension_1 = require("@prisma/client/extension");
const PermitClient_1 = require("../client/PermitClient");
const PermissionModels_1 = require("../models/PermissionModels");
const error_1 = require("../utils/error");
const prismaPermitMapper_1 = require("../utils/prismaPermitMapper");
const logger_1 = __importDefault(require("../utils/logger"));
function createPermitClientExtension(config) {
const permitClient = new PermitClient_1.PermitClient(config.permitConfig);
let currentUser;
return extension_1.Prisma.defineExtension({
name: "permit-prisma",
client: {
$permit: {
client: permitClient,
check(user, action, resource, context) {
return __awaiter(this, void 0, void 0, function* () {
return permitClient.check(user, action, resource, context);
});
},
enforceCheck(user, action, resource, context) {
return __awaiter(this, void 0, void 0, function* () {
return permitClient.enforceCheck(user, action, resource, context);
});
},
setUser(user) {
currentUser = user;
return { $permitContext: { user } };
},
getPermitClient() {
return permitClient;
},
getConfig() {
return config;
},
getAllowedResourceIds(userId, resourceType, action) {
return __awaiter(this, void 0, void 0, function* () {
return permitClient.getAllowedResourceIds(userId, resourceType, action);
});
},
filterQueryResults(action, resourceType, results) {
return __awaiter(this, void 0, void 0, function* () {
if (!currentUser) {
throw new error_1.PermitError("No user set for permission filtering");
}
const resources = results.map(result => ({
type: resourceType,
id: result.id,
attributes: result
}));
return permitClient.filterObjects(currentUser, action, resources);
});
}
},
},
query: {
$allModels: {
$allOperations: ({ model, operation, args, query }) => __awaiter(this, void 0, void 0, function* () {
var _a, _b;
if (!config.enableAutomaticChecks) {
return query(args);
}
if (config.permitConfig.debug) {
logger_1.default.info(`[Permit] Intercepting ${operation} operation on ${model}`);
}
const user = currentUser;
if (!user) {
if (config.permitConfig.debug) {
logger_1.default.info(`[Permit] No user provided, skipping permission check`);
}
return query(args);
}
if (config.enableDataFiltering &&
config.accessControlModel === PermissionModels_1.AccessControlModel.ReBAC &&
operation === "findMany") {
const resourceType = (0, prismaPermitMapper_1.mapModelToResourceType)(model, config.resourceTypeMapping);
const allowedIds = yield permitClient.getAllowedResourceIds(user, resourceType, "read");
if (allowedIds.length === 0) {
if (config.permitConfig.debug) {
logger_1.default.info(`[Permit] ReBAC filter: user has no access to any ${resourceType}`);
}
return [];
}
args.where = args.where
? { AND: [args.where, { id: { in: allowedIds } }] }
: { id: { in: allowedIds } };
if (config.permitConfig.debug) {
logger_1.default.info(`[Permit] ReBAC filter applied to ${model}, allowed IDs: ${JSON.stringify(allowedIds)}`);
}
return query(args);
}
if (((_a = config.excludedModels) === null || _a === void 0 ? void 0 : _a.includes(model)) ||
((_b = config.excludedOperations) === null || _b === void 0 ? void 0 : _b.includes(operation))) {
if (config.permitConfig.debug) {
logger_1.default.info(`[Permit] Skipping excluded model/operation: ${model}/${operation}`);
}
return query(args);
}
// Perform check for RBAC/ABAC (before query)
let action = (0, prismaPermitMapper_1.mapOperationToAction)(operation);
const resourceType = (0, prismaPermitMapper_1.mapModelToResourceType)(model, config.resourceTypeMapping);
let resource = (0, prismaPermitMapper_1.createResourceObject)(resourceType, args, operation, config.accessControlModel);
if (config.permitConfig.debug) {
logger_1.default.info(`[Permit] Checking permission: ${typeof user === "string" ? user : JSON.stringify(user)} -> ${action} -> ${typeof resource === "string"
? resource
: JSON.stringify(resource)}`);
}
const allowed = yield permitClient.check(user, action, resource);
if (!allowed) {
const userStr = typeof user === "string" ? user : user.key;
const resourceStr = typeof resource === "string"
? resource
: `${resource.type}${resource.key ? `:${resource.key}` : ""}`;
const errorMessage = `Permission denied: User ${userStr} is not allowed to perform ${action} operation on ${resourceStr} resource`;
if (config.permitConfig.debug) {
logger_1.default.info(`[Permit] ${errorMessage}`);
}
throw new error_1.PermitError(errorMessage);
}
const result = yield query(args);
if (config.enableAutoSync &&
config.accessControlModel === PermissionModels_1.AccessControlModel.ReBAC &&
["create", "update", "delete"].includes(operation)) {
const resourceKey = (0, prismaPermitMapper_1.getResourceIdForSync)(result, operation);
if (resourceKey) {
resource = { type: resourceType, key: resourceKey };
if (config.permitConfig.debug) {
logger_1.default.info(`[Permit] Checking ReBAC mutation permission: ${typeof user === "string" ? user : JSON.stringify(user)} -> ${action} -> ${JSON.stringify(resource)}`);
}
const allowed = yield permitClient.check(user, action, resource);
if (!allowed) {
const userStr = typeof user === "string" ? user : user.key;
const resourceStr = `${resource.type}:${resource.key}`;
const errorMessage = `ReBAC Permission denied: User ${userStr} is not allowed to perform ${action} operation on ${resourceStr} resource`;
if (config.permitConfig.debug) {
logger_1.default.info(`[Permit] ${errorMessage}`);
}
throw new error_1.PermitError(errorMessage);
}
const tenant = config.defaultTenant || "default";
const attributes = (result === null || result === void 0 ? void 0 : result.attributes) || {};
if (operation === "create") {
yield permitClient.syncResourceInstanceCreate(resourceType, resourceKey, tenant, attributes);
}
else if (operation === "update") {
yield permitClient.syncResourceInstanceUpdate(resourceType, resourceKey, tenant, attributes);
}
else if (operation === "delete") {
yield permitClient.syncResourceInstanceDelete(resourceType, resourceKey);
}
}
}
return result;
}),
},
},
});
}
exports.createPermitClientExtension = createPermitClientExtension;