UNPKG

passbooker

Version:
356 lines (254 loc) 8.02 kB
'use strict'; /** * Module dependencies */ var path = require('path'); var fs = require('fs'); var crypto = require('crypto'); var exec = require('child_process').exec; var os = require('os'); var async = require('async'); var JSZip = require('JSZip'); var Utils = require('./utils'); var Hoek = require('hoek'); /** * Config */ var signatureCommandTpl = 'openssl smime -binary -sign -certfile {{wwdrCertPath}} -signer {{passCertPath}} -inkey {{passKeyPath}} -in {{manifestFilePath}} -out {{signatureFilePath}} -outform DER'; var supportedImagesByPassType = { 'boardingPass': [ 'icon.png', 'icon@2x.png', 'logo.png', 'logo@2x.png', 'footer.png', 'footer@2x.png' ], 'coupon': [ 'icon.png', 'icon@2x.png', 'logo.png', 'logo@2x.png', 'strip.png', 'strip@2x.png' ], 'eventTicket': [ 'icon.png', 'icon@2x.png', 'logo.png', 'logo@2x.png', 'strip.png', 'strip@2x.png', 'background.png', 'background@2x.png', 'thumbnail.png', 'thumbnail@2x.png' ], 'storeCard': [ 'icon.png', 'icon@2x.png', 'logo.png', 'logo@2x.png', 'strip.png', 'strip@2x.png' ], 'generic': [ 'icon.png', 'icon@2x.png', 'logo.png', 'logo@2x.png', 'thumbnail.png', 'thumbnail@2x.png' ], }; /** * Pass */ var Pass = function fnPass( template, options ) { if ( !template ) { throw new Error('No `template` supplied!'); } if ( !options ) { throw new Error('No `options` supplied!'); } var that = this; that.options = options; that.template = template; that.type = that.template.type; that.pass = {}; that.barcode = {}; that._setImagePaths(); that._setPass(); }; /** * Get SHA1 checksum of a string * @api Private */ Pass.prototype._getSHA1Checksum = function( string ) { return crypto.createHash( 'sha1' ).update( string, 'utf8' ).digest( 'hex' ); }; /** * Set image paths * @api Private */ Pass.prototype._setImagePaths = function fn_setImagePaths() { var that = this; var supportedImages = supportedImagesByPassType[that.type]; that.imagePaths = fs .readdirSync( that.template.options.imagePath ) .filter(function( file ) { return supportedImages.indexOf( file ) > -1; }) .map(function( file ) { return path.join( that.template.options.imagePath, file ); }); return true; }; /** * Set pass object * @api Private */ Pass.prototype._setPass = function fn_setPass() { var that = this; that.pass = Hoek.applyToDefaults( that.template.getPass(), that.pass ); that.pass.serialNumber = this.options.serialNumber || Utils.guid(); that.pass[that.type].headerFields = Hoek.merge( that.pass[that.type].headerFields, this.options.headerFields ); that.pass[that.type].primaryFields = Hoek.merge( that.pass[that.type].primaryFields, this.options.primaryFields ); that.pass[that.type].secondaryFields = Hoek.merge( that.pass[that.type].secondaryFields, this.options.secondaryFields ); that.pass[that.type].auxiliaryFields = Hoek.merge( that.pass[that.type].auxiliaryFields, this.options.auxiliaryFields ); that.pass[that.type].backFields = Hoek.merge( that.pass[that.type].backFields, this.options.backFields ); that.pass.barcode = this.options.barcode || that.pass.barcode || {}; return true; }; /** * Set manifest object * @api Private */ Pass.prototype._setManifest = function() { var that = this; that.manifest = { 'pass.json': that._getSHA1Checksum( JSON.stringify(that.pass) ) }; that.imagePaths.forEach(function( image ) { that.manifest[path.basename(image)] = that._getSHA1Checksum( fs.readFileSync(image) ); }); return true; }; /** * Get signature * @api Private */ Pass.prototype._getSignature = function fn_getSignature( callback ) { var that = this; var command = ''; var manifestFilePath = path.join( os.tmpdir(), Utils.guid() ); var signatureFilePath = path.join( os.tmpdir(), Utils.guid() ); var keysPath = that.template.options.keysPath; var passCertPath = path.join( keysPath, that.template.options.passCertName ); var passKeyPath = path.join( keysPath, that.template.options.passKeytName ); var wwdrCertPath = path.join( keysPath, that.template.options.wwdrCertName ); if ( !fs.existsSync(keysPath) ) { throw new Error('`keysPath` does not exist!'); } if ( !fs.existsSync(passCertPath) ) { throw new Error('Pass certificate does not exist!'); } if ( !fs.existsSync(passKeyPath) ) { throw new Error('Pass key does not exist!'); } if ( !fs.existsSync(wwdrCertPath) ) { throw new Error('Worldwide Developer Relations Certificate certificate does not exist!'); } command = signatureCommandTpl .replace('{{manifestFilePath}}', manifestFilePath ) .replace('{{signatureFilePath}}', signatureFilePath ) .replace('{{passCertPath}}', passCertPath ) .replace('{{passKeyPath}}', passKeyPath ) .replace('{{wwdrCertPath}}', wwdrCertPath ); var tasks = { writeManifest: function( fn ) { that._setManifest(); fs.writeFile( manifestFilePath, JSON.stringify(that.manifest), { encoding: 'utf8' }, fn ); }, signManifest: function( fn ) { return exec( command, fn ); }, signature: function( fn ) { return fs.readFile( signatureFilePath, fn ); }, deleteManifestJSON: function( fn ) { return fs.unlink( manifestFilePath, fn ); }, deleteSignature: function( fn ) { return fs.unlink( signatureFilePath, fn ); }, }; async.series( tasks, function( err, results ) { return callback( null, results.signature ); }); }; /** * Getter / Setter */ Pass.prototype._setContentField = function fn_setContentField( fieldType, input ) { var that = this; if ( !input.value ) { input.value = input.label; input.label = null; } var fields = {}; that.pass[that.type][fieldType].forEach(function( primaryField ) { fields[primaryField.key] = primaryField; }); fields[input.key] = fields[input.key] || {}; fields[input.key].key = input.key; fields[input.key].value = input.value; fields[input.key].label = input.label; if ( !input.label ) { delete fields[input.key].label; } that.pass[that.type][fieldType] = Object.keys( fields ).map(function( aKey ) { return fields[aKey]; }); return that; }; Pass.prototype.setPrimaryField = function fnSetPrimaryField( key, label, value ) { var that = this; return that._setContentField( 'primaryFields', { key: key, label: label, value: value }); }; Pass.prototype.setSecondaryField = function fnSetSecondaryField( key, label, value ) { var that = this; return that._setContentField( 'secondaryFields', { key: key, label: label, value: value }); }; Pass.prototype.setAuxiliaryField = function fnSetAuxiliaryField( key, label, value ) { var that = this; return that._setContentField( 'auxiliaryFields', { key: key, label: label, value: value }); }; Pass.prototype.setBackField = function fnSetBackField( key, label, value ) { var that = this; return that._setContentField( 'backFields', { key: key, label: label, value: value }); }; Pass.prototype.setBarcode = function fn_setBarcode( options ) { var that = this; if ( !options ) { return that; } that.pass.barcode = options; return that; }; /** * Save pass * @api Public */ Pass.prototype.getPackage = function fnSavePass( callback ) { var that = this; that._getSignature(function( err, signature ) { if ( !!err ) { return callback( err ); } var zip = new JSZip(); zip.file( 'pass.json', JSON.stringify(that.pass) ); zip.file( 'manifest.json', JSON.stringify(that.manifest) ); zip.file( 'signature', signature ); that.imagePaths.forEach(function( imagePath ) { zip.file( path.basename(imagePath), fs.readFileSync(imagePath) ); }); var zipBuffer = zip.generate({ type: 'nodebuffer' }); return callback( null, zipBuffer ); }); }; /** * Export */ module.exports = Pass;