@m92/express-utils
Version:
Utility Module for Express Framework
154 lines (119 loc) • 5.39 kB
JavaScript
'use strict'
import autoBind from 'auto-bind'
import { Aes, AesUtils, Pgp } from '@m92/crypto'
import ResponseBody from '../helpers/ResponseBody'
import CRYPTO_ERRORS from '../constants/CRYPTO_ERRORS'
export default class Crypto {
constructor (CONFIG, CONSTANTS, options = {}, thisExpressUtils) {
const { CustomError, logger } = thisExpressUtils
this.CONFIG = CONFIG
this.CONSTANTS = CONSTANTS
this.options = options
this.CustomError = CustomError
this.logger = logger
autoBind(this)
}
validatePgpOptions () {
const { options, CustomError } = this
const {
getPassphrase,
getPublicKeyArmored,
getPrivateKeyArmored
} = options
if (!getPassphrase || typeof getPassphrase !== 'function') {
throw new CustomError(undefined, CRYPTO_ERRORS.INVALID_PASSPHRASE_FUNC)
}
if (!getPublicKeyArmored || typeof getPublicKeyArmored !== 'function') {
throw new CustomError(undefined, CRYPTO_ERRORS.INVALID_PUBLIC_KEY_FUNC)
}
if (!getPrivateKeyArmored || typeof getPrivateKeyArmored !== 'function') {
throw new CustomError(undefined, CRYPTO_ERRORS.INVALID_PRIVATE_KEY_FUNC)
}
}
encryptPayload (request, response, next) {
const { CONFIG, CONSTANTS, CustomError } = this
const { DISABLE_PAYLOAD_CRYPTOGRAPHY } = CONFIG
if (DISABLE_PAYLOAD_CRYPTOGRAPHY) { return process.nextTick(next) }
try {
const { DEFAULT_AUTH, AUTH_HEADER, ACCESS_TOKEN_PROP, ACCESS_TOKEN_HEADER_KEY } = CONSTANTS
if (!DEFAULT_AUTH && !ACCESS_TOKEN_PROP) { return process.nextTick(next) }
if (DEFAULT_AUTH && !AUTH_HEADER.REQUEST_PROP) { return process.nextTick(next) }
const tokenProp = (DEFAULT_AUTH && AUTH_HEADER.REQUEST_PROP) || ACCESS_TOKEN_PROP
const tokenHeader = (DEFAULT_AUTH && AUTH_HEADER.HEADER_KEY) || ACCESS_TOKEN_HEADER_KEY.toLowerCase()
const token = response[tokenProp] || response.get(tokenHeader) || ''
const { body } = response
if (!body || !token) { return process.nextTick(next) }
const encryptionKey = AesUtils.extractKeyFromToken(token)
const bodyString = JSON.stringify(body)
const encryptParams = { key: encryptionKey, data: bodyString }
const encryptedDataObj = Aes.encrypt('aes-256-gcm', encryptParams)
const { payload } = encryptedDataObj
const encryptedBody = { payload }
const encryptedResponseBody = new ResponseBody(200, undefined, encryptedBody)
response.encryptedBody = encryptedResponseBody
return process.nextTick(next)
} catch (e) {
const error = new CustomError(e, CRYPTO_ERRORS.PAYLOAD_ENCRYPTION)
response.body = error
return process.nextTick(next)
}
}
decryptPayload (request, response, next) {
if (response.body) { return process.nextTick(next) }
const { CONFIG, CONSTANTS, CustomError } = this
const { DISABLE_PAYLOAD_CRYPTOGRAPHY } = CONFIG
const { DEFAULT_AUTH, AUTH_HEADER, ACCESS_TOKEN_PROP, ACCESS_TOKEN_HEADER_KEY } = CONSTANTS
if (DISABLE_PAYLOAD_CRYPTOGRAPHY) { return process.nextTick(next) }
if (!DEFAULT_AUTH && !ACCESS_TOKEN_PROP) { return process.nextTick(next) }
if (DEFAULT_AUTH && !AUTH_HEADER.REQUEST_PROP) { return process.nextTick(next) }
try {
const { body } = request
const tokenProp = (DEFAULT_AUTH && AUTH_HEADER.REQUEST_PROP) || ACCESS_TOKEN_PROP
const tokenHeader = (DEFAULT_AUTH && AUTH_HEADER.HEADER_KEY) || ACCESS_TOKEN_HEADER_KEY.toLowerCase()
const token = request[tokenProp] || request.get(tokenHeader) || ''
if (!body || !token) { return process.nextTick(next) }
const decryptionKey = AesUtils.extractKeyFromToken(token)
const { payload = '' } = body
if (!payload) { return process.nextTick(next) }
const decryptParams = { key: decryptionKey, payload }
const decryptedDataObj = Aes.decrypt('aes-256-gcm', decryptParams)
const { data: decryptedDataString } = decryptedDataObj
const decryptedData = JSON.parse(decryptedDataString)
request.body = decryptedData
return process.nextTick(next)
} catch (e) {
const error = new CustomError(e, CRYPTO_ERRORS.PAYLOAD_DECRYPTION)
response.body = error
return process.nextTick(next)
}
}
async decryptCredentials (request, response, next) {
const { CONFIG, options, CustomError } = this
const { getPassphrase, getPrivateKeyArmored } = options
const { DISABLE_CREDENTIAL_CRYPTOGRAPHY } = CONFIG
if (DISABLE_CREDENTIAL_CRYPTOGRAPHY) { return process.nextTick(next) }
try {
const { body } = request
if (!body) { return process.nextTick(next) }
this.validatePgpOptions()
const passphrase = getPassphrase.constructor.name === 'AsyncFunction'
? await getPassphrase()
: getPassphrase()
const privateKeyArmored = getPrivateKeyArmored.constructor.name === 'AsyncFunction'
? await getPrivateKeyArmored()
: getPrivateKeyArmored()
const decryptParams = {
data: body,
privateKeyArmored,
passphrase
}
const decryptedData = await Pgp.decrypt(decryptParams)
request.body = decryptedData
return next()
} catch (e) {
const error = new CustomError(e, CRYPTO_ERRORS.CREDENTIAL_DECRYPTION)
response.body = error
return process.nextTick(next)
}
}
}