typeorm-pii-compliance
Version:
TypeORM PII Compliance Service: Cascading Personally Identifiable Information Disposal
131 lines (130 loc) • 6.43 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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PiiComplianceService = void 0;
const pii_metadata_1 = require("./pii-metadata");
const pii_type_1 = require("./types/pii.type");
class PiiComplianceService {
constructor(options) {
this.options = options;
}
entities() {
if (!this._sorted) {
this._sorted = [...pii_metadata_1.PiiMetadata.entities];
this._sorted.sort((a, b) => b.hierarchyPriority - a.hierarchyPriority);
}
return this._sorted;
}
process(groupOrEntityId, idOrCallback, callbackFunc) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
let [group, entityId, beforeDisposal] = [
undefined,
[],
undefined,
];
if (idOrCallback && callbackFunc)
[group, entityId, beforeDisposal] = [
groupOrEntityId,
idArrayize(idOrCallback),
callbackFunc,
];
else if (idOrCallback) {
if (typeof idOrCallback === 'function')
[entityId, beforeDisposal] = [idArrayize(groupOrEntityId), idOrCallback];
else
[group, entityId] = [groupOrEntityId, idArrayize(idOrCallback)];
}
else
entityId = idArrayize(groupOrEntityId);
beforeDisposal = beforeDisposal || ((_a = this.options) === null || _a === void 0 ? void 0 : _a.beforeDisposal);
const entityConfigs = this.entities().filter((item) => (!group && !item.group) || (group && item.group === group));
for (const { entity, columns, strategy } of entityConfigs) {
const where = columns.reduce((result, item, idx) => {
result[item] = entityId[idx];
return result;
}, {});
let columnsMeta = [];
if (strategy === pii_type_1.PiiDisposalStrategy.MASKING) {
columnsMeta = pii_metadata_1.PiiMetadata.columns.filter((item) => item.entity === entity);
if (columnsMeta.length <= 0) {
console.warn(`PiiComplianceService: [${entity.constructor.name}] entity does not have a column referenced by @PiiColumn decorator.`);
continue;
}
}
if (beforeDisposal || strategy === pii_type_1.PiiDisposalStrategy.MASKING) {
const records = yield entity.find({ where });
for (const record of records) {
if (beforeDisposal)
yield beforeDisposal(entity, record);
if (strategy === pii_type_1.PiiDisposalStrategy.MASKING) {
for (let column of columnsMeta)
record[column.name] = yield PiiComplianceService.transform(record[column.name], column.maskingMethod, (_b = this.options) === null || _b === void 0 ? void 0 : _b.replaceChar);
yield record.save();
}
}
}
if (strategy === pii_type_1.PiiDisposalStrategy.DELETE)
yield entity.delete(where);
}
});
}
static transform(value, maskingMethod, replaceChar = '*') {
return __awaiter(this, void 0, void 0, function* () {
if (typeof maskingMethod === 'function')
return yield maskingMethod(value);
switch (maskingMethod) {
case 'null':
return null;
case 'zero':
return 0;
case 'blank':
return '';
}
replaceChar = replaceChar || '*';
if (value === null || typeof value === 'undefined')
value = '';
value = value.toString();
switch (maskingMethod) {
case 'email':
return value.replace(/(.{0,5})@(.{0,5})/g, (...args) => replaceChar.repeat(args[1].length) + '@' + replaceChar.repeat(args[2].length));
default:
const method = maskingMethod;
switch (method.substr(0, method.length - 1)) {
case 'edge': {
const maskLen = Number(method.substr(-1));
return value.replace(new RegExp(`(^.{0,${maskLen}})|(.{0,${maskLen}}$)`, 'g'), replacer(replaceChar));
}
case 'center': {
const maskLen = Number(method.substr(-1));
const len = value.length;
if (len <= maskLen)
return replaceChar.repeat(len);
const center = Math.ceil(len / 2);
const left = value.substr(0, center);
const right = value.substr(center);
return (left.replace(new RegExp(`.{0,${Math.ceil(maskLen / 2)}}$`), replacer(replaceChar)) +
(maskLen > 1 ? right.replace(new RegExp(`^.{0,${Math.floor(maskLen / 2)}}`), replacer(replaceChar)) : right));
}
}
}
});
}
}
exports.PiiComplianceService = PiiComplianceService;
function replacer(replaceChar) {
return (substring, ...args) => {
return replaceChar.repeat(substring.length);
};
}
function idArrayize(id) {
return Array.isArray(id) ? id : [id];
}