User = require('../model/users').User
crypto = require 'crypto'
logger = require 'winston'
config = require "../config/config"
config.api = config.get('api')
config.auditing = config.get('auditing')
atna = require 'atna-audit'
auditing = require '../auditing'
os = require 'os'
himSourceID = config.auditing.auditEvents.auditSourceID
# will NOT audit any successful logins on the following paths (specified as regex patterns)
# only 'noisy' endpoints should be included, such as heartbeats or endpoints that get polled
#
# /transactions is treated as a special case - see below
auditingExemptPaths = [
/\/tasks/
/\/events.*/
/\/metrics.*/
/\/mediators\/.*\/heartbeat/
/\/audits/
/\/logs/
]
isUndefOrEmpty = (string) ->
return not string? or string is ''
exports.authenticate = (next) ->
header = this.request.header
email = header['auth-username']
authTS = header['auth-ts']
authSalt = header['auth-salt']
authToken = header['auth-token']
auditAuthFailure = ->
audit = atna.userLoginAudit atna.OUTCOME_SERIOUS_FAILURE, himSourceID, os.hostname(), email
audit = atna.wrapInSyslog audit
auditing.sendAuditEvent audit, -> logger.debug 'Processed internal audit'
# if any of the required headers aren't present
if isUndefOrEmpty(email) or isUndefOrEmpty(authTS) or isUndefOrEmpty(authSalt) or isUndefOrEmpty(authToken)
logger.info "API request made by #{email} from #{this.request.host} is missing required API authentication headers, denying access"
this.status = 401
auditAuthFailure()
return
# check if request is recent
requestDate = new Date Date.parse authTS
authWindowSeconds = config.api.authWindowSeconds ? 10
to = new Date()
to.setSeconds(to.getSeconds() + authWindowSeconds)
from = new Date()
from.setSeconds(from.getSeconds() - authWindowSeconds)
if requestDate < from or requestDate > to
# request expired
logger.info "API request made by #{email} from #{this.request.host} has expired, denying access"
this.status = 401
auditAuthFailure()
return
user = yield User.findOne(email: email).exec()
this.authenticated = user
if not user
# not authenticated - user not found
logger.info "No user exists for #{email}, denying access to API, request originated from #{this.request.host}"
this.status = 401
auditAuthFailure()
return
hash = crypto.createHash 'sha512'
hash.update user.passwordHash
hash.update authSalt
hash.update authTS
if authToken is hash.digest 'hex'
# authenticated
if this.path is '/transactions'
if not this.query.filterRepresentation or this.query.filterRepresentation isnt 'full'
# exempt from auditing success
yield next
return
else
for pathTest in auditingExemptPaths
if pathTest.test this.path
# exempt from auditing success
yield next
return
# send audit
audit = atna.userLoginAudit atna.OUTCOME_SUCCESS, himSourceID, os.hostname(), email, user.groups.join(','), user.groups.join(',')
audit = atna.wrapInSyslog audit
auditing.sendAuditEvent audit, -> logger.debug 'Processed internal audit'
yield next
else
# not authenticated - token mismatch
logger.info "API token did not match expected value, denying access to API, the request was made by #{email} from #{this.request.host}"
this.status = 401
auditAuthFailure()
|