UNPKG

tuain-ecosystem-lib

Version:

Servicio de gestión mensajería instantanea de la plataforma Tuain

398 lines (372 loc) 16.3 kB
const { ObjectId } = require('mongodb'); const { v4: uuidv4 } = require('uuid'); const forge = require('node-forge'); const { dbQueries: { devices: deviceQueries }, collections: { devices: devicesColl, stores: storesColl }, } = require('../../config'); const deviceStates = Object.freeze({ DRAFT: 'draft', ACTIVE: 'active', REJECTED: 'rejected', BLOCKED: 'blocked', }); const modErrs = { findStore: { notFound: ['01', 'No se encontró sucursal que se ajuste a la búsqueda'], }, findDevice: { notFound: ['01', 'No se encontró dispositivo que se ajuste a la búsqueda'], notBlocked: ['02', 'Dispositivo no está bloqueado'], }, }; async function generateKeyPair() { return new Promise((resolve, reject) => { forge.pki.rsa.generateKeyPair(2048, (err, keypair) => { if (err) { return reject(new Error('Error generando llaves')); } return resolve(keypair); }); }); } class DeviceManager { constructor(getDb, logger, errMgr, options) { this.options = options; this.getDb = getDb; this.logger = logger; this.errMgr = errMgr; this.errMgr.addModuleSet('lib-devices', modErrs); } async getDevices(reqData) { const { deviceIds, storeIds, state, deviceData } = reqData; const deviceCol = this.getDb().collection(devicesColl); const devices = await deviceCol .find(deviceQueries.findDevices(deviceIds, storeIds, state, deviceData)) .sort({ _id: -1 }) .toArray(); return [null, devices]; } async requestDeviceId(reqData) { const { storeId } = reqData; const deviceCol = this.getDb().collection(devicesColl); const storeCol = this.getDb().collection(storesColl); const storeDetail = await storeCol.findOne(deviceQueries.findById(storeId)); if (!storeDetail) { const errorDetail = `Tercero con Id ${storeId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findStore.notFound, errorDetail); return [errorObj, null]; } const keypair = await generateKeyPair(); const keyPairPem = { publicKey: forge.pki.publicKeyToPem(keypair.publicKey), privateKey: forge.pki.privateKeyToPem(keypair.privateKey), }; const newDeviceInfo = { ...reqData, storeId: ObjectId(storeId), ...keyPairPem, state: deviceStates.DRAFT, }; const insert = await deviceCol.insertOne(newDeviceInfo); const deviceId = insert?.insertedId; return [null, { deviceId, publicKey: keyPairPem.publicKey }]; } async modifyDevice(reqData) { const { deviceId, storeId, name, lastActivityDate, expireAt, additionalData = {} } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const objectToUpdate = { additionalData }; if (storeId) { objectToUpdate.storeId = ObjectId(storeId); } if (name) { objectToUpdate.name = name; } if (lastActivityDate) { objectToUpdate.lastActivityDate = lastActivityDate; } if (expireAt) { objectToUpdate.expireAt = expireAt; } const actionResult = await deviceCol.updateOne(deviceQueries.findById(deviceId), deviceQueries.updateObj(objectToUpdate)); const result = actionResult?.result?.nModified > 0; return [null, { result }]; } async deleteDevice(reqData) { const { deviceId } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const actionResult = await deviceCol.deleteOne(deviceQueries.findById(deviceId)); const result = actionResult?.result?.n > 0; return [null, { result }]; } async checkDeviceSignature(reqData) { const { deviceId, deviceChallenge, signedChallenge } = reqData; const [devErr, devRes] = await this.getDevice({ deviceId }); const deviceKey = !devErr && devRes ? forge.pki.publicKeyFromPem(devRes?.deviceKey) : null; if (!deviceKey) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado o sin llave`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const sign = forge.util.decode64(signedChallenge); const md = forge.md.sha256.create(); md.update(deviceChallenge); const data = md.digest().bytes(); return [null, { validation: deviceKey.verify(data, sign) }]; } async deviceSign(reqData) { const { deviceId, dataToSign } = reqData; const [devErr, devRes] = await this.getDevice({ deviceId }); const privateKey = !devErr && devRes ? forge.pki.publicKeyFromPem(devRes?.privateKey) : null; if (!privateKey) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado o sin llave`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const md = forge.md.sha256.create(); md.update(dataToSign); const sign = privateKey.sign(md); const signedData = forge.util.encode64(sign); return [null, { signedData }]; } async deviceEncrypt(reqData) { const { deviceId, dataToEncrypt } = reqData; const [devErr, devRes] = await this.getDevice({ deviceId }); const deviceKey = !devErr && devRes ? forge.pki.publicKeyFromPem(devRes?.deviceKey) : null; if (!deviceKey) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado o sin llave`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const encryptedDataRaw = deviceKey.encrypt(dataToEncrypt); const encryptedData = forge.util.encode64(encryptedDataRaw); return [null, encryptedData]; } async deviceDecrypt(reqData) { const { deviceId, dataToDecrypt } = reqData; const [devErr, devRes] = await this.getDevice({ deviceId }); const privateKey = !devErr && devRes ? forge.pki.publicKeyFromPem(devRes?.privateKey) : null; if (!privateKey) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado o sin llave`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const dataToDecryptRaw = forge.util.decode64(dataToDecrypt); const decryptedData = privateKey.decrypt(dataToDecryptRaw); return [null, decryptedData]; } async registerDeviceKey(reqData) { const { storeId, deviceId, additionalData, deviceKey } = reqData; const deviceCol = this.getDb().collection(devicesColl); const storeCol = this.getDb().collection(storesColl); const storeDetail = await storeCol.findOne(deviceQueries.findById(storeId)); if (!storeDetail) { const errorDetail = `Establecimiento ${storeId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findStore.notFound, errorDetail); return [errorObj, null]; } const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const actionResult = await deviceCol.updateOne( deviceQueries.findById(deviceId), deviceQueries.updateObj({ deviceKey, additionalData }), ); const result = actionResult?.result?.nModified > 0; return [null, { result }]; } async activateDevice(reqData) { const { deviceId } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const actionResult = await deviceCol.updateOne( deviceQueries.findById(deviceId), deviceQueries.updateObj({ state: deviceStates.ACTIVE }), ); const result = actionResult?.result?.nModified > 0; return [null, { result }]; } async rejectDevice(reqData) { const { deviceId } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const actionResult = await deviceCol.updateOne( deviceQueries.findById(deviceId), deviceQueries.updateObj({ state: deviceStates.REJECTED }), ); const result = actionResult?.result?.nModified > 0; return [null, { result }]; } async blockDevice(reqData) { const { deviceId } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const actionResult = await deviceCol.updateOne( deviceQueries.findById(deviceId), deviceQueries.updateObj({ state: deviceStates.BLOCKED }), ); const result = actionResult?.result?.nModified > 0; return [null, { result }]; } async unlockDevice(reqData) { const { deviceId } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } if (deviceDetail.state !== 'blocked') { const errorDetail = `Dispositivo con Id ${deviceId} no se encuentra bloqueado`; const errorObj = this.errMgr.get(modErrs.findDevice.notBlocked, errorDetail); return [errorObj, null]; } const actionResult = await deviceCol.updateOne( deviceQueries.findById(deviceId), deviceQueries.updateObj({ state: deviceStates.DRAFT }), ); const result = actionResult?.result?.nModified > 0; return [null, { result }]; } async generateSessionKey(reqData) { const { deviceId } = reqData; const deviceCol = this.getDb().collection(devicesColl); const device = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!device) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const pub2 = forge.pki.publicKeyFromPem(device.deviceKey); const sessionKey = uuidv4(); const encrypted = pub2.encrypt(sessionKey); return [null, { encrypted, sessionKey }]; } async addExternalReference(reqData) { const { deviceId, externalSource, externalCode, externalDetail } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const currentCoding = deviceDetail.externalCoding?.find((item) => item.externalSource === externalSource); if (currentCoding) { await deviceCol.updateOne(deviceQueries.findById(deviceId), deviceQueries.removeExternalRef(externalSource)); } const externalRefData = { externalSource, externalCode, externalDetail: externalDetail || {} }; const actionResult = await deviceCol.updateOne( deviceQueries.findById(deviceId), deviceQueries.addExternalRef(externalRefData), ); const result = actionResult?.result?.nModified > 0; this.logger.debug(`Adición de una referencia externa a un tercero ${externalRefData} con resultado ${result}`); return [null, { result }]; } async deleteExternalReference(reqData) { const { deviceId, externalSource } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const actionResult = await deviceCol.updateOne( deviceQueries.findById(deviceId), deviceQueries.removeExternalRef(externalSource), ); const result = actionResult?.result?.nModified > 0; this.logger.debug(`eliminación de una referencia externa a un tercero ${externalSource} con resultado ${result}`); return [null, { result }]; } async addExternalReferenceAttributes(reqData) { const { deviceId, externalSource, externalDetail } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const actionResult = await deviceCol.updateOne( ...deviceQueries.addExternalRefAttribute(deviceId, externalSource, externalDetail), ); const result = actionResult?.result?.nModified > 0; this.logger.debug(`Adición de attributos a referencia externa a un tercero ${externalDetail} con resultado ${result}`); return [null, { result }]; } async deleteExternalReferenceAttributes(reqData) { const { deviceId, externalSource, externalName } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findById(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } const actionResult = await deviceCol.updateOne( ...deviceQueries.deleteExternalRefAttribute(deviceId, externalSource, externalName), ); const result = actionResult?.result?.nModified > 0; this.logger.debug(`Eliminación de attributos a referencia externa a un tercero ${externalName} con resultado ${result}`); return [null, { result }]; } async getStoreDevices(reqData) { const { storeId, deviceId } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceList = await deviceCol.find(deviceQueries.findStoreDevices(storeId, deviceId)).toArray(); if (!deviceList) { const errorDetail = !deviceId ? `Establecimiento ${storeId} no encontrado` : `Establecimiento ${storeId} y dispositivo ${deviceId} no relacionados`; const errorObj = this.errMgr.get(modErrs.findStore.notFound, errorDetail); return [errorObj, null]; } return [null, deviceList]; } async getDevice(reqData) { const { deviceId } = reqData; const deviceCol = this.getDb().collection(devicesColl); const deviceDetail = await deviceCol.findOne(deviceQueries.findDevice(deviceId)); if (!deviceDetail) { const errorDetail = `Dispositivo con Id ${deviceId} no encontrado`; const errorObj = this.errMgr.get(modErrs.findDevice.notFound, errorDetail); return [errorObj, null]; } return [null, deviceDetail]; } } module.exports = DeviceManager;