Assumptions:
- This assumes you have completed the prerequisites for the JavaScript client on your MacOS or Linux server.
- Create a file named
auditClient.js
with the following code:const fs = require('fs');
const axios = require('axios');
const crypto = require('crypto');
const path = require('path');
const { URLSearchParams } = require('url');
const dotenv = require('dotenv');
// Load environment variables for HMAC
dotenv.config();
const apiToken = process.env.API_TOKEN;
const apiTokenId = process.env.API_TOKEN_ID;
// HMAC can also be applied directly into the script
//const apiToken = '<ADD API_TOKEN>'; // uncomment to apply the token in the script
//const apiTokenId = '<ADD API_TOKEN_ID>'; // uncomment to apply the token_id in the script
const method = 'GET';
const apiPath = '/audit/api/v1/events';
let queryParams = '';
const host = 'api.virtru.com';
// Function to generate date intervals
function generateDateIntervals(startDate, endDate, delta) {
let currentDate = new Date(startDate);
const intervals = [];
while (currentDate < endDate) {
const intervalEnd = new Date(Math.min(currentDate.getTime() + delta, endDate.getTime()));
intervals.push([currentDate, intervalEnd]);
currentDate = intervalEnd;
}
return intervals;
}
// Parsing start and end dates
const startDateStr = '2024-10-13T11:00:00Z'; //start date can be changed to your desire date
const endDateStr = new Date().toISOString().split('T')[0] + 'T00:00:00Z';
const startDate = new Date(startDateStr);
const endDate = new Date(endDateStr);
const intervalLength = 24 * 60 * 60 * 1000; // 1 day in milliseconds
console.log('Start Date:', startDate);
console.log('End Date:', endDate);
// Function to hash string
function hashString(stringToHash) {
return crypto.createHash('sha256').update(stringToHash).digest('hex');
}
// Function to build string to hash
function buildStringToHash(headers, path, query, method, body) {
const headerString = `content-type:${headers['content-type']}\ndate:${headers.date}\nhost:${headers.host}\n`;
const stringToHash = `${method.toUpperCase()}\n${path}\n${query}\n${headerString}\ncontent-type;date;host\n${hashString(body)}`;
return stringToHash;
}
// Function to fetch data
async function fetchData(startDate, endDate, apiToken, apiTokenId, queryParams, outputDir = 'audit_output') {
let data = [];
let nextBookmark = null;
const maxIterations = 10;
let currentIteration = 0;
while (currentIteration < maxIterations) {
const now = new Date().toUTCString();
const bodyHex = hashString('');
if (nextBookmark) {
queryParams = `bookmark=${encodeURIComponent(nextBookmark)}&from=${encodeURIComponent(startDate)}&to=${encodeURIComponent(endDate)}`;
} else {
queryParams = `from=${encodeURIComponent(startDate)}&to=${encodeURIComponent(endDate)}`;
}
const headers = {
accept: 'application/json',
date: now,
'content-type': 'application/json; charset=utf-8',
host: host,
'X-Request-Limit': '1000'
};
const stringHash = buildStringToHash(headers, apiPath + (queryParams ? `?${queryParams}` : ''), queryParams, method, '');
const signature = crypto.createHmac('sha256', apiToken).update(stringHash).digest('hex');
headers['X-Auth-Signedheaders'] = 'content-type;date;host';
headers.Authorization = `HMAC ${apiTokenId}:${signature}`;
const url = `https://${host}${apiPath}?${queryParams}`;
const params = new URLSearchParams(queryParams);
try {
const response = await axios.get(url, { headers });
if (response.status !== 200) {
console.error(`Error fetching data: ${response.status}, ${response.statusText}`);
break;
}
const responseData = response.data;
data = data.concat(responseData.events || []);
nextBookmark = responseData.bookmarks?.nextBookmark;
currentIteration += 1;
if (!nextBookmark) {
console.log('No more bookmarks found. Stopping...');
break;
}
} catch (error) {
console.error(`Request failed: ${error.message}`);
break;
}
}
return data;
}
// Function to create directories
function createDirectories(baseDir = 'audit_output') {
const jsonDir = path.join(baseDir, 'json_files');
const csvDir = path.join(baseDir, 'csv_files');
if (!fs.existsSync(baseDir)) {
fs.mkdirSync(baseDir);
}
if (!fs.existsSync(csvDir)) {
fs.mkdirSync(csvDir);
}
if (!fs.existsSync(jsonDir)) {
fs.mkdirSync(jsonDir);
}
}
// Function to write data to files
function writeDataToFiles(data, dateStr, baseDir = 'audit_output') {
const jsonFileName = `${dateStr}.json`;
const csvFileName = `${dateStr}.csv`;
const jsonFilePath = path.join(baseDir, 'json_files', jsonFileName);
const csvFilePath = path.join(baseDir, 'csv_files', csvFileName);
if (!fs.existsSync(jsonFilePath)) {
fs.writeFileSync(jsonFilePath, JSON.stringify(data, null, 2));
console.log(`Data for ${dateStr} written to ${jsonFilePath}`);
} else {
console.log(`JSON file for ${dateStr} already exists. Skipping.`);
}
if (!fs.existsSync(csvFilePath)) {
// Write data to CSV file
const csvData = data.map((record) => {
return Object.values(record).join(',');
});
fs.writeFileSync(csvFilePath, csvData.join('\n'));
console.log(`Data for ${dateStr} written to ${csvFilePath}`);
} else {
console.log(`CSV file for ${dateStr} already exists. Skipping.`);
}
}
// Main script execution
createDirectories();
const intervals = generateDateIntervals(startDate, endDate, intervalLength);
intervals.forEach(async ([intervalStart, intervalEnd]) => {
const formattedStartDate = intervalStart.toISOString();
const formattedEndDate = intervalEnd.toISOString();
const intervalData = await fetchData(formattedStartDate, formattedEndDate, apiToken, apiTokenId, queryParams);
if (intervalData.length > 0) {
const dateStr = intervalStart.toISOString().split('T')[0];
console.log(`Number of records received for ${dateStr}: ${intervalData.length}`);
writeDataToFiles(intervalData, dateStr);
} else {
console.log(`No data received for interval starting ${formattedStartDate}`);
}
});
console.log('\n######################################################');
console.log('### ####');
console.log("### See the 'audit_output' path for the audit file ###");
console.log('### ####');
console.log('######################################################\n'); - Run the script from the command line to start fetching data.
node auditClient.js