1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 | 1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
1x
| 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}'"
|