First Commit

This commit is contained in:
Donavon McDowell 2023-11-16 09:42:07 -07:00
commit 15cfaf3dda
27 changed files with 2220 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/node_modules

20
Dockerfile Normal file
View File

@ -0,0 +1,20 @@
# Use the official Node.js 14 image as the base image
FROM node:14
# Set the working directory inside the container
WORKDIR /app
# Copy package.json and package-lock.json to the working directory
COPY package*.json ./
# Install project dependencies
RUN npm install
# Copy the project files to the working directory
COPY . .
# Expose port 3000 for the Node.js application
EXPOSE 3000
# Start the Node.js application
CMD [ "node", "server.js" ]

12
README.md Normal file
View File

@ -0,0 +1,12 @@
# MPE Log Engine
Keeping an eye on the logs, so you don't have to 😉
## Description
The Log Engine is a powerful Node server designed to enhance cybersecurity and risk management within Azure environments. This innovative system diligently monitors logs, meticulously analyzing activities and sign-ins for potential risks. When it identifies suspicious or high-risk activities, the Log Engine seamlessly triggers notifications to Microsoft Teams, enabling swift and effective responses to potential security threats. With its robust capabilities and real-time alerts, the Log Engine empowers organizations to proactively safeguard their Azure resources and data integrity.
### Installing Dependencies
1. Run ```npm install``` This will install any package dependencies
2. Next run ```node src/app.js```
3. Done! The server is now running

837
package-lock.json generated Normal file
View File

@ -0,0 +1,837 @@
{
"name": "signinlogs",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "signinlogs",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"i18n-iso-countries": "^7.7.0",
"luxon": "^3.4.3",
"mariadb": "^3.2.1",
"ms-teams-webhook": "^2.0.2",
"node-fetch": "^3.3.2"
}
},
"node_modules/@types/geojson": {
"version": "7946.0.11",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.11.tgz",
"integrity": "sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg=="
},
"node_modules/@types/node": {
"version": "17.0.45",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dependencies": {
"follow-redirects": "^1.14.0"
}
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"engines": {
"node": ">= 12"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/diacritics": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
"integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA=="
},
"node_modules/dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"node_modules/get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/has-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/i18n-iso-countries": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.7.0.tgz",
"integrity": "sha512-07zMatrSsR1Z+cnxW//7s14Xf4v5g6U6ORHPaH8+Ox4uPqV+y46Uq78veYV8H1DKTr76EfdjSeaTxHpnaYq+bw==",
"dependencies": {
"diacritics": "1.3.0"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/lru-cache": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz",
"integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==",
"engines": {
"node": "14 || >=16.14"
}
},
"node_modules/luxon": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz",
"integrity": "sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==",
"engines": {
"node": ">=12"
}
},
"node_modules/mariadb": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.2.1.tgz",
"integrity": "sha512-EWCsvu8IwK5WRYPJ9iRekFiBfHfKB/wBcUwAzbhQKhSc6ivbvLcO/cktkYTCz3HVoGNYVWhBN38485Cq3qMSbQ==",
"dependencies": {
"@types/geojson": "^7946.0.10",
"@types/node": "^17.0.45",
"denque": "^2.1.0",
"iconv-lite": "^0.6.3",
"lru-cache": "^10.0.1"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/mariadb/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/ms-teams-webhook": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/ms-teams-webhook/-/ms-teams-webhook-2.0.2.tgz",
"integrity": "sha512-yRIekmDQtxvprfm/PLtKoqDatEhoW70mIh96JmWOBSVyE+Lfr6SPQmWaSHKHy6DVusVm1rYDhXs0rbA5CgvB1w==",
"dependencies": {
"axios": "^0.21.1"
}
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
"engines": {
"node": ">= 8"
}
}
}
}

21
package.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "signinlogs",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"i18n-iso-countries": "^7.7.0",
"luxon": "^3.4.3",
"mariadb": "^3.2.1",
"ms-teams-webhook": "^2.0.2",
"node-fetch": "^3.3.2"
}
}

20
src/.env Normal file
View File

