js-backend/utils/audit.js
const ipCountry = require("ip-country");
const syslogPro = require("syslog-pro");
class Audit {
constructor(dbConnection, ipCountrySettings) {
this.db = dbConnection;
ipCountry.init(ipCountrySettings);
}
prepareLoggers(customPages, version) {
this.customPages = customPages;
this.version = version;
// Send hello
const scheduledPromises = [];
for (let websiteIndex of Object.keys(this.customPages)) {
const thisPage = this.customPages[websiteIndex];
if(!thisPage.hasOwnProperty("syslog")) continue;
scheduledPromises.push(this.cefSend(thisPage.syslog, {
"meta": "start",
}));
}
Promise.all(scheduledPromises).then(() => {});
// Stop previous and start new heartbeat
if(this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
}
this.heartbeatTimer = setInterval(this.heartbeat.bind(this), (process.env.SYSLOGHEARTBEAT || 60) * 1000);
}
add(req, object, action, attribute) {
return new Promise((resolve, reject) => {
const userId = req.user ? req.user.id : null;
const userName = req.user && req.user.username ? req.user.username : null;
const ip = this.getIP(req);
const ipInfo = ipCountry.lookup(ip);
const country = (ipInfo && ipInfo.country) ? ipInfo.country.iso_code : null;
const loggers = [];
if(this.customPages["default"].hasOwnProperty("syslog")) {
loggers.push(this.customPages["default"].syslog);
}
if(req.ssoRequest) {
const thisPage = this.customPages[req.ssoRequest.pageId.toString()];
if(thisPage.hasOwnProperty("syslog")) {
loggers.push(thisPage.syslog);
}
}
this.databaseAdd(userId, ip, country, object, action, attribute).then(auditId => {
const scheduledPromises = [];
loggers.forEach(syslogItem => {
scheduledPromises.push(this.cefSend(syslogItem, {userName, userId, auditId, ip, country, object, action, attribute}));
});
Promise.all(scheduledPromises).then(results => {
resolve(auditId);
}).catch(err => {
console.error(err);
reject(err);
});
});
});
}
getList(userId, offset, length) {
return new Promise((resolve, reject) => {
this.db.execute("SELECT * FROM audit where user = ? ORDER BY created DESC LIMIT ?, ?", [userId, offset, length], (err, results) => {
if(err) return reject(err);
resolve(results);
});
});
}
get(id) {
return new Promise((resolve, reject) => {
this.db.execute("SELECT * FROM audit where id = ?", [id], (err, results) => {
if(err) return reject(err);
resolve(results);
});
});
}
databaseAdd(userId, ip, country, object, action, attribute) {
return new Promise((resolve, reject) => {
this.db.execute("INSERT INTO audit (user, ip, country, object, action, attribute1) VALUES (?, ?, ?, ?, ?, ?)", [userId, ip, country, object, action, attribute], (err, result) => {
if(err) return reject(err);
resolve(result.insertId);
});
});
}
heartbeat() {
const scheduledPromises = [];
for (let websiteIndex of Object.keys(this.customPages)) {
const thisPage = this.customPages[websiteIndex];
if(!thisPage.hasOwnProperty("syslog")) continue;
scheduledPromises.push(this.cefSend(thisPage.syslog, {
"meta": "heartbeat",
}));
}
Promise.all(scheduledPromises).then(() => {}).catch(err => {
console.error("Heartbeat failed", err);
});
}
cefSend(syslogDst, attributes) {
const currentDate = new Date();
attributes.time = currentDate.toISOString();
const cefMessage = new syslogPro.CEF({
deviceVendor: "OWASP Foundation",
deviceProduct: "OWASP SSO",
deviceVersion: this.version,
deviceEventClassId: "audit",
name: "OWASP SSO",
extensions: attributes,
server: syslogDst,
});
return cefMessage.send();
}
getIP(req) {
const forwardedFor = req.headers["x-forwarded-for"];
const clientIP = req.connection.remoteAddress;
if(forwardedFor) {
if(forwardedFor.indexOf(",") != -1) {
return ((forwardedFor.split(","))[0]).trim();
} else {
return forwardedFor;
}
} else {
return clientIP;
}
}
}
exports.Audit = Audit;