mirror of
https://github.com/marcogll/passkit-generator.git
synced 2026-03-15 21:25:26 +00:00
Removed generateManifest, getUUID and old generateManifestSignature functions
Removed passModelsDir and outputDir constants and putted them asconfiguration object keys Removed configuration loading from loadConfiguration and moved into init() Removed generateManifest function Created first implementation of pass saving option inside configuration output directory
This commit is contained in:
170
index.js
170
index.js
@@ -5,15 +5,19 @@ const forge = require("node-forge");
|
||||
const { spawn } = require("child_process");
|
||||
const archiver = require("archiver");
|
||||
const async = require("async");
|
||||
|
||||
let _configuration = Object.freeze(require("./config.json"));
|
||||
const stream = require("stream");
|
||||
|
||||
const supportedTypesOfPass = /(boardingPass|eventTicket|coupon|generic|storeCard)/i;
|
||||
const passModelsDir = _configuration.models.dir;
|
||||
const outputDir = _configuration.output.dir;
|
||||
const Certificates = {
|
||||
status: false
|
||||
};
|
||||
const Configuration = {
|
||||
passModelsDir: null,
|
||||
output: {
|
||||
shouldWrite: false,
|
||||
dir: null,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Apply a filter to arg0 to remove hidden files names (starting with dot)
|
||||
@@ -30,8 +34,7 @@ function capitalizeFirst(str) {
|
||||
return str[0].toUpperCase()+str.slice(1);
|
||||
}
|
||||
|
||||
function loadConfiguration(configurationPath) {
|
||||
let setup = require(path.resolve(__dirname, configurationPath));
|
||||
function loadConfiguration(setup) {
|
||||
let reqFilesKeys = ["wwdr", "signerCert", "signerKey"];
|
||||
|
||||
// Node-Forge also accepts .cer certificates
|
||||
@@ -129,25 +132,17 @@ function checkSignatureRequirements() {
|
||||
return Promise.all([checkCertificate, checkKey]);
|
||||
}
|
||||
|
||||
/**
|
||||
Generates a unique UUID
|
||||
From Github Gist: https://gist.github.com/jed/982883
|
||||
*/
|
||||
|
||||
function UUIDGen(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,UUIDGen)}
|
||||
|
||||
/**
|
||||
Generates the cryptografic signature for the manifest file.
|
||||
Spawns Openssl process since Node.js has no support for PKCSs.
|
||||
|
||||
@function generateManifestSignature
|
||||
@function createSignature
|
||||
@params {String} manifestPath - temp dir path created to keep the manifest file.
|
||||
@returns {Object} Promise
|
||||
*/
|
||||
|
||||
|
||||
function generateManifestSignature(manifest) {
|
||||
// return new Promise(function(done, rejected) {
|
||||
function createSignature(manifest) {
|
||||
let signature = forge.pkcs7.createSignedData();
|
||||
|
||||
if (typeof manifest === "object") {
|
||||
@@ -196,85 +191,8 @@ function generateManifestSignature(manifest) {
|
||||
|
||||
// Converting the JSON Structure into a DER (which is a subset of BER), ASN.1 valid structure
|
||||
// Returning the buffer of the signature
|
||||
// return done(Buffer.from(forge.asn1.toDer(signature.toAsn1()).getBytes(), 'binary'));
|
||||
|
||||
return Buffer.from(forge.asn1.toDer(signature.toAsn1()).getBytes(), 'binary');
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
// function generateManifestSignature(manifestUUID) {
|
||||
// return new Promise(function(done, rejected) {
|
||||
// checkSignatureRequirements()
|
||||
// .then(function() {
|
||||
// let opensslError = false;
|
||||
// let opensslBuffer = [];
|
||||
|
||||
// let opensslProcess = spawn("openssl", [
|
||||
// "smime",
|
||||
// "-binary",
|
||||
// "-sign",
|
||||
// "-certfile", path.resolve(Certificates.dir, Certificates.files["wwdr_pem"]),
|
||||
// "-signer", path.resolve(Certificates.dir, Certificates.files["certificate"]),
|
||||
// "-inkey", path.resolve(Certificates.dir, Certificates.files["key"]),
|
||||
// "-in", path.resolve(`${os.tmpdir()}/manifest-${manifestUUID}.json`),
|
||||
// // "-out", path.resolve("passCreator", "event.pass", "./signature"),
|
||||
// "-outform", "DER",
|
||||
// "-passin", `pass:${Certificates.credentials["privateKeySecret"]}`
|
||||
// ]);
|
||||
|
||||
// opensslProcess.stdout.on("data", function(data) {
|
||||
// opensslBuffer.push(data);
|
||||
// });
|
||||
|
||||
// opensslProcess.stderr.on("data", function(data) {
|
||||
// opensslBuffer.push(data);
|
||||
// opensslError = true;
|
||||
// });
|
||||
|
||||
// opensslProcess.stdout.on("end", function() {
|
||||
// if (opensslError) {
|
||||
// return rejected(Buffer.concat(opensslBuffer));
|
||||
// }
|
||||
|
||||
// return done(Buffer.concat(opensslBuffer));
|
||||
// });
|
||||
// })
|
||||
// .catch(function(e) {
|
||||
// return rejected(`Cannot fulfill signature requirements.\n${e}`);
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
/**
|
||||
Generates a Buffer of JSON file (manifest)
|
||||
@function generateManifest
|
||||
@params {Object} fromObject - Manifest content
|
||||
@params {String} manifestUUID
|
||||
@return {Promise} - Promise with the manifest buffer
|
||||
@see https://apple.co/2IhJr0Q (PassKit Package Structure)
|
||||
@see https://apple.co/2K2aY3v (Passes Are Cryptographically Signed and Compressed)
|
||||
*/
|
||||
|
||||
function generateManifest(fromObject, manifestUUID) {
|
||||
return new Promise(function(done, failed) {
|
||||
if (!fromObject || typeof fromObject !== "object" && typeof fromObject !== "string") {
|
||||
return failed("generateManifest: Argument 0 is required and must be of an object or a string (source object)");
|
||||
}
|
||||
|
||||
if (!manifestUUID || typeof manifestUUID !== "string") {
|
||||
return failed("generateManifest: Argument 1 is required and must be a string (unique uuid).");
|
||||
}
|
||||
|
||||
const source = typeof fromObject === "object" ? JSON.stringify(fromObject) : fromObject;
|
||||
let manifestWS = fs.createWriteStream(`${os.tmpdir()}/manifest-${manifestUUID}.json`);
|
||||
|
||||
manifestWS.write(source);
|
||||
manifestWS.end();
|
||||
|
||||
//return done(Buffer.from(source));
|
||||
return done(Buffer.from(source).toString());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,8 +291,8 @@ function RequestHandler(request, response) {
|
||||
return;
|
||||
}
|
||||
|
||||
fs.readdir(`${passModelsDir}/${request.params.type}.pass`, function (err, files) {
|
||||
/* Invalid path for passModelsDir */
|
||||
fs.readdir(`${Configuration.passModelsDir}/${request.params.type}.pass`, function (err, files) {
|
||||
/* Invalid path for Configuration.passModelsDir */
|
||||
if (err) {
|
||||
// 😊
|
||||
response.set("Content-Type", "application/json");
|
||||
@@ -399,7 +317,7 @@ function RequestHandler(request, response) {
|
||||
}
|
||||
|
||||
let options = (request.method === "POST" ? request.body : (request.method === "GET" ? request.params : {}));
|
||||
fileStreamToBuffer(`${passModelsDir}/${request.params.type}.pass/pass.json`, function _returnBuffer(bufferResult) {
|
||||
fileStreamToBuffer(`${Configuration.passModelsDir}/${request.params.type}.pass/pass.json`, function _returnBuffer(bufferResult) {
|
||||
editPassStructure(filterPassOptions(options), bufferResult).then(function _afterJSONParse(passFileBuffer) {
|
||||
// Manifest dictionary
|
||||
let manifest = {};
|
||||
@@ -416,11 +334,11 @@ function RequestHandler(request, response) {
|
||||
}
|
||||
|
||||
// adding the files to the zip - i'm not using .directory method because it adds also hidden files like .DS_Store on macOS
|
||||
archive.file(`${passModelsDir}/${request.params.type}.pass/${file}`, { name: file });
|
||||
archive.file(`${Configuration.passModelsDir}/${request.params.type}.pass/${file}`, { name: file });
|
||||
|
||||
let hashFlow = forge.md.sha1.create();
|
||||
|
||||
fs.createReadStream(`${passModelsDir}/${request.params.type}.pass/${file}`)
|
||||
fs.createReadStream(`${Configuration.passModelsDir}/${request.params.type}.pass/${file}`)
|
||||
.on("data", function(data) {
|
||||
hashFlow.update(data.toString("binary"));
|
||||
})
|
||||
@@ -436,47 +354,42 @@ function RequestHandler(request, response) {
|
||||
throw new Error(`Unable to compile manifest. ${error}`);
|
||||
}
|
||||
|
||||
// let uuid = UUIDGen();
|
||||
|
||||
// generateManifest(manifestRaw, uuid)
|
||||
// .then(function(manifestBuffer) {
|
||||
|
||||
archive.append(Buffer.from(JSON.stringify(manifest), "utf8"), { name: "manifest.json" });
|
||||
|
||||
let signatureBuffer = generateManifestSignature(manifest);
|
||||
|
||||
|
||||
console.log(signatureBuffer)
|
||||
let signatureBuffer = createSignature(manifest);
|
||||
|
||||
if (!fs.existsSync("output")) {
|
||||
fs.mkdirSync("output");
|
||||
}
|
||||
|
||||
archive.append(signatureBuffer, { name: "signature" });
|
||||
let outputWS = fs.createWriteStream(`${outputDir}/${request.params.type}.pkpass`);
|
||||
|
||||
response.set({
|
||||
"Content-type": "application/vnd.apple.pkpass",
|
||||
"Content-disposition": `attachment; filename=${request.params.type}.pkpass`
|
||||
})
|
||||
|
||||
if (Configuration.output.shouldWrite && Configuration.output.dir != null) {
|
||||
// Memorize and then make it download
|
||||
let outputWS = fs.createWriteStream(`${Configuration.output.dir}/${request.params.type}.pkpass`);
|
||||
|
||||
archive.pipe(outputWS);
|
||||
archive.finalize();
|
||||
|
||||
outputWS.on("close", function() {
|
||||
response.status(201).download(`${outputDir}/${request.params.type}.pkpass`, `${request.params.type}.pkpass`, {
|
||||
cacheControl: false,
|
||||
headers: {
|
||||
"Content-type": "application/vnd.apple.pkpass",
|
||||
"Content-length": fs.statSync(`${outputDir}/${request.params.type}.pkpass`).size
|
||||
response.status(201).download(`${Configuration.output.dir}/${request.params.type}.pkpass`, `${request.params.type}.pkpass`, {
|
||||
cacheControl: false
|
||||
});
|
||||
});
|
||||
} else {
|
||||
archive.pipe(response)
|
||||
response.status(201)
|
||||
}
|
||||
});
|
||||
});
|
||||
// })
|
||||
// .catch(function(error) {
|
||||
// throw error;
|
||||
// });
|
||||
|
||||
archive.finalize();
|
||||
});
|
||||
|
||||
})
|
||||
.catch(function(err) {
|
||||
throw err;
|
||||
|
||||
// 😊
|
||||
response.set("Content-Type", "application/json");
|
||||
response.status(418).send({ ecode: 418, status: false, message: `Got error while parsing pass.json file: ${err}` });
|
||||
@@ -493,11 +406,15 @@ function init(configPath) {
|
||||
throw new Error("Initialization must be triggered only once.");
|
||||
}
|
||||
|
||||
if (!configPath || fs.accessSync(path.resolve(__dirname, configPath)) !== undefined) {
|
||||
let configPathResolved = path.resolve(__dirname, configPath);
|
||||
|
||||
if (!configPath || fs.accessSync(configPathResolved) !== undefined) {
|
||||
throw new Error(`Cannot load configuration from 'path' (${configPath}). File not existing or missing path.`);
|
||||
}
|
||||
|
||||
loadConfiguration(configPath).then(function(config) {
|
||||
let setup = require(configPathResolved);
|
||||
|
||||
loadConfiguration(setup).then(function(config) {
|
||||
Certificates.wwdr = config[0];
|
||||
Certificates.signerCert = config[1];
|
||||
Certificates.signerKey = config[2];
|
||||
@@ -506,6 +423,11 @@ function init(configPath) {
|
||||
.catch(function(error) {
|
||||
throw new Error(`Error: ${error}`);
|
||||
});
|
||||
|
||||
Configuration.passModelsDir = setup.models.dir;
|
||||
Configuration.output.dir = setup.output.dir;
|
||||
// for a future implementation
|
||||
Configuration.output.shouldWrite = false
|
||||
}
|
||||
|
||||
module.exports = { init, RequestHandler };
|
||||
|
||||
Reference in New Issue
Block a user