UNPKG

@godspeedsystems/prisma-deterministic-search-field-encryption

Version:

Transparent and customizable field-level encryption at rest for Prisma based on prisma-field-encryption package

87 lines (86 loc) 4.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseAnnotation = exports.analyseDMMF = void 0; const errors_1 = require("./errors"); const types_1 = require("./types"); const supportedCursorTypes = ['Int', 'String']; function analyseDMMF(input) { const dmmf = types_1.dmmfDocumentParser.parse(input); const allModels = [...dmmf.datamodel.models, ...dmmf.datamodel.types]; return allModels.reduce((output, model) => { var _a, _b, _c; const idField = model.fields.find(field => field.isId && supportedCursorTypes.includes(String(field.type))); const uniqueField = model.fields.find(field => field.isUnique && supportedCursorTypes.includes(String(field.type))); const cursorField = model.fields.find(field => { var _a; return (_a = field.documentation) === null || _a === void 0 ? void 0 : _a.includes('@encryption:cursor'); }); if (cursorField) { // Make sure custom cursor field is valid if (!cursorField.isUnique) { throw new Error(errors_1.errors.nonUniqueCursor(model.name, cursorField.name)); } if (!supportedCursorTypes.includes(String(cursorField.type))) { throw new Error(errors_1.errors.unsupportedCursorType(model.name, cursorField.name, String(cursorField.type))); } if ((_a = cursorField.documentation) === null || _a === void 0 ? void 0 : _a.includes('@encrypted')) { throw new Error(errors_1.errors.encryptedCursor(model.name, cursorField.name)); } } const modelDescriptor = { cursor: (_c = (_b = cursorField === null || cursorField === void 0 ? void 0 : cursorField.name) !== null && _b !== void 0 ? _b : idField === null || idField === void 0 ? void 0 : idField.name) !== null && _c !== void 0 ? _c : uniqueField === null || uniqueField === void 0 ? void 0 : uniqueField.name, fields: model.fields.reduce((fields, field) => { const fieldConfig = parseAnnotation(field.documentation, model.name, field.name); if (fieldConfig && field.type !== 'String') { throw new Error(errors_1.errors.unsupportedFieldType(model, field)); } return fieldConfig ? { ...fields, [field.name]: fieldConfig } : fields; }, {}), connections: model.fields.reduce((connections, field) => { const targetModel = allModels.find(model => field.type === model.name); if (!targetModel) { return connections; } const connection = { modelName: targetModel.name, isList: field.isList }; return { ...connections, [field.name]: connection }; }, {}) }; if (Object.keys(modelDescriptor.fields).length > 0 && !modelDescriptor.cursor) { console.warn(errors_1.warnings.noCursorFound(model.name)); } return { ...output, [model.name]: modelDescriptor }; }, {}); } exports.analyseDMMF = analyseDMMF; // -- const annotationRegex = /@encrypted(?<query>\?[\w=&]+)?/; function parseAnnotation(annotation = '', model, field) { var _a, _b; const match = annotation.match(annotationRegex); if (!match) { return null; } const query = new URLSearchParams((_b = (_a = match.groups) === null || _a === void 0 ? void 0 : _a.query) !== null && _b !== void 0 ? _b : ''); const readonly = query.get('readonly') !== null; const strict = query.get('strict') !== null; /* istanbul ignore next */ if (process.env.NODE_ENV === 'development' && strict && readonly && model && field) { console.warn(errors_1.warnings.strictAndReadonlyAnnotation(model, field)); } return { encrypt: !readonly, strictDecryption: !readonly && strict }; } exports.parseAnnotation = parseAnnotation;