This commit is contained in:
Donavon McDowell 2023-10-03 08:08:21 -06:00
parent 2251b40756
commit faca208a50
5 changed files with 181 additions and 181 deletions

View File

@ -2,10 +2,10 @@ const express = require('express');
const cors = require('cors');
const dotenv = require('dotenv'); // Corrected require statement
const Token = require('./middleware/Token');
const DB = require('./middleware/DB');
const Api = require('./middleware/Api');
const SignIn = require('./models/SignIn');
const countries = require("i18n-iso-countries");
const RiskDetections = require('./models/RiskDetection');
const db = require('./config/db');
const MSTeams = require('./utilities/MSTeams');
@ -24,6 +24,9 @@ app.use('/logs/signin', signinRoutes);
//Get the logs from MS Graph
const retrieveLogs = async () => {
//Base URL for the graph API
const baseUrl = 'https://graph.microsoft.com/v1.0';
//Get the bearer token
const bearer = await Token.get();
@ -32,45 +35,41 @@ const retrieveLogs = async () => {
'Content-type': 'application/json'
};
const baseUrl = 'https://graph.microsoft.com/v1.0';
const url = baseUrl + '/auditLogs/signIns?$top=500';
//DEBUGGING FOR THE HEADER
//console.log(header);
console.log(header);
//Fetch MS Graph API - data is equal to the json returned
//Fetch MS Graph API - sign ins
var url = baseUrl + '/auditLogs/signIns?$top=500';
const signIns = await Api.call(url, header);
//Fetch MS Graph API - risk detections
url = baseUrl + '/identityProtection/riskDetections';
const riskDetections = await Api.call(url, header);
//This add the fetched data to the signins table in mariadb
await SignIn.add(signIns);
await RiskDetections.add(riskDetections);
//This will add the suspicious users to the suspicioususer table in maria db
//console.log(await SignIn.suspicious());
await SignIn.addSuspicious(await SignIn.getSuspicious());
await SignIn.updateAttemptCount(await SignIn.getSuspiciousAccounts());
}
const notify = async () => {
//Debugging
console.log("Sending notifications if any exist");
const currentTime = new Date();
console.log("[" + currentTime + "] " + "Sending notifications if any exist");
//console.log(await SignIn.getRecentSuspicious());
try {
const results = await SignIn.getRecentSuspicious(); // You should use 'await' here to wait for the results.
const results = await SignIn.getRecentSuspicious();
if (results && results.length > 0) { // Check if results exist and if it's not an empty array.
for (const result of results) {
var status = 0;
countryOrRegion = countries.getName(result.countryOrRegion, "en");
if(result.status == 0){
status = "<h1 style='color: green'>Success</h1>";
} else {
status = "<h1 style='color: red'>Failure</h1>";
}
MSTeams.send("Alert: Suspicious Login Attempt Detected", "Suspicious sign-in attempt for "+ "<h2>" + result.userDisplayName + " (" + result.userPrincipleName + ")" + "</h2>" + " originating from " + "<h2>" + result.state + ", " + countryOrRegion + "<br></br><div></h1>" + "<h2>Status:</h2> " + status + "</div>"); // Fixed typo 'Suspcious' to 'Suspicious'.
}
}
SignIn.checkIfNotify(results);
} catch (error) {
// Handle any errors that occur during the process.
console.error("Error notifying: " + error);
@ -83,24 +82,41 @@ retrieveLogs();
//Initially notify of any suspicious activities
notify();
const cleanse = async () => {
const connection = await db.getConnection();
const currentTime = new Date();
console.log("[" + currentTime + "] " + "Cleansing the system of old records");
// Delete old records from 'signins' table
const deleteSigninsQuery = "DELETE FROM signins WHERE insertTime <= CONVERT_TZ(NOW(), 'UTC', 'America/Denver') - INTERVAL 3 DAY";
await connection.query(deleteSigninsQuery);
// Delete old records from 'suspiciousAccounts' table
const deleteSuspiciousQuery = "DELETE FROM suspiciousAccounts WHERE insertTime <= CONVERT_TZ(NOW(), 'UTC', 'America/Denver') - INTERVAL 3 DAY";
await connection.query(deleteSuspiciousQuery);
connection.release(); // You should use 'await' here to wait for the results.
};
cleanse();
//TODO:
//Change the url in the MSTeams.js file before you put this into production!!!!
//MSTeams.send("bob", "bobs description");
const interval = 240000; // 60 seconds
const intervalId = setInterval(retrieveLogs, interval);
const intervalNotify = 120100; // 2 Minutes + a little (to ensure the alert is not triggered twice)
const intervalIdNotify = setInterval(notify, intervalNotify);
const intervalDelete = 43200000; // 12 hours
const intervalIdDelete = setInterval(DB.deleteOldRecords, intervalDelete);
const intervalDelete = 7200000; // 2 hours
const intervalIdDelete = setInterval(cleanse, intervalDelete);
const PORT = process.env.APIPORT || 3001;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
const currentTime = new Date();
console.log(`[` + currentTime + `] ` + `Server is running on port ${PORT}`);
});

