/**
* @module router-utils
*/
const utils = require( './utils' );
const config = require( '../models/config-model' ).server;
/**
* @static
* @name idEncryptionKeys
* @constant
* @property { string } singleOnce
* @property { string } view
*/
const keys = {
singleOnce: config[ 'less secure encryption key' ],
view: `${config[ 'less secure encryption key' ]}view`,
};
/**
* @static
* @name enketoId
* @function
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
* @param { string } id - Enketo ID
*/
function enketoIdParam( req, res, next, id ) {
if ( /^[A-z0-9]{4,31}$/.test( id ) ) {
req.enketoId = id;
next();
} else {
next( 'route' );
}
}
/**
* Wrapper function for {@link module:router-utils~_encryptedEnketoIdParam|_encryptedEnketoIdParam}
*
* @static
* @name encryptedEnketoIdSingle
* @function
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
* @param { string } id - Enketo ID
*/
function encryptedEnketoIdParamSingle( req, res, next, id ) {
_encryptedEnketoIdParam( req, res, next, id, keys.singleOnce );
}
/**
* Wrapper function for {@link module:router-utils~_encryptedEnketoIdParam|_encryptedEnketoIdParam}
*
* @static
* @name encryptedEnketoIdView
* @function
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
* @param { string } id - Enketo ID
*/
function encryptedEnketoIdParamView( req, res, next, id ) {
_encryptedEnketoIdParam( req, res, next, id, keys.view );
}
/**
* Returns decrypted Enketo ID
*
* @param {module:api-controller~ExpressRequest} req - HTTP request
* @param {module:api-controller~ExpressResponse} res - HTTP response
* @param {Function} next - Express callback
* @param { string } id - Enketo ID
* @param { string } key - Encryption key
*/
function _encryptedEnketoIdParam( req, res, next, id, key ) {
// Do not do a size check because we now have a configurable id size which can be used on an existing server,
// and therefore old (encrypted) IDs may have different lengths as new (encrypted) IDs.
// Routing takes care of FIRST checking whether the ID is a regular unencrypted ID.
try {
// Just see if it can be decrypted. Storing the encrypted value might
// increases chance of leaking underlying enketo_id but for now this is used
// in the submission controller and transformation controller.
const decrypted = utils.insecureAes192Decrypt( id, key );
// Sometimes decryption by incorrect keys works and results in gobledigook.
// A really terrible way of working around this is to check if the result is
// alphanumeric (as Enketo IDs always are).
if ( /^[a-z0-9]+$/i.test( decrypted ) ) {
req.enketoId = decrypted;
req.encryptedEnketoId = id;
next();
} else {
console.error( `decryption with "${key}" worked but result is not alphanumeric, ignoring result:`, decrypted );
next( 'route' );
}
} catch ( e ) {
// console.error( 'Could not decrypt:', req.encryptedEnketoId );
next( 'route' );
}
}
module.exports = {
enketoId: enketoIdParam,
idEncryptionKeys: keys,
encryptedEnketoIdSingle: encryptedEnketoIdParamSingle,
encryptedEnketoIdView: encryptedEnketoIdParamView
};