@ -0,0 +1,20 @@
#Database details
HOST="192.168.2.56"
USER="root"
PASSWORD="@4y@4Ab6bP"
DATABASE="logs"
DBPORT="3308"
BASE_URL = "http://localhost:3000"
TENANTID = "538b9b1c-23fa-4102-b36e-a4d83fc9c4c1"
APPLICATIONID = "15366443-975d-443a-9c47-6f8a345d538f"
CLIENTKEY = "AZU8Q~2CLc-KyHDJb4ArQR~wSCZsZ5d49KzkVaBx"
URL = "https://login.microsoftonline.com/$tenantId/oauth2/token"
RESOURCE = "https://graph.microsoft.com/"
#JSONWEBTOKEN
#API PORT TO LISTEN ON
APIPORT="3000"

185
src/app.js Normal file
View File

@ -0,0 +1,185 @@
const express = require('express');
const cors = require('cors');
const dotenv = require('dotenv'); // Corrected require statement
const Token = require('./middleware/Token');
const Api = require('./middleware/Api');
const SignIn = require('./models/SignIn');
const RiskDetections = require('./models/RiskDetection');
const Power = require('./utilities/Power');
const db = require('./config/db');
const MSTeams = require('./utilities/MSTeams');
dotenv.config(); // Call the config method to load environment variables
const app = express();
// Enable CORS for all routes
app.use(cors());
app.use(express.json());
// Import and use signin routes
const signinRoutes = require('./routes/signinRoutes');
app.use('/logs/signin', signinRoutes);
// Import and use settings routes
const settingsRoutes = require('./routes/settingsRoutes');
app.use('/settings', settingsRoutes);
// Import and use settings routes
const countRoutes = require('./routes/countRoutes');
app.use('/count', countRoutes);
// Import and use settings routes
const exceptionRoutes = require('./routes/exceptionRoutes');
app.use('/exception', exceptionRoutes);
//Get the logs from MS Graph
const retrieveLogs = async () => {
if(await Power.get() == 1){
//Base URL for the graph API
const baseUrl = 'https://graph.microsoft.com/v1.0';
//Get the bearer token
const bearer = await Token.get();
const header = {
'Authorization': `${bearer.token_type} ${bearer.access_token}`,
'Content-type': 'application/json'
};
//DEBUGGING FOR THE HEADER
//console.log(header);
//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);
//Only add this if you are interested in maintiaining the risk detections portion of the code
//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());
}
}
function exceptionFound(array1, array2) {
const matchingEmails = [];
for (const record1 of array1) {
for (const record2 of array2) {
if (
record1.userPrincipleName.toLowerCase() === record2.email.toLowerCase() &&
record1.countryOrRegion === record2.countryOrRegion
) {
matchingEmails.push(record2.email);
}
}
}
if (matchingEmails.length > 0) {
console.log("Matches found:");
for (const email of matchingEmails) {
console.log("Found a math with an exception for " + email + "not sending notification");
}
return true; // Common records found
} else {
console.log("No matches found");
return false; // No common records found
}
}
const notify = async () => {
if(await Power.get() == 1){
//Debugging
const currentTime = new Date();
console.log("[" + currentTime + "] " + "Sending notifications if any exist");
//console.log(await SignIn.getRecentSuspicious());
try {
const results = await SignIn.getRecentlySuspicious();
//TODO: Make the sendNotification function only work if the sign in was successful
//Get all of the excecptions
const connection = await db.getConnection();
const query = `SELECT * FROM exceptionAccounts`;
const exceptions = await connection.query(query);
connection.release();
//DEBUGGING!
//console.log('The exceptions:' , exceptions);
//console.log('The results:' , results);
if(exceptionFound(exceptions, results)){
console.log("Not sending alert because user is in exception list");
} else {
SignIn.sendNotification(results);
}
} catch (error) {
// Handle any errors that occur during the process.
console.error("Error notifying: " + error);
}
}
};
//Initially retrieve logs when the script is first ran
retrieveLogs();
//Initially notify of any suspicious activities
notify();
const cleanse = async () => {
if(await Power.get() == 1){
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);
// Delete old records from 'exceptionAccounts' table
const deleteOldExceptions = "DELETE FROM exceptionAccounts WHERE date >= CONVERT_TZ(NOW(), 'UTC', 'America/Denver') + INTERVAL 1 DAY";
await connection.query(deleteSuspiciousQuery);
connection.release(); // You should use 'await' here to wait for the results.
} else {
}
};
cleanse();
//TODO:
//Change the url in the MSTeams.js file before you put this into production!!!!
//MSTeams.send("bob", "bobs description");
const interval = 240000; // 240 seconds (4 minutes)
//Timer to collect the logs
setInterval(() => {
retrieveLogs();
}, interval);
//Notification timer
setInterval(() => {
notify();
}, interval + 5);
//Timer to delete old sign in logs
setInterval(cleanse, interval * 3);
const PORT = process.env.APIPORT || 3001;
app.listen(PORT, () => {
const currentTime = new Date();
console.log(`[` + currentTime + `] ` + `Server is running on port ${PORT}`);
});