View File

@ -1,24 +0,0 @@
const db = require('../config/db');
const deleteOldRecords = async () => {
try {
const connection = await db.getConnection();
// Delete old records from 'signins' table
const deleteSigninsQuery = 'DELETE FROM signins WHERE insertTime >= NOW() + INTERVAL 4 DAY';
await connection.query(deleteSigninsQuery);
// Delete old records from 'suspiciousAccounts' table
const deleteSuspiciousQuery = 'DELETE FROM suspiciousAccounts WHERE insertTime >= NOW() + INTERVAL 4 DAY';
await connection.query(deleteSuspiciousQuery);
connection.release();
} catch (error) {
throw error;
}
};
module.exports = {
deleteOldRecords,
};

View File

@ -1,10 +1,10 @@
const db = require('../config/db'); // Import the database connection pool
// Get a list of all sign ins
const all = async () => {
const retrieve = async () => {
try {
const connection = await db.getConnection();
const query = 'SELECT * FROM signins';
const query = 'SELECT * FROM riskDetections';
const signins = await connection.query(query);
connection.release();
return signins;
@ -13,52 +13,20 @@ const all = async () => {
}
};
const getRiskyDetections = async () => {
try {
const connection = await db.getConnection();
const query = 'SELECT * FROM signins WHERE countryOrRegion <> ?'; // <> means "not equal to"
const countryToExclude = 'CA'; // Change this if you want to exclude a different value
const signins = await connection.query(query, [countryToExclude]);
//console.log({events: signins});
connection.release();
return signins;
} catch (error) {
throw error;
}
};
const getSuspiciousAccounts = async () => {
try {
const connection = await db.getConnection();
const query = 'SELECT * FROM suspiciousAccounts'; // <> means "not equal to"
const countryToExclude = 'CA'; // Change this if you want to exclude a different value
const signins = await connection.query(query, [countryToExclude]);
connection.release();
return signins;
} catch (error) {
throw error;
}
};
// Add data to the sign in table
// Get a list of all sign ins
const add = async (data) => {
//For debugging
//console.log(data.value);
console.log("Added new signins to database, starting again in 60 seconds");
const currentTime = new Date();
console.log("[" + currentTime + "] " + "Added new risk detections to database, starting again in 60 seconds");
try {
const connection = await db.getConnection();
const results = []; // To store the results
const currentDate = new Date();
// Format the current date as "YYYY-MM-DD" for sql
const formattedDate = `${currentDate.getFullYear()}-${padNumber(currentDate.getMonth() + 1)}-${padNumber(currentDate.getDate())}`;
function padNumber(number) {
return number.toString().padStart(2, '0');
}
@ -68,51 +36,44 @@ const add = async (data) => {
const {
id,
riskState,
riskLevel,
riskDetail,
source,
activity,
ipAddress,
detectedDateTime,
userId,
createdDateTime,
userDisplayName,
userPrincipalName,
appDisplayName,
ipAddress,
deviceDetail,
location
additionalInfo
} = logEntry;
const {
displayName,
operatingSystem,
browser
} = deviceDetail;
const {
city,
state,
countryOrRegion
} = location;
const errorCode = logEntry.status.errorCode;
const city = logEntry.location.city;
const state = logEntry.location.state;
const countryOrRegion = logEntry.location.countryOrRegion;
const query = `
INSERT IGNORE INTO signins
(id, userId, createdDateTime, ip, userDisplayName, email, appDisplayName, deviceDisplayName, operatingSystem, browser, city, state, countryOrRegion, insertTime, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CONVERT_TZ(NOW(), 'UTC', 'America/Denver'), ?)`;
INSERT IGNORE INTO riskDetections
(id, riskState, riskLevel, riskDetail, source, activity, ipAddress, detectedDateTime, userId, userDisplayName, userPrincipalName, additionalInfo, city, state, countryOrRegion, insertDate)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CONVERT_TZ(NOW(), 'UTC', 'America/Denver'))`;
const values = [
id,
userId,
createdDateTime,
riskState,
riskLevel,
riskDetail,
source,
activity,
ipAddress,
detectedDateTime,
userId,
userDisplayName,
userPrincipalName,
appDisplayName,
displayName,
operatingSystem,
browser,
additionalInfo,
city,
state,
countryOrRegion,
errorCode
countryOrRegion
];
await connection.query(query, values);
@ -125,53 +86,6 @@ const add = async (data) => {
}
};
const addSuspicious = async (data) => {
console.log("Adding new suspicious users to database");
try {
const connection = await db.getConnection();
const results = []; // To store the results
for (const logEntry of data) { // Iterate directly if data.value is an array
const {
userId,
userDisplayName,
email,
countryOrRegion,
state,
city,
status
} = logEntry;
// Check if countryOrRegion is not empty or contains only whitespace characters
if (countryOrRegion && countryOrRegion.trim() !== '') {
const query = `
INSERT IGNORE INTO suspiciousAccounts
(id, userDisplayName, userPrincipleName, countryOrRegion, state, city, insertTime, status)
VALUES (?, ?, ?, ?, ?, ?, CONVERT_TZ(NOW(), 'UTC', 'America/Denver'), ?)`;
const values = [
userId,
userDisplayName,
email,
countryOrRegion,
state,
city,
status
];
await connection.query(query, values);
}
}
connection.release();
return results;
} catch (error) {
throw error;
}
};
const getRecentSuspicious = async () => {
try {
const connection = await db.getConnection();
@ -189,12 +103,29 @@ const getRecentSuspicious = async () => {
}
};
const checkIfNotify = async (results) => {
try {
if (results && results.length > 0) { // Check if results exist and if it's not an empty array.
for (const result of results) {
var status = 0;
countryOrRegion = countries.getName(result.countryOrRegion, "en");
if(result.status == 0){
status = "<h1 style='color: green'>Success</h1>";
} else {
status = "<h1 style='color: red'>Failure</h1>";
}
MSTeams.send("Alert: Risky Event Detected", "Risky sign-in attempt for "+ "<h2>" + result.userDisplayName + " (" + result.userPrincipleName + ")" + "</h2>" + " originating from " + "<h2>" + result.state + ", " + countryOrRegion + "<br></br><div></h1>" + "<h2>Status:</h2> " + status + "</div>"); // Fixed typo 'Suspcious' to 'Suspicious'.
}
}
} catch (error) {
throw error;
}
};
module.exports = {
all,
retrieve,
add,
getSuspicious,
addSuspicious,
getSuspiciousAccounts,
getRecentSuspicious
checkIfNotify
};

View File

@ -1,4 +1,6 @@
const db = require('../config/db'); // Import the database connection pool
const Countries = require("i18n-iso-countries");
const MSTeams = require('../utilities/MSTeams'); // Import the MS teams module
// Get a list of all sign ins
const all = async () => {
@ -47,16 +49,14 @@ const add = async (data) => {
//For debugging
//console.log(data.value);
console.log("Added new signins to database, starting again in 60 seconds");
const currentTime = new Date();
console.log("[" + currentTime + "] " + "Added new signins to database, starting again in 60 seconds");
try {
const connection = await db.getConnection();
const results = []; // To store the results
const currentDate = new Date();
// Format the current date as "YYYY-MM-DD" for sql
const formattedDate = `${currentDate.getFullYear()}-${padNumber(currentDate.getMonth() + 1)}-${padNumber(currentDate.getDate())}`;
function padNumber(number) {
return number.toString().padStart(2, '0');
}
@ -123,8 +123,51 @@ const add = async (data) => {
}
};
const getAttemptCount = async (id) => {
const connection = await db.getConnection();
const query = "SELECT COUNT(*) AS attempts FROM signins WHERE userId = ? AND countryOrRegion <> ?";
const country = "CA";
const results = await connection.query(query, [id, country]);
// Convert the BigInt to a regular JavaScript number
const attempts = results[0] ? Number(results[0].attempts) : 0;
connection.release();
return attempts;
};
const updateAttemptCount = async (userDetails) => {
/*
* @summary Takes an object of objects and updates the attempts column in the suspiciousAccounts table
* @params {Object} userDetails - An object of JSON results from the suspiciousAccounts table showing all user details
* @returns null
*/
const connection = await db.getConnection();
for (const user of userDetails) {
let query = "SELECT COUNT(*) AS attempts FROM signins WHERE userId = ? AND countryOrRegion <> ?";
const country = "CA";
const results = await connection.query(query, [user.id, country]);
const attempts = results[0] ? Number(results[0].attempts) : 0;
query = "UPDATE suspiciousAccounts SET attemptCount = ? WHERE id = ?";
await connection.query(query, [attempts, user.id]);
}
connection.release();
};
const addSuspicious = async (data) => {
console.log("Adding new suspicious users to database");
const currentTime = new Date();
console.log("[" + currentTime + "] " + "Adding new suspicious users to database");
try {
const connection = await db.getConnection();
const results = []; // To store the results
@ -169,14 +212,19 @@ const addSuspicious = async (data) => {
};
const getRecentSuspicious = async () => {
/*
* @params null
* @returns {Object} - All of the suspicious accounts that exist that had successful signIns
*
* */
try {
const connection = await db.getConnection();
const query = `
SELECT *
FROM suspiciousAccounts
WHERE insertTime >= CONVERT_TZ(NOW(), 'UTC', 'America/Denver') - INTERVAL 2 MINUTE`;
WHERE insertTime >= CONVERT_TZ(NOW(), 'UTC', 'America/Denver') - INTERVAL 2 MINUTE
AND status = 0`;
const signins = await connection.query(query);
@ -187,6 +235,32 @@ const getRecentSuspicious = async () => {
}
};
//Takes a
const checkIfNotify = async (results) => {
/*
* @summary Check if a notification needs to be sent out about a suspicious login
* @param {json Object} results - An object of all of the users and their details that are suspicious and were inserted into the table in the last 2 minutes
* @returns null or {string} error message
*/
try {
if (results && results.length > 0) { // Check if results exist and if it's not an empty array.
for (const result of results) {
var status = 0;
countryOrRegion = Countries.getName(result.countryOrRegion, "en");
if(result.status == 0){
status = "<h1 style='color: green'>Success</h1>";
} else {
status = "<h1 style='color: red'>Failure</h1>";
}
MSTeams.send("Alert: Suspicious Login Attempt Detected", "Suspicious sign-in attempt for "+ "<h2>" + result.userDisplayName + " (" + result.userPrincipleName + ")" + "</h2>" + " originating from " + "<h2>" + result.state + ", " + countryOrRegion + "<br></br><div></h1>" + "<h2>Status:</h2> " + status + "</div>");
}
}
} catch (error) {
throw error;
}
};
module.exports = {
all,
@ -194,5 +268,8 @@ module.exports = {
getSuspicious,
addSuspicious,
getSuspiciousAccounts,
getRecentSuspicious
getRecentSuspicious,
checkIfNotify,
getAttemptCount,
updateAttemptCount
};

View File

@ -4,8 +4,8 @@ var url = "https://mpeeng.webhook.office.com/webhookb2/698bec0f-4bb5-455a-a5c1-5
var testingUrl = "https://mpeeng.webhook.office.com/webhookb2/1616eaa3-c14c-43eb-881d-517a69df2329@538b9b1c-23fa-4102-b36e-a4d83fc9c4c1/IncomingWebhook/50a58aa7e3d0407a96cea03d467d7b6e/3745f560-1ea1-46a8-96ec-dc6772195127"
//Remove this if you are not testing
//url = testingUrl;
//Comment this out if you are not testing
url = testingUrl;
const webhookUrl = url;
const webhook = new IncomingWebhook(webhookUrl);