@permitio/permit-prisma
Version:
Prisma extension for integrating Permit.io authorization (RBAC, ABAC, ReBAC) into your Prisma application.
233 lines (232 loc) • 9.99 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.PermitClient = void 0;
const error_1 = require("../utils/error");
const permitio_1 = require("permitio");
const logger_1 = __importDefault(require("../utils/logger"));
class PermitClient {
constructor(config) {
this.initialized = false;
this.initializationPromise = null;
this.config = config;
this.permitInstance = new permitio_1.Permit({
token: config.token,
pdp: config.pdp,
apiUrl: config.apiUrl,
log: config.debug
? {
level: "debug",
}
: undefined,
throwOnError: Boolean(config.throwOnError),
});
this.initializationPromise = this.initialize();
}
/**
* Initialize the Permit SDK connection
* This is called automatically when the client is constructed
*/
initialize() {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this.permitInstance.api.tenants.list();
this.initialized = true;
if (this.config.debug) {
logger_1.default.info("Permit SDK initialized successfully");
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
if (this.config.debug) {
logger_1.default.error(`Failed to initialize SDK: ${errorMessage}`);
}
this.initialized = false;
}
});
}
/**
* Ensure the SDK is initialized before performing operations
*/
ensureInitialized() {
return __awaiter(this, void 0, void 0, function* () {
if (this.initializationPromise) {
yield this.initializationPromise;
this.initializationPromise = null;
}
if (!this.initialized) {
const error = new error_1.PermitError("Permit SDK is not properly initialized");
if (this.config.throwOnError) {
throw error;
}
if (this.config.debug) {
logger_1.default.error("[Permit] Operation attempted but SDK is not initialized");
}
}
});
}
/**
* Check if a user has permission to perform an action on a resource
*/
check(user, action, resource, context) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this.ensureInitialized();
if (this.config.debug) {
logger_1.default.info(`[Permit] Checking permission for user=${typeof user === "string" ? user : JSON.stringify(user)}, action=${action}, resource=${typeof resource === "string" ? resource : JSON.stringify(resource)}`);
}
const allowed = yield this.permitInstance.check(user, action, resource, context);
if (this.config.debug) {
logger_1.default.info(`Permission result: ${allowed ? "ALLOWED" : "DENIED"}`);
}
return allowed;
}
catch (error) {
const errorCause = error instanceof Error ? error : new Error(String(error));
const errorMessage = `Permission check failed: ${errorCause.message}`;
if (this.config.debug) {
logger_1.default.error(`[Permit] ${errorMessage}`);
}
if (this.config.throwOnError) {
throw new error_1.PermitError(errorMessage, { cause: errorCause });
}
return false;
}
});
}
/**
* Enforce permission check - throws if permission is denied
*/
enforceCheck(user, action, resource, context) {
return __awaiter(this, void 0, void 0, function* () {
const allowed = yield this.check(user, action, resource, context);
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 ${action} on ${resourceStr}`;
if (this.config.debug) {
logger_1.default.error(`[Permit] ${errorMessage}`);
}
throw new error_1.PermitError(errorMessage);
}
});
}
getUserPermissions(userId) {
return __awaiter(this, void 0, void 0, function* () {
return this.permitInstance.getUserPermissions(userId);
});
}
getAllowedResourceIds(userId, resourceType, action) {
return __awaiter(this, void 0, void 0, function* () {
yield this.ensureInitialized();
const userKey = typeof userId === "string" ? userId : userId.key;
const permissions = yield this.getUserPermissions(userKey);
if (this.config.debug) {
logger_1.default.info(`Fetched permissions for ${userId}: ${JSON.stringify(permissions)}`);
}
const allowedKeys = [];
for (const [key, data] of Object.entries(permissions)) {
const [type, id] = key.split(":");
if (type === resourceType &&
data.permissions.includes(`${resourceType}:${action}`)) {
allowedKeys.push(id);
}
}
return allowedKeys;
});
}
filterObjects(user, action, resources) {
return __awaiter(this, void 0, void 0, function* () {
yield this.ensureInitialized();
const checkRequests = resources.map(resource => ({
user: user,
action: action,
resource: `${resource.type}:${resource.id}`,
context: {
resourceAttributes: resource.attributes
}
}));
const results = yield this.permitInstance.bulkCheck(checkRequests);
const allowedResources = resources.filter((resource, index) => results[index]);
return allowedResources;
});
}
syncResourceInstanceCreate(resourceType, resourceKey, tenant = "default", attributes = {}) {
return __awaiter(this, void 0, void 0, function* () {
yield this.ensureInitialized();
const instanceData = {
resource: resourceType,
key: resourceKey,
tenant,
attributes,
};
try {
const result = yield this.permitInstance.api.resourceInstances.create(instanceData);
if (this.config.debug) {
logger_1.default.info(`Synced resource instance: ${resourceType}:${resourceKey} to Permit`);
}
return result;
}
catch (error) {
logger_1.default.error(`Failed to sync resource instance ${resourceType}:${resourceKey}: ${error.message}`);
throw error;
}
});
}
syncResourceInstanceUpdate(resourceType, resourceKey, tenant = "default", attributes = {}) {
return __awaiter(this, void 0, void 0, function* () {
yield this.ensureInitialized();
const instanceData = {
attributes,
};
const instanceKey = `${resourceType}:${resourceKey}`;
try {
const result = yield this.permitInstance.api.resourceInstances.update(instanceKey, instanceData);
if (this.config.debug) {
logger_1.default.info(`Updated resource instance: ${instanceKey} in Permit`);
}
return result;
}
catch (error) {
logger_1.default.error(`Failed to update resource instance ${instanceKey}: ${error.message}`);
throw error;
}
});
}
syncResourceInstanceDelete(resourceType, resourceKey) {
return __awaiter(this, void 0, void 0, function* () {
yield this.ensureInitialized();
const instanceKey = `${resourceType}:${resourceKey}`;
try {
yield this.permitInstance.api.resourceInstances.delete(instanceKey);
if (this.config.debug) {
logger_1.default.info(`Deleted resource instance: ${instanceKey} from Permit`);
}
}
catch (error) {
logger_1.default.error(`Failed to delete resource instance ${instanceKey}: ${error.message}`);
throw error;
}
});
}
/**
* Get the underlying Permit SDK instance for advanced usage
*/
getPermitInstance() {
return this.permitInstance;
}
}
exports.PermitClient = PermitClient;