UNPKG

openhim-core

Version:

The OpenHIM core application that provides logging and routing of http requests

160 lines (119 loc) 6.27 kB
logger = require 'winston' syslogParser = require('glossy').Parse parseString = require('xml2js').parseString firstCharLowerCase = require('xml2js').processors.firstCharLowerCase Audit = require('./model/audits').Audit AuditMeta = require('./model/audits').AuditMeta tlsAuthentication = require "./middleware/tlsAuthentication" dgram = require 'dgram' tls = require 'tls' net = require 'net' config = require "./config/config" config.auditing = config.get('auditing') parseAuditRecordFromXML = (xml, callback) -> # DICOM mappers csdCodeToCode = (name) -> if name is 'csd-code' then 'code' else name originalTextToDisplayName = (name) -> if name is 'originalText' then 'displayName' else name options = mergeAttrs: true, explicitArray: false tagNameProcessors: [firstCharLowerCase] attrNameProcessors: [firstCharLowerCase, csdCodeToCode, originalTextToDisplayName] parseString xml, options, (err, result) -> return callback err if err if not result?.auditMessage return callback new Error 'Document is not a valid AuditMessage' audit = {} if result.auditMessage.eventIdentification audit.eventIdentification = result.auditMessage.eventIdentification audit.activeParticipant = [] if result.auditMessage.activeParticipant # xml2js will only use an array if multiple items exist (explicitArray: false), else it's an object if result.auditMessage.activeParticipant instanceof Array for ap in result.auditMessage.activeParticipant audit.activeParticipant.push ap else audit.activeParticipant.push result.auditMessage.activeParticipant if result.auditMessage.auditSourceIdentification audit.auditSourceIdentification = result.auditMessage.auditSourceIdentification audit.participantObjectIdentification = [] if result.auditMessage.participantObjectIdentification # xml2js will only use an array if multiple items exist (explicitArray: false), else it's an object if result.auditMessage.participantObjectIdentification instanceof Array for poi in result.auditMessage.participantObjectIdentification audit.participantObjectIdentification.push poi else audit.participantObjectIdentification.push result.auditMessage.participantObjectIdentification callback null, audit codeInArray = (code, arr) -> (code in arr.map (a) -> a.code) exports.processAuditMeta = processAuditMeta = (audit, callback) -> AuditMeta.findOne {}, (err, auditMeta) -> if err logger.error err return callback() if not auditMeta then auditMeta = new AuditMeta() if audit.eventIdentification?.eventTypeCode?.code and not codeInArray audit.eventIdentification.eventTypeCode.code, auditMeta.eventType auditMeta.eventType.push audit.eventIdentification.eventTypeCode if audit.eventIdentification?.eventID?.code and not codeInArray audit.eventIdentification.eventID.code, auditMeta.eventID auditMeta.eventID.push audit.eventIdentification.eventID if audit.activeParticipant for activeParticipant in audit.activeParticipant if activeParticipant.roleIDCode?.code and not codeInArray activeParticipant.roleIDCode.code, auditMeta.activeParticipantRoleID auditMeta.activeParticipantRoleID.push activeParticipant.roleIDCode if audit.participantObjectIdentification for participantObject in audit.participantObjectIdentification if participantObject.participantObjectIDTypeCode?.code and not codeInArray participantObject.participantObjectIDTypeCode.code, auditMeta.participantObjectIDTypeCode auditMeta.participantObjectIDTypeCode.push participantObject.participantObjectIDTypeCode if audit.auditSourceIdentification?.auditSourceID and audit.auditSourceIdentification.auditSourceID not in auditMeta.auditSourceID auditMeta.auditSourceID.push audit.auditSourceIdentification.auditSourceID auditMeta.save (err) -> if err then logger.error err callback() exports.processAudit = processAudit = (msg, callback=(->)) -> parsedMsg = syslogParser.parse(msg) if not parsedMsg or not parsedMsg.message logger.info 'Invalid message received' return callback() parseAuditRecordFromXML parsedMsg.message, (xmlErr, result) -> audit = new Audit result audit.rawMessage = msg audit.syslog = parsedMsg delete audit.syslog.originalMessage delete audit.syslog.message audit.save (saveErr) -> if saveErr then logger.error "An error occurred while processing the audit entry: #{saveErr}" if xmlErr then logger.info "Failed to parse message as an AuditMessage XML document: #{xmlErr}" processAuditMeta audit, callback sendUDPAudit = (msg, callback) -> client = dgram.createSocket('udp4') client.send msg, 0, msg.length, config.auditing.auditEvents.port, config.auditing.auditEvents.host, (err) -> client.close() callback err sendTLSAudit = (msg, callback) -> tlsAuthentication.getServerOptions true, (err, options) -> return callback err if err client = tls.connect config.auditing.auditEvents.port, config.auditing.auditEvents.host, options, -> if not client.authorized then return callback client.authorizationError client.write "#{msg.length} #{msg}" client.end() client.on 'error', (err) -> logger.error err client.on 'close', -> callback() sendTCPAudit = (msg, callback) -> client = net.connect config.auditing.auditEvents.port, config.auditing.auditEvents.host, -> client.write "#{msg.length} #{msg}" client.end() client.on 'error', (err) -> logger.error client.on 'close', -> callback() # Send an audit event exports.sendAuditEvent = (msg, callback=(->)) -> done = (err) -> if err then logger.error err callback() if not config.auditing?.auditEvents? return done new Error 'Unable to record audit event: Missing config.auditing.auditEvents' switch config.auditing.auditEvents.interface when 'internal' then processAudit msg, done when 'udp' then sendUDPAudit msg, done when 'tls' then sendTLSAudit msg, done when 'tcp' then sendTCPAudit msg, done else done new Error "Invalid audit event interface '#{config.auditing.auditEvents.interface}'"