xml-fiesta
Version:
Electronic signed document XML Protocol for Node & Browser
236 lines • 9.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var Promise = require("promise");
var xmlCrypto = require("xml-crypto");
var select = require("xpath.js");
var Dom = require("xmldom").DOMParser;
var xml2js_1 = require("xml2js");
var common_1 = require("./common");
var certificate_1 = require("./certificate");
var xmlPatch_1 = require("./patches/xmlPatch");
var ExclusiveCanonicalization = xmlCrypto.SignedXml.CanonicalizationAlgorithms["http://www.w3.org/2001/10/xml-exc-c14n#"];
var versionToNumber = function (version) {
var _a = version
.split(/\./)
.map(function (v) { return parseInt(v); }), firstNumber = _a[0], secondNumber = _a[1], thirdNumber = _a[2];
return firstNumber * 100 + secondNumber * 10 + thirdNumber;
};
var START_VERSION_WITHOUT_SINGERS_CER = versionToNumber("2.5.0");
var XML = (function () {
function XML() {
this.tracked = false;
this.destroyed = false;
this.nameSpaces = null;
}
XML.parse = function (string) {
var xml = new xmlPatch_1.default();
return xml.parse(string);
};
XML.parseByElectronicDocument = function (electronicDocument) {
var xml = new xmlPatch_1.default();
return xml.parseByElectronicDocument(electronicDocument);
};
XML.toXML = function (eDocument, file) {
var edoc = JSON.parse(JSON.stringify(eDocument));
this.removeEncrypedData(edoc);
edoc.file[0]._ = file;
var builder = new xml2js_1.Builder({
rootName: "electronicDocument",
renderOpts: {
pretty: false,
},
});
return builder.buildObject(edoc);
};
XML.removeEncrypedData = function (xmljs) {
var _a, _b, _c;
if (xmljs.file && xmljs.file[0]) {
delete xmljs.file[0].$.encrypted;
xmljs.file[0].$.name = xmljs.file[0].$.name.replace(".enc", "");
}
(_c = (_b = (_a = xmljs.signers) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.signer) === null || _c === void 0 ? void 0 : _c.forEach(function (signer) {
delete signer.ePass;
});
};
XML.removeGeolocation = function (xmljs) {
var _a, _b, _c;
(_c = (_b = (_a = xmljs.signers) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.signer) === null || _c === void 0 ? void 0 : _c.forEach(function (signer) {
if (signer.auditTrail) {
signer.auditTrail[0].event.forEach(function (event, index) {
if (event.$.name === "geolocation") {
delete signer.auditTrail[0].event[index];
}
});
}
});
};
XML.removeBlockchain = function (xmljs) {
delete xmljs.blockchain;
};
XML.removeTransfer = function (xmljs) {
delete xmljs.transfers;
};
XML.detectNamespacePrefix = function (xmlString) {
var prefixMatch = xmlString.match(/<\s*([A-Za-z_][\w.-]*):electronicDocument\b/);
return prefixMatch ? prefixMatch[1] : null;
};
XML.createAttrNameStripper = function (xmlString) {
var detectedPrefix = XML.detectNamespacePrefix(xmlString);
return function (name) {
if (detectedPrefix && name) {
return name
.replace(new RegExp(detectedPrefix + ":", "g"), "")
.replace(new RegExp(":" + detectedPrefix, "g"), "");
}
return name;
};
};
XML.removeSignersCertificate = function (xmljs) {
var _a, _b, _c;
(_c = (_b = (_a = xmljs.signers) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.signer) === null || _c === void 0 ? void 0 : _c.forEach(function (signer) {
delete signer.$.name;
delete signer.certificate[0]._;
});
};
XML.prototype.parseByElectronicDocument = function (electronicDocument) {
var el = this;
if (electronicDocument.blockchain) {
el.tracked = true;
}
el.eDocument = electronicDocument;
var eDocumentAttrs = el.eDocument.$;
el.version = eDocumentAttrs.version;
el.signed = eDocumentAttrs.signed;
el.version_int = versionToNumber(el.version);
el.destroyed = eDocumentAttrs.cancel || false;
if (el.version_int < 100) {
el.fileElementName = "pdf";
}
else {
el.fileElementName = "file";
}
var pdfAttrs = el.eDocument[el.fileElementName][0].$;
el.encrypted = pdfAttrs.encrypted;
el.name = pdfAttrs.name;
el.contentType = pdfAttrs.contentType;
el.originalHash = pdfAttrs.originalHash;
return el;
};
XML.prototype.parse = function (xml) {
var el = this;
return new Promise(function (resolve, reject) {
return xml2js_1.parseString(xml, {
tagNameProcessors: [xml2js_1.processors.stripPrefix],
attrNameProcessors: [XML.createAttrNameStripper(xml)],
}, function (err, _a) {
var electronicDocument = _a.electronicDocument;
if (err) {
return reject(err);
}
el.parseByElectronicDocument(electronicDocument);
return resolve(el);
});
});
};
XML.prototype.canonical = function (electronicDocumentAttributes) {
if (electronicDocumentAttributes === void 0) { electronicDocumentAttributes = {}; }
var edoc = JSON.parse(JSON.stringify(this.eDocument));
if (electronicDocumentAttributes &&
Object.keys(electronicDocumentAttributes).length) {
Object.entries(electronicDocumentAttributes).map(function (_a) {
var key = _a[0], value = _a[1];
if (key.includes("xmlns")) {
edoc.$[key] = value;
}
});
}
delete edoc.$.cancel;
delete edoc.conservancyRecord;
var xml = this.constructor;
xml.removeEncrypedData(edoc);
xml.removeGeolocation(edoc);
xml.removeBlockchain(edoc);
xml.removeTransfer(edoc);
if (this.version_int >= START_VERSION_WITHOUT_SINGERS_CER) {
xml.removeSignersCertificate(edoc);
}
if (this.version_int >= 100) {
edoc[this.fileElementName][0]._ = "";
}
var builder = new xml2js_1.Builder({
rootName: "electronicDocument",
renderOpts: {
pretty: false,
},
});
var originalXml = builder.buildObject(edoc);
var doc = new Dom().parseFromString(originalXml);
var elem = select(doc, "//*")[0];
var can = new ExclusiveCanonicalization();
var canonicalString = can.process(elem).toString();
return canonicalString.replace(/
/g, "");
};
XML.prototype.getCanonicalBuffer = function (electronicDocumentAttributes) {
return Buffer.from(this.canonical(electronicDocumentAttributes), "utf-8");
};
XML.prototype.file = function () {
return this.eDocument[this.fileElementName][0]._;
};
XML.prototype.pdf = function () {
return this.file();
};
XML.prototype.xmlSigners = function () {
var parsedSigners = [];
var signers = this.eDocument.signers;
if (!signers)
return;
signers[0].signer.forEach(function (signer) {
var attrs = signer.$;
var cerHex = common_1.b64toHex(signer.certificate[0]._);
var certificate = new certificate_1.default(null, cerHex);
var xmlSigner = {
name: attrs.name,
taxId: attrs.id,
email: attrs.email,
cer: cerHex,
signature: common_1.b64toHex(signer.signature[0]._),
signedAt: signer.signature[0].$.signedAt,
legalEntity: certificate.getUniqueIdentifier().length > 1,
};
if (signer.ePass) {
xmlSigner.ePass = {
content: common_1.b64toHex(signer.ePass[0]._),
algorithm: signer.ePass[0].$.algorithm,
iterations: signer.ePass[0].$.iterations,
keySize: signer.ePass[0].$.keySize,
};
}
return parsedSigners.push(xmlSigner);
});
return parsedSigners;
};
XML.prototype.getConservancyRecord = function () {
var crVersion, userCertificate;
if (!this.eDocument.conservancyRecord) {
return null;
}
var cr = this.eDocument.conservancyRecord[0];
if (!cr.$.version) {
userCertificate = cr.userCertificate[0]._;
}
else {
crVersion = cr.$.version;
}
return {
caCert: cr.caCertificate[0]._,
userCert: userCertificate,
record: cr.record[0],
timestamp: cr.$.timestamp,
originalXmlHash: common_1.sha256(this.canonical()),
version: crVersion,
};
};
return XML;
}());
exports.default = XML;
//# sourceMappingURL=xml.js.map