First commit!

This commit is contained in:
Donavon McDowell 2023-09-28 16:51:25 -06:00
commit c34b1d44d6
15 changed files with 1587 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" ]

828
package-lock.json generated Normal file
View File

@ -0,0 +1,828 @@
{
"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",
"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/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"
}
}
}
}

20
package.json Normal file
View File

@ -0,0 +1,20 @@
{
"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",
"mariadb": "^3.2.1",
"ms-teams-webhook": "^2.0.2",
"node-fetch": "^3.3.2"
}
}

18
src/.env Normal file
View File

@ -0,0 +1,18 @@
#Database details
HOST="192.168.2.56"
USER="root"
PASSWORD="@4y@4Ab6bP"
DATABASE="logs"
DBPORT="3306"
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"

106
src/app.js Normal file
View File

@ -0,0 +1,106 @@
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 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 userRoutes
const signinRoutes = require('./routes/signinRoutes');
app.use('/logs/signin', signinRoutes);
//Get the logs from MS Graph
const retrieveLogs = async () => {
//Get the bearer token
const bearer = await Token.get();
const header = {
'Authorization': `${bearer.token_type} ${bearer.access_token}`,
'Content-type': 'application/json'
};
const baseUrl = 'https://graph.microsoft.com/v1.0';
const url = baseUrl + '/auditLogs/signIns?$top=500';
console.log(header);
//Fetch MS Graph API - data is equal to the json returned
const signIns = await Api.call(url, header);
//This add the fetched data to the signins table in mariadb
await SignIn.add(signIns);
//This will add the suspicious users to the suspicioususer table in maria db
//console.log(await SignIn.suspicious());
await SignIn.addSuspicious(await SignIn.getSuspicious());
}
const notify = async () => {
//Debugging
console.log("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.
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'.
}
}
} 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();
//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 PORT = process.env.APIPORT || 3001;
app.listen(PORT, () => {
console.log(`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,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
};

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
};

24
src/middleware/DB.js Normal file
View File

@ -0,0 +1,24 @@
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,
};

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
};

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

@ -0,0 +1,200 @@
const db = require('../config/db'); // Import the database connection pool
// 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 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");
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');
}
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 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();
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;
}
};
module.exports = {
all,
add,
getSuspicious,
addSuspicious,
getSuspiciousAccounts,
getRecentSuspicious
};

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

@ -0,0 +1,198 @@
const db = require('../config/db'); // Import the database connection pool
// 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 <> ?'; // <> 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");
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');
}
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 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();
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;
}
};
module.exports = {
all,
add,
getSuspicious,
addSuspicious,
getSuspiciousAccounts,
getRecentSuspicious
};

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('/suspiciousEvents', getSuspicious);
//Return suspicious logins
router.get('/suspiciousAccounts', getSuspiciousAccounts);
module.exports = router;

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

@ -0,0 +1,37 @@
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"
//Remove this 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
};