15
src/config/db.js Normal file
View File

@ -0,0 +1,15 @@
// A location to store the database credentials and connect to the DB
const mariadb = require('mariadb');
const dotenv = require('dotenv'); // Corrected require statement
dotenv.config(); // Call the config method to load environment variables
// Create a MariaDB connection pool
const db = mariadb.createPool({
host: process.env.HOST,
user: process.env.USER,
password: process.env.PASSWORD,
database: process.env.DATABASE,
port: process.env.DBPORT,
});
module.exports = db;

View File

@ -0,0 +1,47 @@
const Count = require('../models/Count');
// Get a list of all users
const getAccounts = async (req, res) => {
try {
const logs = await Count.countAccounts();
if (logs.length === 0) {
return res.status(200).json("No accounts exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const getSignIns = async (req, res) => {
try {
const logs = await Count.countSignIns();
if (logs.length === 0) {
return res.status(200).json("No signins exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const getExceptions = async (req, res) => {
try {
const logs = await Count.countExceptions();
if (logs.length === 0) {
return res.status(200).json("No exceptions exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
module.exports = {
getSignIns,
getAccounts,
getExceptions
};

View File

@ -0,0 +1,58 @@
const Exception = require('../models/Exceptions');
const getAllExceptions = async (req, res) => {
try {
const logs = await Exception.getAll();
if (logs.length === 0) {
return res.status(200).json("No exceptions exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const addException = async (req, res) => {
const email = req.body.email;
const date = req.body.date;
const countryOrRegion = req.body.countryOrRegion;
try {
const exception = await Exception.add(email, date, countryOrRegion);
return res.status(200).json("Added exception");
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const editException = async (req, res) => {
const email = req.body.email;
const date = req.body.date;
const countryOrRegion = req.body.countryOrRegion;
try {
const exception = await Exception.edit(email, date, countryOrRegion);
return res.status(200).json("Edited exception");
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const removeException = async (req, res) => {
const email = req.params.email;
try {
const exception = await Exception.remove(email);
return res.status(200).json("Deleted Exception");
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
module.exports = {
addException,
getAllExceptions,
editException,
removeException
};

View File

@ -0,0 +1,48 @@
const Logs = require('../models/SignIn');
// Get a list of all users
const getSignIns = async (req, res) => {
try {
const logs = await Logs.all();
// Check if no logs were found and return an empty array
if (logs.length === 0) {
return res.status(200).json("No logs exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const getSuspicious = async (req, res) => {
try {
const logs = await Logs.getSuspicious();
if (logs.length === 0) {
return res.status(200).json("No logs exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const getSuspiciousAccounts = async (req, res) => {
try {
const logs = await Logs.getSuspiciousAccounts();
if (logs.length === 0) {
return res.status(200).json("No logs exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
module.exports = {
getSignIns,
getSuspicious,
getSuspiciousAccounts
};

View File

@ -0,0 +1,25 @@
const Settings = require('../models/Settings');
const get = async (req, res) => {
try {
const logs = await Settings.get();
if (logs.length === 0) {
return res.status(200).json("No settings exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const flip = async (req, res) => {
let power = await Settings.flip();
power = parseInt(power);
return res.status(200).json(power);
};
module.exports = {
get,
flip
};

View File

@ -0,0 +1,66 @@
const Logs = require('../models/SignIn');
// Get a list of all users
const getSignIns = async (req, res) => {
try {
const logs = await Logs.all();
// Check if no logs were found and return an empty array
if (logs.length === 0) {
return res.status(200).json("No logs exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const getSuspicious = async (req, res) => {
try {
const logs = await Logs.getSuspicious();
console.log(logs);
if (logs.length === 0) {
return res.status(200).json("No logs exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const getSuspiciousAccounts = async (req, res) => {
try {
const logs = await Logs.getSuspiciousAccounts();
//console.log(logs);
if (logs.length === 0) {
return res.status(200).json("No logs exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
const checked = async (req, res) => {
const userPrincipleName = req.params.email;
try {
const logs = await Logs.toggleChecked(userPrincipleName);
// Check if no logs were found and return an empty array
if (logs.length === 0) {
return res.status(200).json("No logs exist");
}
return res.status(200).json(logs);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
}
};
module.exports = {
getSignIns,
getSuspicious,
getSuspiciousAccounts,
checked
};

20
src/middleware/Api.js Normal file
View File

@ -0,0 +1,20 @@
//Used to fetch the url that is set
const call = async (url, header) => {
//For debugging
//console.log(header);
return fetch(url, {
method: 'GET',
headers: header
})
.then(response => response.json())
.then(data => {
return data;
})
.catch(error => {
console.error('Error:', error);
});
}
module.exports = {
call
};

36
src/middleware/Token.js Normal file
View File

@ -0,0 +1,36 @@
const get = () => {
const tenantId = process.env.TENANTID;
const applicationId= process.env.APPLICATIONID;
const clientKey = process.env.CLIENTKEY;
const url = "https://login.microsoftonline.com/"+ tenantId +"/oauth2/v2.0/token"
const resource = "https://graph.microsoft.com/.default"
const restbody = {
"grant_type": "client_credentials",
"client_id": applicationId,
"client_secret": clientKey,
"scope": resource
};
const formData = new URLSearchParams(restbody);
// Return the promise chain to propagate the access_token
return fetch(url, {
method: 'POST',
body: formData,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(response => response.json())
.then(data => {
return data;
})
.catch(error => {
console.error('Error:', error);
});
}
module.exports = {
get
};

47
src/models/Count.js Normal file
View File

@ -0,0 +1,47 @@
const db = require('../config/db'); // Import the database connection pool
const countAccounts = async () => {
try {
const connection = await db.getConnection();
const query = 'SELECT COUNT(*) AS count FROM suspiciousAccounts'; // Alias the count as 'count'
const result = await connection.query(query);
const count = result[0].count; // Access the count value from the result
connection.release();
return Number(count);
} catch (error) {
throw error;
}
};
const countSignIns = async() => {
try {
const connection = await db.getConnection();
const query = 'SELECT COUNT(*) AS count FROM signins'; // Alias the count as 'count'
const result = await connection.query(query);
const count = result[0].count; // Access the count value from the result
connection.release();
return Number(count);
} catch (error) {
throw error;
}
};
const countExceptions = async() => {
try {
const connection = await db.getConnection();
const query = 'SELECT COUNT(*) AS count FROM exceptionAccounts'; // Alias the count as 'count'
const result = await connection.query(query);
const count = result[0].count; // Access the count value from the result
connection.release();
return Number(count);
} catch (error) {
throw error;
}
};
module.exports = {
countAccounts,
countSignIns,
countExceptions
};

92
src/models/Exceptions.js Normal file
View File

@ -0,0 +1,92 @@
const db = require('../config/db'); // Import the database connection pool
const add = async (email, date, countryOrRegion) => {
try {
const connection = await db.getConnection();
// Prepare the SQL query to insert a new record into the exceptionAccounts table
const insertQuery = `
INSERT INTO exceptionAccounts (userPrincipleName, date, countryOrRegion)
VALUES (?, ?, ?)
`;
// Execute the insert query with the provided parameters
const result = await connection.query(insertQuery, [email, date, countryOrRegion]);
connection.release();
// Return the result of the insert operation
return result;
} catch (error) {
throw error;
}
};
const getAll = async () => {
try {
const connection = await db.getConnection();
const query = 'SELECT * FROM exceptionAccounts';
const exceptions = await connection.query(query);
connection.release();
if (exceptions.length === 0) {
// Handle the case where the userPrincipleName is not found
connection.release();
return;
}
return exceptions;
} catch (error) {
throw error;
}
};
const edit = async (email, date, countryOrRegion) => {
try {
const connection = await db.getConnection();
// Prepare the SQL query to update a record in the exceptionAccounts table
const updateQuery = `
UPDATE exceptionAccounts
SET date = ?, countryOrRegion = ?
WHERE userPrincipleName = ?
`;
// Execute the update query with the provided parameters
const result = await connection.query(updateQuery, [date, countryOrRegion, email]);
connection.release();
// Return the result of the update operation
return result;
} catch (error) {
throw error;
}
};
const remove = async (email) => {
try {
const connection = await db.getConnection();
// Prepare the SQL query to delete a record from the exceptionAccounts table
const deleteQuery = `
DELETE FROM exceptionAccounts
WHERE userPrincipleName = ?
`;
// Execute the delete query with the provided email parameter
const result = await connection.query(deleteQuery, [email]);
connection.release();
// Return the result of the delete operation
return result;
} catch (error) {
throw error;
}
};
module.exports = {
getAll,
add,
edit,
remove
};

131
src/models/RiskDetection.js Normal file
View File

@ -0,0 +1,131 @@
const db = require('../config/db'); // Import the database connection pool
// Get a list of all sign ins
const retrieve = async () => {
try {
const connection = await db.getConnection();
const query = 'SELECT * FROM riskDetections';
const signins = await connection.query(query);
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);
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();
function padNumber(number) {
return number.toString().padStart(2, '0');
}
for (const logEntry of data.value) { // Iterate directly if data.value is an array
const {
id,
riskState,
riskLevel,
riskDetail,
source,
activity,
ipAddress,
detectedDateTime,
userId,
userDisplayName,
userPrincipalName,
additionalInfo
} = logEntry;
const city = logEntry.location.city;
const state = logEntry.location.state;
const countryOrRegion = logEntry.location.countryOrRegion;
const query = `
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,
riskState,
riskLevel,
riskDetail,
source,
activity,
ipAddress,
detectedDateTime,
userId,
userDisplayName,
userPrincipalName,
additionalInfo,
city,
state,
countryOrRegion
];
await connection.query(query, values);
}
connection.release();
return results;
} catch (error) {
throw error;
}
};
const getRecentSuspicious = async () => {
try {
const connection = await db.getConnection();
const query = `
SELECT *
FROM suspiciousAccounts
WHERE insertTime >= CONVERT_TZ(NOW(), 'UTC', 'America/Denver') - INTERVAL 2 MINUTE`;
const signins = await connection.query(query);
connection.release();
return signins;
} catch (error) {
throw error;
}
};
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 = {
retrieve,
add,
checkIfNotify
};

56
src/models/Settings.js Normal file
View File

@ -0,0 +1,56 @@
const db = require('../config/db'); // Import the database connection pool
const get = async () => {
try {
const connection = await db.getConnection();
const query = 'SELECT power FROM settings';
const settings = await connection.query(query);
connection.release();
return settings;
} catch (error) {
throw error;
}
};
const flip = async () => {
try {
const connection = await db.getConnection();
const selectQuery = 'SELECT power FROM settings';
const updateQuery = 'UPDATE settings SET power = ?';
// Read the current power value from the database
const [result] = await connection.query(selectQuery);
// Check if there are any rows returned
if (result.length === 0) {
throw new Error('No rows found in the settings table.');
}
// Access the power property within the first row and convert it to a number
const currentPower = parseInt(result.power);
// Toggle the power value (0 to 1 or 1 to 0)
let newPower = currentPower === 0 ? 1 : 0;
// Update the database with the new power value
await connection.query(updateQuery, [newPower]);
connection.release();
newPower = parseInt(newPower);
// Return the updated power value
return newPower;
} catch (error) {
throw error;
}
};
module.exports = {
get,
flip
};

354
src/models/SignIn.js Normal file
View File

@ -0,0 +1,354 @@
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
const { DateTime } = require('luxon');
// Get a list of all sign ins
const all = async () => {
try {
const connection = await db.getConnection();
const query = 'SELECT * FROM signins';
const signins = await connection.query(query);
connection.release();
return signins;
} catch (error) {
throw error;
}
};
const getSuspicious = async () => {
try {
const connection = await db.getConnection();
const query = 'SELECT * FROM signins WHERE countryOrRegion <> ? AND State <> "" ORDER BY createdDateTime DESC;'; // <> 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 getRecentlySuspicious = async () => {
try {
const connection = await db.getConnection();
// Calculate the timestamp for 2 minutes ago
const twoMinutesAgo = DateTime.now().setZone('America/Edmonton').minus({ minutes: 4 }).toISO();
const query = `
SELECT *
FROM signins
WHERE countryOrRegion <> ?
AND state <> ""
AND insertTime >= ? -- Filter for records within the last 4 minutes
ORDER BY createdDateTime DESC;`;
const countryToExclude = 'CA'; // Change this if you want to exclude a different value
const signins = await connection.query(query, [countryToExclude, twoMinutesAgo]);
connection.release();
return signins;
} catch (error) {
throw error;
}
};
const toggleChecked = async (userPrincipleName) => {
try {
const connection = await db.getConnection();
// First, retrieve the current value of the 'checked' column
const selectQuery = 'SELECT checked FROM suspiciousAccounts WHERE userPrincipleName = ?';
const [rows] = await connection.query(selectQuery, userPrincipleName);
if (rows.length === 0) {
// Handle the case where the userPrincipleName is not found
connection.release();
return { success: false, message: 'User not found' };
}
// Toggle the value between '✓' and 'x'
const currentCheckedValue = rows.checked;
const newCheckedValue = currentCheckedValue === '✓' ? '✗' : '✓';
// Update the 'checked' column with the new value
const updateQuery = 'UPDATE suspiciousAccounts SET checked = ? WHERE userPrincipleName = ?';
await connection.query(updateQuery, [newCheckedValue, userPrincipleName]);
connection.release();
return { success: true, newCheckedValue };
} 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);
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();
function padNumber(number) {
return number.toString().padStart(2, '0');
}
//console.log(data.value);
for (const logEntry of data.value) { // Iterate directly if data.value is an array
const {
id,
userId,
createdDateTime,
userDisplayName,
userPrincipalName,
appDisplayName,
ipAddress,
deviceDetail,
location
} = logEntry;
const {
displayName,
operatingSystem,
browser
} = deviceDetail;
const {
city,
state,
countryOrRegion
} = location;
const errorCode = logEntry.status.errorCode;
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'), ?)
`;
const values = [
id,
userId,
createdDateTime,
ipAddress,
userDisplayName,
userPrincipalName,
appDisplayName,
displayName,
operatingSystem,
browser,
city,
state,
countryOrRegion,
errorCode
];
await connection.query(query, values);
}
connection.release();
return results;
} catch (error) {
throw error;
}
};
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();
};
//This function is used to take the data from the signIns table and create a suspicious user from it
const addSuspicious = async (data) => {
// Get all of the exceptions
const connection = await db.getConnection();
const query = `SELECT * FROM exceptionAccounts`;
const exceptions = await connection.query(query);
const currentTime = new Date();
console.log("[" + currentTime + "] " + "Adding new suspicious users to the database");
try {
const connection = await db.getConnection();
const results = []; // To store the results
for (const logEntry of data) {
const {
userId,
userDisplayName,
email,
countryOrRegion,
state,
city,
status
} = logEntry;
// Check if countryOrRegion is not empty or contains only whitespace characters
if (countryOrRegion && countryOrRegion.trim() !== '') {
// Check if the userPrincipleName and countryOrRegion match any entry in the exceptions table
const isException = exceptions.some(exception => (
exception.userPrincipleName === logEntry.email &&
exception.countryOrRegion === logEntry.countryOrRegion
));
// If it's not an exception, insert it into the suspiciousAccounts table
if (!isException) {
const query = `
INSERT IGNORE INTO suspiciousAccounts
(id, userDisplayName, userPrincipleName, countryOrRegion, state, city, insertTime, status, checked)
VALUES (?, ?, ?, ?, ?, ?, CONVERT_TZ(NOW(), 'UTC', 'America/Denver'), ?, '✗')`;
const values = [
userId,
userDisplayName,
email,
countryOrRegion,
state,
city,
status
];
await connection.query(query, values);
} else {
return;
}
}
}
connection.release();
return results;
} catch (error) {
throw error;
}
};
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
AND status = 0`;
const signins = await connection.query(query);
connection.release();
return signins;
} catch (error) {
throw error;
}
};
//Takes the recentSuspicious accounts as results
const sendNotification = 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>";
MSTeams.send("Alert: Suspicious Login Attempt Detected", "Suspicious sign-in attempt for "+ "<h2>" + result.userDisplayName + " (" + result.email + ")" + "</h2>" + " originating from " + "<h2>" + result.state + ", " + countryOrRegion + "<br></br><div></h1>" + "<div></h1>" + "<h2>Status:</h2> " + status + "</div>");
} else {
//This does not really matter anymore
status = "<h1 style='color: red'>Failure</h1>";
}
}
}
} catch (error) {
throw error;
}
};
module.exports = {
all,
add,
getSuspicious,
addSuspicious,
getSuspiciousAccounts,
getRecentSuspicious,
sendNotification,
getAttemptCount,
updateAttemptCount,
toggleChecked,
getRecentlySuspicious
};

16
src/routes/countRoutes.js Normal file
View File

@ -0,0 +1,16 @@
const express = require('express');
const router = express.Router();
const { getSignIns, getAccounts, getExceptions } = require('../controllers/countController');
// Define routes for the User resource
router.get('/accounts', getAccounts);
//Return suspicious logins
router.get('/signins', getSignIns);
//Return suspicious logins
router.get('/exceptions', getExceptions);
module.exports = router;

View File

@ -0,0 +1,11 @@
const express = require('express');
const router = express.Router();
const { addException, getAllExceptions, editException, removeException } = require('../controllers/exceptionController');
//Return suspicious logins
router.get('/', getAllExceptions);
router.post('/', addException);
router.put('/', editException);
router.delete('/:email', removeException);
module.exports = router;

View File

@ -0,0 +1,16 @@
const express = require('express');
const router = express.Router();
const { getSignIns, getSuspicious, getSuspiciousAccounts } = require('../controllers/signinController');
// Define routes for the User resource
router.get('/all', getSignIns);
//Return suspicious logins
router.get('/suspiciousSignIns', getSuspicious);
//Return suspicious logins
router.get('/suspiciousAccounts', getSuspiciousAccounts);
module.exports = router;

View File

@ -0,0 +1,12 @@
const express = require('express');
const router = express.Router();
const { get, flip } = require('../controllers/settingsController.js');
router.get('/', get);
router.post('/', flip);
module.exports = router;

View File

@ -0,0 +1,19 @@
const express = require('express');
const router = express.Router();
const { getSignIns, getSuspicious, getSuspiciousAccounts, checked } = require('../controllers/signinController');
// Define routes for the User resource
router.get('/all', getSignIns);
//Return suspicious logins
router.get('/suspiciousSignIns', getSuspicious);
//Return suspicious logins
router.get('/suspiciousAccounts', getSuspiciousAccounts);
//Return suspicious logins
router.get('/checked/:email', checked);
module.exports = router;

38
src/utilities/MSTeams.js Normal file
View File

@ -0,0 +1,38 @@
const { IncomingWebhook } = require('ms-teams-webhook');
var url = "https://mpeeng.webhook.office.com/webhookb2/698bec0f-4bb5-455a-a5c1-532b1e0b80d6@538b9b1c-23fa-4102-b36e-a4d83fc9c4c1/IncomingWebhook/5091b03cd1d2474699388433e4c889df/3745f560-1ea1-46a8-96ec-dc6772195127";
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"
//Comment this out if you are not testing
//url = testingUrl;
const webhookUrl = url;
const webhook = new IncomingWebhook(webhookUrl);
const send = async (title, description) => {
//TODO: Future Donny please use the email not the account DisplayName
const currentTime = new Date();
console.log("[" + currentTime + "] Sending Microsoft Teams notifications");
const body = description;
(async () => {
await webhook.send({
"@type": "MessageCard",
"@context": "https://schema.org/extensions",
summary: "Issue",
themeColor: "0078D7",
title: title,
sections: [
{
text: body,
},
],
});
})();
}
module.exports = {
send
};

17
src/utilities/Power.js Normal file
View File

@ -0,0 +1,17 @@
const db = require('../config/db'); // Import the database connection pool
const get = async() => {
try {
const connection = await db.getConnection();
const query = 'SELECT power FROM settings';
const settings = await connection.query(query);
connection.release();
return parseInt(settings[0].power);
} catch (error) {
throw error;
}
};
module.exports = {
get
};