From be3b4f69f1a62e491bf118afc6d23a363a350b22 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 8 Apr 2023 23:50:30 -0400 Subject: [PATCH 001/229] tests_added --- package-lock.json | 69 +++++++++++++++++++---- package.json | 1 + src/__tests__/ldap_authentication.test.js | 20 +++++++ src/__tests__/ldap_connection.js | 41 -------------- src/__tests__/ldap_connection2.js | 61 -------------------- src/__tests__/ldap_integration.test.js | 34 +++++++++++ 6 files changed, 113 insertions(+), 113 deletions(-) create mode 100644 src/__tests__/ldap_authentication.test.js delete mode 100644 src/__tests__/ldap_connection.js delete mode 100644 src/__tests__/ldap_connection2.js create mode 100644 src/__tests__/ldap_integration.test.js diff --git a/package-lock.json b/package-lock.json index 649399a0..5d15daf7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@hapi/boom": "^10.0.1", + "axios": "^1.3.5", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", "cors": "^2.8.5", @@ -1558,8 +1559,17 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/axios": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz", + "integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } }, "node_modules/babel-jest": { "version": "29.5.0", @@ -2108,7 +2118,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2373,7 +2382,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -2975,11 +2983,29 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -5530,6 +5556,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -8270,8 +8301,17 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "axios": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz", + "integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } }, "babel-jest": { "version": "29.5.0", @@ -8667,7 +8707,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -8873,8 +8912,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "denque": { "version": "2.0.1", @@ -9307,11 +9345,15 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -11253,6 +11295,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index 84e338a6..f66dd832 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ ], "dependencies": { "@hapi/boom": "^10.0.1", + "axios": "^1.3.5", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", "cors": "^2.8.5", diff --git a/src/__tests__/ldap_authentication.test.js b/src/__tests__/ldap_authentication.test.js new file mode 100644 index 00000000..0ec992b3 --- /dev/null +++ b/src/__tests__/ldap_authentication.test.js @@ -0,0 +1,20 @@ +const axios = require('axios') + +const loginUrl = 'http://127.0.0.1:4000/login' + +describe('Prueba de autenticación de usuario', () => { + test('La autenticacion debe funcionar con los usuarios que estan en el LDAP y devolver un JWT', async () => { + //Peticion correcta + const loginData = { + username: 'agonzalezb', + password: '00092068426', + } + const respuesta = await axios.post(loginUrl, loginData) // Realizar una petición POST al endpoint de login + expect(respuesta.status).toBe(200) // Verificar que la respuesta tenga un estado 200 (OK) + expect(respuesta.data.data === undefined).toBeFalse // Verificar que la respuesta tenga un cuerpo (no sea nula o vacía) + + const token = respuesta.data.data.token + expect(token === undefined || token === null).toBeFalse + expect(token.length).toBeGreaterThan(0) + }) +}) diff --git a/src/__tests__/ldap_connection.js b/src/__tests__/ldap_connection.js deleted file mode 100644 index 1d0861c6..00000000 --- a/src/__tests__/ldap_connection.js +++ /dev/null @@ -1,41 +0,0 @@ -import SimpleLDAP from 'simple-ldap-search' - -const config = { - url: 'ldap://10.8.176.9', - base: 'dc=cujae,dc=edu,dc=cu', - dn: 'uid=agonzalezb,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu', - password: '00092068426', - // optionally pass tls options to ldapjs - tlsOptions: { - // tls options ... - }, -} - -// create a new client -const ldap = new SimpleLDAP(config) -ldap.bindDN() - -// setup a filter and attributes for your LDAP query -const filter = '(objectclass=iesEducationalStaff)' -const attributes = ['uid', 'displayName', 'CI'] - -// using async/await -const getUsers = async () => { - console.log('filters: ', filter) - console.log('atts: ', attributes) - ldap - .search(filter,attributes) - .then((res) => { - console.log('Response', res) - }) - .catch((err) => console.log('ERROR EN RESPONSE', err)) -} -getUsers() -// [{ -// dn: 'uid=artvandelay, dc=users, dc=localhost', -// idNumber: 1234567, -// uid: 'artvandelay', -// givenName: 'Art', -// sn: 'Vandelay', -// telephoneNumber: '555-123-4567', -// }] diff --git a/src/__tests__/ldap_connection2.js b/src/__tests__/ldap_connection2.js deleted file mode 100644 index 98aa9527..00000000 --- a/src/__tests__/ldap_connection2.js +++ /dev/null @@ -1,61 +0,0 @@ -const LDAP = require('LDAP') - -var options = { - uri: 'ldap://10.8.176.9', // string - version: 3, // integer, default is 3, - starttls: false, // boolean, default is false - connecttimeout: -1, // seconds, default is -1 (infinite timeout), connect timeout - timeout: 5000, // milliseconds, default is 5000 (infinite timeout is unsupported), operation timeout - reconnect: true, // boolean, default is true, - backoffmax: 32, // seconds, default is 32, reconnect timeout -} - -const ldap = new LDAP() - -ldap.open(options, function (err, client) { - if (err) { - console.log('Error: ' + err) - } else { - console.log('Connected') - client.bind( - 'uid=agonzalezb,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu', - '00092068426', - function (err) { - if (err) { - console.log('Error: ' + err) - } else { - console.log('Binded') - client.search( - 'dc=cujae,dc=edu,dc=cu', - { filter: '(uid=agonzalezb)' }, - function (err, res) { - if (err) { - console.log('Error: ' + err) - } else { - res.on('searchEntry', function (entry) { - console.log('entry: ' + JSON.stringify(entry.object)) - }) - res.on('searchReference', function (referral) { - console.log('referral: ' + referral.uris.join()) - }) - res.on('error', function (err) { - console.log('error: ' + err.message) - }) - res.on('end', function (result) { - console.log('status: ' + result.status) - client.unbind(function (err) { - if (err) { - console.log('Error: ' + err) - } else { - console.log('Unbinded') - } - }) - }) - } - } - ) - } - } - ) - } -}) diff --git a/src/__tests__/ldap_integration.test.js b/src/__tests__/ldap_integration.test.js new file mode 100644 index 00000000..e615655d --- /dev/null +++ b/src/__tests__/ldap_integration.test.js @@ -0,0 +1,34 @@ +const axios = require('axios') + +const loginUrl = 'http://127.0.0.1:4000/login' +const usersUrl = 'http://127.0.0.1:4000/users/' + + + +describe('Prueba de integracion con el LDAP', () => { + test('La integracion con el LDAP debe funcionar y devolver usuarios dentro del servidor', async () => { + const loginData = { + username: 'agonzalezb', + password: '00092068426', + } + const respuesta = await axios.post(loginUrl, loginData) + const token = respuesta.data.data.token + // Realizar una petición GET al endpoint de "Get User By Username" + const response = await axios.get(usersUrl + 'agonzalezb', { + headers: { + Authorization: 'Bearer ' + token, + }, + }) + //verificar que el estado de la peticion no sea 401 + expect(response.status).toEqual(200) + //verificar que la propiedad success sea true + expect(response.data.success).toEqual(true) + //verificar que el mensaje de la peticion sea "data fetched succesfully" + expect(response.data.message).toEqual('data fetched succesfully') + //verificar que el usuario solicitado sea el correcto + expect(response.data.data.objectName).toEqual( + 'uid=agonzalezb,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu' + ) + }) +}) + From 03d54d8fc0c854f09050a0d76c9f4fbef200da86 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 9 Apr 2023 17:35:01 -0400 Subject: [PATCH 002/229] server-sanitation --- server.js | 64 ++++--------------------------- src/middlewares/logger.handler.js | 31 ++++++++++++++- src/utils/ldap_initialization.js | 27 +++++++++++++ 3 files changed, 65 insertions(+), 57 deletions(-) create mode 100644 src/utils/ldap_initialization.js diff --git a/server.js b/server.js index 2de4329e..94bb629e 100644 --- a/server.js +++ b/server.js @@ -1,15 +1,14 @@ /* jshint node:true */ /* global require */ const CONFIG = require('./src/config/config.js') -const mongoose = require('mongoose') const bodyParser = require('body-parser') -const logger = require('./src/middlewares/logger.handler.js') const express = require('express') -const morgan = require('morgan') const addRoutes = require('./src/routes/routes.js') -const LdapAuth = require('./src/modules/authentication/LdapAuth.js') const sessionMiddleWare = require('./src/middlewares/session.handler.js') -const User = require('./src/schemas/user.schema.js').User +const addLoggerMiddleware = require('./src/middlewares/logger.handler.js') +const ldap_initialization = require('./src/utils/ldap_initialization.js') +const path = require('path') + //app initialization const app = express() @@ -19,63 +18,16 @@ app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: false })) app.use(express.json()) app.use(sessionMiddleWare) - -morgan.token('user', (req) => { - return req.user ? req.user.uid : 'anonymous' -}) -app.use( - morgan(function (tokens, req, res) { - const log = { - method: tokens.method(req, res), - url: tokens.url(req, res), - status: tokens.status(req, res), - content_length: tokens.res(req, res, 'content-length'), - response_time: tokens['response-time'](req, res), - user: (tokens.user = req.user.uid), - } - logger.info({ ...log }) - - return [ - `method:${log.method}`, - `url:${log.url}`, - `status:${log.status}`, - `content-lenght:${log.content_length}`, - `response-time:${log.response_time}ms`, - `user_uid:${log.user}`, - ].join(' ') - }) -) +addLoggerMiddleware(app) //LDAP initialization -const { usernameAttr, userOptions } = require('./src/constants/ldap_options.js') -LdapAuth.initialize( - userOptions, - app, - (id) => User.findOne({ username: id }).exec(), - async (user) => { - console.log(`${user[usernameAttr]} has logged in`) - let foundUser = await User.findOneAndUpdate( - { username: user[usernameAttr] }, - user, - { - upsert: true, - new: true, - } - ).exec() - console.log(`${foundUser.username} is retrieved from database`) - return foundUser - } -) +ldap_initialization(app) //add routes to application addRoutes(app) -// use the library express-passport-ldap-mongoose -// backward compatible mode -/*LdapAuth.init(CONFIG.ldap.dn, CONFIG.ldap.url, app, - (id) => User.findOne({ uid: id }).exec(), - (user) => User.findOneAndUpdate({ uid: user.uid }, user, { upsert: true, new: true }).exec() -)*/ +// Configure app to require modules from "src" directory +app.set('~', path.join(__dirname, 'src')) // serve static pages app.use(express.static('./src/public')) diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js index 0c04ffd7..c28ab996 100644 --- a/src/middlewares/logger.handler.js +++ b/src/middlewares/logger.handler.js @@ -1,6 +1,7 @@ const winston = require('winston') const { MongoDB } = require('winston-mongodb') const config = require('../config/config') +const morgan = require('morgan') const logger = winston.createLogger({ level: 'info', @@ -19,4 +20,32 @@ const logger = winston.createLogger({ ], }) -module.exports = logger +const addLoggerMiddleware = (app) => { + morgan.token('user', (req) => { + return req.user ? req.user.uid : 'anonymous' + }) + app.use( + morgan(function (tokens, req, res) { + const log = { + method: tokens.method(req, res), + url: tokens.url(req, res), + status: tokens.status(req, res), + content_length: tokens.res(req, res, 'content-length'), + response_time: tokens['response-time'](req, res), + user: (tokens.user = req.user.uid), + } + logger.info({ ...log }) + + return [ + `method:${log.method}`, + `url:${log.url}`, + `status:${log.status}`, + `content-lenght:${log.content_length}`, + `response-time:${log.response_time}ms`, + `user_uid:${log.user}`, + ].join(' ') + }) + ) +} + +module.exports = addLoggerMiddleware diff --git a/src/utils/ldap_initialization.js b/src/utils/ldap_initialization.js new file mode 100644 index 00000000..cd621bb1 --- /dev/null +++ b/src/utils/ldap_initialization.js @@ -0,0 +1,27 @@ +const LdapAuth = require('./../modules/authentication/LdapAuth') +const { usernameAttr, userOptions } = require('./../constants/ldap_options') +const User = require('../schemas/user.schema').User + +const ldap_initialization = (app) => { + //LDAP initialization + LdapAuth.initialize( + userOptions, + app, + (id) => User.findOne({ username: id }).exec(), + async (user) => { + console.log(`${user[usernameAttr]} has logged in`) + let foundUser = await User.findOneAndUpdate( + { username: user[usernameAttr] }, + user, + { + upsert: true, + new: true, + } + ).exec() + console.log(`${foundUser.username} is retrieved from database`) + return foundUser + } + ) +} + +module.exports = ldap_initialization From fdfa6133292085bc17997088928c69042e154d48 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 10 Apr 2023 14:11:17 -0400 Subject: [PATCH 003/229] pagination_and_querys_added --- server.js | 3 + src/connections/LDAP_client.js | 2 +- src/middlewares/logger.handler.js | 2 +- src/modules/authentication/LdapAuth.js | 2 +- src/routes/tree.routes.js | 30 ------- src/routes/user.routes.js | 27 ++++++- src/schemas/response.schema.js | 20 +++-- src/services/user.services.js | 108 ++++++++++++++++++++++--- src/utils/ldap_search_utils.js | 60 +++++++------- 9 files changed, 176 insertions(+), 78 deletions(-) diff --git a/server.js b/server.js index 94bb629e..b335b4d9 100644 --- a/server.js +++ b/server.js @@ -8,10 +8,13 @@ const sessionMiddleWare = require('./src/middlewares/session.handler.js') const addLoggerMiddleware = require('./src/middlewares/logger.handler.js') const ldap_initialization = require('./src/utils/ldap_initialization.js') const path = require('path') +const cors = require('cors') //app initialization const app = express() +app.use(cors()) + // load app middlewares // The order of the following middleware is very important!! app.use(bodyParser.json()) diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index 28d5caad..439f596f 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -1,11 +1,11 @@ const ldap = require('ldapjs') const config = require('../config/config') var assert = require('assert') -const { promisify } = require('util') const client = ldap.createClient({ url: [`${config.ldap.url}:${config.ldap.port}`], connectTimeout: 60000, + reconnect: true, }) client.on('connectError', (err) => { diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js index c28ab996..a778c951 100644 --- a/src/middlewares/logger.handler.js +++ b/src/middlewares/logger.handler.js @@ -32,7 +32,7 @@ const addLoggerMiddleware = (app) => { status: tokens.status(req, res), content_length: tokens.res(req, res, 'content-length'), response_time: tokens['response-time'](req, res), - user: (tokens.user = req.user.uid), + user: req.user.sub, } logger.info({ ...log }) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index c77cc97a..4f1e1db0 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -200,7 +200,7 @@ var login = function (req, res, next) { ci: user.CI, roles: ['user'], } - const token = signToken(payload, { expiresIn: '15 minutes' }) + const token = signToken(payload, { expiresIn: '45 minutes' }) const refreshToken = signToken(payload, { expiresIn: '1 day' }) req.login(user, (loginErr) => { diff --git a/src/routes/tree.routes.js b/src/routes/tree.routes.js index 880fa988..93e167c9 100644 --- a/src/routes/tree.routes.js +++ b/src/routes/tree.routes.js @@ -11,18 +11,6 @@ const service = TreeServices() const PROFESSORS_CLASS = config.ldap.objectClasses[5].name const STUDENT_CLASS = config.ldap.objectClasses[3].name -router.get('/estudiantes', checkAuth, validateResponse, (req, res) => { - service - .getAllStudents() - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) -router.get('/profesores', checkAuth, validateResponse, (req, res) => { - service - .getAllProffesors() - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) router.get('/year/:year', checkAuth, validateResponse, (req, res) => { service .getUsersByYear(req.params.year) @@ -35,23 +23,5 @@ router.get('/branch/:branch', checkAuth, validateResponse, (req, res) => { .then((data) => responseSuccess(res, 'data fetched succesfully', data)) .catch((err) => responseError(res, err.message, err.errors)) }) -//get students by year and branch -router.get('/estudiantes/', checkAuth, validateResponse, (req, res) => { - const year = req.query.year - const branch = req.query.branch - service - .getUsersByYearAndBranch(year, branch, STUDENT_CLASS) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) -//get professors by year and branch -router.get('/profesores/', checkAuth, validateResponse, (req, res) => { - const year = req.query.year - const branch = req.query.branch - service - .getUsersByYearAndBranch(year, branch, PROFESSORS_CLASS) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) module.exports = router diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index cc3e563c..c706dd30 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -6,9 +6,34 @@ const validateResponse = require('../middlewares/validateResponse') const { checkAuth } = require('../middlewares/auth.handler') const service = UserServices() +//Get all users router.get('/', checkAuth, validateResponse, (req, res) => { + const page = req.query.page || undefined + const branch = req.query.branch || undefined service - .getAll() + .getAll(page, branch) + .then((data) => responseSuccess(res, 'data fetched succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) +}) +//Get students +router.get('/students', checkAuth, validateResponse, (req, res) => { + const page = req.query.page || undefined + const branch = req.query.branch || undefined + const group = req.query.group || undefined + service + .getStudents(page, branch, group) + .then((data) => responseSuccess(res, 'data fetched succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) +}) +//Get professors +router.get('/professors', checkAuth, validateResponse, (req, res) => { + const page = req.query.page || undefined + const branch = req.query.branch || undefined + const orgRole = req.query.orgRole || undefined + const PCC = req.query.PCC || undefined + const researchGroup = req.query.researchGroup || undefined + service + .getProfessors(page, branch, orgRole, PCC, researchGroup) .then((data) => responseSuccess(res, 'data fetched succesfully', data)) .catch((err) => responseError(res, err.message, err.errors)) }) diff --git a/src/schemas/response.schema.js b/src/schemas/response.schema.js index 6f699946..7bd3f59f 100644 --- a/src/schemas/response.schema.js +++ b/src/schemas/response.schema.js @@ -1,10 +1,16 @@ const responseSuccess = (res, message, data) => { - res.status(200).json({ - success: true, - message: message, - data: data, - }) - + data.length > 1 + ? res.status(200).json({ + success: true, + message: message, + length: data.length, + data: data, + }) + : res.status(200).json({ + success: true, + message: message, + data: data, + }) return res } @@ -18,7 +24,7 @@ const responseError = (res, message, errors) => { return res } -module.exports = { +module.exports = { responseSuccess, responseError, } diff --git a/src/services/user.services.js b/src/services/user.services.js index 95b55ac6..4987e887 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -5,30 +5,116 @@ const LDAP = require('ldapjs') const config = require('../config/config') const assert = require('assert') const searchSchema = require('../utils/ldap_search_utils') - ldap.bind(config.ldap.username_bind, config.ldap.password_bind, (err) => { assert.ifError(err) }) const UserServices = () => { - const getAll = () => { - const filter = `(objectclass=*)` - return searchSchema(filter) + const getAll = (page, branch) => { + const dn = + branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` + const opts = { + filter: '(objectClass=person)', + scope: 'sub', + attributes: ['uid', 'cn', 'mail', 'ci'], + paged: page === undefined ? false : true, + pageNum: page === undefined ? undefined : parseInt(page), + sizeLimit: page === undefined ? 500 : undefined, + } + return searchSchema(dn, opts) + } + + const getStudents = (page, branch, group) => { + const dn = + branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` + const opts = { + filter: + group !== undefined + ? `(studentClassGroup=${parseInt(group)})` + : `(objectClass=iesStudent)`, + scope: 'sub', + attributes: [ + 'uid', + 'cn', + 'mail', + 'ci', + 'studentClassGroup', + 'displayName', + ], + paged: page === undefined ? false : true, + pageNum: page === undefined ? undefined : parseInt(page), + sizeLimit: page === undefined ? 500 : undefined, + } + return searchSchema(dn, opts) + } + + const getProfessors = ( + page, + branch, + orgRole = undefined, + PCC = undefined, + researchGroup = undefined + ) => { + const dn = + branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` + + var pccValue = PCC + console.log('PCC VALUE', pccValue) + + if (PCC !== undefined) { + pccValue = PCC === true ? 'TRUE' : 'FALSE' + } + const basefilter = '(objectClass=iesEducationalStaff)' + const pCCfilter = PCC !== undefined ? `(PCC=${pccValue})` : '' + const roleFilter = orgRole !== undefined ? `(orgRole=${orgRole})` : '' + const researchGroupFilter = + researchGroup !== undefined ? `(researchGroup=${researchGroup})` : '' + + const filters = `(&${basefilter}${pCCfilter}${roleFilter}${researchGroupFilter})` + + const opts = { + filter: filters, + scope: 'sub', + attributes: [ + 'uid', + 'cn', + 'mail', + 'ci', + 'displayName', + 'orgRole', + 'PCC', + 'researchGroup', + ], + paged: page === undefined ? false : true, + pageNum: page === undefined ? undefined : parseInt(page), + sizeLimit: page === undefined ? 500 : undefined, + } + return searchSchema(dn, opts) } const getByUsername = (username) => { - const filter = `(uid=${username})` - return searchSchema(filter) + const opts = { + filter: `(uid=${username})`, + scope: 'sub', + timeLimit: 60, + } + return searchSchema(config.ldap.dn, opts) } const getByCI = (ci) => { - const filter = `(ci=${ci})` - return searchSchema(filter) + const opts = { + filter: `(ci=${ci})`, + scope: 'sub', + } + return searchSchema(config.ldap.dn, opts) } const getByEmail = (email) => { - const filter = `(maildrop=${email})` - return searchSchema(filter) + const opts = { + filter: `(maildrop=${email})`, + scope: 'sub', + } + return searchSchema(config.ldap.dn, opts) } return { @@ -36,6 +122,8 @@ const UserServices = () => { getByUsername, getByCI, getByEmail, + getStudents, + getProfessors, } } diff --git a/src/utils/ldap_search_utils.js b/src/utils/ldap_search_utils.js index 69d43e4a..61b6fccd 100644 --- a/src/utils/ldap_search_utils.js +++ b/src/utils/ldap_search_utils.js @@ -2,37 +2,43 @@ const config = require('../config/config') const transformData = require('./transform_user_schema') const ldap = require('../connections/LDAP_client') -const searchSchema = (filter, customDN) => { - const dn = - customDN !== null && customDN !== undefined ? customDN : config.ldap.dn - const attributes = ['uid', 'displayName', 'CI'] - const search_options = { - scope: 'sub', - filter: filter, - attrs: attributes, - } - +const searchSchema = (dn, opt) => { const results = [] return new Promise((resolve, reject) => { - ldap.search(dn, search_options, (err, res) => { - if (err) { - return reject(new Error(`Search failed: ${err.message}`)) - } - return res - .on('searchEntry', (entry) => - results.push({ - objectName: entry.pojo.objectName, - attributes: transformData(entry), - }) - ) - .once('error', (resError) => - reject(new Error(`Search error: ${resError}`)) - ) - .on('end', (result) => { - console.log('status: ' + result.status) + ldap.search(dn, opt, (err, res) => { + res.on('searchEntry', (entry) => { + if (opt.sizeLimit !== undefined) { + if (results.length === opt.sizeLimit - 1) { + resolve(results.length === 1 ? results[0] : results) + } + } + results.push({ + objectName: entry.pojo.objectName, + attributes: transformData(entry), }) - .once('end', () => resolve(results[0])) + }) + res.on('page', (result, cb) => { + console.log('page finish') + opt.pageNum * 100 === results.length + ? resolve( + results.length === 1 + ? results[0] + : results.slice(opt.pageNum * 100 - 100, opt.pageNum * 100) + ) + : null + }) + res.on('searchReference', (referral) => { + console.log('referral: ' + referral.uris.join()) + }) + res.on('error', (resError) => + reject(new Error(`Search error: ${resError}`)) + ) + res.on('end', (result) => { + console.log('status: ' + result.status) + console.log('RESULTS', results.length) + resolve(results.length === 1 ? results[0] : results) + }) }) }) } From cd27ca534d7c3541132e23280ebd8a47c81f5787 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 10 Apr 2023 18:57:49 -0400 Subject: [PATCH 004/229] adding group routes --- src/connections/LDAP_client.js | 4 ++ src/routes/groups.routes.js | 6 +-- src/routes/routes.js | 2 + src/routes/tree.routes.js | 13 ------ src/services/group.services.js | 82 ++++++++-------------------------- 5 files changed, 27 insertions(+), 80 deletions(-) diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index 439f596f..7c8bec44 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -12,6 +12,10 @@ client.on('connectError', (err) => { assert.ifError(err) }) +client.on('connection', (stream) => { + console.log('someone connected!') +}) + client.bind(config.ldap.username_bind, config.ldap.password_bind, (err) => { assert.ifError(err) }) diff --git a/src/routes/groups.routes.js b/src/routes/groups.routes.js index e487f6b6..721d2e0e 100644 --- a/src/routes/groups.routes.js +++ b/src/routes/groups.routes.js @@ -1,7 +1,5 @@ const express = require('express') const router = express.Router() - -const { responseSuccess, responseError } = require('../schemas/response.schema') const { responseSuccess, responseError } = require('../schemas/response.schema') const validateResponse = require('../middlewares/validateResponse') const { checkAuth } = require('../middlewares/auth.handler') @@ -10,8 +8,10 @@ const GroupServices = require('../services/group.services') const service = GroupServices() router.get('/:group', checkAuth, validateResponse, (req, res) => { + const group = req.params.group + const branch = req.query.branch service - .getUserByGroup(req.params.group) + .getAll(group, branch) .then((data) => responseSuccess(res, 'data fetched succesfully', data)) .catch((err) => responseError(res, err.message, err.errors)) }) diff --git a/src/routes/routes.js b/src/routes/routes.js index 18919335..e4c8a599 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -1,11 +1,13 @@ const AuthRoutes = require('./auth.routes') const TreeRoutes = require('./tree.routes') const UserRoutes = require('./user.routes') +const GroupRoutes = require('./groups.routes') const addRoutes = (app) => { app.use('/', AuthRoutes) app.use('/tree', TreeRoutes) app.use('/users', UserRoutes) + app.use('groups', GroupRoutes) } module.exports = addRoutes diff --git a/src/routes/tree.routes.js b/src/routes/tree.routes.js index 93e167c9..2e65bfc5 100644 --- a/src/routes/tree.routes.js +++ b/src/routes/tree.routes.js @@ -11,17 +11,4 @@ const service = TreeServices() const PROFESSORS_CLASS = config.ldap.objectClasses[5].name const STUDENT_CLASS = config.ldap.objectClasses[3].name -router.get('/year/:year', checkAuth, validateResponse, (req, res) => { - service - .getUsersByYear(req.params.year) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) -router.get('/branch/:branch', checkAuth, validateResponse, (req, res) => { - service - .getUserByBranch(req.params.branch) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) - module.exports = router diff --git a/src/services/group.services.js b/src/services/group.services.js index 38cd0e87..51c930f9 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -1,76 +1,30 @@ -const boom = require('@hapi/boom') const ldap = require('../connections/LDAP_client') -const LDAP = require('ldapjs') const config = require('../config/config') const assert = require('assert') +const searchSchema = require('../utils/ldap_search_utils') -ldap.bind( - 'uid=agonzalezb,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu', - '00092068426', - (err) => { - assert.ifError(err) - } -) - -const STUDENT_CLASS = config.ldap.objectClasses[3].name +ldap.bind(config.ldap.username_bind, config.ldap.password_bind, (err) => { + assert.ifError(err) +}) const GroupServices = () => { - const searchAtt = (atts, value) => { - let values = {} - atts.map((att) => { - if (att.type === value) { - values = att.values - } - }) - return values[0] - } - - const getAll = (group) => { - const filter = `(studentClassGroup=${group})` - const attributes = ['uid', 'displayName', 'CI'] - const search_options = { + const getAll = (group, branch) => { + const page = undefined + const dn = + branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` + console.log(dn) + const opts = { + filter: `(&(objectClass=posixGroup)(cn=${group}))`, scope: 'sub', - filter: filter, - attrs: attributes, + attributes: ['cn', 'memberUid'], + paged: page === undefined ? false : true, + pageNum: page === undefined ? undefined : parseInt(page), + sizeLimit: page === undefined ? 500 : undefined, } + console.log(dn) + console.log(opts) - const results = [] - - const promise = new Promise((resolve, reject) => { - ldap.search(config.ldap.dn, search_options, (err, res) => { - if (err) { - return reject(new Error(`Search failed: ${err.message}`)) - } - return res - .on('searchEntry', (entry) => - results.push({ - objectName: entry.pojo.objectName, - attributes: { - uid: searchAtt(entry.pojo.attributes, 'uid'), - givenName: searchAtt(entry.pojo.attributes, 'givenName'), - cn: searchAtt(entry.pojo.attributes, 'cn'), - sn: searchAtt(entry.pojo.attributes, 'sn'), - ci: searchAtt(entry.pojo.attributes, 'CI'), - area: searchAtt(entry.pojo.attributes, 'area'), - displayName: searchAtt(entry.pojo.attributes, 'displayName'), - maildrop: searchAtt(entry.pojo.attributes, 'maildrop'), - email: searchAtt(entry.pojo.attributes, 'email'), - mailService: searchAtt(entry.pojo.attributes, 'mailService'), - type: searchAtt(entry.pojo.attributes, 'type'), - }, - }) - ) - .once('error', (resError) => - reject(new Error(`Search error: ${resError}`)) - ) - .on('end', (result) => { - console.log('status: ' + result.status) - }) - .once('end', () => resolve(results)) - }) - }) - - return promise + return searchSchema(config.ldap.dn, opts) } return { From 6abac7e3a5728b3aca7bc1316ac425b558009e24 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 10 Apr 2023 19:12:12 -0400 Subject: [PATCH 005/229] helmet middleware added --- package-lock.json | 14 ++++++++++++++ package.json | 1 + server.js | 3 +++ 3 files changed, 18 insertions(+) diff --git a/package-lock.json b/package-lock.json index 5d15daf7..ae3ce921 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "dotenv": "^16.0.3", "express-rate-limit": "^6.7.0", "handlebars": "^4.7.7", + "helmet": "^6.1.3", "jest-fetch-mock": "^3.0.3", "joi": "^13.0.2", "jsonwebtoken": "^9.0.0", @@ -3233,6 +3234,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/helmet": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.1.3.tgz", + "integrity": "sha512-W92WikLkFSwuQ+bvdSayleIwFu9kbYmGcOlVz5EPnrB5iP2ezAiP0+sWxndta1Nn2r4zFhxGygIU/gVKrxIrUg==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/hexoid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", @@ -9508,6 +9517,11 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, + "helmet": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.1.3.tgz", + "integrity": "sha512-W92WikLkFSwuQ+bvdSayleIwFu9kbYmGcOlVz5EPnrB5iP2ezAiP0+sWxndta1Nn2r4zFhxGygIU/gVKrxIrUg==" + }, "hexoid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", diff --git a/package.json b/package.json index f66dd832..8503f40f 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "dotenv": "^16.0.3", "express-rate-limit": "^6.7.0", "handlebars": "^4.7.7", + "helmet": "^6.1.3", "jest-fetch-mock": "^3.0.3", "joi": "^13.0.2", "jsonwebtoken": "^9.0.0", diff --git a/server.js b/server.js index b335b4d9..146dc464 100644 --- a/server.js +++ b/server.js @@ -9,11 +9,14 @@ const addLoggerMiddleware = require('./src/middlewares/logger.handler.js') const ldap_initialization = require('./src/utils/ldap_initialization.js') const path = require('path') const cors = require('cors') +const helmet = require('helmet') //app initialization const app = express() +//security middlewares app.use(cors()) +app.use(helmet()) // load app middlewares // The order of the following middleware is very important!! From efb1af845160cef67f5192ba58732e0de9766559 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 10 Apr 2023 19:24:51 -0400 Subject: [PATCH 006/229] added express-rate-limit middleware --- server.js | 12 +++++++----- src/middlewares/rate_limiter.handler.js | 8 ++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 src/middlewares/rate_limiter.handler.js diff --git a/server.js b/server.js index 146dc464..f4b2d893 100644 --- a/server.js +++ b/server.js @@ -10,23 +10,25 @@ const ldap_initialization = require('./src/utils/ldap_initialization.js') const path = require('path') const cors = require('cors') const helmet = require('helmet') +const limiter = require('./src/middlewares/rate_limiter.handler.js') //app initialization const app = express() -//security middlewares -app.use(cors()) -app.use(helmet()) - // load app middlewares // The order of the following middleware is very important!! app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: false })) app.use(express.json()) + +//security middlewares +app.use(cors()) +app.use(helmet()) +app.use(limiter) app.use(sessionMiddleWare) addLoggerMiddleware(app) -//LDAP initialization +//LDAP and passport initialization ldap_initialization(app) //add routes to application diff --git a/src/middlewares/rate_limiter.handler.js b/src/middlewares/rate_limiter.handler.js new file mode 100644 index 00000000..ff13feeb --- /dev/null +++ b/src/middlewares/rate_limiter.handler.js @@ -0,0 +1,8 @@ +const rateLimit = require('express-rate-limit') + +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // limit each IP to 100 requests per windowMs +}) + +module.exports = limiter From 174af713bf48d4bfa2a50533872196ad76c98952 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 12 Apr 2023 14:51:19 -0400 Subject: [PATCH 007/229] ldap_binding_admin --- src/config/config.js | 4 ++++ src/connections/LDAP_client.js | 6 +----- src/routes/routes.js | 4 ++-- src/services/user.services.js | 9 +++------ src/utils/ldap_search_utils.js | 14 ++++++-------- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/config/config.js b/src/config/config.js index 12019964..e78abc5d 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -20,5 +20,9 @@ module.exports = { password: process.env.LDAP_PASS, password_bind: process.env.LDAP_PASS_BIND, username_bind: process.env.LDAP_USER_BIND, + admin: { + username: process.env.ADMIN_USER, + password: process.env.ADMIN_PASS, + }, }, } diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index 7c8bec44..64397a19 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -12,11 +12,7 @@ client.on('connectError', (err) => { assert.ifError(err) }) -client.on('connection', (stream) => { - console.log('someone connected!') -}) - -client.bind(config.ldap.username_bind, config.ldap.password_bind, (err) => { +client.bind(config.ldap.admin.username, config.ldap.admin.password, (err) => { assert.ifError(err) }) diff --git a/src/routes/routes.js b/src/routes/routes.js index e4c8a599..3c785053 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -1,13 +1,13 @@ const AuthRoutes = require('./auth.routes') const TreeRoutes = require('./tree.routes') const UserRoutes = require('./user.routes') -const GroupRoutes = require('./groups.routes') +//const GroupRoutes = require('./groups.routes') const addRoutes = (app) => { app.use('/', AuthRoutes) app.use('/tree', TreeRoutes) app.use('/users', UserRoutes) - app.use('groups', GroupRoutes) + // app.use('/groups', GroupRoutes) } module.exports = addRoutes diff --git a/src/services/user.services.js b/src/services/user.services.js index 4987e887..19591e28 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -5,9 +5,6 @@ const LDAP = require('ldapjs') const config = require('../config/config') const assert = require('assert') const searchSchema = require('../utils/ldap_search_utils') -ldap.bind(config.ldap.username_bind, config.ldap.password_bind, (err) => { - assert.ifError(err) -}) const UserServices = () => { const getAll = (page, branch) => { @@ -17,10 +14,10 @@ const UserServices = () => { filter: '(objectClass=person)', scope: 'sub', attributes: ['uid', 'cn', 'mail', 'ci'], - paged: page === undefined ? false : true, - pageNum: page === undefined ? undefined : parseInt(page), - sizeLimit: page === undefined ? 500 : undefined, + paged: true, } + + console.log(opts) return searchSchema(dn, opts) } diff --git a/src/utils/ldap_search_utils.js b/src/utils/ldap_search_utils.js index 61b6fccd..dd6d7dd8 100644 --- a/src/utils/ldap_search_utils.js +++ b/src/utils/ldap_search_utils.js @@ -3,11 +3,14 @@ const transformData = require('./transform_user_schema') const ldap = require('../connections/LDAP_client') const searchSchema = (dn, opt) => { - const results = [] + let results = [] + let pageCount = 0 return new Promise((resolve, reject) => { ldap.search(dn, opt, (err, res) => { res.on('searchEntry', (entry) => { + console.log('LIMIT', opt.sizeLimit) + console.log('LENGTH', results.length) if (opt.sizeLimit !== undefined) { if (results.length === opt.sizeLimit - 1) { resolve(results.length === 1 ? results[0] : results) @@ -20,13 +23,8 @@ const searchSchema = (dn, opt) => { }) res.on('page', (result, cb) => { console.log('page finish') - opt.pageNum * 100 === results.length - ? resolve( - results.length === 1 - ? results[0] - : results.slice(opt.pageNum * 100 - 100, opt.pageNum * 100) - ) - : null + pageCount = pageCount + 1 + resolve(results.length === 1 ? results[0] : results) }) res.on('searchReference', (referral) => { console.log('referral: ' + referral.uris.join()) From 84c93345cec0970a9abbb57e4492ba1694f87682 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 12 Apr 2023 15:02:48 -0400 Subject: [PATCH 008/229] change_login_response_to_incorrect_credentials --- src/modules/authentication/LdapAuth.js | 6 +++--- src/utils/ldap_search_utils.js | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 4f1e1db0..b38a5608 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -7,8 +7,6 @@ const CustomStrategy = require('passport-custom').Strategy const JwtStrategy = require('../../utils/authentication/strategies/jwtStrategy') const { authenticate } = require('ldap-authentication') const TreeServices = require('../../services/user.services') -const logger = require('../../middlewares/logger.handler') -const morgan = require('morgan') const { signToken } = require('../../utils/authentication/tokens/token_sign') const { @@ -74,6 +72,9 @@ var init = function ( let username = req.body.username let password = req.body.password let response = await service.getByUsername(username) + if (response.attributes === undefined) { + throw new Error('username or password incorrect') + } const branch = response.objectName .toString() .split(',')[2] @@ -196,7 +197,6 @@ var login = function (req, res, next) { lastname: user.sn, fullname: user.cn, email: user.mail, - password: user.userPassword, ci: user.CI, roles: ['user'], } diff --git a/src/utils/ldap_search_utils.js b/src/utils/ldap_search_utils.js index dd6d7dd8..a7b2ca1c 100644 --- a/src/utils/ldap_search_utils.js +++ b/src/utils/ldap_search_utils.js @@ -9,8 +9,6 @@ const searchSchema = (dn, opt) => { return new Promise((resolve, reject) => { ldap.search(dn, opt, (err, res) => { res.on('searchEntry', (entry) => { - console.log('LIMIT', opt.sizeLimit) - console.log('LENGTH', results.length) if (opt.sizeLimit !== undefined) { if (results.length === opt.sizeLimit - 1) { resolve(results.length === 1 ? results[0] : results) From 7ad4783fd82688285ceef08d3b3d318d87ea0be8 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 12 Apr 2023 16:17:22 -0400 Subject: [PATCH 009/229] adding_roles_on_jwt --- src/modules/authentication/LdapAuth.js | 23 ++++++++++--- .../{groups.routes.js => group.routes.js} | 16 +++++++--- src/routes/routes.js | 4 +-- src/services/group.services.js | 32 +++++++++---------- src/utils/ldap_search_utils.js | 1 + src/utils/transform_user_schema.js | 6 ++-- 6 files changed, 50 insertions(+), 32 deletions(-) rename src/routes/{groups.routes.js => group.routes.js} (60%) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index b38a5608..1c5ef92e 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -6,7 +6,11 @@ const passport = require('passport') const CustomStrategy = require('passport-custom').Strategy const JwtStrategy = require('../../utils/authentication/strategies/jwtStrategy') const { authenticate } = require('ldap-authentication') -const TreeServices = require('../../services/user.services') +const UserServices = require('../../services/user.services') +const GroupServices = require('../../services/group.services') + +//helpers +const get_dn_from_user = require('../../helpers/get_dn_from_user') const { signToken } = require('../../utils/authentication/tokens/token_sign') const { @@ -15,7 +19,8 @@ const { } = require('../../schemas/response.schema') const validateResponse = require('../../middlewares/validateResponse') -const service = TreeServices() +const userService = UserServices() +const groupService = GroupServices() var _backwardCompatible = false var _dn @@ -71,7 +76,8 @@ var init = function ( } let username = req.body.username let password = req.body.password - let response = await service.getByUsername(username) + let response = await userService.getByUsername(username) + // if user doesn't exists if (response.attributes === undefined) { throw new Error('username or password incorrect') } @@ -182,7 +188,7 @@ var initialize = function ( * on successful authenticate, or {success: false} on failed authenticate */ var login = function (req, res, next) { - passport.authenticate('ldap', (err, user) => { + passport.authenticate('ldap', async (err, user) => { if (err) { res.status(401).json({ success: false, message: err.message }) return @@ -190,6 +196,13 @@ var login = function (req, res, next) { if (!user) { res.status(401).json({ success: false, message: 'User cannot be found' }) } else { + console.log('USER', user) + const userUID = user.uid + const userDN = user.member + const branch = userDN.split(',')[1].replace('ou=', '') + const response = await groupService.getAdminsGroups(branch) + const isAdmin = response.attributes.memberUid.includes(userUID) + const payload = { sub: user.uid, dn: user.dn, @@ -198,7 +211,7 @@ var login = function (req, res, next) { fullname: user.cn, email: user.mail, ci: user.CI, - roles: ['user'], + roles: isAdmin ? ['admin', 'user'] : ['user'], } const token = signToken(payload, { expiresIn: '45 minutes' }) const refreshToken = signToken(payload, { expiresIn: '1 day' }) diff --git a/src/routes/groups.routes.js b/src/routes/group.routes.js similarity index 60% rename from src/routes/groups.routes.js rename to src/routes/group.routes.js index 721d2e0e..3dd8625e 100644 --- a/src/routes/groups.routes.js +++ b/src/routes/group.routes.js @@ -1,17 +1,23 @@ const express = require('express') const router = express.Router() +const GroupServices = require('../services/group.services') const { responseSuccess, responseError } = require('../schemas/response.schema') const validateResponse = require('../middlewares/validateResponse') const { checkAuth } = require('../middlewares/auth.handler') - -const GroupServices = require('../services/group.services') const service = GroupServices() -router.get('/:group', checkAuth, validateResponse, (req, res) => { - const group = req.params.group +//Get all users +router.get('/', checkAuth, validateResponse, (req, res) => { + const branch = req.query.branch + service + .getAll(branch) + .then((data) => responseSuccess(res, 'data fetched succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) +}) +router.get('/admins', checkAuth, validateResponse, (req, res) => { const branch = req.query.branch service - .getAll(group, branch) + .getAdminsGroups(branch) .then((data) => responseSuccess(res, 'data fetched succesfully', data)) .catch((err) => responseError(res, err.message, err.errors)) }) diff --git a/src/routes/routes.js b/src/routes/routes.js index 3c785053..14d1abb3 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -1,13 +1,13 @@ const AuthRoutes = require('./auth.routes') const TreeRoutes = require('./tree.routes') const UserRoutes = require('./user.routes') -//const GroupRoutes = require('./groups.routes') +const GroupRoutes = require('./group.routes') const addRoutes = (app) => { app.use('/', AuthRoutes) app.use('/tree', TreeRoutes) app.use('/users', UserRoutes) - // app.use('/groups', GroupRoutes) + app.use('/groups', GroupRoutes) } module.exports = addRoutes diff --git a/src/services/group.services.js b/src/services/group.services.js index 51c930f9..d57afd38 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -1,34 +1,34 @@ +const boom = require('@hapi/boom') +require('dotenv').config({ path: __dirname + '/../../.env' }) const ldap = require('../connections/LDAP_client') +const LDAP = require('ldapjs') const config = require('../config/config') const assert = require('assert') const searchSchema = require('../utils/ldap_search_utils') -ldap.bind(config.ldap.username_bind, config.ldap.password_bind, (err) => { - assert.ifError(err) -}) - const GroupServices = () => { - const getAll = (group, branch) => { - const page = undefined + const getAll = (branch) => { const dn = branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` - console.log(dn) const opts = { - filter: `(&(objectClass=posixGroup)(cn=${group}))`, + filter: `(objectClass=posixGroup)`, scope: 'sub', - attributes: ['cn', 'memberUid'], - paged: page === undefined ? false : true, - pageNum: page === undefined ? undefined : parseInt(page), - sizeLimit: page === undefined ? 500 : undefined, } - console.log(dn) - console.log(opts) - - return searchSchema(config.ldap.dn, opts) + return searchSchema(dn, opts) } + const getAdminsGroups = (branch) => { + const dn = + branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` + const opts = { + filter: `(cn=admins)`, + scope: 'sub', + } + return searchSchema(dn, opts) + } return { getAll, + getAdminsGroups, } } diff --git a/src/utils/ldap_search_utils.js b/src/utils/ldap_search_utils.js index a7b2ca1c..b176c423 100644 --- a/src/utils/ldap_search_utils.js +++ b/src/utils/ldap_search_utils.js @@ -9,6 +9,7 @@ const searchSchema = (dn, opt) => { return new Promise((resolve, reject) => { ldap.search(dn, opt, (err, res) => { res.on('searchEntry', (entry) => { + console.log("ENTRO") if (opt.sizeLimit !== undefined) { if (results.length === opt.sizeLimit - 1) { resolve(results.length === 1 ? results[0] : results) diff --git a/src/utils/transform_user_schema.js b/src/utils/transform_user_schema.js index 414e2774..30a58943 100644 --- a/src/utils/transform_user_schema.js +++ b/src/utils/transform_user_schema.js @@ -7,10 +7,8 @@ const getObject = (arr) => { } const transform = (entry) => { - const data = { - objectName: entry.pojo.objectName, - attributes: getObject(entry.pojo.attributes), - } + const data = getObject(entry.pojo.attributes) + return data } From e461eb81e2bbdc4a83b431746eb72ae34c2c547e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 12 Apr 2023 16:26:07 -0400 Subject: [PATCH 010/229] roles_middleware_added --- src/middlewares/auth.handler.js | 25 +++++++++++++++++++++++++ src/modules/authentication/LdapAuth.js | 3 --- src/routes/user.routes.js | 4 ++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/middlewares/auth.handler.js b/src/middlewares/auth.handler.js index 99b7e870..a0d73335 100644 --- a/src/middlewares/auth.handler.js +++ b/src/middlewares/auth.handler.js @@ -1,12 +1,37 @@ const passport = require('passport') const { verifyToken } = require('../utils/authentication/tokens/token_verify') const boom = require('@hapi/boom') +const { responseError } = require('../schemas/response.schema') const checkAuth = (req, res, next) => { const auth = passport.authenticate('jwt', { session: false }) auth(req, res, next) } +const checkRoles = (...roles) => { + return (req, res, next) => { + const payload = verifyToken(req.headers.authorization.split(' ')[1]) + if (!payload) { + const error = boom.unauthorized('Token is not valid') + next(error) + } + const { roles: userRoles } = payload + const hasRole = roles.some((role) => userRoles.includes(role)) + if (hasRole) { + next() + } else { + responseError( + res, + `You don't have permission to access`, + boom.unauthorized( + 'Invalid token, you are not authorized to perform this action' + ) + ) + } + } +} + module.exports = { checkAuth, + checkRoles, } diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 1c5ef92e..4ea90ae6 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -9,9 +9,6 @@ const { authenticate } = require('ldap-authentication') const UserServices = require('../../services/user.services') const GroupServices = require('../../services/group.services') -//helpers -const get_dn_from_user = require('../../helpers/get_dn_from_user') - const { signToken } = require('../../utils/authentication/tokens/token_sign') const { responseSuccess, diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index c706dd30..abe39fbd 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -3,7 +3,7 @@ const router = express.Router() const UserServices = require('../services/user.services') const { responseSuccess, responseError } = require('../schemas/response.schema') const validateResponse = require('../middlewares/validateResponse') -const { checkAuth } = require('../middlewares/auth.handler') +const { checkAuth, checkRoles } = require('../middlewares/auth.handler') const service = UserServices() //Get all users @@ -37,7 +37,7 @@ router.get('/professors', checkAuth, validateResponse, (req, res) => { .then((data) => responseSuccess(res, 'data fetched succesfully', data)) .catch((err) => responseError(res, err.message, err.errors)) }) -router.get('/:username', checkAuth, validateResponse, (req, res) => { +router.get('/:username', checkAuth,checkRoles("admin"), validateResponse, (req, res) => { service .getByUsername(req.params.username) .then((data) => responseSuccess(res, 'data fetched succesfully', data)) From 2c4e7feb7b347523c053a64b5b47428f30b99233 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 25 Apr 2023 19:18:28 -0400 Subject: [PATCH 011/229] added pagination and students filters --- package-lock.json | 53 +++++++++ package.json | 1 + server.js | 4 + src/config/config.js | 1 + src/modules/authentication/LdapAuth.js | 3 +- src/routes/user.routes.js | 150 +++++++++++++++++-------- src/schemas/response.schema.js | 20 ++-- src/services/user.services.js | 29 +++-- src/utils/ldap_search_utils.js | 12 +- src/utils/paginateResults.js | 25 +++++ 10 files changed, 220 insertions(+), 78 deletions(-) create mode 100644 src/utils/paginateResults.js diff --git a/package-lock.json b/package-lock.json index ae3ce921..17146128 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "body-parser": "^1.20.2", "cors": "^2.8.5", "dotenv": "^16.0.3", + "express-paginate": "^1.0.2", "express-rate-limit": "^6.7.0", "handlebars": "^4.7.7", "helmet": "^6.1.3", @@ -2782,6 +2783,17 @@ "node": ">= 0.10.0" } }, + "node_modules/express-paginate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/express-paginate/-/express-paginate-1.0.2.tgz", + "integrity": "sha512-z0VTaLrsMe4PJFifjJCC4Q11cwrveSOejicYOgFi6RzqUMPd8kIlK95x/xq6g6k6urCI2Fd3gadj3AZ9AGqguw==", + "dependencies": { + "lodash.assign": "^4.2.0", + "lodash.clone": "^4.5.0", + "lodash.isobject": "^3.0.2", + "qs": "^6.5.1" + } + }, "node_modules/express-rate-limit": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz", @@ -4620,6 +4632,21 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha512-6X37Sq9KCpLSXEh8uM12AKYlviHPNNk4RxiGBn4cmKGJinbXBneWIV7iE/nXkM928O7ytHcHb6+X6Svl0f4hXg==" }, + "node_modules/lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==" + }, + "node_modules/lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==" + }, + "node_modules/lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" + }, "node_modules/logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -9243,6 +9270,17 @@ } } }, + "express-paginate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/express-paginate/-/express-paginate-1.0.2.tgz", + "integrity": "sha512-z0VTaLrsMe4PJFifjJCC4Q11cwrveSOejicYOgFi6RzqUMPd8kIlK95x/xq6g6k6urCI2Fd3gadj3AZ9AGqguw==", + "requires": { + "lodash.assign": "^4.2.0", + "lodash.clone": "^4.5.0", + "lodash.isobject": "^3.0.2", + "qs": "^6.5.1" + } + }, "express-rate-limit": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz", @@ -10583,6 +10621,21 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha512-6X37Sq9KCpLSXEh8uM12AKYlviHPNNk4RxiGBn4cmKGJinbXBneWIV7iE/nXkM928O7ytHcHb6+X6Svl0f4hXg==" }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==" + }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==" + }, + "lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" + }, "logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", diff --git a/package.json b/package.json index 8503f40f..597b4ceb 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "body-parser": "^1.20.2", "cors": "^2.8.5", "dotenv": "^16.0.3", + "express-paginate": "^1.0.2", "express-rate-limit": "^6.7.0", "handlebars": "^4.7.7", "helmet": "^6.1.3", diff --git a/server.js b/server.js index f4b2d893..5d0f0c2f 100644 --- a/server.js +++ b/server.js @@ -11,6 +11,7 @@ const path = require('path') const cors = require('cors') const helmet = require('helmet') const limiter = require('./src/middlewares/rate_limiter.handler.js') +const paginate = require('express-paginate') //app initialization const app = express() @@ -31,6 +32,9 @@ addLoggerMiddleware(app) //LDAP and passport initialization ldap_initialization(app) +//pagination middleware +app.use(paginate.middleware(100, CONFIG.ldap.sizeLimit)) + //add routes to application addRoutes(app) diff --git a/src/config/config.js b/src/config/config.js index e78abc5d..5f4368e7 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -24,5 +24,6 @@ module.exports = { username: process.env.ADMIN_USER, password: process.env.ADMIN_PASS, }, + sizeLimit: parseInt(process.env.LDAP_SIZE_LIMIT), }, } diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 4ea90ae6..594fea94 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -192,8 +192,7 @@ var login = function (req, res, next) { } if (!user) { res.status(401).json({ success: false, message: 'User cannot be found' }) - } else { - console.log('USER', user) + } else { const userUID = user.uid const userDN = user.member const branch = userDN.split(',')[1].replace('ou=', '') diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index abe39fbd..f6bc5f42 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -5,57 +5,111 @@ const { responseSuccess, responseError } = require('../schemas/response.schema') const validateResponse = require('../middlewares/validateResponse') const { checkAuth, checkRoles } = require('../middlewares/auth.handler') const service = UserServices() +const paginateResults = require('../utils/paginateResults') //Get all users -router.get('/', checkAuth, validateResponse, (req, res) => { - const page = req.query.page || undefined - const branch = req.query.branch || undefined - service - .getAll(page, branch) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) +router.get( + '/', + checkAuth, + checkRoles('admin'), + validateResponse, + (req, res) => { + const branch = req.query.branch || undefined + service + .getAll(branch) + .then((data) => + responseSuccess( + res, + 'data fetched succesfully', + paginateResults(data, req) + ) + ) + .catch((err) => responseError(res, err.message, err.errors)) + } +) //Get students -router.get('/students', checkAuth, validateResponse, (req, res) => { - const page = req.query.page || undefined - const branch = req.query.branch || undefined - const group = req.query.group || undefined - service - .getStudents(page, branch, group) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) +router.get( + '/students', + checkAuth, + checkRoles('admin'), + validateResponse, + (req, res) => { + const branch = req.query.branch || undefined + const group = req.query.group || undefined + const year = req.query.year || undefined + service + .getStudents(branch, group, year) + .then((data) => + responseSuccess( + res, + 'data fetched succesfully', + paginateResults(data, req) + ) + ) + .catch((err) => responseError(res, err.message, err.errors)) + } +) //Get professors -router.get('/professors', checkAuth, validateResponse, (req, res) => { - const page = req.query.page || undefined - const branch = req.query.branch || undefined - const orgRole = req.query.orgRole || undefined - const PCC = req.query.PCC || undefined - const researchGroup = req.query.researchGroup || undefined - service - .getProfessors(page, branch, orgRole, PCC, researchGroup) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) -router.get('/:username', checkAuth,checkRoles("admin"), validateResponse, (req, res) => { - service - .getByUsername(req.params.username) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => { - responseError(res, err.message, err.errors) - }) -}) -router.get('/ci/:ci', checkAuth, validateResponse, (req, res) => { - service - .getByCI(req.params.ci) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) -router.get('/email/:email', checkAuth, validateResponse, (req, res) => { - service - .getByEmail(req.params.email) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) +router.get( + '/professors', + checkAuth, + checkRoles('admin'), + validateResponse, + (req, res) => { + const page = req.query.page || undefined + const branch = req.query.branch || undefined + const orgRole = req.query.orgRole || undefined + const PCC = req.query.PCC || undefined + const researchGroup = req.query.researchGroup || undefined + service + .getProfessors(page, branch, orgRole, PCC, researchGroup) + .then((data) => responseSuccess(res, 'data fetched succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) + } +) +router.get( + '/:username', + checkAuth, + checkRoles('admin'), + validateResponse, + (req, res) => { + service + .getByUsername(req.params.username) + .then((data) => + responseSuccess( + res, + 'data fetched succesfully', + paginateResults(data, req) + ) + ) + .catch((err) => { + responseError(res, err.message, err.errors) + }) + } +) +router.get( + '/ci/:ci', + checkAuth, + checkRoles('admin'), + validateResponse, + (req, res) => { + service + .getByCI(req.params.ci) + .then((data) => responseSuccess(res, 'data fetched succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) + } +) +router.get( + '/email/:email', + checkAuth, + checkRoles('admin'), + validateResponse, + (req, res) => { + service + .getByEmail(req.params.email) + .then((data) => responseSuccess(res, 'data fetched succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) + } +) module.exports = router diff --git a/src/schemas/response.schema.js b/src/schemas/response.schema.js index 7bd3f59f..589a6f56 100644 --- a/src/schemas/response.schema.js +++ b/src/schemas/response.schema.js @@ -1,16 +1,12 @@ const responseSuccess = (res, message, data) => { - data.length > 1 - ? res.status(200).json({ - success: true, - message: message, - length: data.length, - data: data, - }) - : res.status(200).json({ - success: true, - message: message, - data: data, - }) + const isPaginated = data.results !== undefined + res.status(200).json({ + success: true, + message: message, + length: isPaginated ? data.results.length : data.length, + data: data, + }) + return res } diff --git a/src/services/user.services.js b/src/services/user.services.js index 19591e28..3edbbe44 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -7,28 +7,36 @@ const assert = require('assert') const searchSchema = require('../utils/ldap_search_utils') const UserServices = () => { - const getAll = (page, branch) => { + const getAll = (branch) => { const dn = branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` const opts = { filter: '(objectClass=person)', scope: 'sub', attributes: ['uid', 'cn', 'mail', 'ci'], - paged: true, + sizeLimit: config.ldap.sizeLimit, } - console.log(opts) + return searchSchema(dn, opts) } - const getStudents = (page, branch, group) => { + const getStudents = (branch, group, year) => { + const getfilter = () => { + let filter = '' + if (group !== undefined) { + filter = `(studentClassGroup=${parseInt(group)})` + } else if (year !== undefined) { + filter = `(studentClassGroup=${year}*)` + } else { + filter = `(objectClass=iesStudent)` + } + return filter + } const dn = branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` const opts = { - filter: - group !== undefined - ? `(studentClassGroup=${parseInt(group)})` - : `(objectClass=iesStudent)`, + filter: getfilter(), scope: 'sub', attributes: [ 'uid', @@ -38,9 +46,7 @@ const UserServices = () => { 'studentClassGroup', 'displayName', ], - paged: page === undefined ? false : true, - pageNum: page === undefined ? undefined : parseInt(page), - sizeLimit: page === undefined ? 500 : undefined, + sizeLimit: config.ldap.sizeLimit, } return searchSchema(dn, opts) } @@ -56,7 +62,6 @@ const UserServices = () => { branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` var pccValue = PCC - console.log('PCC VALUE', pccValue) if (PCC !== undefined) { pccValue = PCC === true ? 'TRUE' : 'FALSE' diff --git a/src/utils/ldap_search_utils.js b/src/utils/ldap_search_utils.js index b176c423..d7dffc46 100644 --- a/src/utils/ldap_search_utils.js +++ b/src/utils/ldap_search_utils.js @@ -5,11 +5,11 @@ const ldap = require('../connections/LDAP_client') const searchSchema = (dn, opt) => { let results = [] let pageCount = 0 + let pagedEntries = [] return new Promise((resolve, reject) => { ldap.search(dn, opt, (err, res) => { res.on('searchEntry', (entry) => { - console.log("ENTRO") if (opt.sizeLimit !== undefined) { if (results.length === opt.sizeLimit - 1) { resolve(results.length === 1 ? results[0] : results) @@ -19,11 +19,17 @@ const searchSchema = (dn, opt) => { objectName: entry.pojo.objectName, attributes: transformData(entry), }) + pagedEntries.push({ + objectName: entry.pojo.objectName, + attributes: transformData(entry), + }) }) res.on('page', (result, cb) => { console.log('page finish') pageCount = pageCount + 1 - resolve(results.length === 1 ? results[0] : results) + pageCount === opt.pageNum + ? resolve(pagedEntries.length === 1 ? pagedEntries[0] : pagedEntries) + : (pagedEntries = []) }) res.on('searchReference', (referral) => { console.log('referral: ' + referral.uris.join()) @@ -32,8 +38,6 @@ const searchSchema = (dn, opt) => { reject(new Error(`Search error: ${resError}`)) ) res.on('end', (result) => { - console.log('status: ' + result.status) - console.log('RESULTS', results.length) resolve(results.length === 1 ? results[0] : results) }) }) diff --git a/src/utils/paginateResults.js b/src/utils/paginateResults.js new file mode 100644 index 00000000..852bc055 --- /dev/null +++ b/src/utils/paginateResults.js @@ -0,0 +1,25 @@ +function paginateResults(array, req) { + const page = parseInt(req.query.page) || 1 + const limit = parseInt(req.query.limit) || 10 + const startIndex = (page - 1) * limit + const endIndex = page * limit + const results = {} + + results.results = array.slice(startIndex, endIndex) + + if (endIndex < array.length) { + results.next = { + page: page + 1, + limit: limit, + } + } + if (startIndex > 0) { + results.previous = { + page: page - 1, + limit: limit, + } + } + return results +} + +module.exports = paginateResults From 722bf9d9703857b59661dd7550751d77595d3585 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 25 Apr 2023 20:29:32 -0400 Subject: [PATCH 012/229] add user endpoint --- src/routes/user.routes.js | 25 +++++++++++++++++++ src/schemas/newUser.schema.js | 15 ++++++++++++ src/services/user.services.js | 46 +++++++++++++++++++++++++++++++++-- 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 src/schemas/newUser.schema.js diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index f6bc5f42..52fededc 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -6,6 +6,7 @@ const validateResponse = require('../middlewares/validateResponse') const { checkAuth, checkRoles } = require('../middlewares/auth.handler') const service = UserServices() const paginateResults = require('../utils/paginateResults') +const newUserSchema = require('../schemas/newUser.schema') //Get all users router.get( @@ -112,4 +113,28 @@ router.get( } ) +//Add Users +router.post( + '/', + checkAuth, + checkRoles('admin'), + validateResponse, + (req, res) => { + const user = req.body + const branch = req.query.branch || undefined + if (!branch) { + responseError(res, 'branch is required') + } + newUserSchema + .validate(user) + .then(() => { + service + .addNewUser(user, branch) + .then((data) => responseSuccess(res, 'user added succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) + }) + .catch((err) => responseError(res, err.message, err.errors)) + } +) + module.exports = router diff --git a/src/schemas/newUser.schema.js b/src/schemas/newUser.schema.js new file mode 100644 index 00000000..3d230a52 --- /dev/null +++ b/src/schemas/newUser.schema.js @@ -0,0 +1,15 @@ +const joi = require('joi') + +const newUserSchema = joi.object({ + uid: joi.string(), + cn: joi.string(), + sn: joi.string(), + email: joi.string(), + ci: joi.string(), + studentClassGroup: joi.string(), + displayName: joi.string(), + password: joi.string(), + confirmPassword: joi.string(), +}) + +module.exports = newUserSchema diff --git a/src/services/user.services.js b/src/services/user.services.js index 3edbbe44..3aa2f1b3 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -1,10 +1,10 @@ const boom = require('@hapi/boom') require('dotenv').config({ path: __dirname + '/../../.env' }) -const ldap = require('../connections/LDAP_client') -const LDAP = require('ldapjs') const config = require('../config/config') const assert = require('assert') const searchSchema = require('../utils/ldap_search_utils') +const ldapClient = require('../connections/LDAP_client') +var bytes = new Uint8Array(1024) const UserServices = () => { const getAll = (branch) => { @@ -119,6 +119,47 @@ const UserServices = () => { return searchSchema(config.ldap.dn, opts) } + const addNewUser = (user, branch) => { + const dn = `uid=${user.uid},ou=usuarios,ou=${branch},${config.ldap.dn}` + + const entry = { + ...user, + objectclass: [ + 'top', + 'person', + 'posixAccount', + 'iesServices', + 'sambaSamAccount', + 'radiusprofile', + 'CourierMailAlias', + ], + homeDirectory: `/home/${user.uid}`, + gidNumber: [1000], + sambaSID: ['S-1-5-21-1255719363-1350762778-3568053751-513'], + sambaLMPassword: [new TextEncoder().encode('sambaLMPassword')], + sambaNTPassword: [new TextEncoder().encode('sambaNTPassword')], + loginShell: [new TextEncoder().encode('/bin/nosh')], + sambaacctflags: [new TextEncoder().encode('[U ]')], + sambapasswordhistory: [ + new TextEncoder().encode( + '000000000000000000000000000000000000000000000000000000 0000000000' + ), + ], + sambaprimarygroupsid: ['S-1-5-21-1255719363-1350762778-3568053751-513'], + sambapwdlastset: [new TextEncoder().encode('1308584948')], + } + console.log(branch) + return new Promise((resolve, reject) => { + ldapClient.add(dn, entry, (err) => { + if (err) { + console.log("ERROR",err) + reject(boom.badImplementation(err)) + } + resolve({ message: 'User added successfully' }) + }) + }) + } + return { getAll, getByUsername, @@ -126,6 +167,7 @@ const UserServices = () => { getByEmail, getStudents, getProfessors, + addNewUser, } } From e3174c76500c1c97b975b9dec3d51ade75a21ae7 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 26 Apr 2023 02:05:29 -0400 Subject: [PATCH 013/229] updating profile --- src/routes/profile.routes.js | 35 ++++++++++++++++++ src/routes/routes.js | 2 ++ src/schemas/response.schema.js | 21 ++++++----- src/services/profile.services.js | 52 +++++++++++++++++++++++++++ src/services/user.services.js | 14 ++++++-- src/utils/ldap_search_utils.js | 1 + src/utils/searchSchemaWithNoFormat.js | 47 ++++++++++++++++++++++++ 7 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 src/routes/profile.routes.js create mode 100644 src/utils/searchSchemaWithNoFormat.js diff --git a/src/routes/profile.routes.js b/src/routes/profile.routes.js new file mode 100644 index 00000000..2e49e352 --- /dev/null +++ b/src/routes/profile.routes.js @@ -0,0 +1,35 @@ +const express = require('express') +const router = express.Router() +const ProfileServices = require('../services/profile.services') +const { responseSuccess, responseError } = require('../schemas/response.schema') +const validateResponse = require('../middlewares/validateResponse') +const { checkAuth, checkRoles } = require('../middlewares/auth.handler') +const service = ProfileServices() + +//Get all users +router.get('/', checkAuth, checkRoles('user'), validateResponse, (req, res) => { + service + .getProfile(req) + .then((data) => responseSuccess(res, 'data fetched succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) +}) + +router.put('/', checkAuth, checkRoles('user'), validateResponse, (req, res) => { + const { email, password, confirmPassword } = req.body + console.log({ email, password, confirmPassword }) + if (!email && !password) { + console.log('entro') + responseError(res, 'fields cannot be empty', null) + } else if (password) { + if (password !== confirmPassword) { + responseError(res, 'passwords must be the same') + } + } else { + service + .updateProfile(email, password, req) + .then((data) => responseSuccess(res, 'data fetched succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) + } +}) + +module.exports = router diff --git a/src/routes/routes.js b/src/routes/routes.js index 14d1abb3..7770e2e3 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -2,12 +2,14 @@ const AuthRoutes = require('./auth.routes') const TreeRoutes = require('./tree.routes') const UserRoutes = require('./user.routes') const GroupRoutes = require('./group.routes') +const ProfileRoutes = require('./profile.routes') const addRoutes = (app) => { app.use('/', AuthRoutes) app.use('/tree', TreeRoutes) app.use('/users', UserRoutes) app.use('/groups', GroupRoutes) + app.use('/profile', ProfileRoutes) } module.exports = addRoutes diff --git a/src/schemas/response.schema.js b/src/schemas/response.schema.js index 589a6f56..71326ce1 100644 --- a/src/schemas/response.schema.js +++ b/src/schemas/response.schema.js @@ -1,12 +1,17 @@ const responseSuccess = (res, message, data) => { - const isPaginated = data.results !== undefined - res.status(200).json({ - success: true, - message: message, - length: isPaginated ? data.results.length : data.length, - data: data, - }) - + console.log(data) + data.length > 1 + ? res.status(200).json({ + success: true, + message: message, + length: data?.results.length ?? data.length, + data: data, + }) + : res.status(200).json({ + success: true, + message: message, + data: data, + }) return res } diff --git a/src/services/profile.services.js b/src/services/profile.services.js index e69de29b..47e38db4 100644 --- a/src/services/profile.services.js +++ b/src/services/profile.services.js @@ -0,0 +1,52 @@ +const boom = require('@hapi/boom') +require('dotenv').config({ path: __dirname + '/../../.env' }) +const ldapClient = require('../connections/LDAP_client') +const UserServices = require('./user.services') +const service = UserServices() +const { verifyToken } = require('../utils/authentication/tokens/token_verify') +const ldap = require('ldapjs') + +const ProfileServices = () => { + const getProfile = (req) => { + const token = req.headers.authorization.split(' ')[1] + const payload = verifyToken(token) + const { sub } = payload + return service.getByUsername(sub) + } + + const updateProfile = async (email, password, req) => { + const token = req.headers.authorization.split(' ')[1] + const payload = verifyToken(token) + const { dn, sub } = payload + + const user = await service.getByUsername(sub) + if (user.dn !== dn) { + return boom.unauthorized('Unauthorized') + } else { + const change = new ldap.Change({ + operation: 'replace', + modification: new ldap.Attribute( + { + type: 'maildrop', + value: [`=${email}`], + }, + { + type: 'password', + value: [`=${password}`], + } + ), + }) + + return ldapClient.modify(dn, change, (err) => { + boom.badImplementation(err) + }) + } + } + + return { + getProfile, + updateProfile, + } +} + +module.exports = ProfileServices diff --git a/src/services/user.services.js b/src/services/user.services.js index 3aa2f1b3..34976fe8 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -3,8 +3,8 @@ require('dotenv').config({ path: __dirname + '/../../.env' }) const config = require('../config/config') const assert = require('assert') const searchSchema = require('../utils/ldap_search_utils') +const searchSchemaWithoutFormat = require('../utils/searchSchemaWithNoFormat') const ldapClient = require('../connections/LDAP_client') -var bytes = new Uint8Array(1024) const UserServices = () => { const getAll = (branch) => { @@ -103,6 +103,15 @@ const UserServices = () => { return searchSchema(config.ldap.dn, opts) } + const getByUsernameWithNoFormat = (username) => { + const opts = { + filter: `(uid=${username})`, + scope: 'sub', + timeLimit: 60, + } + return searchSchemaWithoutFormat(config.ldap.dn, opts) + } + const getByCI = (ci) => { const opts = { filter: `(ci=${ci})`, @@ -152,7 +161,7 @@ const UserServices = () => { return new Promise((resolve, reject) => { ldapClient.add(dn, entry, (err) => { if (err) { - console.log("ERROR",err) + console.log('ERROR', err) reject(boom.badImplementation(err)) } resolve({ message: 'User added successfully' }) @@ -163,6 +172,7 @@ const UserServices = () => { return { getAll, getByUsername, + getByUsernameWithNoFormat, getByCI, getByEmail, getStudents, diff --git a/src/utils/ldap_search_utils.js b/src/utils/ldap_search_utils.js index d7dffc46..1a3d837c 100644 --- a/src/utils/ldap_search_utils.js +++ b/src/utils/ldap_search_utils.js @@ -44,4 +44,5 @@ const searchSchema = (dn, opt) => { }) } + module.exports = searchSchema diff --git a/src/utils/searchSchemaWithNoFormat.js b/src/utils/searchSchemaWithNoFormat.js new file mode 100644 index 00000000..7fdf634b --- /dev/null +++ b/src/utils/searchSchemaWithNoFormat.js @@ -0,0 +1,47 @@ +const config = require('../config/config') +const transformData = require('./transform_user_schema') +const ldap = require('../connections/LDAP_client') + +const searchSchemaWithoutFormat = (dn, opt) => { + let results = [] + let pageCount = 0 + let pagedEntries = [] + + return new Promise((resolve, reject) => { + ldap.search(dn, opt, (err, res) => { + res.on('searchEntry', (entry) => { + if (opt.sizeLimit !== undefined) { + if (results.length === opt.sizeLimit - 1) { + resolve(results.length === 1 ? results[0] : results) + } + } + results.push({ + objectName: entry.pojo.objectName, + attributes: entry.pojo, + }) + pagedEntries.push({ + objectName: entry.pojo.objectName, + attributes: entry.pojo, + }) + }) + res.on('page', (result, cb) => { + console.log('page finish') + pageCount = pageCount + 1 + pageCount === opt.pageNum + ? resolve(pagedEntries.length === 1 ? pagedEntries[0] : pagedEntries) + : (pagedEntries = []) + }) + res.on('searchReference', (referral) => { + console.log('referral: ' + referral.uris.join()) + }) + res.on('error', (resError) => + reject(new Error(`Search error: ${resError}`)) + ) + res.on('end', (result) => { + resolve(results.length === 1 ? results[0] : results) + }) + }) + }) +} + +module.exports = searchSchemaWithoutFormat From ef60416b9de78033e5aeeaee6cfbb03efa4ccd22 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 26 Apr 2023 02:16:06 -0400 Subject: [PATCH 014/229] adding server-monitor --- package-lock.json | 675 +++++++++++++++++++++++++++++- package.json | 1 + server.js | 1 + src/middlewares/logger.handler.js | 2 +- 4 files changed, 674 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 17146128..c1668c2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "dotenv": "^16.0.3", "express-paginate": "^1.0.2", "express-rate-limit": "^6.7.0", + "express-status-monitor": "^1.3.4", "handlebars": "^4.7.7", "helmet": "^6.1.3", "jest-fetch-mock": "^3.0.3", @@ -1413,6 +1414,11 @@ "node": ">=0.4.0" } }, + "node_modules/after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==" + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -1519,6 +1525,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "node_modules/arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -1664,6 +1675,11 @@ "@babel/core": "^7.0.0" } }, + "node_modules/backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==" + }, "node_modules/backoff": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", @@ -1681,6 +1697,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -1743,6 +1767,11 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, "node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -2127,11 +2156,20 @@ "node": ">= 0.8" } }, + "node_modules/component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==" + }, "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "node_modules/component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==" }, "node_modules/concat-map": { "version": "0.0.1", @@ -2532,6 +2570,64 @@ "node": ">=10.0.0" } }, + "node_modules/engine.io-client": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.3.tgz", + "integrity": "sha512-qsgyc/CEhJ6cgMUwxRRtOndGVhIu5hpL5tR4umSpmX/MvkFoIxUTM7oFMDQumHNzlNLwSVy6qhstFPoWTf7dOw==", + "dependencies": { + "component-emitter": "~1.3.0", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "xmlhttprequest-ssl": "~1.6.2", + "yeast": "0.1.2" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/engine.io-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", + "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", + "dependencies": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.4", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/engine.io-parser": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", @@ -2694,6 +2790,19 @@ "node": ">= 0.6" } }, + "node_modules/event-loop-stats": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/event-loop-stats/-/event-loop-stats-1.2.0.tgz", + "integrity": "sha512-h/leAlXqoEf+D9w1dnFG0srR5vfIq59rLm9PHzcl3/GwFppd+UR46UMuLdp/mvJvuA+MjSd/dNShmuM2/dPFFw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.14.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2853,6 +2962,148 @@ } ] }, + "node_modules/express-status-monitor": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/express-status-monitor/-/express-status-monitor-1.3.4.tgz", + "integrity": "sha512-EyqHvgX57ujN4fqfUT+x6Bv2xwRyzQdv3AJvWQxcG+jK4TcF9vhrKVqGcE0T6bhT4rstpvOKRuxHBwC/Q6AXQg==", + "dependencies": { + "axios": "0.26.0", + "debug": "4.1.1", + "handlebars": "^4.7.7", + "on-headers": "1.0.2", + "pidusage": "2.0.18", + "socket.io": "^2.4.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "individual", + "url": "http://dynobase.com/buy" + }, + "optionalDependencies": { + "event-loop-stats": "1.2.0" + } + }, + "node_modules/express-status-monitor/node_modules/axios": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", + "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/express-status-monitor/node_modules/component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==" + }, + "node_modules/express-status-monitor/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-status-monitor/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/express-status-monitor/node_modules/engine.io": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.6.1.tgz", + "integrity": "sha512-dfs8EVg/i7QjFsXxn7cCRQ+Wai1G1TlEvHhdYEi80fxn5R1vZ2K661O6v/rezj1FP234SZ14r9CmJke99iYDGg==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "~7.4.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/express-status-monitor/node_modules/engine.io-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", + "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", + "dependencies": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.4", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "node_modules/express-status-monitor/node_modules/isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + }, + "node_modules/express-status-monitor/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/express-status-monitor/node_modules/socket.io": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.5.0.tgz", + "integrity": "sha512-gGunfS0od3VpwDBpGwVkzSZx6Aqo9uOcf1afJj2cKnKFAoyl16fvhpsUhmUFd4Ldbvl5JvRQed6eQw6oQp6n8w==", + "dependencies": { + "debug": "~4.1.0", + "engine.io": "~3.6.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.5.0", + "socket.io-parser": "~3.4.0" + } + }, + "node_modules/express-status-monitor/node_modules/socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" + }, + "node_modules/express-status-monitor/node_modules/socket.io-parser": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.2.tgz", + "integrity": "sha512-QFZBaZDNqZXeemwejc7D39jrq2eGK/qZuVDiMPKzZK1hLlNvjGilGt4ckfQZeVX4dGmuPzCytN9ZW1nQlEWjgA==", + "dependencies": { + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "isarray": "2.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/express-status-monitor/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/express/node_modules/body-parser": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", @@ -3226,6 +3477,24 @@ "node": ">= 0.4.0" } }, + "node_modules/has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dependencies": { + "isarray": "2.0.1" + } + }, + "node_modules/has-binary2/node_modules/isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + }, + "node_modules/has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3472,6 +3741,11 @@ "node": ">=0.8.19" } }, + "node_modules/indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -4979,6 +5253,12 @@ "node": ">= 6.0.0" } }, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "optional": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5374,6 +5654,16 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "node_modules/parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -5488,6 +5778,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidusage": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.18.tgz", + "integrity": "sha512-Y/VfKfh3poHjMEINxU+gJTeVOBjiThQeFAmzR7z56HSNiMx+etl+yBhk42nRPciPYt/VZl8DQLVXNC6P5vH11A==", + "dependencies": { + "safe-buffer": "^5.1.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -6082,6 +6383,47 @@ } } }, + "node_modules/socket.io-client": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.5.0.tgz", + "integrity": "sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw==", + "dependencies": { + "backo2": "1.0.2", + "component-bind": "1.0.0", + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "engine.io-client": "~3.5.0", + "has-binary2": "~1.0.2", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/socket.io-client/node_modules/isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + }, + "node_modules/socket.io-client/node_modules/socket.io-parser": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.3.tgz", + "integrity": "sha512-qOg87q1PMWWTeO01768Yh9ogn7chB9zkKtQnya41Y355S0UmpXgpcrFwAgjYJxu9BdKug5r5e9YtVSeWhKBUZg==", + "dependencies": { + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "isarray": "2.0.1" + } + }, "node_modules/socket.io-parser": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", @@ -6435,6 +6777,11 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, + "node_modules/to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -7044,6 +7391,14 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xmlhttprequest-ssl": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", + "integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -7093,6 +7448,11 @@ "node": ">=12" } }, + "node_modules/yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==" + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -8219,6 +8579,11 @@ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==" + }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -8298,6 +8663,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -8419,6 +8789,11 @@ "babel-preset-current-node-syntax": "^1.0.0" } }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==" + }, "backoff": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", @@ -8433,6 +8808,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==" + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -8472,6 +8852,11 @@ "safe-buffer": "^5.1.1" } }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -8747,11 +9132,20 @@ "delayed-stream": "~1.0.0" } }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==" + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==" }, "concat-map": { "version": "0.0.1", @@ -9086,6 +9480,52 @@ } } }, + "engine.io-client": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.3.tgz", + "integrity": "sha512-qsgyc/CEhJ6cgMUwxRRtOndGVhIu5hpL5tR4umSpmX/MvkFoIxUTM7oFMDQumHNzlNLwSVy6qhstFPoWTf7dOw==", + "requires": { + "component-emitter": "~1.3.0", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "xmlhttprequest-ssl": "~1.6.2", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "engine.io-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", + "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.4", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "requires": {} + } + } + }, "engine.io-parser": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", @@ -9159,6 +9599,15 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "event-loop-stats": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/event-loop-stats/-/event-loop-stats-1.2.0.tgz", + "integrity": "sha512-h/leAlXqoEf+D9w1dnFG0srR5vfIq59rLm9PHzcl3/GwFppd+UR46UMuLdp/mvJvuA+MjSd/dNShmuM2/dPFFw==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -9317,6 +9766,117 @@ } } }, + "express-status-monitor": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/express-status-monitor/-/express-status-monitor-1.3.4.tgz", + "integrity": "sha512-EyqHvgX57ujN4fqfUT+x6Bv2xwRyzQdv3AJvWQxcG+jK4TcF9vhrKVqGcE0T6bhT4rstpvOKRuxHBwC/Q6AXQg==", + "requires": { + "axios": "0.26.0", + "debug": "4.1.1", + "event-loop-stats": "1.2.0", + "handlebars": "^4.7.7", + "on-headers": "1.0.2", + "pidusage": "2.0.18", + "socket.io": "^2.4.1" + }, + "dependencies": { + "axios": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", + "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==" + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "engine.io": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.6.1.tgz", + "integrity": "sha512-dfs8EVg/i7QjFsXxn7cCRQ+Wai1G1TlEvHhdYEi80fxn5R1vZ2K661O6v/rezj1FP234SZ14r9CmJke99iYDGg==", + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "~7.4.2" + } + }, + "engine.io-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", + "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.4", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "socket.io": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.5.0.tgz", + "integrity": "sha512-gGunfS0od3VpwDBpGwVkzSZx6Aqo9uOcf1afJj2cKnKFAoyl16fvhpsUhmUFd4Ldbvl5JvRQed6eQw6oQp6n8w==", + "requires": { + "debug": "~4.1.0", + "engine.io": "~3.6.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.5.0", + "socket.io-parser": "~3.4.0" + } + }, + "socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" + }, + "socket.io-parser": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.2.tgz", + "integrity": "sha512-QFZBaZDNqZXeemwejc7D39jrq2eGK/qZuVDiMPKzZK1hLlNvjGilGt4ckfQZeVX4dGmuPzCytN9ZW1nQlEWjgA==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "isarray": "2.0.1" + } + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "requires": {} + } + } + }, "extsprintf": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", @@ -9544,6 +10104,26 @@ "function-bind": "^1.1.1" } }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==" + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9709,6 +10289,11 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -10898,6 +11483,12 @@ "xtend": "^4.0.0" } }, + "nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "optional": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -11200,6 +11791,16 @@ "entities": "^4.4.0" } }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -11283,6 +11884,14 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "pidusage": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.18.tgz", + "integrity": "sha512-Y/VfKfh3poHjMEINxU+gJTeVOBjiThQeFAmzR7z56HSNiMx+etl+yBhk42nRPciPYt/VZl8DQLVXNC6P5vH11A==", + "requires": { + "safe-buffer": "^5.1.2" + } + }, "pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -11759,6 +12368,49 @@ } } }, + "socket.io-client": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.5.0.tgz", + "integrity": "sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw==", + "requires": { + "backo2": "1.0.2", + "component-bind": "1.0.0", + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "engine.io-client": "~3.5.0", + "has-binary2": "~1.0.2", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + }, + "socket.io-parser": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.3.tgz", + "integrity": "sha512-qOg87q1PMWWTeO01768Yh9ogn7chB9zkKtQnya41Y355S0UmpXgpcrFwAgjYJxu9BdKug5r5e9YtVSeWhKBUZg==", + "requires": { + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "isarray": "2.0.1" + } + } + } + }, "socket.io-parser": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", @@ -12009,6 +12661,11 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==" + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -12446,6 +13103,11 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "xmlhttprequest-ssl": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", + "integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -12483,6 +13145,11 @@ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==" + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 597b4ceb..437dc27a 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "dotenv": "^16.0.3", "express-paginate": "^1.0.2", "express-rate-limit": "^6.7.0", + "express-status-monitor": "^1.3.4", "handlebars": "^4.7.7", "helmet": "^6.1.3", "jest-fetch-mock": "^3.0.3", diff --git a/server.js b/server.js index 5d0f0c2f..875cea59 100644 --- a/server.js +++ b/server.js @@ -16,6 +16,7 @@ const paginate = require('express-paginate') //app initialization const app = express() +app.use(require('express-status-monitor')()) // load app middlewares // The order of the following middleware is very important!! app.use(bodyParser.json()) diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js index a778c951..9446e027 100644 --- a/src/middlewares/logger.handler.js +++ b/src/middlewares/logger.handler.js @@ -32,7 +32,7 @@ const addLoggerMiddleware = (app) => { status: tokens.status(req, res), content_length: tokens.res(req, res, 'content-length'), response_time: tokens['response-time'](req, res), - user: req.user.sub, + user: req.user === undefined ? 'anonimus' : req.user.sub, } logger.info({ ...log }) From 474596053d8f98c75914b47825f550aa118d4e54 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 27 Apr 2023 01:21:03 -0400 Subject: [PATCH 015/229] integration test passed --- package-lock.json | 153 +++++++++++----------- package.json | 4 +- server.js | 2 + src/__tests__/integration.test.js | 35 +++++ src/__tests__/ldap_authentication.test.js | 20 --- src/__tests__/ldap_integration.test.js | 34 ----- src/__tests__/unit.test.js | 101 ++++++++++++++ src/routes/profile.routes.js | 1 - src/schemas/response.schema.js | 3 +- src/services/user.services.js | 3 +- 10 files changed, 220 insertions(+), 136 deletions(-) create mode 100644 src/__tests__/integration.test.js delete mode 100644 src/__tests__/ldap_authentication.test.js delete mode 100644 src/__tests__/ldap_integration.test.js create mode 100644 src/__tests__/unit.test.js diff --git a/package-lock.json b/package-lock.json index c1668c2c..214e72c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "mongoose": "^6.5.2", "nodemon": "^2.0.22", "superagent": "^8.0.0", - "supertest": "^6.2.4" + "supertest": "^6.3.3" } }, "node_modules/@ampproject/remapping": { @@ -1533,7 +1533,7 @@ "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, "node_modules/asn1": { @@ -2288,9 +2288,9 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "node_modules/cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, "node_modules/core-util-is": { @@ -2462,9 +2462,9 @@ } }, "node_modules/dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, "dependencies": { "asap": "^2.0.0", @@ -3280,25 +3280,28 @@ } }, "node_modules/formidable": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz", - "integrity": "sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", "dev": true, "dependencies": { - "dezalgo": "1.0.3", - "hexoid": "1.0.0", - "once": "1.4.0", - "qs": "6.9.3" + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } }, "node_modules/formidable/node_modules/qs": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", - "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==", + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" }, @@ -6639,22 +6642,21 @@ } }, "node_modules/superagent": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.0.tgz", - "integrity": "sha512-iudipXEel+SzlP9y29UBWGDjB+Zzag+eeA1iLosaR2YHBRr1Q1kC29iBrF2zIVD9fqVbpZnXkN/VJmwFMVyNWg==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", + "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", "dev": true, "dependencies": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", + "cookiejar": "^2.1.4", "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^2.0.1", + "formidable": "^2.1.2", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.10.3", - "readable-stream": "^3.6.0", - "semver": "^7.3.7" + "qs": "^6.11.0", + "semver": "^7.3.8" }, "engines": { "node": ">=6.4.0 <13 || >=14" @@ -6695,28 +6697,29 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/superagent/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/superagent/node_modules/qs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "side-channel": "^1.0.4" }, "engines": { - "node": ">= 6" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/supertest": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.2.4.tgz", - "integrity": "sha512-M8xVnCNv+q2T2WXVzxDECvL2695Uv2uUj2O0utxsld/HRyJvOU8W9f1gvsYxSNU4wmIe0/L/ItnpU4iKq0emDA==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", + "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", "dev": true, "dependencies": { "methods": "^1.1.2", - "superagent": "^8.0.0" + "superagent": "^8.0.5" }, "engines": { "node": ">=6.4.0" @@ -8671,7 +8674,7 @@ "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, "asn1": { @@ -9228,9 +9231,9 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, "core-util-is": { @@ -9367,9 +9370,9 @@ "dev": true }, "dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, "requires": { "asap": "^2.0.0", @@ -9968,22 +9971,25 @@ } }, "formidable": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz", - "integrity": "sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", "dev": true, "requires": { - "dezalgo": "1.0.3", - "hexoid": "1.0.0", - "once": "1.4.0", - "qs": "6.9.3" + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" }, "dependencies": { "qs": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", - "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==", - "dev": true + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } } } }, @@ -12556,22 +12562,21 @@ "dev": true }, "superagent": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.0.tgz", - "integrity": "sha512-iudipXEel+SzlP9y29UBWGDjB+Zzag+eeA1iLosaR2YHBRr1Q1kC29iBrF2zIVD9fqVbpZnXkN/VJmwFMVyNWg==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", + "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", "dev": true, "requires": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", + "cookiejar": "^2.1.4", "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^2.0.1", + "formidable": "^2.1.2", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.10.3", - "readable-stream": "^3.6.0", - "semver": "^7.3.7" + "qs": "^6.11.0", + "semver": "^7.3.8" }, "dependencies": { "debug": { @@ -12595,27 +12600,25 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "qs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "side-channel": "^1.0.4" } } } }, "supertest": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.2.4.tgz", - "integrity": "sha512-M8xVnCNv+q2T2WXVzxDECvL2695Uv2uUj2O0utxsld/HRyJvOU8W9f1gvsYxSNU4wmIe0/L/ItnpU4iKq0emDA==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", + "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", "dev": true, "requires": { "methods": "^1.1.2", - "superagent": "^8.0.0" + "superagent": "^8.0.5" } }, "supports-color": { diff --git a/package.json b/package.json index 437dc27a..2ae3086d 100644 --- a/package.json +++ b/package.json @@ -57,10 +57,10 @@ "mongoose": "^6.5.2", "nodemon": "^2.0.22", "superagent": "^8.0.0", - "supertest": "^6.2.4" + "supertest": "^6.3.3" }, "scripts": { - "test": "jest", + "test": "jest --forceExit --detectOpenHandles --watchAll --maxWorkers=1", "test:watch": "jest --watch", "start": "node server.js", "dev": "nodemon server.js" diff --git a/server.js b/server.js index 875cea59..ecceebf1 100644 --- a/server.js +++ b/server.js @@ -49,3 +49,5 @@ app.use(express.static('./src/public')) let port = CONFIG.server.port || 4000 console.log(`server listen on port ${port}`) app.listen(port, CONFIG.server.host || '127.0.0.1') + +module.exports = app diff --git a/src/__tests__/integration.test.js b/src/__tests__/integration.test.js new file mode 100644 index 00000000..cc2fd051 --- /dev/null +++ b/src/__tests__/integration.test.js @@ -0,0 +1,35 @@ +const request = require('supertest') +const app = require('../../server') // your Express app +const UserServices = require('../services/user.services') +const mongoose = require('mongoose') +const User = require('../schemas/user.schema').User +const config = require('../config/config') +const ldapClient = require('../connections/LDAP_client') + +describe('Prueba de Integración: Verifique que el servidor pueda comunicarse correctamente con la base de datos y el servidor LDAP', () => { + it('should correctly communicate with the database and the LDAP server', async () => { + // Get all users from the 'users' collection in the 'ldapDB' database + const users = await User.find({}) + + for (const user of users) { + // For each user, check if their 'username' attribute matches a 'uid' in the LDAP server + const searchOptions = { + filter: `(uid=${user.username})`, + scope: 'sub', + } + + await new Promise((resolve, reject) => { + ldapClient.search(config.ldap.dn, searchOptions, (err, res) => { + if (err) return reject(err) + + res.on('searchEntry', (entry) => { + const uid = entry.pojo.attributes[0].values[0] + expect(uid).toBe(user.username) + }) + + res.on('end', resolve) + }) + }) + } + }) +}) diff --git a/src/__tests__/ldap_authentication.test.js b/src/__tests__/ldap_authentication.test.js deleted file mode 100644 index 0ec992b3..00000000 --- a/src/__tests__/ldap_authentication.test.js +++ /dev/null @@ -1,20 +0,0 @@ -const axios = require('axios') - -const loginUrl = 'http://127.0.0.1:4000/login' - -describe('Prueba de autenticación de usuario', () => { - test('La autenticacion debe funcionar con los usuarios que estan en el LDAP y devolver un JWT', async () => { - //Peticion correcta - const loginData = { - username: 'agonzalezb', - password: '00092068426', - } - const respuesta = await axios.post(loginUrl, loginData) // Realizar una petición POST al endpoint de login - expect(respuesta.status).toBe(200) // Verificar que la respuesta tenga un estado 200 (OK) - expect(respuesta.data.data === undefined).toBeFalse // Verificar que la respuesta tenga un cuerpo (no sea nula o vacía) - - const token = respuesta.data.data.token - expect(token === undefined || token === null).toBeFalse - expect(token.length).toBeGreaterThan(0) - }) -}) diff --git a/src/__tests__/ldap_integration.test.js b/src/__tests__/ldap_integration.test.js deleted file mode 100644 index e615655d..00000000 --- a/src/__tests__/ldap_integration.test.js +++ /dev/null @@ -1,34 +0,0 @@ -const axios = require('axios') - -const loginUrl = 'http://127.0.0.1:4000/login' -const usersUrl = 'http://127.0.0.1:4000/users/' - - - -describe('Prueba de integracion con el LDAP', () => { - test('La integracion con el LDAP debe funcionar y devolver usuarios dentro del servidor', async () => { - const loginData = { - username: 'agonzalezb', - password: '00092068426', - } - const respuesta = await axios.post(loginUrl, loginData) - const token = respuesta.data.data.token - // Realizar una petición GET al endpoint de "Get User By Username" - const response = await axios.get(usersUrl + 'agonzalezb', { - headers: { - Authorization: 'Bearer ' + token, - }, - }) - //verificar que el estado de la peticion no sea 401 - expect(response.status).toEqual(200) - //verificar que la propiedad success sea true - expect(response.data.success).toEqual(true) - //verificar que el mensaje de la peticion sea "data fetched succesfully" - expect(response.data.message).toEqual('data fetched succesfully') - //verificar que el usuario solicitado sea el correcto - expect(response.data.data.objectName).toEqual( - 'uid=agonzalezb,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu' - ) - }) -}) - diff --git a/src/__tests__/unit.test.js b/src/__tests__/unit.test.js new file mode 100644 index 00000000..e9fa145f --- /dev/null +++ b/src/__tests__/unit.test.js @@ -0,0 +1,101 @@ +const app = require('../../server') +const supertest = require('supertest') +const config = require('../config/config') + +describe('Pruebas de funcionalidad', () => { + + describe('Caso de uso 1: Verificar que la API pueda autenticar a los usuarios en el directorio LDAP', () => { + it('debería autenticar a un usuario con credenciales válidas', async () => { + const request = supertest(app) + const response = await request.post('/login').send({ + username: config.ldap.username_bind, + password: config.ldap.password_bind, + }) + + expect(response.status).toBe(200) + expect(response.body).toHaveProperty('data') + }) + + it('no debería autenticar a un usuario con credenciales inválidas', async () => { + const request = supertest(app) + const response = await request + .post('/login') + .send({ username: 'invalid-username', password: 'invalid-password' }) + + expect(response.status).toBe(401) + expect(response.body).toStrictEqual({ + success: false, + message: 'username or password incorrect', + }) + }) + + it('no debería autenticar a un usuario sin credenciales', async () => { + const request = supertest(app) + const response = await request.post('/login').send({}) + + expect(response.status).toBe(401) + expect(response.body).toStrictEqual({ + success: false, + message: 'username and password must be both provided', + }) + }) + }) +}) + + + describe('Caso de uso 2: Comprobar que la API pueda realizar búsquedas en el directorio LDAP y devolver resultados precisos', () => { + it('debería realizar una búsqueda de todos los estudiantes del grupo 31 y de la facultad de Ing. Informatica', async () => { + const request = supertest(app) + const { body } = await request.post('/login').send({ + username: config.ldap.username_bind, + password: config.ldap.password_bind, + }) + const { data } = body + const { token } = data + const response = await request + .get('/users/students') + .set('Authorization', `Bearer ${token}`) + .query({ group: '31', branch: 'informatica' }) + + expect(response.status).toBe(200) + expect(response.body.message).toEqual('data fetched succesfully') + }) + + it('debería realizar una búsqueda de todos los profesores que pertenezcan al PCC y su grupo de investigacion sea IA', async () => { + const request = supertest(app) + const { body } = await request.post('/login').send({ + username: config.ldap.username_bind, + password: config.ldap.password_bind, + }) + const { data } = body + const { token } = data + const response = await request + .get('/users/professors') + .set('Authorization', `Bearer ${token}`) + .query({ PCC: 'true', researchGroup: 'IA' }) + expect(response.status).toBe(200) + expect(response.body.message).toEqual('data fetched succesfully') + }) + +}) + + + describe('Caso de uso 3: verificar que la API puede proporcionar información de perfil personal del usuario a través de su JWT', () => { + it('debe proporcionar información de perfil personal del usuario a través de su JWT', async () => { + const request = supertest(app) + const { body } = await request.post('/login').send({ + username: config.ldap.username_bind, + password: config.ldap.password_bind, + }) + const { data } = body + const { token } = data + + const response = await request + .get('/profile') + .set('Authorization', `Bearer ${token}`) + + expect(response.status).toBe(200) + expect(response.body.message).toEqual('data fetched succesfully') + }) + }) + diff --git a/src/routes/profile.routes.js b/src/routes/profile.routes.js index 2e49e352..fa745ea0 100644 --- a/src/routes/profile.routes.js +++ b/src/routes/profile.routes.js @@ -16,7 +16,6 @@ router.get('/', checkAuth, checkRoles('user'), validateResponse, (req, res) => { router.put('/', checkAuth, checkRoles('user'), validateResponse, (req, res) => { const { email, password, confirmPassword } = req.body - console.log({ email, password, confirmPassword }) if (!email && !password) { console.log('entro') responseError(res, 'fields cannot be empty', null) diff --git a/src/schemas/response.schema.js b/src/schemas/response.schema.js index 71326ce1..80d9c9fe 100644 --- a/src/schemas/response.schema.js +++ b/src/schemas/response.schema.js @@ -1,10 +1,9 @@ const responseSuccess = (res, message, data) => { - console.log(data) data.length > 1 ? res.status(200).json({ success: true, message: message, - length: data?.results.length ?? data.length, + length: data.results !== undefined ? data.results.length : data.length, data: data, }) : res.status(200).json({ diff --git a/src/services/user.services.js b/src/services/user.services.js index 34976fe8..7d194695 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -64,7 +64,7 @@ const UserServices = () => { var pccValue = PCC if (PCC !== undefined) { - pccValue = PCC === true ? 'TRUE' : 'FALSE' + pccValue = PCC === "true" ? 'TRUE' : 'FALSE' } const basefilter = '(objectClass=iesEducationalStaff)' const pCCfilter = PCC !== undefined ? `(PCC=${pccValue})` : '' @@ -157,7 +157,6 @@ const UserServices = () => { sambaprimarygroupsid: ['S-1-5-21-1255719363-1350762778-3568053751-513'], sambapwdlastset: [new TextEncoder().encode('1308584948')], } - console.log(branch) return new Promise((resolve, reject) => { ldapClient.add(dn, entry, (err) => { if (err) { From fa43ecf8391cdae897c3b1f96b55549629917a31 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 8 Jun 2023 22:03:40 -0400 Subject: [PATCH 016/229] authentication working with new ldap --- package-lock.json | 37 +++++++++++++++++++ package.json | 3 ++ src/__tests__/performance/artillery.yml | 27 ++++++++++++++ src/__tests__/performance/performance.test.js | 0 src/__tests__/shapiro.test.js | 3 ++ src/connections/LDAP_client.js | 2 +- src/middlewares/session.handler.js | 1 + src/modules/authentication/LdapAuth.js | 10 +++-- src/routes/user.routes.js | 29 ++++++++++++++- src/services/tree.services.js | 4 +- 10 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 src/__tests__/performance/artillery.yml create mode 100644 src/__tests__/performance/performance.test.js create mode 100644 src/__tests__/shapiro.test.js diff --git a/package-lock.json b/package-lock.json index 214e72c7..c13fdfbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,9 +20,11 @@ "express-status-monitor": "^1.3.4", "handlebars": "^4.7.7", "helmet": "^6.1.3", + "jerzy": "^0.2.1", "jest-fetch-mock": "^3.0.3", "joi": "^13.0.2", "jsonwebtoken": "^9.0.0", + "jstat": "^1.9.6", "LDAP": "^1.2.1", "ldap-authentication": "^2.3.1", "ldapjs": "^3.0.1", @@ -35,6 +37,7 @@ "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", "simple-ldap-search": "^3.1.2", + "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", "winston": "^3.8.2", "winston-mongodb": "^5.1.1" @@ -4012,6 +4015,12 @@ "integrity": "sha512-qybtBUesniQdW6n+QIHMng2vDOHscIC/dEXjW+JzO9+LoAZMb03RCUC5xFOv/btSKPm1xL42fn+RjlU4oB42Lg==", "dev": true }, + "node_modules/jerzy": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/jerzy/-/jerzy-0.2.1.tgz", + "integrity": "sha512-NHuYTuLgwwBrGKNORh+mSqnvJUeXe9OX9HLP60CT4iRQPgi+OPKBjThOLvwc5SA4yfjTQ/RpyhPfczfavS+Gsg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." + }, "node_modules/jest": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", @@ -4746,6 +4755,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/jstat": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/jstat/-/jstat-1.9.6.tgz", + "integrity": "sha512-rPBkJbK2TnA8pzs93QcDDPlKcrtZWuuCo2dVR0TFLOJSxhqfWOVCSp8aV3/oSbn+4uY4yw1URtLpHQedtmXfug==" + }, "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -6274,6 +6288,14 @@ "node": ">=10.13.0" } }, + "node_modules/simple-statistics": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.3.tgz", + "integrity": "sha512-JFvMY00t6SBGtwMuJ+nqgsx9ylkMiJ5JlK9bkj8AdvniIe5615wWQYkKHXe84XtSuc40G/tlrPu0A5/NlJvv8A==", + "engines": { + "node": "*" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -10505,6 +10527,11 @@ "integrity": "sha512-qybtBUesniQdW6n+QIHMng2vDOHscIC/dEXjW+JzO9+LoAZMb03RCUC5xFOv/btSKPm1xL42fn+RjlU4oB42Lg==", "dev": true }, + "jerzy": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/jerzy/-/jerzy-0.2.1.tgz", + "integrity": "sha512-NHuYTuLgwwBrGKNORh+mSqnvJUeXe9OX9HLP60CT4iRQPgi+OPKBjThOLvwc5SA4yfjTQ/RpyhPfczfavS+Gsg==" + }, "jest": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", @@ -11072,6 +11099,11 @@ } } }, + "jstat": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/jstat/-/jstat-1.9.6.tgz", + "integrity": "sha512-rPBkJbK2TnA8pzs93QcDDPlKcrtZWuuCo2dVR0TFLOJSxhqfWOVCSp8aV3/oSbn+4uY4yw1URtLpHQedtmXfug==" + }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -12271,6 +12303,11 @@ } } }, + "simple-statistics": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.3.tgz", + "integrity": "sha512-JFvMY00t6SBGtwMuJ+nqgsx9ylkMiJ5JlK9bkj8AdvniIe5615wWQYkKHXe84XtSuc40G/tlrPu0A5/NlJvv8A==" + }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", diff --git a/package.json b/package.json index 2ae3086d..ee5c6cf6 100644 --- a/package.json +++ b/package.json @@ -27,9 +27,11 @@ "express-status-monitor": "^1.3.4", "handlebars": "^4.7.7", "helmet": "^6.1.3", + "jerzy": "^0.2.1", "jest-fetch-mock": "^3.0.3", "joi": "^13.0.2", "jsonwebtoken": "^9.0.0", + "jstat": "^1.9.6", "LDAP": "^1.2.1", "ldap-authentication": "^2.3.1", "ldapjs": "^3.0.1", @@ -42,6 +44,7 @@ "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", "simple-ldap-search": "^3.1.2", + "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", "winston": "^3.8.2", "winston-mongodb": "^5.1.1" diff --git a/src/__tests__/performance/artillery.yml b/src/__tests__/performance/artillery.yml new file mode 100644 index 00000000..e1c134e3 --- /dev/null +++ b/src/__tests__/performance/artillery.yml @@ -0,0 +1,27 @@ +config: + target: 'http://127.0.0.1:4000' # URL base de tu API + phases: + - duration: 60 # Duración de la prueba en segundos + arrivalRate: 20 # Número de solicitudes por segundo + variables: + jwt: '' # Variable para almacenar el JWT capturado en el login +scenarios: + - flow: + - post: + url: '/login' # Endpoint para iniciar sesión y obtener un JWT + json: + username: 'agonzalezb' + password: '00092068426' + capture: + json: '$.data.token' + as: 'jwt' + - get: + url: '/users' # Endpoint para obtener información de usuarios utilizando un JWT + headers: + Authorization: 'Bearer {{ jwt }}' + - post: + url: '/users/artillery' # Endpoint para crear un nuevo usuario en el directorio LDAP + json: + username: 'newuser' + password: 'password' + email: 'newuser@example.com' \ No newline at end of file diff --git a/src/__tests__/performance/performance.test.js b/src/__tests__/performance/performance.test.js new file mode 100644 index 00000000..e69de29b diff --git a/src/__tests__/shapiro.test.js b/src/__tests__/shapiro.test.js new file mode 100644 index 00000000..cd784af3 --- /dev/null +++ b/src/__tests__/shapiro.test.js @@ -0,0 +1,3 @@ +const jerzy = require('jerzy') +var v = new jerzy.Vector([1, 2, 3, 4, 20]) +console.log(JSON.stringify(jerzy.Normality.shapiroWilk(v), null, 4)) diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index 64397a19..374115ca 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -9,7 +9,7 @@ const client = ldap.createClient({ }) client.on('connectError', (err) => { - assert.ifError(err) + console.log("ERROR") }) client.bind(config.ldap.admin.username, config.ldap.admin.password, (err) => { diff --git a/src/middlewares/session.handler.js b/src/middlewares/session.handler.js index 58a6ce85..f5d2e4ce 100644 --- a/src/middlewares/session.handler.js +++ b/src/middlewares/session.handler.js @@ -13,6 +13,7 @@ const mongoClientPromise = mongoose authSource: 'admin', }) .then((m) => m.connection.getClient()) + .catch((e)=>console.log(e)) const sessionMiddleWare = session({ secret: CONFIG.sessionSecret, diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 594fea94..7305fae4 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -192,12 +192,14 @@ var login = function (req, res, next) { } if (!user) { res.status(401).json({ success: false, message: 'User cannot be found' }) - } else { + } else { const userUID = user.uid - const userDN = user.member - const branch = userDN.split(',')[1].replace('ou=', '') + const userDN = user.dn + const branch = userDN.split(',')[2].replace('ou=', '') const response = await groupService.getAdminsGroups(branch) - const isAdmin = response.attributes.memberUid.includes(userUID) + console.log("RESPONSE", response) + console.log("User", user) + const isAdmin = user.right === 'Todos' const payload = { sub: user.uid, diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 52fededc..fcca7bb7 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -7,6 +7,33 @@ const { checkAuth, checkRoles } = require('../middlewares/auth.handler') const service = UserServices() const paginateResults = require('../utils/paginateResults') const newUserSchema = require('../schemas/newUser.schema') +const mongoose = require('mongoose') +const Schema = mongoose.Schema +// test +const Person = mongoose.model( + 'Person', + Schema({ + name: String, + email: String, + password: String, + }) +) + +router.post('/artillery', async (req, res) => { + const user = req.body + const person = Person({ + name: user.name, + email: user.email, + password: user.password, + }) + + await person.save() + res.status(200).json({ + success: true, + message: 'user added', + data: user, + }) +}) //Get all users router.get( @@ -32,7 +59,7 @@ router.get( router.get( '/students', checkAuth, - checkRoles('admin'), + checkRoles('admino'), validateResponse, (req, res) => { const branch = req.query.branch || undefined diff --git a/src/services/tree.services.js b/src/services/tree.services.js index 64ebf659..970ec244 100644 --- a/src/services/tree.services.js +++ b/src/services/tree.services.js @@ -6,8 +6,8 @@ const LDAP = require('ldapjs') const searchSchema = require('../utils/ldap_search_utils') ldap.bind( - 'uid=agonzalezb,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu', - '00092068426', + config.ldap.admin.username, + config.ldap.admin.password, (err) => { assert.ifError(err) } From 3370ce9bb385f76f9dee72f217c1bd378846786e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 26 Jun 2023 13:02:01 -0400 Subject: [PATCH 017/229] limit query working fine --- package-lock.json | 323 +++++++++++++++++++++++++ package.json | 3 + server.js | 6 + src/helpers/dnHelper.js | 6 + src/modules/authentication/LdapAuth.js | 8 +- src/public/index.html | 1 - src/routes/profile.routes.js | 1 - src/routes/user.routes.js | 3 +- src/utils/ldap_search_utils.js | 14 +- src/utils/paginateResults.js | 6 +- 10 files changed, 356 insertions(+), 15 deletions(-) create mode 100644 src/helpers/dnHelper.js diff --git a/package-lock.json b/package-lock.json index c13fdfbe..887135d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,8 @@ "axios": "^1.3.5", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", + "cluster": "^0.7.7", + "compression": "^1.7.4", "cors": "^2.8.5", "dotenv": "^16.0.3", "express-paginate": "^1.0.2", @@ -33,6 +35,7 @@ "multer": "^1.4.5-lts.1", "nodemailer": "^6.9.1", "nodemailer-smtp-transport": "^2.7.4", + "os": "^0.1.2", "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", @@ -2075,6 +2078,18 @@ "node": ">=12" } }, + "node_modules/cluster": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/cluster/-/cluster-0.7.7.tgz", + "integrity": "sha512-16LzEZSoBUgRHSN7NA46ntGnI9tf0NnxTm3KCDvHd6aj4EsBqUbYDN3ImCfjYegBXJZiutULF5rWkcRusMIozQ==", + "dependencies": { + "log": ">= 1.2.0", + "mkdirp": ">= 0.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -2174,6 +2189,42 @@ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==" }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2347,6 +2398,20 @@ "node": ">=14" } }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/d/node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, "node_modules/data-urls": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", @@ -2503,6 +2568,15 @@ "node": ">=12" } }, + "node_modules/duration": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", + "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.46" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -2709,6 +2783,39 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2793,6 +2900,15 @@ "node": ">= 0.6" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/event-loop-stats": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/event-loop-stats/-/event-loop-stats-1.2.0.tgz", @@ -3163,6 +3279,14 @@ } ] }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, "node_modules/extsprintf": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", @@ -4938,6 +5062,20 @@ "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" }, + "node_modules/log": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/log/-/log-6.3.1.tgz", + "integrity": "sha512-McG47rJEWOkXTDioZzQNydAVvZNeEkSyLJ1VWkFwfW+o1knW+QSi8D1KjPn/TnctV+q99lkvJNe1f0E1IjfY2A==", + "dependencies": { + "d": "^1.0.1", + "duration": "^0.2.2", + "es5-ext": "^0.10.53", + "event-emitter": "^0.3.5", + "sprintf-kit": "^2.0.1", + "type": "^2.5.0", + "uni-global": "^1.0.0" + } + }, "node_modules/logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -5295,6 +5433,11 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -5590,6 +5733,11 @@ "node": ">= 0.8.0" } }, + "node_modules/os": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", + "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6550,6 +6698,14 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sprintf-kit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sprintf-kit/-/sprintf-kit-2.0.1.tgz", + "integrity": "sha512-2PNlcs3j5JflQKcg4wpdqpZ+AjhQJ2OZEo34NXDtlB0tIPG84xaaXhpA8XFacFiwjKA4m49UOYG83y3hbMn/gQ==", + "dependencies": { + "es5-ext": "^0.10.53" + } + }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -6895,6 +7051,11 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -6980,6 +7141,14 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==" }, + "node_modules/uni-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/uni-global/-/uni-global-1.0.0.tgz", + "integrity": "sha512-WWM3HP+siTxzIWPNUg7hZ4XO8clKi6NoCAJJWnuRL+BAqyFXF8gC03WNyTefGoUXYc47uYgXxpKLIEvo65PEHw==", + "dependencies": { + "type": "^2.5.0" + } + }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", @@ -9081,6 +9250,15 @@ "wrap-ansi": "^7.0.0" } }, + "cluster": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/cluster/-/cluster-0.7.7.tgz", + "integrity": "sha512-16LzEZSoBUgRHSN7NA46ntGnI9tf0NnxTm3KCDvHd6aj4EsBqUbYDN3ImCfjYegBXJZiutULF5rWkcRusMIozQ==", + "requires": { + "log": ">= 1.2.0", + "mkdirp": ">= 0.0.1" + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -9172,6 +9350,35 @@ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==" }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -9300,6 +9507,22 @@ "rrweb-cssom": "^0.6.0" } }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + }, + "dependencies": { + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + } + } + }, "data-urls": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", @@ -9421,6 +9644,15 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" }, + "duration": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", + "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", + "requires": { + "d": "1", + "es5-ext": "~0.10.46" + } + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -9571,6 +9803,35 @@ "is-arrayish": "^0.2.1" } }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -9624,6 +9885,15 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "event-loop-stats": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/event-loop-stats/-/event-loop-stats-1.2.0.tgz", @@ -9902,6 +10172,14 @@ } } }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "requires": { + "type": "^2.7.2" + } + }, "extsprintf": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", @@ -11259,6 +11537,20 @@ "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" }, + "log": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/log/-/log-6.3.1.tgz", + "integrity": "sha512-McG47rJEWOkXTDioZzQNydAVvZNeEkSyLJ1VWkFwfW+o1knW+QSi8D1KjPn/TnctV+q99lkvJNe1f0E1IjfY2A==", + "requires": { + "d": "^1.0.1", + "duration": "^0.2.2", + "es5-ext": "^0.10.53", + "event-emitter": "^0.3.5", + "sprintf-kit": "^2.0.1", + "type": "^2.5.0", + "uni-global": "^1.0.0" + } + }, "logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -11543,6 +11835,11 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -11773,6 +12070,11 @@ "word-wrap": "~1.2.3" } }, + "os": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", + "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==" + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -12518,6 +12820,14 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "sprintf-kit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sprintf-kit/-/sprintf-kit-2.0.1.tgz", + "integrity": "sha512-2PNlcs3j5JflQKcg4wpdqpZ+AjhQJ2OZEo34NXDtlB0tIPG84xaaXhpA8XFacFiwjKA4m49UOYG83y3hbMn/gQ==", + "requires": { + "es5-ext": "^0.10.53" + } + }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -12776,6 +13086,11 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -12837,6 +13152,14 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==" }, + "uni-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/uni-global/-/uni-global-1.0.0.tgz", + "integrity": "sha512-WWM3HP+siTxzIWPNUg7hZ4XO8clKi6NoCAJJWnuRL+BAqyFXF8gC03WNyTefGoUXYc47uYgXxpKLIEvo65PEHw==", + "requires": { + "type": "^2.5.0" + } + }, "universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", diff --git a/package.json b/package.json index ee5c6cf6..75482544 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "axios": "^1.3.5", "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", + "cluster": "^0.7.7", + "compression": "^1.7.4", "cors": "^2.8.5", "dotenv": "^16.0.3", "express-paginate": "^1.0.2", @@ -40,6 +42,7 @@ "multer": "^1.4.5-lts.1", "nodemailer": "^6.9.1", "nodemailer-smtp-transport": "^2.7.4", + "os": "^0.1.2", "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", diff --git a/server.js b/server.js index ecceebf1..31dbb9dc 100644 --- a/server.js +++ b/server.js @@ -12,6 +12,7 @@ const cors = require('cors') const helmet = require('helmet') const limiter = require('./src/middlewares/rate_limiter.handler.js') const paginate = require('express-paginate') +const compression = require('compression') //app initialization const app = express() @@ -23,10 +24,15 @@ app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: false })) app.use(express.json()) +// Middleware para la compresión de respuestas +app.use(compression()) + //security middlewares app.use(cors()) app.use(helmet()) +// Middleware para limitar el numero de solicitudes app.use(limiter) +// Middleware para el registro de solicitudes app.use(sessionMiddleWare) addLoggerMiddleware(app) diff --git a/src/helpers/dnHelper.js b/src/helpers/dnHelper.js new file mode 100644 index 00000000..354f313b --- /dev/null +++ b/src/helpers/dnHelper.js @@ -0,0 +1,6 @@ +const getFacultyFromDN = (dn) => { + const branch = dn.toString().split(',')[2].replace('ou=', '') + return branch +} + +module.exports = { getFacultyFromDN } diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 7305fae4..36b736e3 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -197,8 +197,6 @@ var login = function (req, res, next) { const userDN = user.dn const branch = userDN.split(',')[2].replace('ou=', '') const response = await groupService.getAdminsGroups(branch) - console.log("RESPONSE", response) - console.log("User", user) const isAdmin = user.right === 'Todos' const payload = { @@ -219,8 +217,10 @@ var login = function (req, res, next) { return next(loginErr) } _insertFunc(user).then((user) => { - var userObj = - typeof user.toObject === 'function' ? user.toObject() : user + var userObj = { + user, + } + const data = { token: token, refreshToken: refreshToken, diff --git a/src/public/index.html b/src/public/index.html index 783feb01..ef605e27 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -39,7 +39,6 @@ .then((response) => { if (response.success) { alert('Login succeed') - console.log(response.user) } else { alert('login failed. Message: ' + response.message) } diff --git a/src/routes/profile.routes.js b/src/routes/profile.routes.js index fa745ea0..0318bbeb 100644 --- a/src/routes/profile.routes.js +++ b/src/routes/profile.routes.js @@ -17,7 +17,6 @@ router.get('/', checkAuth, checkRoles('user'), validateResponse, (req, res) => { router.put('/', checkAuth, checkRoles('user'), validateResponse, (req, res) => { const { email, password, confirmPassword } = req.body if (!email && !password) { - console.log('entro') responseError(res, 'fields cannot be empty', null) } else if (password) { if (password !== confirmPassword) { diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index fcca7bb7..7f51bcb6 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -43,6 +43,7 @@ router.get( validateResponse, (req, res) => { const branch = req.query.branch || undefined + console.log('req', req.query) service .getAll(branch) .then((data) => @@ -59,7 +60,7 @@ router.get( router.get( '/students', checkAuth, - checkRoles('admino'), + checkRoles('admin'), validateResponse, (req, res) => { const branch = req.query.branch || undefined diff --git a/src/utils/ldap_search_utils.js b/src/utils/ldap_search_utils.js index 1a3d837c..e8ed6ac4 100644 --- a/src/utils/ldap_search_utils.js +++ b/src/utils/ldap_search_utils.js @@ -25,11 +25,14 @@ const searchSchema = (dn, opt) => { }) }) res.on('page', (result, cb) => { - console.log('page finish') - pageCount = pageCount + 1 - pageCount === opt.pageNum - ? resolve(pagedEntries.length === 1 ? pagedEntries[0] : pagedEntries) - : (pagedEntries = []) + if (!!opt.pageNum) { + pageCount = pageCount + 1 + pageCount === opt.pageNum + ? resolve( + pagedEntries.length === 1 ? pagedEntries[0] : pagedEntries + ) + : (pagedEntries = []) + } }) res.on('searchReference', (referral) => { console.log('referral: ' + referral.uris.join()) @@ -44,5 +47,4 @@ const searchSchema = (dn, opt) => { }) } - module.exports = searchSchema diff --git a/src/utils/paginateResults.js b/src/utils/paginateResults.js index 852bc055..4545e523 100644 --- a/src/utils/paginateResults.js +++ b/src/utils/paginateResults.js @@ -1,22 +1,24 @@ function paginateResults(array, req) { const page = parseInt(req.query.page) || 1 const limit = parseInt(req.query.limit) || 10 + console.log('page', page) + console.log('limit', limit) + const startIndex = (page - 1) * limit const endIndex = page * limit const results = {} results.results = array.slice(startIndex, endIndex) + results.length = results.results.length if (endIndex < array.length) { results.next = { page: page + 1, - limit: limit, } } if (startIndex > 0) { results.previous = { page: page - 1, - limit: limit, } } return results From e576b28afc0b695c62a67fbf1f876af1145e07af Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 26 Jun 2023 13:05:42 -0400 Subject: [PATCH 018/229] get by username working ok --- src/routes/user.routes.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 7f51bcb6..3750388b 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -104,13 +104,9 @@ router.get( (req, res) => { service .getByUsername(req.params.username) - .then((data) => - responseSuccess( - res, - 'data fetched succesfully', - paginateResults(data, req) - ) - ) + .then((data) => { + responseSuccess(res, 'data fetched succesfully', data) + }) .catch((err) => { responseError(res, err.message, err.errors) }) From a264ff4037c957e7b958172b8e9c42147ba5396b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 26 Jun 2023 13:16:20 -0400 Subject: [PATCH 019/229] get users by year working fine --- src/routes/user.routes.js | 25 +++++++++++++++++++++++++ src/services/user.services.js | 15 +++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 3750388b..b8c8e4d5 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -112,6 +112,8 @@ router.get( }) } ) + +/* get user by ci */ router.get( '/ci/:ci', checkAuth, @@ -124,6 +126,8 @@ router.get( .catch((err) => responseError(res, err.message, err.errors)) } ) + +/* get user by email */ router.get( '/email/:email', checkAuth, @@ -137,6 +141,27 @@ router.get( } ) +/* get users by year */ +router.get( + '/year/:year', + checkAuth, + checkRoles('admin'), + validateResponse, + (req, res) => { + const branch = req.query.branch + service + .getByYear(req.params.year, branch) + .then((data) => + responseSuccess( + res, + 'data fetched succesfully', + paginateResults(data, req) + ) + ) + .catch((err) => responseError(res, err.message, err.errors)) + } +) + //Add Users router.post( '/', diff --git a/src/services/user.services.js b/src/services/user.services.js index 7d194695..0bb3d5d8 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -16,8 +16,18 @@ const UserServices = () => { attributes: ['uid', 'cn', 'mail', 'ci'], sizeLimit: config.ldap.sizeLimit, } - console.log(opts) + return searchSchema(dn, opts) + } + const getByYear = (year = 1, branch) => { + const dn = + branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` + const opts = { + filter: `(studentYear=${year})`, + scope: 'sub', + attributes: ['uid', 'cn', 'mail', 'ci'], + sizeLimit: config.ldap.sizeLimit, + } return searchSchema(dn, opts) } @@ -64,7 +74,7 @@ const UserServices = () => { var pccValue = PCC if (PCC !== undefined) { - pccValue = PCC === "true" ? 'TRUE' : 'FALSE' + pccValue = PCC === 'true' ? 'TRUE' : 'FALSE' } const basefilter = '(objectClass=iesEducationalStaff)' const pCCfilter = PCC !== undefined ? `(PCC=${pccValue})` : '' @@ -170,6 +180,7 @@ const UserServices = () => { return { getAll, + getByYear, getByUsername, getByUsernameWithNoFormat, getByCI, From d258fb8c2e6352b20a4cb2334a3a4dce7dc2a6e4 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 26 Jun 2023 23:06:32 -0400 Subject: [PATCH 020/229] cache middleware added --- package-lock.json | 139 ++++++++++++++++++++++++++++++---------------- package.json | 2 + server.js | 4 ++ 3 files changed, 97 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index 887135d5..34b7ade8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "compression": "^1.7.4", "cors": "^2.8.5", "dotenv": "^16.0.3", + "express-cache-ctrl": "^1.1.0", "express-paginate": "^1.0.2", "express-rate-limit": "^6.7.0", "express-status-monitor": "^1.3.4", @@ -33,6 +34,7 @@ "lodash": "^4.17.4", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", + "node-cache": "^5.1.2", "nodemailer": "^6.9.1", "nodemailer-smtp-transport": "^2.7.4", "os": "^0.1.2", @@ -1807,20 +1809,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/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/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2078,6 +2066,14 @@ "node": ">=12" } }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/cluster": { "version": "0.7.7", "resolved": "https://registry.npmjs.org/cluster/-/cluster-0.7.7.tgz", @@ -2971,13 +2967,13 @@ } }, "node_modules/express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "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.0", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", @@ -2996,7 +2992,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -3011,6 +3007,23 @@ "node": ">= 0.10.0" } }, + "node_modules/express-cache-ctrl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/express-cache-ctrl/-/express-cache-ctrl-1.1.0.tgz", + "integrity": "sha512-lCpcHbgvWJPA8FVAOjKVMy6/Hn73etTpZvRfkYfv1m0MID16UObxHYnoObd8k24m7ZU+z/QINm/hv8jINFcYBA==", + "dependencies": { + "express": "^4.18.2", + "ms": "^2.1.3" + }, + "engines": { + "node": ">= 12.0" + } + }, + "node_modules/express-cache-ctrl/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/express-paginate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/express-paginate/-/express-paginate-1.0.2.tgz", @@ -3224,9 +3237,9 @@ } }, "node_modules/express/node_modules/body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "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", @@ -3236,7 +3249,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", + "qs": "6.11.0", "raw-body": "2.5.1", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -5438,6 +5451,17 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, + "node_modules/node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "dependencies": { + "clone": "2.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -6100,9 +6124,9 @@ ] }, "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "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" }, @@ -9074,16 +9098,6 @@ "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" - }, - "dependencies": { - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - } } }, "brace-expansion": { @@ -9250,6 +9264,11 @@ "wrap-ansi": "^7.0.0" } }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" + }, "cluster": { "version": "0.7.7", "resolved": "https://registry.npmjs.org/cluster/-/cluster-0.7.7.tgz", @@ -9940,13 +9959,13 @@ } }, "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", @@ -9965,7 +9984,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -9978,9 +9997,9 @@ }, "dependencies": { "body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.4", @@ -9990,7 +10009,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", + "qs": "6.11.0", "raw-body": "2.5.1", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -10014,6 +10033,22 @@ } } }, + "express-cache-ctrl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/express-cache-ctrl/-/express-cache-ctrl-1.1.0.tgz", + "integrity": "sha512-lCpcHbgvWJPA8FVAOjKVMy6/Hn73etTpZvRfkYfv1m0MID16UObxHYnoObd8k24m7ZU+z/QINm/hv8jINFcYBA==", + "requires": { + "express": "^4.18.2", + "ms": "^2.1.3" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "express-paginate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/express-paginate/-/express-paginate-1.0.2.tgz", @@ -11840,6 +11875,14 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, + "node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "requires": { + "clone": "2.x" + } + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -12340,9 +12383,9 @@ "dev": true }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "requires": { "side-channel": "^1.0.4" } diff --git a/package.json b/package.json index 75482544..28bc8fc4 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "compression": "^1.7.4", "cors": "^2.8.5", "dotenv": "^16.0.3", + "express-cache-ctrl": "^1.1.0", "express-paginate": "^1.0.2", "express-rate-limit": "^6.7.0", "express-status-monitor": "^1.3.4", @@ -40,6 +41,7 @@ "lodash": "^4.17.4", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", + "node-cache": "^5.1.2", "nodemailer": "^6.9.1", "nodemailer-smtp-transport": "^2.7.4", "os": "^0.1.2", diff --git a/server.js b/server.js index 31dbb9dc..69541d76 100644 --- a/server.js +++ b/server.js @@ -13,6 +13,7 @@ const helmet = require('helmet') const limiter = require('./src/middlewares/rate_limiter.handler.js') const paginate = require('express-paginate') const compression = require('compression') +const cache = require('express-cache-ctrl') //app initialization const app = express() @@ -42,6 +43,9 @@ ldap_initialization(app) //pagination middleware app.use(paginate.middleware(100, CONFIG.ldap.sizeLimit)) +// Middleware de caché +app.use(cache.private(3600)); // + //add routes to application addRoutes(app) From 864c6b2a20171666fa192f5e7a538cd052e26768 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 2 Aug 2023 18:43:07 -0400 Subject: [PATCH 021/229] add cache middleware --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 69541d76..33be2d8c 100644 --- a/server.js +++ b/server.js @@ -44,7 +44,7 @@ ldap_initialization(app) app.use(paginate.middleware(100, CONFIG.ldap.sizeLimit)) // Middleware de caché -app.use(cache.private(3600)); // +app.use(cache.private(3600)) //add routes to application addRoutes(app) From 0a27bbe9a7eb0dbaed6d7bdc6b2d90189d13fbe7 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 2 Aug 2023 18:43:53 -0400 Subject: [PATCH 022/229] query to filters --- src/routes/user.routes.js | 6 ++++-- src/services/user.services.js | 8 +++++--- src/utils/getQueryToFilters.js | 27 +++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 src/utils/getQueryToFilters.js diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index b8c8e4d5..b2bdd40d 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -9,6 +9,8 @@ const paginateResults = require('../utils/paginateResults') const newUserSchema = require('../schemas/newUser.schema') const mongoose = require('mongoose') const Schema = mongoose.Schema +const getQueryToFilters = require('../utils/getQueryToFilters') + // test const Person = mongoose.model( 'Person', @@ -43,9 +45,9 @@ router.get( validateResponse, (req, res) => { const branch = req.query.branch || undefined - console.log('req', req.query) + const filter = getQueryToFilters(req) service - .getAll(branch) + .getAll(branch, filter) .then((data) => responseSuccess( res, diff --git a/src/services/user.services.js b/src/services/user.services.js index 0bb3d5d8..27e91a0e 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -7,14 +7,16 @@ const searchSchemaWithoutFormat = require('../utils/searchSchemaWithNoFormat') const ldapClient = require('../connections/LDAP_client') const UserServices = () => { - const getAll = (branch) => { + const getAll = (branch, filter) => { + console.log('filter', filter) const dn = branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` const opts = { - filter: '(objectClass=person)', + filter: filter, scope: 'sub', - attributes: ['uid', 'cn', 'mail', 'ci'], + attributes: ['uid', 'cn', 'mail', 'ci', 'sex', 'year'], sizeLimit: config.ldap.sizeLimit, + timeLimit: 50000, } return searchSchema(dn, opts) } diff --git a/src/utils/getQueryToFilters.js b/src/utils/getQueryToFilters.js new file mode 100644 index 00000000..b9b6fe0a --- /dev/null +++ b/src/utils/getQueryToFilters.js @@ -0,0 +1,27 @@ +const getQueryToFilters = (req) => { + let filters = [] + const { sex, condition, status, type, year, country, group, UJC, skinColor } = + req.query + if (sex !== undefined) filters.push(`sex=${sex.toString().toUpperCase()}`) + else if (condition !== undefined) + filters.push(`userCondition=${condition.toString().capitalize()}`) + else if (status !== undefined) + filters.push(`userStatus=${status.toString().capitalize()}`) + else if (type !== undefined) + filters.push(`userType=${type.toString().capitalize()}`) + else if (year !== undefined) filters.push(`studentYear=${year.toString()}`) + else if (country !== undefined) + filters.push(`contry=${country.toString().capitalize()}`) + else if (group !== undefined) + filters.push(`studentClassGroup=${group.toString()}`) + else if (UJC !== undefined) filters.push(`UJC=${UJC}`) + else if (skinColor !== undefined) + filters.push(`skinColor=${skinColor.toString().capitalize()}`) + + const filter = filters.join(',') + return filter === '' || filter === undefined + ? '(objectClass=person)' + : `(${filter})` +} + +module.exports = getQueryToFilters From d7e2ad26f49202b3f4b9ef821af65d18fa142115 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 2 Aug 2023 18:51:56 -0400 Subject: [PATCH 023/229] return entire user on login --- src/modules/authentication/LdapAuth.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 36b736e3..8d014619 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -8,6 +8,7 @@ const JwtStrategy = require('../../utils/authentication/strategies/jwtStrategy') const { authenticate } = require('ldap-authentication') const UserServices = require('../../services/user.services') const GroupServices = require('../../services/group.services') +const User = require('../../schemas/user.schema').User const { signToken } = require('../../utils/authentication/tokens/token_sign') const { @@ -196,9 +197,8 @@ var login = function (req, res, next) { const userUID = user.uid const userDN = user.dn const branch = userDN.split(',')[2].replace('ou=', '') - const response = await groupService.getAdminsGroups(branch) + /* const response = await groupService.getAdminsGroups(branch) */ const isAdmin = user.right === 'Todos' - const payload = { sub: user.uid, dn: user.dn, @@ -209,6 +209,7 @@ var login = function (req, res, next) { ci: user.CI, roles: isAdmin ? ['admin', 'user'] : ['user'], } + const userObj = { ...user } const token = signToken(payload, { expiresIn: '45 minutes' }) const refreshToken = signToken(payload, { expiresIn: '1 day' }) @@ -217,10 +218,6 @@ var login = function (req, res, next) { return next(loginErr) } _insertFunc(user).then((user) => { - var userObj = { - user, - } - const data = { token: token, refreshToken: refreshToken, From 78e4f82e136747d459d26d9aaa2ad0b2113e8ecb Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 3 Aug 2023 19:55:44 -0400 Subject: [PATCH 024/229] showing user uid on log middleware --- src/middlewares/logger.handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js index 9446e027..f0ec43be 100644 --- a/src/middlewares/logger.handler.js +++ b/src/middlewares/logger.handler.js @@ -32,7 +32,7 @@ const addLoggerMiddleware = (app) => { status: tokens.status(req, res), content_length: tokens.res(req, res, 'content-length'), response_time: tokens['response-time'](req, res), - user: req.user === undefined ? 'anonimus' : req.user.sub, + user: req.user === undefined ? 'anonimus' : req.user.uid, } logger.info({ ...log }) From 516042915b88c86bfc0c1cbd00b885ec84ee09a4 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 3 Aug 2023 21:07:52 -0400 Subject: [PATCH 025/229] add filtering logs endpoints --- src/routes/logs.routes.js | 31 ++++++++++++++++++++++++++++ src/routes/routes.js | 2 ++ src/schemas/logs.schema.js | 28 ++++++++++++++++++++++++++ src/services/logs.services.js | 38 +++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 src/routes/logs.routes.js create mode 100644 src/schemas/logs.schema.js create mode 100644 src/services/logs.services.js diff --git a/src/routes/logs.routes.js b/src/routes/logs.routes.js new file mode 100644 index 00000000..447a7f8c --- /dev/null +++ b/src/routes/logs.routes.js @@ -0,0 +1,31 @@ +const express = require('express') +const LogService = require('../services/logs.services') +const router = express.Router() +const { responseSuccess, responseError } = require('../schemas/response.schema') +const validateResponse = require('../middlewares/validateResponse') +const { checkAuth, checkRoles } = require('../middlewares/auth.handler') + +// Controller function to get logs filtered by method, url, status, and user +router.get( + '/', + checkAuth, + checkRoles('user'), + validateResponse, + async (req, res) => { + try { + const filters = { + method: req.query.method, + url: req.query.url, + status: req.query.status, + user: req.query.user, + } + const logs = await LogService.filterLogs(filters) + res.json(logs) + } catch (error) { + console.error('Error fetching filtered logs:', error) + res.status(500).json({ error: 'Internal Server Error' }) + } + } +) + +module.exports = router diff --git a/src/routes/routes.js b/src/routes/routes.js index 7770e2e3..5f37134e 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -3,6 +3,7 @@ const TreeRoutes = require('./tree.routes') const UserRoutes = require('./user.routes') const GroupRoutes = require('./group.routes') const ProfileRoutes = require('./profile.routes') +const LogsRoutes = require('./logs.routes') const addRoutes = (app) => { app.use('/', AuthRoutes) @@ -10,6 +11,7 @@ const addRoutes = (app) => { app.use('/users', UserRoutes) app.use('/groups', GroupRoutes) app.use('/profile', ProfileRoutes) + app.use('/logs', LogsRoutes) } module.exports = addRoutes diff --git a/src/schemas/logs.schema.js b/src/schemas/logs.schema.js new file mode 100644 index 00000000..a14cf9a2 --- /dev/null +++ b/src/schemas/logs.schema.js @@ -0,0 +1,28 @@ +const mongoose = require('mongoose') + +const logSchema = new mongoose.Schema( + { + timestamp: { + type: Date, + required: true, + }, + level: { + type: String, + required: true, + }, + message: { + type: String, + required: true, + }, + meta: { + type: mongoose.Schema.Types.Mixed, + }, + }, + { + versionKey: false, + } +) + +const Log = mongoose.model('Log', logSchema) + +module.exports = Log diff --git a/src/services/logs.services.js b/src/services/logs.services.js new file mode 100644 index 00000000..ee37c8c1 --- /dev/null +++ b/src/services/logs.services.js @@ -0,0 +1,38 @@ +const Log = require('../schemas/logs.schema') + +const createLog = async (logData) => { + return await Log.create(logData) +} + +const getAllLogs = async () => { + return await Log.find() +} + +const filterLogs = async (query) => { + // Construir un objeto de filtro basado en el query recibido + const filter = {} + if (query.method) filter.message = new RegExp(`method: '${query.method}'`) + if (query.url) filter.message = new RegExp(`url: '${query.url}'`) + if (query.status) filter.message = new RegExp(`status: '${query.status}'`) + if (query.user) filter.message = new RegExp(`user: '${query.user}'`) + + // Realizar la búsqueda de logs con el filtro + const logs = await Log.find(filter) + + // Si deseas filtrar múltiples campos a la vez (por ejemplo, method y url), puedes usar el operador $and + // const logs = await Log.find({ + // $and: [ + // { message: new RegExp(`method: '${query.method}'`) }, + // { message: new RegExp(`url: '${query.url}'`) } + // ] + // }); + + // Devolver los logs filtrados + return logs +} + +module.exports = { + createLog, + getAllLogs, + filterLogs, +} From 00dedd07498785e5fccc7910586be7c83bee4f6c Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 3 Aug 2023 21:10:57 -0400 Subject: [PATCH 026/229] add uid to jwt payload --- src/modules/authentication/LdapAuth.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 8d014619..55148eb8 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -202,6 +202,7 @@ var login = function (req, res, next) { const payload = { sub: user.uid, dn: user.dn, + uid: user.uid, firstname: user.givenName, lastname: user.sn, fullname: user.cn, From a9dd13d15db01228d278a776c2eb8504c19d7e61 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 21 Aug 2023 10:52:32 -0400 Subject: [PATCH 027/229] protect database connection in case of error --- src/middlewares/logger.handler.js | 60 ++++++++++++++++++------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js index f0ec43be..7b8831bb 100644 --- a/src/middlewares/logger.handler.js +++ b/src/middlewares/logger.handler.js @@ -20,32 +20,42 @@ const logger = winston.createLogger({ ], }) +morgan.token('user', (req) => { + return req.user ? req.user.uid : 'anonymous' +}) + +const logFormat = (tokens, req, res) => { + const log = { + method: tokens.method(req, res), + url: tokens.url(req, res), + status: tokens.status(req, res), + content_length: tokens.res(req, res, 'content-length'), + response_time: tokens['response-time'](req, res), + user: req.user === undefined ? 'anonymous' : req.user.uid, + } + + logger.info({ ...log }) + + return [ + `method:${log.method}`, + `url:${log.url}`, + `status:${log.status}`, + `content-length:${log.content_length}`, + `response-time:${log.response_time}ms`, + `user_uid:${log.user}`, + ].join(' ') +} + const addLoggerMiddleware = (app) => { - morgan.token('user', (req) => { - return req.user ? req.user.uid : 'anonymous' - }) - app.use( - morgan(function (tokens, req, res) { - const log = { - method: tokens.method(req, res), - url: tokens.url(req, res), - status: tokens.status(req, res), - content_length: tokens.res(req, res, 'content-length'), - response_time: tokens['response-time'](req, res), - user: req.user === undefined ? 'anonimus' : req.user.uid, - } - logger.info({ ...log }) - - return [ - `method:${log.method}`, - `url:${log.url}`, - `status:${log.status}`, - `content-lenght:${log.content_length}`, - `response-time:${log.response_time}ms`, - `user_uid:${log.user}`, - ].join(' ') - }) - ) + try { + // Attempt to establish the database connection and configure logger + winston.add(logger.transports[0]) + + // Register the morgan middleware + app.use(morgan(logFormat)) + } catch (error) { + console.error('Error connecting to the database:', error.message) + } } module.exports = addLoggerMiddleware From 63e476253f4be947664fc0a88e8aa2414cda4933 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 21 Aug 2023 11:03:26 -0400 Subject: [PATCH 028/229] Validate ldap connection error --- src/connections/LDAP_client.js | 41 ++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index 374115ca..e612a036 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -2,18 +2,35 @@ const ldap = require('ldapjs') const config = require('../config/config') var assert = require('assert') -const client = ldap.createClient({ - url: [`${config.ldap.url}:${config.ldap.port}`], - connectTimeout: 60000, - reconnect: true, -}) +const initializeLDAPConnection = () => { + try { + const client = ldap.createClient({ + url: [`${config.ldap.url}:${config.ldap.port}`], + connectTimeout: 60000, + reconnect: true, + }) -client.on('connectError', (err) => { - console.log("ERROR") -}) + client.on('connectError', (err) => { + console.log('LDAP Connection Error:', err.message) + }) -client.bind(config.ldap.admin.username, config.ldap.admin.password, (err) => { - assert.ifError(err) -}) + client.bind( + config.ldap.admin.username, + config.ldap.admin.password, + (err) => { + if (err) { + console.error('LDAP Bind Error:', err.message) + } else { + console.log('LDAP Connection Successful') + } + } + ) -module.exports = client + return client + } catch (error) { + console.error('Error initializing LDAP connection:', error.message) + return null // Return null or a default client if the connection setup fails + } +} + +module.exports = initializeLDAPConnection From 2f09ca2f3767740debe396f275cc1c85fbffe589 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 21 Aug 2023 13:29:45 -0400 Subject: [PATCH 029/229] Go back on ldap connection --- src/connections/LDAP_client.js | 41 ++++++++++------------------------ 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index e612a036..374115ca 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -2,35 +2,18 @@ const ldap = require('ldapjs') const config = require('../config/config') var assert = require('assert') -const initializeLDAPConnection = () => { - try { - const client = ldap.createClient({ - url: [`${config.ldap.url}:${config.ldap.port}`], - connectTimeout: 60000, - reconnect: true, - }) +const client = ldap.createClient({ + url: [`${config.ldap.url}:${config.ldap.port}`], + connectTimeout: 60000, + reconnect: true, +}) - client.on('connectError', (err) => { - console.log('LDAP Connection Error:', err.message) - }) +client.on('connectError', (err) => { + console.log("ERROR") +}) - client.bind( - config.ldap.admin.username, - config.ldap.admin.password, - (err) => { - if (err) { - console.error('LDAP Bind Error:', err.message) - } else { - console.log('LDAP Connection Successful') - } - } - ) +client.bind(config.ldap.admin.username, config.ldap.admin.password, (err) => { + assert.ifError(err) +}) - return client - } catch (error) { - console.error('Error initializing LDAP connection:', error.message) - return null // Return null or a default client if the connection setup fails - } -} - -module.exports = initializeLDAPConnection +module.exports = client From aaf491b40c37b9b713bd6106f397ab15646f7cd1 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 21 Aug 2023 13:30:19 -0400 Subject: [PATCH 030/229] Retrieve last time logged from DB on login --- src/modules/authentication/LdapAuth.js | 7 +++++ src/services/profile.services.js | 41 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 55148eb8..be9f9d9b 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -9,6 +9,7 @@ const { authenticate } = require('ldap-authentication') const UserServices = require('../../services/user.services') const GroupServices = require('../../services/group.services') const User = require('../../schemas/user.schema').User +const ProfileServices = require('../../services/profile.services') const { signToken } = require('../../utils/authentication/tokens/token_sign') const { @@ -19,6 +20,7 @@ const validateResponse = require('../../middlewares/validateResponse') const userService = UserServices() const groupService = GroupServices() +const profileService = ProfileServices() var _backwardCompatible = false var _dn @@ -199,6 +201,9 @@ var login = function (req, res, next) { const branch = userDN.split(',')[2].replace('ou=', '') /* const response = await groupService.getAdminsGroups(branch) */ const isAdmin = user.right === 'Todos' + const last_time_logged = await profileService.getLastLoginByUsername( + user.uid + ) const payload = { sub: user.uid, dn: user.dn, @@ -209,6 +214,7 @@ var login = function (req, res, next) { email: user.mail, ci: user.CI, roles: isAdmin ? ['admin', 'user'] : ['user'], + last_time_logged, } const userObj = { ...user } const token = signToken(payload, { expiresIn: '45 minutes' }) @@ -224,6 +230,7 @@ var login = function (req, res, next) { refreshToken: refreshToken, user: userObj, } + profileService.updateLastTimeLogged(user.uid) return responseSuccess(res, 'authentication succeeded', data) }) }) diff --git a/src/services/profile.services.js b/src/services/profile.services.js index 47e38db4..e0621240 100644 --- a/src/services/profile.services.js +++ b/src/services/profile.services.js @@ -5,6 +5,7 @@ const UserServices = require('./user.services') const service = UserServices() const { verifyToken } = require('../utils/authentication/tokens/token_verify') const ldap = require('ldapjs') +const { User } = require('../schemas/user.schema') const ProfileServices = () => { const getProfile = (req) => { @@ -43,9 +44,49 @@ const ProfileServices = () => { } } + async function getLastLoginByUsername(username) { + try { + const user = await User.findOne({ username }) + .sort({ updatedAt: -1 }) + .exec() + + if (!user) { + return 'User not found' + } + + return user.updatedAt + } catch (error) { + console.error('Error fetching last login:', error) + return null + } + } + + async function updateLastTimeLogged(username) { + try { + const currentDate = new Date() + + const updatedUser = await User.findOneAndUpdate( + { username }, + { updatedAt: currentDate }, + { new: true } // Return the updated document + ).exec() + + if (!updatedUser) { + return 'User not found' + } + const lastLoginDate = updatedUser.updatedAt.toLocaleString() + return lastLoginDate + } catch (error) { + console.error('Error updating last login:', error) + return null + } + } + return { getProfile, updateProfile, + getLastLoginByUsername, + updateLastTimeLogged, } } From 8db50d9606271a67bcc6a1aac0fbd9dd174a9901 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 21 Aug 2023 15:04:46 -0400 Subject: [PATCH 031/229] add registry and deserialization --- src/modules/authentication/LdapAuth.js | 116 ++++++++++++++----------- src/schemas/user.schema.js | 38 +++++--- src/services/profile.services.js | 15 +++- 3 files changed, 107 insertions(+), 62 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index be9f9d9b..005b91a0 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -144,16 +144,19 @@ var init = function ( } }) - passport.deserializeUser((id, done) => { - _findFunc(id).then((user) => { + passport.deserializeUser(async (id, done) => { + try { + const user = await User.findOne({ + username: id, + }) if (!user) { - done( - new Error(`Deserialize user failed. ${id} is deleted from local DB`) - ) - } else { - done(null, user) + // User does not exist + return done(null, user) } - }) + return done(null, user) + } catch (error) { + return done(error) + } }) router.use(passport.initialize()) @@ -188,54 +191,67 @@ var initialize = function ( * on successful authenticate, or {success: false} on failed authenticate */ var login = function (req, res, next) { - passport.authenticate('ldap', async (err, user) => { - if (err) { - res.status(401).json({ success: false, message: err.message }) - return - } - if (!user) { - res.status(401).json({ success: false, message: 'User cannot be found' }) - } else { - const userUID = user.uid - const userDN = user.dn - const branch = userDN.split(',')[2].replace('ou=', '') - /* const response = await groupService.getAdminsGroups(branch) */ - const isAdmin = user.right === 'Todos' - const last_time_logged = await profileService.getLastLoginByUsername( - user.uid - ) - const payload = { - sub: user.uid, - dn: user.dn, - uid: user.uid, - firstname: user.givenName, - lastname: user.sn, - fullname: user.cn, - email: user.mail, - ci: user.CI, - roles: isAdmin ? ['admin', 'user'] : ['user'], - last_time_logged, + passport.authenticate( + 'ldap', + { + successRedirect: '/dashboard', + failureRedirect: '/login', + failureFlash: true, + }, + async (err, user) => { + if (err) { + res.status(401).json({ success: false, message: err.message }) + return } - const userObj = { ...user } - const token = signToken(payload, { expiresIn: '45 minutes' }) - const refreshToken = signToken(payload, { expiresIn: '1 day' }) + if (!user) { + res + .status(401) + .json({ success: false, message: 'User cannot be found' }) + } else { + const userUID = user.uid + const userDN = user.dn + const branch = userDN.split(',')[2].replace('ou=', '') + /* const response = await groupService.getAdminsGroups(branch) */ + const isAdmin = user.right === 'Todos' + const last_time_logged = await profileService.getLastLoginByUsername( + user.uid + ) + const loginInfo = await profileService.updateLastTimeLogged(user.uid) + console.log('loginInfo', loginInfo) - req.login(user, (loginErr) => { - if (loginErr) { - return next(loginErr) + const payload = { + sub: user.uid, + dn: user.dn, + uid: user.uid, + firstname: user.givenName, + lastname: user.sn, + fullname: user.cn, + email: user.mail, + ci: user.CI, + roles: isAdmin ? ['admin', 'user'] : ['user'], + last_time_logged, } - _insertFunc(user).then((user) => { - const data = { - token: token, - refreshToken: refreshToken, - user: userObj, + + const userObj = { ...user } + const token = signToken(payload, { expiresIn: '45 minutes' }) + const refreshToken = signToken(payload, { expiresIn: '1 day' }) + + req.login(user, (loginErr) => { + if (loginErr) { + return next(loginErr) } - profileService.updateLastTimeLogged(user.uid) - return responseSuccess(res, 'authentication succeeded', data) + _insertFunc(user).then((user) => { + const data = { + token: token, + refreshToken: refreshToken, + user: userObj, + } + return responseSuccess(res, 'authentication succeeded', data) + }) }) - }) + } } - })(req, res, next) + )(req, res, next) } module.exports.init = init diff --git a/src/schemas/user.schema.js b/src/schemas/user.schema.js index 53b77444..2639182b 100644 --- a/src/schemas/user.schema.js +++ b/src/schemas/user.schema.js @@ -1,19 +1,35 @@ -let mongoose = require('mongoose') +const mongoose = require('mongoose') -/////////////////// -let userSchema = mongoose.Schema( +const loginRecordSchema = new mongoose.Schema( { - // these fields are from ldap + timestamp: { type: Date, required: true }, + }, + { + _id: false, // Disable automatic generation of _id for subdocuments + } +) + +const userSchema = new mongoose.Schema( + { + // Fields from LDAP username: { type: String, lowercase: true }, - cn: { type: String}, - sn: { type: String}, - dn: {type: String}, - mail: { type: String, lowercase: true} + cn: { type: String }, + sn: { type: String }, + dn: { type: String }, + mail: { type: String, lowercase: true }, + + // Array of login records + registry: [ + { + timestamp: { type: Date, required: true }, + }, + ], }, { - timestamps: true + timestamps: true, } ) -let User = mongoose.model('User', userSchema) -module.exports.User = User \ No newline at end of file +const User = mongoose.model('User', userSchema) + +module.exports.User = User diff --git a/src/services/profile.services.js b/src/services/profile.services.js index e0621240..bc1a7351 100644 --- a/src/services/profile.services.js +++ b/src/services/profile.services.js @@ -67,13 +67,26 @@ const ProfileServices = () => { const updatedUser = await User.findOneAndUpdate( { username }, - { updatedAt: currentDate }, + { + updatedAt: currentDate, + $push: { + registry: { + timestamp: currentDate, + }, + }, // Add new login record + }, { new: true } // Return the updated document ).exec() if (!updatedUser) { return 'User not found' } + + // Ensure that loginRecords is an array (initialize if needed) + if (!Array.isArray(updatedUser.loginRecords)) { + updatedUser.loginRecords = [] + } + const lastLoginDate = updatedUser.updatedAt.toLocaleString() return lastLoginDate } catch (error) { From 9dea5da469c95647410d121a72cd1f4bd6fdf429 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 21 Aug 2023 20:06:18 -0400 Subject: [PATCH 032/229] Send login Info by jwt --- src/modules/authentication/LdapAuth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 005b91a0..d3541b4c 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -217,7 +217,6 @@ var login = function (req, res, next) { user.uid ) const loginInfo = await profileService.updateLastTimeLogged(user.uid) - console.log('loginInfo', loginInfo) const payload = { sub: user.uid, @@ -230,6 +229,7 @@ var login = function (req, res, next) { ci: user.CI, roles: isAdmin ? ['admin', 'user'] : ['user'], last_time_logged, + loginInfo, } const userObj = { ...user } From 1ebb9d26484231d08892ad639803334c507ef479 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 15:22:52 -0400 Subject: [PATCH 033/229] add dnHelpers and pass groups, base and uid by jwt --- src/helpers/dnHelper.js | 31 +++++++++++++++++++++++++- src/modules/authentication/LdapAuth.js | 13 +++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/helpers/dnHelper.js b/src/helpers/dnHelper.js index 354f313b..e5aae28f 100644 --- a/src/helpers/dnHelper.js +++ b/src/helpers/dnHelper.js @@ -3,4 +3,33 @@ const getFacultyFromDN = (dn) => { return branch } -module.exports = { getFacultyFromDN } +// Function to extract uid from an LDAP DN +function extractUidFromDn(dn) { + const uidMatches = dn.match(/uid=([^,]+)/) + return uidMatches ? uidMatches[1] : null +} + +// Function to extract groups from an LDAP DN +function extractGroupsFromDn(dn) { + const groupMatches = dn.match(/ou=([^,]+)/g) + if (groupMatches) { + return groupMatches.map((group) => group.replace('ou=', '')) + } + return [] +} + +// Function to extract the base from an LDAP DN +function extractBaseFromDn(dn) { + const baseMatches = dn.match(/dc=[^,]+/g) + if (baseMatches) { + return baseMatches.join(',') + } + return '' +} + +module.exports = { + extractBaseFromDn, + extractUidFromDn, + extractGroupsFromDn, + getFacultyFromDN, +} diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index d3541b4c..a538f641 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -11,6 +11,13 @@ const GroupServices = require('../../services/group.services') const User = require('../../schemas/user.schema').User const ProfileServices = require('../../services/profile.services') +/* helpers */ +const { + extractBaseFromDn, + extractGroupsFromDn, + extractUidFromDn, +} = require('../../helpers/dnHelper') + const { signToken } = require('../../utils/authentication/tokens/token_sign') const { responseSuccess, @@ -217,11 +224,17 @@ var login = function (req, res, next) { user.uid ) const loginInfo = await profileService.updateLastTimeLogged(user.uid) + // Example usage + const ldapDn = user.dn + const groups = extractGroupsFromDn(ldapDn) + const base = extractBaseFromDn(ldapDn) const payload = { sub: user.uid, dn: user.dn, uid: user.uid, + groups: groups, + base: base, firstname: user.givenName, lastname: user.sn, fullname: user.cn, From 3c36f542382d35922b1d4369a7b4e21e24b5bf09 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 17:31:49 -0400 Subject: [PATCH 034/229] add src require alias --- server.js => index.js | 1 + package-lock.json | 392 +++++++++++++++++++++++++++++------------- package.json | 11 +- 3 files changed, 278 insertions(+), 126 deletions(-) rename server.js => index.js (98%) diff --git a/server.js b/index.js similarity index 98% rename from server.js rename to index.js index 33be2d8c..18703278 100644 --- a/server.js +++ b/index.js @@ -1,5 +1,6 @@ /* jshint node:true */ /* global require */ +require('module-alias/register') const CONFIG = require('./src/config/config.js') const bodyParser = require('body-parser') const express = require('express') diff --git a/package-lock.json b/package-lock.json index 34b7ade8..d2b9fe40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,6 +55,7 @@ "jest": "^29.5.0", "jsdom": "^21.1.1", "jsdom-global": "^3.0.2", + "module-alias": "^2.2.3", "mongoose": "^6.5.2", "nodemon": "^2.0.22", "superagent": "^8.0.0", @@ -75,17 +76,89 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", @@ -164,12 +237,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", - "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", "dev": true, "dependencies": { - "@babel/types": "^7.21.3", + "@babel/types": "^7.22.10", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -236,34 +309,34 @@ "dev": true }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -322,30 +395,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -375,13 +448,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -460,9 +533,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", - "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.13.tgz", + "integrity": "sha512-3l6+4YOvc9wx7VlCSw4yQfcBo01ECA8TicQfbnCPuCEpRQrf+gTUyGdxNw+pyTUyywp6JRD1w0YQs9TpBXYlkw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -649,33 +722,33 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", - "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.3", - "@babel/types": "^7.21.3", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", + "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.11", + "@babel/types": "^7.22.11", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -707,13 +780,13 @@ "dev": true }, "node_modules/@babel/types": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", - "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", + "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -5273,6 +5346,12 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/module-alias": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", + "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==", + "dev": true + }, "node_modules/mongodb": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.8.1.tgz", @@ -7696,12 +7775,71 @@ } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { @@ -7763,12 +7901,12 @@ } }, "@babel/generator": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", - "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", "dev": true, "requires": { - "@babel/types": "^7.21.3", + "@babel/types": "^7.22.10", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -7824,28 +7962,28 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true }, "@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { @@ -7889,24 +8027,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true }, "@babel/helper-validator-option": { @@ -7927,13 +8065,13 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -7996,9 +8134,9 @@ } }, "@babel/parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", - "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.13.tgz", + "integrity": "sha512-3l6+4YOvc9wx7VlCSw4yQfcBo01ECA8TicQfbnCPuCEpRQrf+gTUyGdxNw+pyTUyywp6JRD1w0YQs9TpBXYlkw==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -8128,30 +8266,30 @@ } }, "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/traverse": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", - "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.3", - "@babel/types": "^7.21.3", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", + "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.11", + "@babel/types": "^7.22.11", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -8174,13 +8312,13 @@ } }, "@babel/types": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", - "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", + "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" } }, @@ -11729,6 +11867,12 @@ "minimist": "^1.2.6" } }, + "module-alias": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", + "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==", + "dev": true + }, "mongodb": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.8.1.tgz", diff --git a/package.json b/package.json index 28bc8fc4..6da63b5a 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "jest": "^29.5.0", "jsdom": "^21.1.1", "jsdom-global": "^3.0.2", + "module-alias": "^2.2.3", "mongoose": "^6.5.2", "nodemon": "^2.0.22", "superagent": "^8.0.0", @@ -70,7 +71,13 @@ "scripts": { "test": "jest --forceExit --detectOpenHandles --watchAll --maxWorkers=1", "test:watch": "jest --watch", - "start": "node server.js", - "dev": "nodemon server.js" + "start": "node index.js", + "dev": "nodemon index.js" + }, + "_moduleAliases": { + "@root" : ".", + "@deep" : "src/some/very/deep/directory/or/file", + "@src" : "src", + "something" : "src/foo" } } From 8114d34d65c49d2fed186b002664b8cf1869bb6e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 18:44:11 -0400 Subject: [PATCH 035/229] filter users by group --- jsconfig.json | 9 ++ src/helpers/ldapUtils.js | 73 +++++++++ src/routes/user.routes.js | 273 ++++++++++++---------------------- src/services/user.services.js | 143 ++---------------- 4 files changed, 191 insertions(+), 307 deletions(-) create mode 100644 jsconfig.json create mode 100644 src/helpers/ldapUtils.js diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 00000000..acb5b353 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@src/*": ["src/*"] + } + } + } + \ No newline at end of file diff --git a/src/helpers/ldapUtils.js b/src/helpers/ldapUtils.js new file mode 100644 index 00000000..d616a215 --- /dev/null +++ b/src/helpers/ldapUtils.js @@ -0,0 +1,73 @@ +const ldapClient = require('@src/connections/LDAP_client') +const config = require('@src/config/config') + +// Bind to the LDAP server using appropriate credentials +const bindLdapClient = () => { + return new Promise((resolve, reject) => { + ldapClient.bind( + config.ldap.admin.username, + config.ldap.admin.password, + (err) => { + if (err) { + reject(err) + } else { + resolve() + } + } + ) + }) +} + +// Unbind the LDAP client to release resources +const unbindLdapClient = () => { + ldapClient.unbind() +} + +const getObject = (arr) => { + const newObj = {} + arr.forEach((obj) => { + newObj[obj.type] = obj.values.length === 1 ? obj.values[0] : obj.values + }) + return newObj +} + +const transform = (entry) => { + const data = getObject(entry.pojo.attributes) + + return data +} + +// Perform a search using the provided filter and return the results +const performLdapSearch = async (baseDn, filter, attributes) => { + await bindLdapClient() // Bind before search + + const searchOptions = { + filter: filter, + scope: 'sub', + attributes, + } + + return new Promise((resolve, reject) => { + const searchResults = [] + + ldapClient.search(baseDn, searchOptions, (err, searchResponse) => { + if (err) { + reject(err) + return + } + + searchResponse.on('searchEntry', (entry) => { + searchResults.push(transform(entry)) + }) + + searchResponse.on('end', () => { + resolve(searchResults) + }) + }) + }) +} + +module.exports = { + performLdapSearch, + unbindLdapClient, +} diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index b2bdd40d..4bf1e284 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -1,191 +1,116 @@ const express = require('express') const router = express.Router() -const UserServices = require('../services/user.services') -const { responseSuccess, responseError } = require('../schemas/response.schema') -const validateResponse = require('../middlewares/validateResponse') -const { checkAuth, checkRoles } = require('../middlewares/auth.handler') +const UserServices = require('@src/services/user.services.js') +const { + responseSuccess, + responseError, +} = require('@src/schemas/response.schema') +const validateResponse = require('@src/middlewares/validateResponse') +const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') const service = UserServices() -const paginateResults = require('../utils/paginateResults') -const newUserSchema = require('../schemas/newUser.schema') +const paginateResults = require('@src/utils/paginateResults') const mongoose = require('mongoose') const Schema = mongoose.Schema -const getQueryToFilters = require('../utils/getQueryToFilters') +const getQueryToFilters = require('@src/utils/getQueryToFilters') +const { performLdapSearch } = require('@src/helpers/ldapUtils') +const config = require('@src/config/config') -// test -const Person = mongoose.model( - 'Person', - Schema({ - name: String, - email: String, - password: String, - }) -) +// Middleware for routes requiring checkAuth and checkRoles('admin') +router.use(checkAuth, checkRoles('admin')) -router.post('/artillery', async (req, res) => { - const user = req.body - const person = Person({ - name: user.name, - email: user.email, - password: user.password, - }) +// Middleware to handle common success and error responses +router.use(validateResponse) - await person.save() - res.status(200).json({ - success: true, - message: 'user added', - data: user, - }) +// Route handler for getting all users +router.get('/', (req, res) => { + const branch = req.query.branch || undefined + const filter = getQueryToFilters(req) + service + .getAll(branch, filter) + .then((data) => + responseSuccess( + res, + 'data fetched successfully', + paginateResults(data, req) + ) + ) + .catch((err) => responseError(res, err.message, err.errors)) }) -//Get all users -router.get( - '/', - checkAuth, - checkRoles('admin'), - validateResponse, - (req, res) => { - const branch = req.query.branch || undefined - const filter = getQueryToFilters(req) - service - .getAll(branch, filter) - .then((data) => - responseSuccess( - res, - 'data fetched succesfully', - paginateResults(data, req) - ) - ) - .catch((err) => responseError(res, err.message, err.errors)) - } -) -//Get students -router.get( - '/students', - checkAuth, - checkRoles('admin'), - validateResponse, - (req, res) => { - const branch = req.query.branch || undefined - const group = req.query.group || undefined - const year = req.query.year || undefined - service - .getStudents(branch, group, year) - .then((data) => - responseSuccess( - res, - 'data fetched succesfully', - paginateResults(data, req) - ) - ) - .catch((err) => responseError(res, err.message, err.errors)) - } -) -//Get professors -router.get( - '/professors', - checkAuth, - checkRoles('admin'), - validateResponse, - (req, res) => { - const page = req.query.page || undefined - const branch = req.query.branch || undefined - const orgRole = req.query.orgRole || undefined - const PCC = req.query.PCC || undefined - const researchGroup = req.query.researchGroup || undefined - service - .getProfessors(page, branch, orgRole, PCC, researchGroup) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) - } -) -router.get( - '/:username', - checkAuth, - checkRoles('admin'), - validateResponse, - (req, res) => { - service - .getByUsername(req.params.username) - .then((data) => { - responseSuccess(res, 'data fetched succesfully', data) - }) - .catch((err) => { - responseError(res, err.message, err.errors) - }) - } -) +// Get users by groups +router.get('/group/:group', async (req, res) => { + try { + const baseDN = `ou=usuarios,ou=${req.params.group},${config.ldap.base}` + const filter = 'objectClass=person' -/* get user by ci */ -router.get( - '/ci/:ci', - checkAuth, - checkRoles('admin'), - validateResponse, - (req, res) => { - service - .getByCI(req.params.ci) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) - } -) + // Define the LDAP attributes you want to retrieve + const attributes = [ + 'uid', + 'cn', + 'sn', + 'givenName', + 'mail', + 'telephoneNumber', + ] -/* get user by email */ -router.get( - '/email/:email', - checkAuth, - checkRoles('admin'), - validateResponse, - (req, res) => { - service - .getByEmail(req.params.email) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) + // Call the performLdapSearch function to retrieve users matching the group filters + const searchResults = await service.handleFilteredSearch( + baseDN, + filter, + attributes + ) + // Send the search results + res.json({ + success: true, + length: searchResults.length, + data: searchResults, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error fetching users', + error: error.message, + }) } -) +}) -/* get users by year */ -router.get( - '/year/:year', - checkAuth, - checkRoles('admin'), - validateResponse, - (req, res) => { - const branch = req.query.branch - service - .getByYear(req.params.year, branch) - .then((data) => - responseSuccess( - res, - 'data fetched succesfully', - paginateResults(data, req) - ) - ) - .catch((err) => responseError(res, err.message, err.errors)) - } -) +// Route handler for getting user by username +router.get('/:username', (req, res) => { + service + .getByUsername(req.params.username) + .then((data) => responseSuccess(res, 'data fetched successfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) +}) -//Add Users -router.post( - '/', - checkAuth, - checkRoles('admin'), - validateResponse, - (req, res) => { - const user = req.body - const branch = req.query.branch || undefined - if (!branch) { - responseError(res, 'branch is required') - } - newUserSchema - .validate(user) - .then(() => { - service - .addNewUser(user, branch) - .then((data) => responseSuccess(res, 'user added succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) - }) - .catch((err) => responseError(res, err.message, err.errors)) - } -) +// Route handler for getting user by CI +router.get('/ci/:ci', (req, res) => { + service + .getByCI(req.params.ci) + .then((data) => responseSuccess(res, 'data fetched successfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) +}) + +// Route handler for getting user by email +router.get('/email/:email', (req, res) => { + service + .getByEmail(req.params.email) + .then((data) => responseSuccess(res, 'data fetched successfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) +}) + +// Route handler for getting users by year +router.get('/year/:year', (req, res) => { + const branch = req.query.branch + service + .getByYear(req.params.year, branch) + .then((data) => + responseSuccess( + res, + 'data fetched successfully', + paginateResults(data, req) + ) + ) + .catch((err) => responseError(res, err.message, err.errors)) +}) module.exports = router diff --git a/src/services/user.services.js b/src/services/user.services.js index 27e91a0e..0eacd6c9 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -1,14 +1,13 @@ -const boom = require('@hapi/boom') require('dotenv').config({ path: __dirname + '/../../.env' }) -const config = require('../config/config') +const config = require('@src/config/config') const assert = require('assert') -const searchSchema = require('../utils/ldap_search_utils') -const searchSchemaWithoutFormat = require('../utils/searchSchemaWithNoFormat') -const ldapClient = require('../connections/LDAP_client') +const searchSchema = require('@src/utils/ldap_search_utils') +const searchSchemaWithoutFormat = require('@src/utils/searchSchemaWithNoFormat') +const ldapClient = require('@src/connections/LDAP_client') +const { performLdapSearch } = require('@src/helpers/ldapUtils') const UserServices = () => { const getAll = (branch, filter) => { - console.log('filter', filter) const dn = branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` const opts = { @@ -21,89 +20,9 @@ const UserServices = () => { return searchSchema(dn, opts) } - const getByYear = (year = 1, branch) => { - const dn = - branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` - const opts = { - filter: `(studentYear=${year})`, - scope: 'sub', - attributes: ['uid', 'cn', 'mail', 'ci'], - sizeLimit: config.ldap.sizeLimit, - } - return searchSchema(dn, opts) - } - - const getStudents = (branch, group, year) => { - const getfilter = () => { - let filter = '' - if (group !== undefined) { - filter = `(studentClassGroup=${parseInt(group)})` - } else if (year !== undefined) { - filter = `(studentClassGroup=${year}*)` - } else { - filter = `(objectClass=iesStudent)` - } - return filter - } - const dn = - branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` - const opts = { - filter: getfilter(), - scope: 'sub', - attributes: [ - 'uid', - 'cn', - 'mail', - 'ci', - 'studentClassGroup', - 'displayName', - ], - sizeLimit: config.ldap.sizeLimit, - } - return searchSchema(dn, opts) - } - - const getProfessors = ( - page, - branch, - orgRole = undefined, - PCC = undefined, - researchGroup = undefined - ) => { - const dn = - branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` - - var pccValue = PCC - - if (PCC !== undefined) { - pccValue = PCC === 'true' ? 'TRUE' : 'FALSE' - } - const basefilter = '(objectClass=iesEducationalStaff)' - const pCCfilter = PCC !== undefined ? `(PCC=${pccValue})` : '' - const roleFilter = orgRole !== undefined ? `(orgRole=${orgRole})` : '' - const researchGroupFilter = - researchGroup !== undefined ? `(researchGroup=${researchGroup})` : '' - - const filters = `(&${basefilter}${pCCfilter}${roleFilter}${researchGroupFilter})` - - const opts = { - filter: filters, - scope: 'sub', - attributes: [ - 'uid', - 'cn', - 'mail', - 'ci', - 'displayName', - 'orgRole', - 'PCC', - 'researchGroup', - ], - paged: page === undefined ? false : true, - pageNum: page === undefined ? undefined : parseInt(page), - sizeLimit: page === undefined ? 500 : undefined, - } - return searchSchema(dn, opts) + const handleFilteredSearch = (baseDN = config.ldap.base, ldapFilter, att) => { + const attributes = att === undefined ? ['dn', 'uid', 'cn'] : att + return performLdapSearch(baseDN, ldapFilter, attributes) } const getByUsername = (username) => { @@ -140,56 +59,14 @@ const UserServices = () => { return searchSchema(config.ldap.dn, opts) } - const addNewUser = (user, branch) => { - const dn = `uid=${user.uid},ou=usuarios,ou=${branch},${config.ldap.dn}` - - const entry = { - ...user, - objectclass: [ - 'top', - 'person', - 'posixAccount', - 'iesServices', - 'sambaSamAccount', - 'radiusprofile', - 'CourierMailAlias', - ], - homeDirectory: `/home/${user.uid}`, - gidNumber: [1000], - sambaSID: ['S-1-5-21-1255719363-1350762778-3568053751-513'], - sambaLMPassword: [new TextEncoder().encode('sambaLMPassword')], - sambaNTPassword: [new TextEncoder().encode('sambaNTPassword')], - loginShell: [new TextEncoder().encode('/bin/nosh')], - sambaacctflags: [new TextEncoder().encode('[U ]')], - sambapasswordhistory: [ - new TextEncoder().encode( - '000000000000000000000000000000000000000000000000000000 0000000000' - ), - ], - sambaprimarygroupsid: ['S-1-5-21-1255719363-1350762778-3568053751-513'], - sambapwdlastset: [new TextEncoder().encode('1308584948')], - } - return new Promise((resolve, reject) => { - ldapClient.add(dn, entry, (err) => { - if (err) { - console.log('ERROR', err) - reject(boom.badImplementation(err)) - } - resolve({ message: 'User added successfully' }) - }) - }) - } - return { getAll, - getByYear, + getByUsername, getByUsernameWithNoFormat, getByCI, getByEmail, - getStudents, - getProfessors, - addNewUser, + handleFilteredSearch, } } From d19063cfdf2523eea3079d2bf5368247c11584a6 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 18:48:03 -0400 Subject: [PATCH 036/229] fix imports on test --- src/__tests__/integration.test.js | 2 +- src/__tests__/unit.test.js | 39 ++++++++++++++----------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/__tests__/integration.test.js b/src/__tests__/integration.test.js index cc2fd051..58464b77 100644 --- a/src/__tests__/integration.test.js +++ b/src/__tests__/integration.test.js @@ -1,5 +1,5 @@ const request = require('supertest') -const app = require('../../server') // your Express app +const app = require('../../index') // your Express app const UserServices = require('../services/user.services') const mongoose = require('mongoose') const User = require('../schemas/user.schema').User diff --git a/src/__tests__/unit.test.js b/src/__tests__/unit.test.js index e9fa145f..39cf8037 100644 --- a/src/__tests__/unit.test.js +++ b/src/__tests__/unit.test.js @@ -1,9 +1,8 @@ -const app = require('../../server') +const app = require('../../index') const supertest = require('supertest') const config = require('../config/config') describe('Pruebas de funcionalidad', () => { - describe('Caso de uso 1: Verificar que la API pueda autenticar a los usuarios en el directorio LDAP', () => { it('debería autenticar a un usuario con credenciales válidas', async () => { const request = supertest(app) @@ -42,8 +41,7 @@ describe('Pruebas de funcionalidad', () => { }) }) - - describe('Caso de uso 2: Comprobar que la API pueda realizar búsquedas en el directorio LDAP y devolver resultados precisos', () => { +describe('Caso de uso 2: Comprobar que la API pueda realizar búsquedas en el directorio LDAP y devolver resultados precisos', () => { it('debería realizar una búsqueda de todos los estudiantes del grupo 31 y de la facultad de Ing. Informatica', async () => { const request = supertest(app) const { body } = await request.post('/login').send({ @@ -76,26 +74,23 @@ describe('Pruebas de funcionalidad', () => { expect(response.status).toBe(200) expect(response.body.message).toEqual('data fetched succesfully') }) - }) +describe('Caso de uso 3: verificar que la API puede proporcionar información de perfil personal del usuario a través de su JWT', () => { + it('debe proporcionar información de perfil personal del usuario a través de su JWT', async () => { + const request = supertest(app) + const { body } = await request.post('/login').send({ + username: config.ldap.username_bind, + password: config.ldap.password_bind, + }) + const { data } = body + const { token } = data - describe('Caso de uso 3: verificar que la API puede proporcionar información de perfil personal del usuario a través de su JWT', () => { - it('debe proporcionar información de perfil personal del usuario a través de su JWT', async () => { - const request = supertest(app) - const { body } = await request.post('/login').send({ - username: config.ldap.username_bind, - password: config.ldap.password_bind, - }) - const { data } = body - const { token } = data - - const response = await request - .get('/profile') - .set('Authorization', `Bearer ${token}`) + const response = await request + .get('/profile') + .set('Authorization', `Bearer ${token}`) - expect(response.status).toBe(200) - expect(response.body.message).toEqual('data fetched succesfully') - }) + expect(response.status).toBe(200) + expect(response.body.message).toEqual('data fetched succesfully') }) - +}) From 6986a0cdca31f8bf4601ccb2a7411ea20bc1667b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 20:10:34 -0400 Subject: [PATCH 037/229] apply pagination on user service --- src/routes/user.routes.js | 6 +++++- src/services/user.services.js | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 4bf1e284..e3554a18 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -14,6 +14,7 @@ const Schema = mongoose.Schema const getQueryToFilters = require('@src/utils/getQueryToFilters') const { performLdapSearch } = require('@src/helpers/ldapUtils') const config = require('@src/config/config') +const paginate = require('express-paginate') // Middleware for routes requiring checkAuth and checkRoles('admin') router.use(checkAuth, checkRoles('admin')) @@ -57,13 +58,16 @@ router.get('/group/:group', async (req, res) => { const searchResults = await service.handleFilteredSearch( baseDN, filter, - attributes + attributes, + req.query.page, + req.query.limit ) // Send the search results res.json({ success: true, length: searchResults.length, data: searchResults, + }) } catch (error) { res.status(500).json({ diff --git a/src/services/user.services.js b/src/services/user.services.js index 0eacd6c9..539cbb28 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -20,9 +20,17 @@ const UserServices = () => { return searchSchema(dn, opts) } - const handleFilteredSearch = (baseDN = config.ldap.base, ldapFilter, att) => { + const handleFilteredSearch = async ( + baseDN = config.ldap.base, + ldapFilter, + att, + page = 1, + limit = 10 + ) => { const attributes = att === undefined ? ['dn', 'uid', 'cn'] : att - return performLdapSearch(baseDN, ldapFilter, attributes) + const startIndex = (page - 1) * limit + const results = await performLdapSearch(baseDN, ldapFilter, attributes) + return results.slice(startIndex, limit * page) } const getByUsername = (username) => { From 4913e381f03b837a15c8de2a759fbd468a4d5330 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 20:37:21 -0400 Subject: [PATCH 038/229] convert query to filter function added : ) --- src/helpers/convertQueryToFilter.js | 19 ++++++++++ src/routes/user.routes.js | 55 ++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 src/helpers/convertQueryToFilter.js diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js new file mode 100644 index 00000000..b4d7685b --- /dev/null +++ b/src/helpers/convertQueryToFilter.js @@ -0,0 +1,19 @@ +/* thank you chatgpt */ +const createLdapFilterFromQuery = (query) => { + const filters = [] + + if (query.uid) { + filters.push(`uid=${query.uid}`) + } + + if (query.cn) { + filters.push(`cn=${query.cn}`) + } + + // Combine multiple filters using logical AND + const ldapFilter = `(${filters.join('')})` + + return ldapFilter +} + +module.exports = { createLdapFilterFromQuery } diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index e3554a18..e2d5a4ea 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -15,6 +15,9 @@ const getQueryToFilters = require('@src/utils/getQueryToFilters') const { performLdapSearch } = require('@src/helpers/ldapUtils') const config = require('@src/config/config') const paginate = require('express-paginate') +const { + createLdapFilterFromQuery, +} = require('@src/helpers/convertQueryToFilter') // Middleware for routes requiring checkAuth and checkRoles('admin') router.use(checkAuth, checkRoles('admin')) @@ -23,19 +26,46 @@ router.use(checkAuth, checkRoles('admin')) router.use(validateResponse) // Route handler for getting all users -router.get('/', (req, res) => { - const branch = req.query.branch || undefined - const filter = getQueryToFilters(req) - service - .getAll(branch, filter) - .then((data) => - responseSuccess( - res, - 'data fetched successfully', - paginateResults(data, req) - ) +router.get('/', async (req, res) => { + try { + const baseDN = `${config.ldap.base}` + const queryFilter = createLdapFilterFromQuery(req.query) + const ldapFilter = `(&(objectClass=person)${queryFilter})` + + console.log('queryFilter', queryFilter) + console.log('ldapFilter', ldapFilter) + + // Define the LDAP attributes you want to retrieve + const attributes = [ + 'uid', + 'cn', + 'sn', + 'givenName', + 'mail', + 'telephoneNumber', + ] + + // Call the performLdapSearch function to retrieve users matching the group filters + const searchResults = await service.handleFilteredSearch( + baseDN, + ldapFilter, + attributes, + req.query.page, + req.query.limit ) - .catch((err) => responseError(res, err.message, err.errors)) + // Send the search results + res.json({ + success: true, + length: searchResults.length, + data: searchResults, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error fetching users', + error: error.message, + }) + } }) // Get users by groups @@ -67,7 +97,6 @@ router.get('/group/:group', async (req, res) => { success: true, length: searchResults.length, data: searchResults, - }) } catch (error) { res.status(500).json({ From b93ad4d4961c36bb60bdf118a769329d8e1d022f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 23:09:12 -0400 Subject: [PATCH 039/229] replace get by username endpoint for generic query --- src/helpers/convertQueryToFilter.js | 4 ++++ src/routes/user.routes.js | 11 ----------- src/services/user.services.js | 18 ------------------ 3 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index b4d7685b..37e19b8d 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -10,6 +10,10 @@ const createLdapFilterFromQuery = (query) => { filters.push(`cn=${query.cn}`) } + if (query.username) { + filters.push(`uid=${query.username}`) + } + // Combine multiple filters using logical AND const ldapFilter = `(${filters.join('')})` diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index e2d5a4ea..51c34b65 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -32,9 +32,6 @@ router.get('/', async (req, res) => { const queryFilter = createLdapFilterFromQuery(req.query) const ldapFilter = `(&(objectClass=person)${queryFilter})` - console.log('queryFilter', queryFilter) - console.log('ldapFilter', ldapFilter) - // Define the LDAP attributes you want to retrieve const attributes = [ 'uid', @@ -107,14 +104,6 @@ router.get('/group/:group', async (req, res) => { } }) -// Route handler for getting user by username -router.get('/:username', (req, res) => { - service - .getByUsername(req.params.username) - .then((data) => responseSuccess(res, 'data fetched successfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) - // Route handler for getting user by CI router.get('/ci/:ci', (req, res) => { service diff --git a/src/services/user.services.js b/src/services/user.services.js index 539cbb28..a3f477b0 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -33,24 +33,6 @@ const UserServices = () => { return results.slice(startIndex, limit * page) } - const getByUsername = (username) => { - const opts = { - filter: `(uid=${username})`, - scope: 'sub', - timeLimit: 60, - } - return searchSchema(config.ldap.dn, opts) - } - - const getByUsernameWithNoFormat = (username) => { - const opts = { - filter: `(uid=${username})`, - scope: 'sub', - timeLimit: 60, - } - return searchSchemaWithoutFormat(config.ldap.dn, opts) - } - const getByCI = (ci) => { const opts = { filter: `(ci=${ci})`, From 5b45f944b0e20878e0c308cbdd696022d6ef0091 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 23:13:03 -0400 Subject: [PATCH 040/229] replace get by CI endpoint for generic query --- src/helpers/convertQueryToFilter.js | 4 ++++ src/routes/user.routes.js | 8 -------- src/services/user.services.js | 8 +++----- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 37e19b8d..3e312ca0 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -14,6 +14,10 @@ const createLdapFilterFromQuery = (query) => { filters.push(`uid=${query.username}`) } + if (query.ci) { + filters.push(`ci=${query.ci}`) + } + // Combine multiple filters using logical AND const ldapFilter = `(${filters.join('')})` diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 51c34b65..af00e9a8 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -104,14 +104,6 @@ router.get('/group/:group', async (req, res) => { } }) -// Route handler for getting user by CI -router.get('/ci/:ci', (req, res) => { - service - .getByCI(req.params.ci) - .then((data) => responseSuccess(res, 'data fetched successfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) - // Route handler for getting user by email router.get('/email/:email', (req, res) => { service diff --git a/src/services/user.services.js b/src/services/user.services.js index a3f477b0..75599628 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -33,10 +33,11 @@ const UserServices = () => { return results.slice(startIndex, limit * page) } - const getByCI = (ci) => { + const getByUsername = (username) => { const opts = { - filter: `(ci=${ci})`, + filter: `(uid=${username})`, scope: 'sub', + timeLimit: 60, } return searchSchema(config.ldap.dn, opts) } @@ -51,10 +52,7 @@ const UserServices = () => { return { getAll, - getByUsername, - getByUsernameWithNoFormat, - getByCI, getByEmail, handleFilteredSearch, } From cecbb546c4f3fbc7737df564f04c8e369dbbe728 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 23:15:41 -0400 Subject: [PATCH 041/229] replace get by email endpoint for generic query --- src/helpers/convertQueryToFilter.js | 4 ++++ src/routes/user.routes.js | 8 -------- src/services/user.services.js | 9 --------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 3e312ca0..8886fdda 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -18,6 +18,10 @@ const createLdapFilterFromQuery = (query) => { filters.push(`ci=${query.ci}`) } + if (query.email) { + filters.push(`maildrop=${query.email}`) + } + // Combine multiple filters using logical AND const ldapFilter = `(${filters.join('')})` diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index af00e9a8..b017fae3 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -104,14 +104,6 @@ router.get('/group/:group', async (req, res) => { } }) -// Route handler for getting user by email -router.get('/email/:email', (req, res) => { - service - .getByEmail(req.params.email) - .then((data) => responseSuccess(res, 'data fetched successfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) - // Route handler for getting users by year router.get('/year/:year', (req, res) => { const branch = req.query.branch diff --git a/src/services/user.services.js b/src/services/user.services.js index 75599628..09e6b45b 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -42,18 +42,9 @@ const UserServices = () => { return searchSchema(config.ldap.dn, opts) } - const getByEmail = (email) => { - const opts = { - filter: `(maildrop=${email})`, - scope: 'sub', - } - return searchSchema(config.ldap.dn, opts) - } - return { getAll, getByUsername, - getByEmail, handleFilteredSearch, } } From d97a95eea778ee87b8022345e78fc29f8761e954 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 29 Aug 2023 23:54:44 -0400 Subject: [PATCH 042/229] get all endpoint implemented --- src/config/config.js | 1 + src/connections/LDAP_client.js | 2 +- src/helpers/convertQueryToFilter.js | 4 +++ src/helpers/ldapUtils.js | 43 ++++++++++++++++------------- src/services/user.services.js | 25 +++++++++++------ 5 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/config/config.js b/src/config/config.js index 5f4368e7..62146caf 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -25,5 +25,6 @@ module.exports = { password: process.env.ADMIN_PASS, }, sizeLimit: parseInt(process.env.LDAP_SIZE_LIMIT), + timeLimit: parseInt(process.env.LDAP_TIME_LIMIT), }, } diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index 374115ca..2994aac9 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -9,7 +9,7 @@ const client = ldap.createClient({ }) client.on('connectError', (err) => { - console.log("ERROR") + console.log('Error trying to connect to LDAP', err) }) client.bind(config.ldap.admin.username, config.ldap.admin.password, (err) => { diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 8886fdda..35bd99e5 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -2,6 +2,10 @@ const createLdapFilterFromQuery = (query) => { const filters = [] + if (query && Object.keys(query).length === 2) { + return '' + } + if (query.uid) { filters.push(`uid=${query.uid}`) } diff --git a/src/helpers/ldapUtils.js b/src/helpers/ldapUtils.js index d616a215..b1b785dc 100644 --- a/src/helpers/ldapUtils.js +++ b/src/helpers/ldapUtils.js @@ -39,31 +39,36 @@ const transform = (entry) => { // Perform a search using the provided filter and return the results const performLdapSearch = async (baseDn, filter, attributes) => { - await bindLdapClient() // Bind before search - - const searchOptions = { - filter: filter, - scope: 'sub', - attributes, - } - return new Promise((resolve, reject) => { - const searchResults = [] + try { + bindLdapClient() // Bind before search - ldapClient.search(baseDn, searchOptions, (err, searchResponse) => { - if (err) { - reject(err) - return + const searchOptions = { + filter: filter, + scope: 'sub', + attributes, + timeLimit: config.ldap.timeLimit, } - searchResponse.on('searchEntry', (entry) => { - searchResults.push(transform(entry)) - }) + const searchResults = [] + + ldapClient.search(baseDn, searchOptions, (err, searchResponse) => { + if (err) { + reject(err) + return // Exit the function early in case of error + } + + searchResponse.on('searchEntry', (entry) => { + searchResults.push(transform(entry)) + }) - searchResponse.on('end', () => { - resolve(searchResults) + searchResponse.on('end', () => { + resolve(searchResults) + }) }) - }) + } catch (err) { + reject(err) + } }) } diff --git a/src/services/user.services.js b/src/services/user.services.js index 09e6b45b..ac933d6c 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -29,17 +29,24 @@ const UserServices = () => { ) => { const attributes = att === undefined ? ['dn', 'uid', 'cn'] : att const startIndex = (page - 1) * limit - const results = await performLdapSearch(baseDN, ldapFilter, attributes) - return results.slice(startIndex, limit * page) - } - const getByUsername = (username) => { - const opts = { - filter: `(uid=${username})`, - scope: 'sub', - timeLimit: 60, + try { + const results = await performLdapSearch(baseDN, ldapFilter, attributes) + + return results.slice(startIndex, limit * page) + } catch (err) { + console.error(err) + throw err } - return searchSchema(config.ldap.dn, opts) + } + + const getByUsername = async (username) => { + const results = await performLdapSearch( + config.ldap.base, + `(uid=${username})` + ) + console.log('results', results) + return results } return { From d5a75c602e6b5008f940380b663e78af86c1604f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 00:30:10 -0400 Subject: [PATCH 043/229] auth login fixed --- src/helpers/ldapUtils.js | 3 ++- src/modules/authentication/LdapAuth.js | 16 ++++++++-------- src/services/user.services.js | 9 +++++++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/helpers/ldapUtils.js b/src/helpers/ldapUtils.js index b1b785dc..3d6ac46a 100644 --- a/src/helpers/ldapUtils.js +++ b/src/helpers/ldapUtils.js @@ -33,7 +33,8 @@ const getObject = (arr) => { const transform = (entry) => { const data = getObject(entry.pojo.attributes) - + data.objectName = entry.pojo.objectName + data.dn = entry.pojo.objectName return data } diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index a538f641..4e66b2b7 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -83,16 +83,13 @@ var init = function ( } let username = req.body.username let password = req.body.password - let response = await userService.getByUsername(username) + let res = await userService.getByUsername(username) + const response = res[0] + // if user doesn't exists - if (response.attributes === undefined) { + if (response === undefined) { throw new Error('username or password incorrect') } - const branch = response.objectName - .toString() - .split(',')[2] - .replace('ou=', '') - // construct the parameter to pass in authenticate() function let options if (_backwardCompatible) { _usernameAttributeName = 'uid' @@ -119,7 +116,10 @@ var init = function ( if (opt.userDn) { options.userDn = opt.userDn .replace('{{username}}', username) - .replace('{{branch}}', branch) + .replace( + '{{branch}}', + response.objectName.split(',')[2].replace('ou=', '') + ) } if (opt.adminDn) { options.adminDn = opt.adminDn diff --git a/src/services/user.services.js b/src/services/user.services.js index ac933d6c..7ef0282c 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -31,7 +31,13 @@ const UserServices = () => { const startIndex = (page - 1) * limit try { - const results = await performLdapSearch(baseDN, ldapFilter, attributes) + const results = await performLdapSearch( + baseDN, + ldapFilter, + attributes, + page, + limit + ) return results.slice(startIndex, limit * page) } catch (err) { @@ -45,7 +51,6 @@ const UserServices = () => { config.ldap.base, `(uid=${username})` ) - console.log('results', results) return results } From 7c8707c141d79067c79ef659ea34ec0aa35e577e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 01:16:13 -0400 Subject: [PATCH 044/229] add query filtering to groups endpoint --- src/routes/user.routes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index b017fae3..e5f23b8b 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -69,7 +69,8 @@ router.get('/', async (req, res) => { router.get('/group/:group', async (req, res) => { try { const baseDN = `ou=usuarios,ou=${req.params.group},${config.ldap.base}` - const filter = 'objectClass=person' + const queryFilter = createLdapFilterFromQuery(req.query) + const ldapFilter = `(&(objectClass=person)${queryFilter})` // Define the LDAP attributes you want to retrieve const attributes = [ @@ -84,7 +85,7 @@ router.get('/group/:group', async (req, res) => { // Call the performLdapSearch function to retrieve users matching the group filters const searchResults = await service.handleFilteredSearch( baseDN, - filter, + ldapFilter, attributes, req.query.page, req.query.limit From b8ceb64319142612c62c8f01837bd7265d112c57 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 10:49:09 -0400 Subject: [PATCH 045/229] return all attributes on search --- src/routes/user.routes.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index e5f23b8b..6d94a553 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -33,14 +33,7 @@ router.get('/', async (req, res) => { const ldapFilter = `(&(objectClass=person)${queryFilter})` // Define the LDAP attributes you want to retrieve - const attributes = [ - 'uid', - 'cn', - 'sn', - 'givenName', - 'mail', - 'telephoneNumber', - ] + const attributes = null // Call the performLdapSearch function to retrieve users matching the group filters const searchResults = await service.handleFilteredSearch( From 17932b852599411f24894c27b8b5bd15e8a6bb20 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 10:49:33 -0400 Subject: [PATCH 046/229] search group by name endpoint --- src/routes/group.routes.js | 22 ++++++++++++++++------ src/services/group.services.js | 29 ++++++++++++++++++----------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index 3dd8625e..7d4123f2 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -7,13 +7,23 @@ const { checkAuth } = require('../middlewares/auth.handler') const service = GroupServices() //Get all users -router.get('/', checkAuth, validateResponse, (req, res) => { - const branch = req.query.branch - service - .getAll(branch) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) +router.get('/:group', checkAuth, validateResponse, async (req, res) => { + try { + const group = req.params.group + const response = await service.getGroup(group) + res.json({ + success: true, + data: response, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error fetching group', + error: `It seems that the group does not exist.`, + }) + } }) + router.get('/admins', checkAuth, validateResponse, (req, res) => { const branch = req.query.branch service diff --git a/src/services/group.services.js b/src/services/group.services.js index d57afd38..abdbc8b7 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -1,20 +1,27 @@ const boom = require('@hapi/boom') require('dotenv').config({ path: __dirname + '/../../.env' }) -const ldap = require('../connections/LDAP_client') +const ldap = require('@src/connections/LDAP_client') const LDAP = require('ldapjs') -const config = require('../config/config') +const config = require('@src/config/config') const assert = require('assert') -const searchSchema = require('../utils/ldap_search_utils') +const searchSchema = require('@src/utils/ldap_search_utils') +const { performLdapSearch } = require('@src/helpers/ldapUtils') const GroupServices = () => { - const getAll = (branch) => { - const dn = - branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` - const opts = { - filter: `(objectClass=posixGroup)`, - scope: 'sub', + const getGroup = async (group) => { + try { + const baseDN = config.ldap.base + const ldapFilter = `(ou=${group})` + const results = await performLdapSearch(baseDN, ldapFilter) + if (results[0] === undefined) { + throw new Error('No existe ese grupo') + } else { + return results + } + } catch (err) { + console.error(err) + throw err } - return searchSchema(dn, opts) } const getAdminsGroups = (branch) => { @@ -27,8 +34,8 @@ const GroupServices = () => { return searchSchema(dn, opts) } return { - getAll, getAdminsGroups, + getGroup, } } From 79e1ec5d59c20846a486d26cf9db3c4e3ede3fec Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 11:23:11 -0400 Subject: [PATCH 047/229] get by baseDN endpoint added --- src/modules/authentication/LdapAuth.js | 8 ++++-- src/routes/user.routes.js | 35 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 4e66b2b7..356de583 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -227,14 +227,18 @@ var login = function (req, res, next) { // Example usage const ldapDn = user.dn const groups = extractGroupsFromDn(ldapDn) - const base = extractBaseFromDn(ldapDn) + const rootBaseDN = extractBaseFromDn(ldapDn) + const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') + + console.log('user.dn') const payload = { sub: user.uid, dn: user.dn, uid: user.uid, groups: groups, - base: base, + base: rootBaseDN, + localBase: localBaseDN, firstname: user.givenName, lastname: user.sn, fullname: user.cn, diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 6d94a553..cb9fa4eb 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -98,6 +98,41 @@ router.get('/group/:group', async (req, res) => { } }) +router.get('/baseDN', async (req, res) => { + try { + const baseDN = req.body.baseDN + if (!baseDN) { + throw new Error('Value of the baseDN has not been sent correctly') + } + const queryFilter = createLdapFilterFromQuery(req.query) + const ldapFilter = `(&(objectClass=person)${queryFilter})` + + // Define the LDAP attributes you want to retrieve + const attributes = null + + // Call the performLdapSearch function to retrieve users matching the group filters + const searchResults = await service.handleFilteredSearch( + baseDN, + ldapFilter, + attributes, + req.query.page, + req.query.limit + ) + // Send the search results + res.json({ + success: true, + length: searchResults.length, + data: searchResults, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error fetching users', + error: error.message, + }) + } +}) + // Route handler for getting users by year router.get('/year/:year', (req, res) => { const branch = req.query.branch From eb3915be50d9b1dd1d6fa6d689baddc57e46b0a5 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 11:55:52 -0400 Subject: [PATCH 048/229] update get by baseDN to be a post petition --- src/routes/user.routes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index cb9fa4eb..3e410654 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -98,7 +98,7 @@ router.get('/group/:group', async (req, res) => { } }) -router.get('/baseDN', async (req, res) => { +router.post('/baseDN', async (req, res) => { try { const baseDN = req.body.baseDN if (!baseDN) { From 0de394621a07e8130d4c197b222d14fce3266f58 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 13:26:41 -0400 Subject: [PATCH 049/229] massive file clean --- index.js | 12 +-- src/__tests__/integration.test.js | 35 ------- src/__tests__/performance/artillery.yml | 27 ------ src/__tests__/performance/performance.test.js | 0 src/__tests__/shapiro.test.js | 3 - src/__tests__/unit.test.js | 96 ------------------- src/modules/authentication/LdapAuth.js | 1 - src/routes/auth.routes.js | 10 -- src/routes/group.routes.js | 8 -- src/routes/routes.js | 4 - src/routes/tree.routes.js | 14 --- src/routes/user.routes.js | 21 ---- src/schemas/newUser.schema.js | 15 --- src/services/auth.services.js | 6 -- src/services/group.services.js | 19 ---- src/services/tree.services.js | 54 ----------- src/services/user.services.js | 18 ---- src/utils/getQueryToFilters.js | 27 ------ src/utils/ldap_search_utils.js | 50 ---------- src/utils/paginateResults.js | 27 ------ src/utils/searchSchemaWithNoFormat.js | 47 --------- src/utils/transform_user_schema.js | 15 --- 22 files changed, 6 insertions(+), 503 deletions(-) delete mode 100644 src/__tests__/integration.test.js delete mode 100644 src/__tests__/performance/artillery.yml delete mode 100644 src/__tests__/performance/performance.test.js delete mode 100644 src/__tests__/shapiro.test.js delete mode 100644 src/__tests__/unit.test.js delete mode 100644 src/routes/auth.routes.js delete mode 100644 src/routes/tree.routes.js delete mode 100644 src/schemas/newUser.schema.js delete mode 100644 src/services/auth.services.js delete mode 100644 src/services/tree.services.js delete mode 100644 src/utils/getQueryToFilters.js delete mode 100644 src/utils/ldap_search_utils.js delete mode 100644 src/utils/paginateResults.js delete mode 100644 src/utils/searchSchemaWithNoFormat.js delete mode 100644 src/utils/transform_user_schema.js diff --git a/index.js b/index.js index 18703278..44c620ad 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,17 @@ /* jshint node:true */ /* global require */ require('module-alias/register') -const CONFIG = require('./src/config/config.js') +const CONFIG = require('@src/config/config.js') const bodyParser = require('body-parser') const express = require('express') -const addRoutes = require('./src/routes/routes.js') -const sessionMiddleWare = require('./src/middlewares/session.handler.js') -const addLoggerMiddleware = require('./src/middlewares/logger.handler.js') -const ldap_initialization = require('./src/utils/ldap_initialization.js') +const addRoutes = require('@src/routes/routes.js') +const sessionMiddleWare = require('@src/middlewares/session.handler.js') +const addLoggerMiddleware = require('@src/middlewares/logger.handler.js') +const ldap_initialization = require('@src/utils/ldap_initialization.js') const path = require('path') const cors = require('cors') const helmet = require('helmet') -const limiter = require('./src/middlewares/rate_limiter.handler.js') +const limiter = require('@src/middlewares/rate_limiter.handler.js') const paginate = require('express-paginate') const compression = require('compression') const cache = require('express-cache-ctrl') diff --git a/src/__tests__/integration.test.js b/src/__tests__/integration.test.js deleted file mode 100644 index 58464b77..00000000 --- a/src/__tests__/integration.test.js +++ /dev/null @@ -1,35 +0,0 @@ -const request = require('supertest') -const app = require('../../index') // your Express app -const UserServices = require('../services/user.services') -const mongoose = require('mongoose') -const User = require('../schemas/user.schema').User -const config = require('../config/config') -const ldapClient = require('../connections/LDAP_client') - -describe('Prueba de Integración: Verifique que el servidor pueda comunicarse correctamente con la base de datos y el servidor LDAP', () => { - it('should correctly communicate with the database and the LDAP server', async () => { - // Get all users from the 'users' collection in the 'ldapDB' database - const users = await User.find({}) - - for (const user of users) { - // For each user, check if their 'username' attribute matches a 'uid' in the LDAP server - const searchOptions = { - filter: `(uid=${user.username})`, - scope: 'sub', - } - - await new Promise((resolve, reject) => { - ldapClient.search(config.ldap.dn, searchOptions, (err, res) => { - if (err) return reject(err) - - res.on('searchEntry', (entry) => { - const uid = entry.pojo.attributes[0].values[0] - expect(uid).toBe(user.username) - }) - - res.on('end', resolve) - }) - }) - } - }) -}) diff --git a/src/__tests__/performance/artillery.yml b/src/__tests__/performance/artillery.yml deleted file mode 100644 index e1c134e3..00000000 --- a/src/__tests__/performance/artillery.yml +++ /dev/null @@ -1,27 +0,0 @@ -config: - target: 'http://127.0.0.1:4000' # URL base de tu API - phases: - - duration: 60 # Duración de la prueba en segundos - arrivalRate: 20 # Número de solicitudes por segundo - variables: - jwt: '' # Variable para almacenar el JWT capturado en el login -scenarios: - - flow: - - post: - url: '/login' # Endpoint para iniciar sesión y obtener un JWT - json: - username: 'agonzalezb' - password: '00092068426' - capture: - json: '$.data.token' - as: 'jwt' - - get: - url: '/users' # Endpoint para obtener información de usuarios utilizando un JWT - headers: - Authorization: 'Bearer {{ jwt }}' - - post: - url: '/users/artillery' # Endpoint para crear un nuevo usuario en el directorio LDAP - json: - username: 'newuser' - password: 'password' - email: 'newuser@example.com' \ No newline at end of file diff --git a/src/__tests__/performance/performance.test.js b/src/__tests__/performance/performance.test.js deleted file mode 100644 index e69de29b..00000000 diff --git a/src/__tests__/shapiro.test.js b/src/__tests__/shapiro.test.js deleted file mode 100644 index cd784af3..00000000 --- a/src/__tests__/shapiro.test.js +++ /dev/null @@ -1,3 +0,0 @@ -const jerzy = require('jerzy') -var v = new jerzy.Vector([1, 2, 3, 4, 20]) -console.log(JSON.stringify(jerzy.Normality.shapiroWilk(v), null, 4)) diff --git a/src/__tests__/unit.test.js b/src/__tests__/unit.test.js deleted file mode 100644 index 39cf8037..00000000 --- a/src/__tests__/unit.test.js +++ /dev/null @@ -1,96 +0,0 @@ -const app = require('../../index') -const supertest = require('supertest') -const config = require('../config/config') - -describe('Pruebas de funcionalidad', () => { - describe('Caso de uso 1: Verificar que la API pueda autenticar a los usuarios en el directorio LDAP', () => { - it('debería autenticar a un usuario con credenciales válidas', async () => { - const request = supertest(app) - const response = await request.post('/login').send({ - username: config.ldap.username_bind, - password: config.ldap.password_bind, - }) - - expect(response.status).toBe(200) - expect(response.body).toHaveProperty('data') - }) - - it('no debería autenticar a un usuario con credenciales inválidas', async () => { - const request = supertest(app) - const response = await request - .post('/login') - .send({ username: 'invalid-username', password: 'invalid-password' }) - - expect(response.status).toBe(401) - expect(response.body).toStrictEqual({ - success: false, - message: 'username or password incorrect', - }) - }) - - it('no debería autenticar a un usuario sin credenciales', async () => { - const request = supertest(app) - const response = await request.post('/login').send({}) - - expect(response.status).toBe(401) - expect(response.body).toStrictEqual({ - success: false, - message: 'username and password must be both provided', - }) - }) - }) -}) - -describe('Caso de uso 2: Comprobar que la API pueda realizar búsquedas en el directorio LDAP y devolver resultados precisos', () => { - it('debería realizar una búsqueda de todos los estudiantes del grupo 31 y de la facultad de Ing. Informatica', async () => { - const request = supertest(app) - const { body } = await request.post('/login').send({ - username: config.ldap.username_bind, - password: config.ldap.password_bind, - }) - const { data } = body - const { token } = data - const response = await request - .get('/users/students') - .set('Authorization', `Bearer ${token}`) - .query({ group: '31', branch: 'informatica' }) - - expect(response.status).toBe(200) - expect(response.body.message).toEqual('data fetched succesfully') - }) - - it('debería realizar una búsqueda de todos los profesores que pertenezcan al PCC y su grupo de investigacion sea IA', async () => { - const request = supertest(app) - const { body } = await request.post('/login').send({ - username: config.ldap.username_bind, - password: config.ldap.password_bind, - }) - const { data } = body - const { token } = data - const response = await request - .get('/users/professors') - .set('Authorization', `Bearer ${token}`) - .query({ PCC: 'true', researchGroup: 'IA' }) - expect(response.status).toBe(200) - expect(response.body.message).toEqual('data fetched succesfully') - }) -}) - -describe('Caso de uso 3: verificar que la API puede proporcionar información de perfil personal del usuario a través de su JWT', () => { - it('debe proporcionar información de perfil personal del usuario a través de su JWT', async () => { - const request = supertest(app) - const { body } = await request.post('/login').send({ - username: config.ldap.username_bind, - password: config.ldap.password_bind, - }) - const { data } = body - const { token } = data - - const response = await request - .get('/profile') - .set('Authorization', `Bearer ${token}`) - - expect(response.status).toBe(200) - expect(response.body.message).toEqual('data fetched succesfully') - }) -}) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 356de583..7622f68e 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -218,7 +218,6 @@ var login = function (req, res, next) { const userUID = user.uid const userDN = user.dn const branch = userDN.split(',')[2].replace('ou=', '') - /* const response = await groupService.getAdminsGroups(branch) */ const isAdmin = user.right === 'Todos' const last_time_logged = await profileService.getLastLoginByUsername( user.uid diff --git a/src/routes/auth.routes.js b/src/routes/auth.routes.js deleted file mode 100644 index 15740443..00000000 --- a/src/routes/auth.routes.js +++ /dev/null @@ -1,10 +0,0 @@ -const express = require('express') -const passport = require('passport') -const CustomStrategy = require('passport-custom').Strategy -const { authenticate } = require('ldap-authentication') -const AuthServices = require('../services/auth.services') -const service = AuthServices() - -const router = express.Router() - -module.exports = router diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index 7d4123f2..03712780 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -24,12 +24,4 @@ router.get('/:group', checkAuth, validateResponse, async (req, res) => { } }) -router.get('/admins', checkAuth, validateResponse, (req, res) => { - const branch = req.query.branch - service - .getAdminsGroups(branch) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) - module.exports = router diff --git a/src/routes/routes.js b/src/routes/routes.js index 5f37134e..5a829622 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -1,13 +1,9 @@ -const AuthRoutes = require('./auth.routes') -const TreeRoutes = require('./tree.routes') const UserRoutes = require('./user.routes') const GroupRoutes = require('./group.routes') const ProfileRoutes = require('./profile.routes') const LogsRoutes = require('./logs.routes') const addRoutes = (app) => { - app.use('/', AuthRoutes) - app.use('/tree', TreeRoutes) app.use('/users', UserRoutes) app.use('/groups', GroupRoutes) app.use('/profile', ProfileRoutes) diff --git a/src/routes/tree.routes.js b/src/routes/tree.routes.js deleted file mode 100644 index 2e65bfc5..00000000 --- a/src/routes/tree.routes.js +++ /dev/null @@ -1,14 +0,0 @@ -const express = require('express') -const router = express.Router() -const config = require('../config/config') -const { responseSuccess, responseError } = require('../schemas/response.schema') -const validateResponse = require('../middlewares/validateResponse') -const { checkAuth } = require('../middlewares/auth.handler') - -const TreeServices = require('../services/tree.services') -const service = TreeServices() - -const PROFESSORS_CLASS = config.ldap.objectClasses[5].name -const STUDENT_CLASS = config.ldap.objectClasses[3].name - -module.exports = router diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 3e410654..69e5dd67 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -8,13 +8,7 @@ const { const validateResponse = require('@src/middlewares/validateResponse') const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') const service = UserServices() -const paginateResults = require('@src/utils/paginateResults') -const mongoose = require('mongoose') -const Schema = mongoose.Schema -const getQueryToFilters = require('@src/utils/getQueryToFilters') -const { performLdapSearch } = require('@src/helpers/ldapUtils') const config = require('@src/config/config') -const paginate = require('express-paginate') const { createLdapFilterFromQuery, } = require('@src/helpers/convertQueryToFilter') @@ -133,19 +127,4 @@ router.post('/baseDN', async (req, res) => { } }) -// Route handler for getting users by year -router.get('/year/:year', (req, res) => { - const branch = req.query.branch - service - .getByYear(req.params.year, branch) - .then((data) => - responseSuccess( - res, - 'data fetched successfully', - paginateResults(data, req) - ) - ) - .catch((err) => responseError(res, err.message, err.errors)) -}) - module.exports = router diff --git a/src/schemas/newUser.schema.js b/src/schemas/newUser.schema.js deleted file mode 100644 index 3d230a52..00000000 --- a/src/schemas/newUser.schema.js +++ /dev/null @@ -1,15 +0,0 @@ -const joi = require('joi') - -const newUserSchema = joi.object({ - uid: joi.string(), - cn: joi.string(), - sn: joi.string(), - email: joi.string(), - ci: joi.string(), - studentClassGroup: joi.string(), - displayName: joi.string(), - password: joi.string(), - confirmPassword: joi.string(), -}) - -module.exports = newUserSchema diff --git a/src/services/auth.services.js b/src/services/auth.services.js deleted file mode 100644 index 2cbdac60..00000000 --- a/src/services/auth.services.js +++ /dev/null @@ -1,6 +0,0 @@ -const boom = require('@hapi/boom') -const passport = require('passport') - -const AuthServices = () => {} - -module.exports = AuthServices diff --git a/src/services/group.services.js b/src/services/group.services.js index abdbc8b7..e3e680aa 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -1,10 +1,5 @@ -const boom = require('@hapi/boom') require('dotenv').config({ path: __dirname + '/../../.env' }) -const ldap = require('@src/connections/LDAP_client') -const LDAP = require('ldapjs') const config = require('@src/config/config') -const assert = require('assert') -const searchSchema = require('@src/utils/ldap_search_utils') const { performLdapSearch } = require('@src/helpers/ldapUtils') const GroupServices = () => { @@ -23,20 +18,6 @@ const GroupServices = () => { throw err } } - - const getAdminsGroups = (branch) => { - const dn = - branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` - const opts = { - filter: `(cn=admins)`, - scope: 'sub', - } - return searchSchema(dn, opts) - } - return { - getAdminsGroups, - getGroup, - } } module.exports = GroupServices diff --git a/src/services/tree.services.js b/src/services/tree.services.js deleted file mode 100644 index 970ec244..00000000 --- a/src/services/tree.services.js +++ /dev/null @@ -1,54 +0,0 @@ -const boom = require('@hapi/boom') -const ldap = require('../connections/LDAP_client') -const config = require('../config/config') -const assert = require('assert') -const LDAP = require('ldapjs') -const searchSchema = require('../utils/ldap_search_utils') - -ldap.bind( - config.ldap.admin.username, - config.ldap.admin.password, - (err) => { - assert.ifError(err) - } -) -const PROFESSORS_CLASS = config.ldap.objectClasses[5].name -const STUDENT_CLASS = config.ldap.objectClasses[3].name - -const TreeServices = () => { - const getAllProffesors = () => { - const filter = `(objectclass=${PROFESSORS_CLASS})` - return searchSchema(filter) - } - - const getAllStudents = () => { - const filter = `(objectclass=${STUDENT_CLASS})` - return searchSchema(filter) - } - - const getUsersByYear = (year) => { - const filter = `(userYears=${year})` - return searchSchema(filter) - } - - const getUsersByBranch = (branch) => { - const dnByBranch = `ou=${branch},${config.ldap.dn}` - const filter = `objectclass=*` - return searchSchema(filter, dnByBranch) - } - - const getUsersByYearAndBranch = (year, branch, type) => { - const dnByBranch = `ou=${branch},${config.ldap.dn}` - const filter = `objectclass=${type} AND studentClassGroup>=${year * 10}` - } - - return { - getAllProffesors, - getAllStudents, - getUsersByYear, - getUsersByBranch, - getUsersByYearAndBranch, - } -} - -module.exports = TreeServices diff --git a/src/services/user.services.js b/src/services/user.services.js index 7ef0282c..940182c0 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -1,25 +1,8 @@ require('dotenv').config({ path: __dirname + '/../../.env' }) const config = require('@src/config/config') -const assert = require('assert') -const searchSchema = require('@src/utils/ldap_search_utils') -const searchSchemaWithoutFormat = require('@src/utils/searchSchemaWithNoFormat') -const ldapClient = require('@src/connections/LDAP_client') const { performLdapSearch } = require('@src/helpers/ldapUtils') const UserServices = () => { - const getAll = (branch, filter) => { - const dn = - branch === undefined ? config.ldap.dn : `ou=${branch},${config.ldap.dn}` - const opts = { - filter: filter, - scope: 'sub', - attributes: ['uid', 'cn', 'mail', 'ci', 'sex', 'year'], - sizeLimit: config.ldap.sizeLimit, - timeLimit: 50000, - } - return searchSchema(dn, opts) - } - const handleFilteredSearch = async ( baseDN = config.ldap.base, ldapFilter, @@ -55,7 +38,6 @@ const UserServices = () => { } return { - getAll, getByUsername, handleFilteredSearch, } diff --git a/src/utils/getQueryToFilters.js b/src/utils/getQueryToFilters.js deleted file mode 100644 index b9b6fe0a..00000000 --- a/src/utils/getQueryToFilters.js +++ /dev/null @@ -1,27 +0,0 @@ -const getQueryToFilters = (req) => { - let filters = [] - const { sex, condition, status, type, year, country, group, UJC, skinColor } = - req.query - if (sex !== undefined) filters.push(`sex=${sex.toString().toUpperCase()}`) - else if (condition !== undefined) - filters.push(`userCondition=${condition.toString().capitalize()}`) - else if (status !== undefined) - filters.push(`userStatus=${status.toString().capitalize()}`) - else if (type !== undefined) - filters.push(`userType=${type.toString().capitalize()}`) - else if (year !== undefined) filters.push(`studentYear=${year.toString()}`) - else if (country !== undefined) - filters.push(`contry=${country.toString().capitalize()}`) - else if (group !== undefined) - filters.push(`studentClassGroup=${group.toString()}`) - else if (UJC !== undefined) filters.push(`UJC=${UJC}`) - else if (skinColor !== undefined) - filters.push(`skinColor=${skinColor.toString().capitalize()}`) - - const filter = filters.join(',') - return filter === '' || filter === undefined - ? '(objectClass=person)' - : `(${filter})` -} - -module.exports = getQueryToFilters diff --git a/src/utils/ldap_search_utils.js b/src/utils/ldap_search_utils.js deleted file mode 100644 index e8ed6ac4..00000000 --- a/src/utils/ldap_search_utils.js +++ /dev/null @@ -1,50 +0,0 @@ -const config = require('../config/config') -const transformData = require('./transform_user_schema') -const ldap = require('../connections/LDAP_client') - -const searchSchema = (dn, opt) => { - let results = [] - let pageCount = 0 - let pagedEntries = [] - - return new Promise((resolve, reject) => { - ldap.search(dn, opt, (err, res) => { - res.on('searchEntry', (entry) => { - if (opt.sizeLimit !== undefined) { - if (results.length === opt.sizeLimit - 1) { - resolve(results.length === 1 ? results[0] : results) - } - } - results.push({ - objectName: entry.pojo.objectName, - attributes: transformData(entry), - }) - pagedEntries.push({ - objectName: entry.pojo.objectName, - attributes: transformData(entry), - }) - }) - res.on('page', (result, cb) => { - if (!!opt.pageNum) { - pageCount = pageCount + 1 - pageCount === opt.pageNum - ? resolve( - pagedEntries.length === 1 ? pagedEntries[0] : pagedEntries - ) - : (pagedEntries = []) - } - }) - res.on('searchReference', (referral) => { - console.log('referral: ' + referral.uris.join()) - }) - res.on('error', (resError) => - reject(new Error(`Search error: ${resError}`)) - ) - res.on('end', (result) => { - resolve(results.length === 1 ? results[0] : results) - }) - }) - }) -} - -module.exports = searchSchema diff --git a/src/utils/paginateResults.js b/src/utils/paginateResults.js deleted file mode 100644 index 4545e523..00000000 --- a/src/utils/paginateResults.js +++ /dev/null @@ -1,27 +0,0 @@ -function paginateResults(array, req) { - const page = parseInt(req.query.page) || 1 - const limit = parseInt(req.query.limit) || 10 - console.log('page', page) - console.log('limit', limit) - - const startIndex = (page - 1) * limit - const endIndex = page * limit - const results = {} - - results.results = array.slice(startIndex, endIndex) - results.length = results.results.length - - if (endIndex < array.length) { - results.next = { - page: page + 1, - } - } - if (startIndex > 0) { - results.previous = { - page: page - 1, - } - } - return results -} - -module.exports = paginateResults diff --git a/src/utils/searchSchemaWithNoFormat.js b/src/utils/searchSchemaWithNoFormat.js deleted file mode 100644 index 7fdf634b..00000000 --- a/src/utils/searchSchemaWithNoFormat.js +++ /dev/null @@ -1,47 +0,0 @@ -const config = require('../config/config') -const transformData = require('./transform_user_schema') -const ldap = require('../connections/LDAP_client') - -const searchSchemaWithoutFormat = (dn, opt) => { - let results = [] - let pageCount = 0 - let pagedEntries = [] - - return new Promise((resolve, reject) => { - ldap.search(dn, opt, (err, res) => { - res.on('searchEntry', (entry) => { - if (opt.sizeLimit !== undefined) { - if (results.length === opt.sizeLimit - 1) { - resolve(results.length === 1 ? results[0] : results) - } - } - results.push({ - objectName: entry.pojo.objectName, - attributes: entry.pojo, - }) - pagedEntries.push({ - objectName: entry.pojo.objectName, - attributes: entry.pojo, - }) - }) - res.on('page', (result, cb) => { - console.log('page finish') - pageCount = pageCount + 1 - pageCount === opt.pageNum - ? resolve(pagedEntries.length === 1 ? pagedEntries[0] : pagedEntries) - : (pagedEntries = []) - }) - res.on('searchReference', (referral) => { - console.log('referral: ' + referral.uris.join()) - }) - res.on('error', (resError) => - reject(new Error(`Search error: ${resError}`)) - ) - res.on('end', (result) => { - resolve(results.length === 1 ? results[0] : results) - }) - }) - }) -} - -module.exports = searchSchemaWithoutFormat diff --git a/src/utils/transform_user_schema.js b/src/utils/transform_user_schema.js deleted file mode 100644 index 30a58943..00000000 --- a/src/utils/transform_user_schema.js +++ /dev/null @@ -1,15 +0,0 @@ -const getObject = (arr) => { - const newObj = {} - arr.forEach((obj) => { - newObj[obj.type] = obj.values.length === 1 ? obj.values[0] : obj.values - }) - return newObj -} - -const transform = (entry) => { - const data = getObject(entry.pojo.attributes) - - return data -} - -module.exports = transform From f82804bcfc258f07fffb4892b1147524c133fbd6 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 13:37:59 -0400 Subject: [PATCH 050/229] refactor auth method --- src/modules/authentication/LdapAuth.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 7622f68e..3e9aae9f 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -15,18 +15,14 @@ const ProfileServices = require('../../services/profile.services') const { extractBaseFromDn, extractGroupsFromDn, - extractUidFromDn, } = require('../../helpers/dnHelper') const { signToken } = require('../../utils/authentication/tokens/token_sign') const { responseSuccess, - responseError, } = require('../../schemas/response.schema') -const validateResponse = require('../../middlewares/validateResponse') const userService = UserServices() -const groupService = GroupServices() const profileService = ProfileServices() var _backwardCompatible = false @@ -215,9 +211,6 @@ var login = function (req, res, next) { .status(401) .json({ success: false, message: 'User cannot be found' }) } else { - const userUID = user.uid - const userDN = user.dn - const branch = userDN.split(',')[2].replace('ou=', '') const isAdmin = user.right === 'Todos' const last_time_logged = await profileService.getLastLoginByUsername( user.uid @@ -229,8 +222,6 @@ var login = function (req, res, next) { const rootBaseDN = extractBaseFromDn(ldapDn) const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') - console.log('user.dn') - const payload = { sub: user.uid, dn: user.dn, From f4f2f727b43b616431507f26ad894e86006a1190 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 17:53:38 -0400 Subject: [PATCH 051/229] clean imports on user routes --- src/routes/user.routes.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 69e5dd67..bc183dbb 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -1,10 +1,6 @@ const express = require('express') const router = express.Router() const UserServices = require('@src/services/user.services.js') -const { - responseSuccess, - responseError, -} = require('@src/schemas/response.schema') const validateResponse = require('@src/middlewares/validateResponse') const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') const service = UserServices() From 262a3e0d3268daaa9bf91246f31dbb0a2a90ec1d Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 18:33:15 -0400 Subject: [PATCH 052/229] add userType filter endpoint --- src/constants/userTypes.js | 8 ++++++++ src/helpers/convertQueryToFilter.js | 24 ++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 src/constants/userTypes.js diff --git a/src/constants/userTypes.js b/src/constants/userTypes.js new file mode 100644 index 00000000..0c9d26fa --- /dev/null +++ b/src/constants/userTypes.js @@ -0,0 +1,8 @@ +const userTypes = ['Estudiante', 'Trabajador Docente', 'Trabajador'] + +const user_types_query = ['student', 'employee', 'docent_employee'] + +module.exports = { + userTypes, + user_types_query, +} diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 35bd99e5..cc0f78b0 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -1,4 +1,12 @@ /* thank you chatgpt */ +const { userTypes } = require('@src/constants/userTypes') + +const userTypeFilters = { + student: userTypes[0], + employee: userTypes[1], + docent_employee: userTypes[2], +} + const createLdapFilterFromQuery = (query) => { const filters = [] @@ -7,27 +15,31 @@ const createLdapFilterFromQuery = (query) => { } if (query.uid) { - filters.push(`uid=${query.uid}`) + filters.push(`(uid=${query.uid})`) } if (query.cn) { - filters.push(`cn=${query.cn}`) + filters.push(`(cn=${query.cn})`) } if (query.username) { - filters.push(`uid=${query.username}`) + filters.push(`(uid=${query.username})`) } if (query.ci) { - filters.push(`ci=${query.ci}`) + filters.push(`(ci=${query.ci})`) } if (query.email) { - filters.push(`maildrop=${query.email}`) + filters.push(`(maildrop=${query.email})`) + } + + if (query.userType && userTypeFilters[query.userType]) { + filters.push(`(userType=${userTypeFilters[query.userType]})`) } // Combine multiple filters using logical AND - const ldapFilter = `(${filters.join('')})` + const ldapFilter = `${filters.join('')}` return ldapFilter } From e68b75932f97a60fed2a4c42ad6cbf6072acd611 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 18:44:46 -0400 Subject: [PATCH 053/229] refact convet query to filter helper --- src/helpers/convertQueryToFilter.js | 47 ++++++++++++----------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index cc0f78b0..9865d29a 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -1,45 +1,36 @@ -/* thank you chatgpt */ const { userTypes } = require('@src/constants/userTypes') +const attributeFilters = { + uid: (value) => `uid=${value}`, + cn: (value) => `cn=${value}`, + username: (value) => `uid=${value}`, + ci: (value) => `ci=${value}`, + email: (value) => `maildrop=${value}`, +} + const userTypeFilters = { student: userTypes[0], - employee: userTypes[1], - docent_employee: userTypes[2], + docent_employee: userTypes[1], + employee: userTypes[2], } const createLdapFilterFromQuery = (query) => { const filters = [] - if (query && Object.keys(query).length === 2) { - return '' - } - - if (query.uid) { - filters.push(`(uid=${query.uid})`) - } - - if (query.cn) { - filters.push(`(cn=${query.cn})`) + for (const key in query) { + if (attributeFilters[key] && query[key]) { + filters.push(`(${attributeFilters[key](query[key])})`) + } else if (key === 'userType' && userTypeFilters[query[key]]) { + filters.push(`(userType=${userTypeFilters[query[key]]})`) + } } - if (query.username) { - filters.push(`(uid=${query.username})`) - } - - if (query.ci) { - filters.push(`(ci=${query.ci})`) - } - - if (query.email) { - filters.push(`(maildrop=${query.email})`) - } - - if (query.userType && userTypeFilters[query.userType]) { - filters.push(`(userType=${userTypeFilters[query.userType]})`) + if (filters.length === 0) { + return '' } // Combine multiple filters using logical AND - const ldapFilter = `${filters.join('')}` + const ldapFilter = filters.join('') return ldapFilter } From 5f1edb0d1769e54c28e7ff29585e7fb3ca8e5f96 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 18:59:50 -0400 Subject: [PATCH 054/229] add query validator middleware --- src/middlewares/queryValidator.js | 43 +++++++++++++++++++++++++++++++ src/routes/user.routes.js | 2 ++ 2 files changed, 45 insertions(+) create mode 100644 src/middlewares/queryValidator.js diff --git a/src/middlewares/queryValidator.js b/src/middlewares/queryValidator.js new file mode 100644 index 00000000..ee001d7a --- /dev/null +++ b/src/middlewares/queryValidator.js @@ -0,0 +1,43 @@ +// queryValidator.js + +const { user_types_query } = require('@src/constants/userTypes') + +const isValidEmail = (email) => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ + return emailRegex.test(email) +} + +const isValidUsername = (username) => { + const usernameRegex = /^[a-z]+\.[a-z]+$/ + return usernameRegex.test(username) +} + +const validateUserType = (userType) => { + if (!!userType && !user_types_query.includes(userType)) { + throw new Error( + `Invalid userType, only "${user_types_query[0]}", "${user_types_query[1]}", and "${user_types_query[2]}" are allowed.` + ) + } +} + +const validateQuery = (query) => { + validateUserType(query.userType) + + if (query.ci && !/^\d{8,10}$/.test(query.ci)) { + throw new Error('Invalid ci parameter') + } + + if (query.email && !isValidEmail(query.email)) { + throw new Error('Invalid email parameter') + } + + if (query.username && !isValidUsername(query.username)) { + throw new Error('Invalid username parameter') + } + + // Add more validations as needed + + return true +} + +module.exports = validateQuery diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index bc183dbb..bc938c4c 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -8,6 +8,7 @@ const config = require('@src/config/config') const { createLdapFilterFromQuery, } = require('@src/helpers/convertQueryToFilter') +const validateQuery = require('@src/middlewares/queryValidator') // Middleware for routes requiring checkAuth and checkRoles('admin') router.use(checkAuth, checkRoles('admin')) @@ -19,6 +20,7 @@ router.use(validateResponse) router.get('/', async (req, res) => { try { const baseDN = `${config.ldap.base}` + const isValid = validateQuery(req.query) const queryFilter = createLdapFilterFromQuery(req.query) const ldapFilter = `(&(objectClass=person)${queryFilter})` From 0bde674b4a30758ab1e431362d8c1834ae089151 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 30 Aug 2023 19:30:16 -0400 Subject: [PATCH 055/229] add query validation middleware to baseDN endpoint --- src/routes/user.routes.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index bc938c4c..c4dc027a 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -54,6 +54,7 @@ router.get('/', async (req, res) => { router.get('/group/:group', async (req, res) => { try { const baseDN = `ou=usuarios,ou=${req.params.group},${config.ldap.base}` + const isValid = validateQuery(req.query) const queryFilter = createLdapFilterFromQuery(req.query) const ldapFilter = `(&(objectClass=person)${queryFilter})` @@ -96,6 +97,7 @@ router.post('/baseDN', async (req, res) => { if (!baseDN) { throw new Error('Value of the baseDN has not been sent correctly') } + const isValid = validateQuery(req.query) const queryFilter = createLdapFilterFromQuery(req.query) const ldapFilter = `(&(objectClass=person)${queryFilter})` From f0ab6321a08726795c1410d47c59913eb1420349 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 4 Sep 2023 10:25:24 -0400 Subject: [PATCH 056/229] add db connection error validation to logger midd --- src/middlewares/logger.handler.js | 10 ++++++ src/middlewares/session.handler.js | 50 ++++++++++++++++-------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js index 7b8831bb..22c8d5ac 100644 --- a/src/middlewares/logger.handler.js +++ b/src/middlewares/logger.handler.js @@ -53,6 +53,16 @@ const addLoggerMiddleware = (app) => { // Register the morgan middleware app.use(morgan(logFormat)) + + // Add error connection handler + app.on('error', (err) => { + if ( + err.name === 'ServerSelectionError' && + err.message === 'connection timed out' + ) { + console.error('Error connecting to the database:', err.message) + } + }) } catch (error) { console.error('Error connecting to the database:', error.message) } diff --git a/src/middlewares/session.handler.js b/src/middlewares/session.handler.js index f5d2e4ce..f86e1266 100644 --- a/src/middlewares/session.handler.js +++ b/src/middlewares/session.handler.js @@ -3,31 +3,33 @@ const MongoStore = require('connect-mongo') const CONFIG = require('../config/config') const mongoose = require('mongoose') - //MONGODB configuration mongoose.Promise = Promise -const mongoClientPromise = mongoose - .connect(CONFIG.mongodb.url, { - user: CONFIG.mongodb.user, - pass: CONFIG.mongodb.pass, - authSource: 'admin', - }) - .then((m) => m.connection.getClient()) - .catch((e)=>console.log(e)) +try { + const mongoClientPromise = mongoose + .connect(CONFIG.mongodb.url, { + user: CONFIG.mongodb.user, + pass: CONFIG.mongodb.pass, + authSource: 'admin', + }) + .then((m) => m.connection.getClient()) -const sessionMiddleWare = session({ - secret: CONFIG.sessionSecret, - store: MongoStore.create({ - clientPromise: mongoClientPromise, - }), - resave: true, - saveUninitialized: true, - unset: 'destroy', - cookie: { - httpOnly: false, - maxAge: 1000 * 3600 * 24, - secure: false, // this need to be false if https is not used. Otherwise, cookie will not be sent. - }, -}) + const sessionMiddleWare = session({ + secret: CONFIG.sessionSecret, + store: MongoStore.create({ + clientPromise: mongoClientPromise, + }), + resave: true, + saveUninitialized: true, + unset: 'destroy', + cookie: { + httpOnly: false, + maxAge: 1000 * 3600 * 24, + secure: false, // this need to be false if https is not used. Otherwise, cookie will not be sent. + }, + }) -module.exports = sessionMiddleWare + module.exports = sessionMiddleWare +} catch (e) { + throw new Error(e) +} From 2e364cc1f94abd6bbd1487131e7b0d74b929cff6 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 4 Sep 2023 10:26:13 -0400 Subject: [PATCH 057/229] add error on reauthentication in less than 15 min --- src/modules/authentication/LdapAuth.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 3e9aae9f..edc5e2f4 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -18,9 +18,7 @@ const { } = require('../../helpers/dnHelper') const { signToken } = require('../../utils/authentication/tokens/token_sign') -const { - responseSuccess, -} = require('../../schemas/response.schema') +const { responseSuccess } = require('../../schemas/response.schema') const userService = UserServices() const profileService = ProfileServices() @@ -47,6 +45,20 @@ var _usernameAttributeName * @param {string} [loginUrl] - path to login page. Default: /login * @param {string} [logoutUrl] - path to logout page. Default: /logout */ + +var sessionStore = new Map() + +function checkLastAuthentication(req, res, next) { + const userId = req.body.username + console.log('sessionStore', sessionStore) + console.log('userID', userId) + const lastAuthTimestamp = sessionStore.get(userId) + if (!lastAuthTimestamp || Date.now() - lastAuthTimestamp >= 15 * 60 * 1000) { + next() + } else { + res.status(401).json({ message: 'Logout first before re-authenticating.' }) + } +} var init = function ( opt, ldapurl, @@ -165,7 +177,7 @@ var init = function ( router.use(passport.initialize()) router.use(passport.session()) // login - router.post(_loginUrl, login) + router.post(_loginUrl, checkLastAuthentication, login) } /** @@ -222,6 +234,8 @@ var login = function (req, res, next) { const rootBaseDN = extractBaseFromDn(ldapDn) const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') + sessionStore.set(user.uid, Date.now()) + const payload = { sub: user.uid, dn: user.dn, From 7d464b149cba8b09ecf1d5cf1863c771afdddbe7 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 4 Sep 2023 10:38:00 -0400 Subject: [PATCH 058/229] change let declarations to const --- src/modules/authentication/LdapAuth.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index edc5e2f4..ce66f960 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -46,12 +46,10 @@ var _usernameAttributeName * @param {string} [logoutUrl] - path to logout page. Default: /logout */ -var sessionStore = new Map() +const sessionStore = new Map() function checkLastAuthentication(req, res, next) { const userId = req.body.username - console.log('sessionStore', sessionStore) - console.log('userID', userId) const lastAuthTimestamp = sessionStore.get(userId) if (!lastAuthTimestamp || Date.now() - lastAuthTimestamp >= 15 * 60 * 1000) { next() @@ -89,16 +87,16 @@ var init = function ( if (!req.body.username || !req.body.password) { throw new Error('username and password must be both provided') } - let username = req.body.username - let password = req.body.password - let res = await userService.getByUsername(username) + const username = req.body.username + const password = req.body.password + const res = await userService.getByUsername(username) const response = res[0] // if user doesn't exists if (response === undefined) { throw new Error('username or password incorrect') } - let options + let options = {} if (_backwardCompatible) { _usernameAttributeName = 'uid' options = { @@ -137,7 +135,7 @@ var init = function ( } } // ldap authenticate the user - let user = await authenticate(options) + const user = await authenticate(options) // success done(null, user) } catch (error) { @@ -190,7 +188,7 @@ var init = function ( * @param {string} [loginUrl] - path to login page. Default: /login * @param {string} [logoutUrl] - path to logout page. Default: /logout */ -var initialize = function ( +const initialize = function ( opt, router, findFunc, @@ -205,7 +203,7 @@ var initialize = function ( * Customized login authentication handler to send {success: true} * on successful authenticate, or {success: false} on failed authenticate */ -var login = function (req, res, next) { +const login = function (req, res, next) { passport.authenticate( 'ldap', { From 3f2225a966fede09092cc9dcdf13e0e1b87f864c Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 11:01:30 -0400 Subject: [PATCH 059/229] add redis client --- package-lock.json | 146 ++++++++++++++++++++++++++++++++ package.json | 9 +- src/config/config.js | 4 + src/connections/redis_client.js | 41 +++++++++ 4 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 src/connections/redis_client.js diff --git a/package-lock.json b/package-lock.json index d2b9fe40..63ca48aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", + "redis": "^4.6.8", "simple-ldap-search": "^3.1.2", "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", @@ -1257,6 +1258,59 @@ "resolved": "https://registry.npmjs.org/@ldapjs/protocol/-/protocol-1.2.1.tgz", "integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ==" }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.9.tgz", + "integrity": "sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ==", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", + "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", + "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.3.tgz", + "integrity": "sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@sinclair/typebox": { "version": "0.25.24", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", @@ -2159,6 +2213,14 @@ "node": "*" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -3563,6 +3625,14 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6285,6 +6355,19 @@ "node": ">=8.10.0" } }, + "node_modules/redis": { + "version": "4.6.8", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.8.tgz", + "integrity": "sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ==", + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.9", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.3", + "@redis/time-series": "1.0.5" + } + }, "node_modules/require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -8712,6 +8795,46 @@ "resolved": "https://registry.npmjs.org/@ldapjs/protocol/-/protocol-1.2.1.tgz", "integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ==" }, + "@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "requires": {} + }, + "@redis/client": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.9.tgz", + "integrity": "sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ==", + "requires": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + } + }, + "@redis/graph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", + "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "requires": {} + }, + "@redis/json": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", + "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "requires": {} + }, + "@redis/search": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.3.tgz", + "integrity": "sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==", + "requires": {} + }, + "@redis/time-series": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", + "requires": {} + }, "@sinclair/typebox": { "version": "0.25.24", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", @@ -9416,6 +9539,11 @@ "mkdirp": ">= 0.0.1" } }, + "cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -10494,6 +10622,11 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -12591,6 +12724,19 @@ "picomatch": "^2.2.1" } }, + "redis": { + "version": "4.6.8", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.8.tgz", + "integrity": "sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ==", + "requires": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.9", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.3", + "@redis/time-series": "1.0.5" + } + }, "require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", diff --git a/package.json b/package.json index 6da63b5a..20512497 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", + "redis": "^4.6.8", "simple-ldap-search": "^3.1.2", "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", @@ -75,9 +76,9 @@ "dev": "nodemon index.js" }, "_moduleAliases": { - "@root" : ".", - "@deep" : "src/some/very/deep/directory/or/file", - "@src" : "src", - "something" : "src/foo" + "@root": ".", + "@deep": "src/some/very/deep/directory/or/file", + "@src": "src", + "something": "src/foo" } } diff --git a/src/config/config.js b/src/config/config.js index 62146caf..70396bf7 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -27,4 +27,8 @@ module.exports = { sizeLimit: parseInt(process.env.LDAP_SIZE_LIMIT), timeLimit: parseInt(process.env.LDAP_TIME_LIMIT), }, + redis: { + host: process.env.REDIS_HOST, + port: process.env.REDIS_PORT, + }, } diff --git a/src/connections/redis_client.js b/src/connections/redis_client.js new file mode 100644 index 00000000..6560ac97 --- /dev/null +++ b/src/connections/redis_client.js @@ -0,0 +1,41 @@ +const redis = require('redis') +const config = require('@src/config/config') + +// Create a Redis client +const client = redis.createClient({ + host: config.redis.host, // Docker Desktop container runs on localhost + port: parseInt(config.redis.port), // Default Redis port +}) + +// Test the connection +client.on('connect', () => { + console.log('Connected to Redis server') +}) + +// Handle errors +client.on('error', (err) => { + console.error('Redis Error:', err) +}) + +// Example: Set a key-value pair +/* client.set('myKey', 'myValue', (err, reply) => { + if (err) { + console.error('Redis Set Error:', err) + } else { + console.log('Set result:', reply) + } +}) + +// Example: Get a value by key +client.get('myKey', (err, reply) => { + if (err) { + console.error('Redis Get Error:', err) + } else { + console.log('Get result:', reply) + } +}) */ + +// Close the Redis connection (when needed) +// client.quit(); + +module.exports = { client } From 7a7c849c30dbfb857673e25050de3a18b43fba3f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 15:30:24 -0400 Subject: [PATCH 060/229] update method added --- src/helpers/ldapUtils.js | 33 +++++++++++++++++++++++++++++++++ src/routes/user.routes.js | 22 ++++++++++++++++++++++ src/services/user.services.js | 21 +++++++++++++++++++-- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/helpers/ldapUtils.js b/src/helpers/ldapUtils.js index 3d6ac46a..7679d098 100644 --- a/src/helpers/ldapUtils.js +++ b/src/helpers/ldapUtils.js @@ -1,5 +1,7 @@ +const ldap = require('ldapjs') const ldapClient = require('@src/connections/LDAP_client') const config = require('@src/config/config') +var assert = require('assert') // Bind to the LDAP server using appropriate credentials const bindLdapClient = () => { @@ -40,6 +42,7 @@ const transform = (entry) => { // Perform a search using the provided filter and return the results const performLdapSearch = async (baseDn, filter, attributes) => { + console.log('filter', filter) return new Promise((resolve, reject) => { try { bindLdapClient() // Bind before search @@ -73,7 +76,37 @@ const performLdapSearch = async (baseDn, filter, attributes) => { }) } +const performLdapUpdate = async (userDN, att, value) => { + console.log('userDN', userDN) + return new Promise((resolve, reject) => { + try { + bindLdapClient() // Bind before search + + const change = new ldap.Change({ + operation: 'replace', + modification: { + type: att, + values: [value], + }, + }) + + ldapClient.modify(userDN, change, (err) => { + if (err) { + console.log('error', err) + assert.ifError(err) + } else { + console.log('updated user') + resolve('updated User') + } + }) + } catch (err) { + reject(err) + } + }) +} + module.exports = { performLdapSearch, unbindLdapClient, + performLdapUpdate, } diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index c4dc027a..461d8037 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -127,4 +127,26 @@ router.post('/baseDN', async (req, res) => { } }) +router.put('/:username', async (req, res) => { + try { + const { att, value } = req.body + const username = req.params.username + + const updatedUser = await service.updateUser(username, att, value) + + // Send the search results + res.json({ + success: true, + message: 'User updated correctly', + data: updatedUser, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error updating users', + error: error.message, + }) + } +}) + module.exports = router diff --git a/src/services/user.services.js b/src/services/user.services.js index 940182c0..49a151a3 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -1,6 +1,9 @@ require('dotenv').config({ path: __dirname + '/../../.env' }) const config = require('@src/config/config') -const { performLdapSearch } = require('@src/helpers/ldapUtils') +const { + performLdapSearch, + performLdapUpdate, +} = require('@src/helpers/ldapUtils') const UserServices = () => { const handleFilteredSearch = async ( @@ -32,14 +35,28 @@ const UserServices = () => { const getByUsername = async (username) => { const results = await performLdapSearch( config.ldap.base, - `(uid=${username})` + `(uid=${username})`, + null ) return results } + const updateUser = async (username, att, value) => { + const results = await performLdapSearch( + config.ldap.base, + `(uid=${username})`, + [] + ) + const user = results[0] + + const updatedUser = await performLdapUpdate(user.dn, att, value) + return updatedUser + } + return { getByUsername, handleFilteredSearch, + updateUser, } } From d3e816b1162e71725a784544266453c44cd5dcd1 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 15:31:40 -0400 Subject: [PATCH 061/229] heckLastAuthentication commented --- src/modules/authentication/LdapAuth.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index ce66f960..50784306 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -49,13 +49,14 @@ var _usernameAttributeName const sessionStore = new Map() function checkLastAuthentication(req, res, next) { - const userId = req.body.username + /* const userId = req.body.username const lastAuthTimestamp = sessionStore.get(userId) if (!lastAuthTimestamp || Date.now() - lastAuthTimestamp >= 15 * 60 * 1000) { next() } else { res.status(401).json({ message: 'Logout first before re-authenticating.' }) - } + } */ + next() } var init = function ( opt, From 94cd3521818f8e7e16c6000c8f005ef0072fa646 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 17:31:25 -0400 Subject: [PATCH 062/229] redis test connection passed --- package-lock.json | 2301 ++++++++++++----------- package.json | 4 +- src/connections/__tests__/redis.test.js | 29 + 3 files changed, 1244 insertions(+), 1090 deletions(-) create mode 100644 src/connections/__tests__/redis.test.js diff --git a/package-lock.json b/package-lock.json index 63ca48aa..69abebc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", - "redis": "^4.6.8", + "redis-om": "^0.4.2", "simple-ldap-search": "^3.1.2", "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", @@ -53,7 +53,7 @@ "express": "^4.18.1", "express-session": "^1.17.3", "jasmine": "^4.3.0", - "jest": "^29.5.0", + "jest": "^29.6.4", "jsdom": "^21.1.1", "jsdom-global": "^3.0.2", "module-alias": "^2.2.3", @@ -64,12 +64,12 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -161,35 +161,35 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", - "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.15.tgz", + "integrity": "sha512-PtZqMmgRrvj8ruoEOIwVA3yoF91O+Hgw9o7DAUTNBA6Mo2jpu31clx9a7Nz/9JznqetTR6zwfC4L3LAjKQXUwA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.3", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.3", - "@babel/types": "^7.21.3", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.22.15", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.15", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -229,21 +229,21 @@ "dev": true }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", + "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.22.15", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -252,37 +252,20 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { @@ -295,9 +278,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -344,52 +327,52 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.15.tgz", + "integrity": "sha512-l1UiX4UyHSFsYt17iQ3Se5pQQZZHa22zyIXURmvkmLCD4t/aU+dvNWHatKac/D9Vm9UES7nvIqHs4jZqKviUmQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.15" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -417,32 +400,32 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", + "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -534,9 +517,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.13.tgz", - "integrity": "sha512-3l6+4YOvc9wx7VlCSw4yQfcBo01ECA8TicQfbnCPuCEpRQrf+gTUyGdxNw+pyTUyywp6JRD1w0YQs9TpBXYlkw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.15.tgz", + "integrity": "sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -606,12 +589,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -708,12 +691,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -723,33 +706,33 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.15.tgz", + "integrity": "sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -781,13 +764,13 @@ "dev": true }, "node_modules/@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz", + "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.15", "to-fast-properties": "^2.0.0" }, "engines": { @@ -857,16 +840,16 @@ } }, "node_modules/@jest/console": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", - "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", + "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "slash": "^3.0.0" }, "engines": { @@ -874,37 +857,37 @@ } }, "node_modules/@jest/core": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", - "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", + "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", "dev": true, "dependencies": { - "@jest/console": "^29.5.0", - "@jest/reporters": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.6.4", + "@jest/reporters": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-resolve-dependencies": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "jest-watcher": "^29.5.0", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-resolve-dependencies": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.4", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -921,89 +904,89 @@ } }, "node_modules/@jest/environment": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", - "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", + "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0" + "jest-mock": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", "dev": true, "dependencies": { - "expect": "^29.5.0", - "jest-snapshot": "^29.5.0" + "expect": "^29.6.4", + "jest-snapshot": "^29.6.4" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", - "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", + "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", - "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", + "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", - "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", + "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", "dev": true, "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/types": "^29.5.0", - "jest-mock": "^29.5.0" + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", - "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", + "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/console": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", @@ -1011,13 +994,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -1036,24 +1019,24 @@ } }, "node_modules/@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { - "@sinclair/typebox": "^0.25.16" + "@sinclair/typebox": "^0.27.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/source-map": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", - "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.15", + "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", "graceful-fs": "^4.2.9" }, @@ -1062,13 +1045,13 @@ } }, "node_modules/@jest/test-result": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", - "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", + "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", "dev": true, "dependencies": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.6.4", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -1077,14 +1060,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", - "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", + "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", "dev": true, "dependencies": { - "@jest/test-result": "^29.5.0", + "@jest/test-result": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.6.4", "slash": "^3.0.0" }, "engines": { @@ -1092,22 +1075,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", - "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", + "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", + "jest-haste-map": "^29.6.4", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -1118,12 +1101,12 @@ } }, "node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -1135,22 +1118,23 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, "engines": { "node": ">=6.0.0" @@ -1166,19 +1150,19 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@ldapjs/asn1": { @@ -1312,27 +1296,27 @@ } }, "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "node_modules/@socket.io/component-emitter": { @@ -1350,9 +1334,9 @@ } }, "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", "dev": true, "dependencies": { "@babel/parser": "^7.20.7", @@ -1382,12 +1366,12 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/cookie": { @@ -1441,12 +1425,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.28.tgz", "integrity": "sha512-UYmIeBnB0On70dN1iGCinsL1qH5JmIEJwa+3KX0Xw4HQJ8KA16ULlyTCNmnzfyzj/BlxZKmZLqp4TYdssnov1w==" }, - "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -1475,9 +1453,9 @@ } }, "node_modules/@types/yargs": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.23.tgz", - "integrity": "sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1720,15 +1698,15 @@ } }, "node_modules/babel-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", - "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", + "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", "dev": true, "dependencies": { - "@jest/transform": "^29.5.0", + "@jest/transform": "^29.6.4", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -1756,10 +1734,35 @@ "node": ">=8" } }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "dependencies": { "@babel/template": "^7.3.3", @@ -1795,12 +1798,12 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { @@ -1959,9 +1962,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "funding": [ { @@ -1971,13 +1974,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -2091,9 +2098,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001470", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001470.tgz", - "integrity": "sha512-065uNwY6QtHCBOExzbV6m236DDhYCCtPmQUCoQtwkVqzud8v5QPidoMr6CoMkC2nfp6nksjttqWQRRh75LqUmA==", + "version": "1.0.30001527", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz", + "integrity": "sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==", "dev": true, "funding": [ { @@ -2103,6 +2110,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -2174,9 +2185,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "node_modules/cliui": { @@ -2232,9 +2243,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, "node_modules/color": { @@ -2597,10 +2608,18 @@ "dev": true }, "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, "node_modules/deep-is": { "version": "0.1.4", @@ -2671,9 +2690,9 @@ } }, "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -2722,9 +2741,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/electron-to-chromium": { - "version": "1.4.340", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.340.tgz", - "integrity": "sha512-zx8hqumOqltKsv/MF50yvdAlPF9S/4PXbyfzJS6ZGhbddGkRegdwImmfSVqCkEziYzrIGZ/TlrzBND4FysfkDg==", + "version": "1.4.508", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz", + "integrity": "sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==", "dev": true }, "node_modules/emittery": { @@ -3086,16 +3105,16 @@ } }, "node_modules/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" + "@jest/expect-utils": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4080,9 +4099,9 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4188,42 +4207,48 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-source-maps": { @@ -4264,9 +4289,9 @@ "dev": true }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -4302,15 +4327,15 @@ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." }, "node_modules/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", + "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", "dev": true, "dependencies": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.6.4", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.5.0" + "jest-cli": "^29.6.4" }, "bin": { "jest": "bin/jest.js" @@ -4328,12 +4353,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", + "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", "dev": true, "dependencies": { "execa": "^5.0.0", + "jest-util": "^29.6.3", "p-limit": "^3.1.0" }, "engines": { @@ -4341,28 +4367,28 @@ } }, "node_modules/jest-circus": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", - "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", + "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", "dev": true, "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "dedent": "^0.7.0", + "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.5.0", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "p-limit": "^3.1.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.6.3", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -4372,21 +4398,21 @@ } }, "node_modules/jest-cli": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", - "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", + "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", "dev": true, "dependencies": { - "@jest/core": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-config": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -4406,31 +4432,31 @@ } }, "node_modules/jest-config": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", - "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", + "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", + "@jest/test-sequencer": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.4", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-circus": "^29.6.4", + "jest-environment-node": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -4451,24 +4477,24 @@ } }, "node_modules/jest-diff": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", - "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", + "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", + "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -4478,33 +4504,33 @@ } }, "node_modules/jest-each": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", - "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", + "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.5.0", - "pretty-format": "^29.5.0" + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", - "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", + "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4520,29 +4546,29 @@ } }, "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", - "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", + "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -4554,46 +4580,46 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", - "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", + "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", - "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", + "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", - "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", + "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -4602,14 +4628,14 @@ } }, "node_modules/jest-mock": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", - "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", + "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.5.0" + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4633,26 +4659,26 @@ } }, "node_modules/jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", - "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", + "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.6.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -4662,43 +4688,43 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", - "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", + "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", "dev": true, "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.5.0" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.4" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", - "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", + "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", "dev": true, "dependencies": { - "@jest/console": "^29.5.0", - "@jest/environment": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.6.4", + "@jest/environment": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-leak-detector": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-resolve": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-util": "^29.5.0", - "jest-watcher": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.4", + "jest-worker": "^29.6.4", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -4707,31 +4733,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", - "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/globals": "^29.5.0", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", + "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/globals": "^29.6.4", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -4740,46 +4766,58 @@ } }, "node_modules/jest-snapshot": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", - "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", + "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", + "@jest/expect-utils": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.5.0", + "expect": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "natural-compare": "^1.4.0", - "pretty-format": "^29.5.0", - "semver": "^7.3.5" + "pretty-format": "^29.6.3", + "semver": "^7.5.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", + "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -4791,17 +4829,17 @@ } }, "node_modules/jest-validate": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", - "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", + "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.5.0" + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4820,18 +4858,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", - "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", + "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", "dev": true, "dependencies": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.5.0", + "jest-util": "^29.6.3", "string-length": "^4.0.1" }, "engines": { @@ -4839,13 +4877,13 @@ } }, "node_modules/jest-worker": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", - "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.5.0", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -5010,6 +5048,14 @@ "node": ">=6" } }, + "node_modules/jsonpath-plus": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", + "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -5040,6 +5086,11 @@ "resolved": "https://registry.npmjs.org/jstat/-/jstat-1.9.6.tgz", "integrity": "sha512-rPBkJbK2TnA8pzs93QcDDPlKcrtZWuuCo2dVR0TFLOJSxhqfWOVCSp8aV3/oSbn+4uY4yw1URtLpHQedtmXfug==" }, + "node_modules/just-clone": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-6.2.0.tgz", + "integrity": "sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==" + }, "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -5262,27 +5313,33 @@ } }, "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/makeerror": { @@ -5656,9 +5713,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/nodemailer": { @@ -6128,9 +6185,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -6166,12 +6223,12 @@ } }, "node_modules/pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -6257,9 +6314,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz", - "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", + "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", "dev": true, "funding": [ { @@ -6368,6 +6425,20 @@ "@redis/time-series": "1.0.5" } }, + "node_modules/redis-om": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/redis-om/-/redis-om-0.4.2.tgz", + "integrity": "sha512-sBah+ljGQY4Zm1f9/+l7HjtIuTQH/rzIX3JSLizymbr6VlTZ2ibt+U3xYKNjIA0tv/D9toEMF7HFXHMtT+l+1A==", + "dependencies": { + "jsonpath-plus": "^7.2.0", + "just-clone": "^6.1.1", + "redis": "^4.6.4", + "ulid": "^2.3.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -6392,12 +6463,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7316,6 +7387,14 @@ "node": ">= 0.8" } }, + "node_modules/ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", + "bin": { + "ulid": "bin/cli.js" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -7353,9 +7432,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -7365,6 +7444,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -7372,7 +7455,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -7802,9 +7885,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { "cliui": "^8.0.1", @@ -7848,12 +7931,12 @@ }, "dependencies": { "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "requires": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, @@ -7926,32 +8009,32 @@ } }, "@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", "dev": true }, "@babel/core": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", - "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.15.tgz", + "integrity": "sha512-PtZqMmgRrvj8ruoEOIwVA3yoF91O+Hgw9o7DAUTNBA6Mo2jpu31clx9a7Nz/9JznqetTR6zwfC4L3LAjKQXUwA==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.3", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.3", - "@babel/types": "^7.21.3", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.22.15", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.15", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "dependencies": { "convert-source-map": { @@ -7976,49 +8059,36 @@ "dev": true }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } }, "@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", + "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", "dev": true, "requires": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.22.15", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } } }, "@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "dependencies": { "lru-cache": { @@ -8031,9 +8101,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, "yallist": { @@ -8070,43 +8140,40 @@ } }, "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.15" } }, "@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.15.tgz", + "integrity": "sha512-l1UiX4UyHSFsYt17iQ3Se5pQQZZHa22zyIXURmvkmLCD4t/aU+dvNWHatKac/D9Vm9UES7nvIqHs4jZqKviUmQ==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.15" } }, "@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true }, "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "requires": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" } }, "@babel/helper-split-export-declaration": { @@ -8125,26 +8192,26 @@ "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", + "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true }, "@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", "dev": true, "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/highlight": { @@ -8217,9 +8284,9 @@ } }, "@babel/parser": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.13.tgz", - "integrity": "sha512-3l6+4YOvc9wx7VlCSw4yQfcBo01ECA8TicQfbnCPuCEpRQrf+gTUyGdxNw+pyTUyywp6JRD1w0YQs9TpBXYlkw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.15.tgz", + "integrity": "sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -8268,12 +8335,12 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -8340,39 +8407,39 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.15.tgz", + "integrity": "sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -8395,13 +8462,13 @@ } }, "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz", + "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.15", "to-fast-properties": "^2.0.0" } }, @@ -8459,124 +8526,124 @@ "dev": true }, "@jest/console": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", - "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", + "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", "dev": true, "requires": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "slash": "^3.0.0" } }, "@jest/core": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", - "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", + "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", "dev": true, "requires": { - "@jest/console": "^29.5.0", - "@jest/reporters": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.6.4", + "@jest/reporters": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-resolve-dependencies": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "jest-watcher": "^29.5.0", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-resolve-dependencies": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.4", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-ansi": "^6.0.0" } }, "@jest/environment": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", - "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", + "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", "dev": true, "requires": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0" + "jest-mock": "^29.6.3" } }, "@jest/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", "dev": true, "requires": { - "expect": "^29.5.0", - "jest-snapshot": "^29.5.0" + "expect": "^29.6.4", + "jest-snapshot": "^29.6.4" } }, "@jest/expect-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", - "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", + "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", "dev": true, "requires": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" } }, "@jest/fake-timers": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", - "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", + "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", "dev": true, "requires": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" } }, "@jest/globals": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", - "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", + "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", "dev": true, "requires": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/types": "^29.5.0", - "jest-mock": "^29.5.0" + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" } }, "@jest/reporters": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", - "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", + "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/console": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", @@ -8584,13 +8651,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -8598,66 +8665,66 @@ } }, "@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "requires": { - "@sinclair/typebox": "^0.25.16" + "@sinclair/typebox": "^0.27.8" } }, "@jest/source-map": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", - "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.15", + "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", "graceful-fs": "^4.2.9" } }, "@jest/test-result": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", - "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", + "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", "dev": true, "requires": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.6.4", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/test-sequencer": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", - "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", + "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", "dev": true, "requires": { - "@jest/test-result": "^29.5.0", + "@jest/test-result": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.6.4", "slash": "^3.0.0" } }, "@jest/transform": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", - "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", + "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", "dev": true, "requires": { "@babel/core": "^7.11.6", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", + "jest-haste-map": "^29.6.4", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -8665,12 +8732,12 @@ } }, "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "requires": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -8679,19 +8746,20 @@ } }, "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true }, "@jridgewell/set-array": { @@ -8701,19 +8769,19 @@ "dev": true }, "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@ldapjs/asn1": { @@ -8836,27 +8904,27 @@ "requires": {} }, "@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, "@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "requires": { "type-detect": "4.0.8" } }, "@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "requires": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "@socket.io/component-emitter": { @@ -8871,9 +8939,9 @@ "dev": true }, "@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", "dev": true, "requires": { "@babel/parser": "^7.20.7", @@ -8903,12 +8971,12 @@ } }, "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dev": true, "requires": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "@types/cookie": { @@ -8962,12 +9030,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.28.tgz", "integrity": "sha512-UYmIeBnB0On70dN1iGCinsL1qH5JmIEJwa+3KX0Xw4HQJ8KA16ULlyTCNmnzfyzj/BlxZKmZLqp4TYdssnov1w==" }, - "@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, "@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -8996,9 +9058,9 @@ } }, "@types/yargs": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.23.tgz", - "integrity": "sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -9199,15 +9261,15 @@ } }, "babel-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", - "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", + "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", "dev": true, "requires": { - "@jest/transform": "^29.5.0", + "@jest/transform": "^29.6.4", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -9224,12 +9286,33 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, "babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "requires": { "@babel/template": "^7.3.3", @@ -9259,12 +9342,12 @@ } }, "babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" } }, @@ -9381,15 +9464,15 @@ } }, "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" } }, "bser": { @@ -9465,9 +9548,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001470", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001470.tgz", - "integrity": "sha512-065uNwY6QtHCBOExzbV6m236DDhYCCtPmQUCoQtwkVqzud8v5QPidoMr6CoMkC2nfp6nksjttqWQRRh75LqUmA==", + "version": "1.0.30001527", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz", + "integrity": "sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==", "dev": true }, "chalk": { @@ -9509,9 +9592,9 @@ "dev": true }, "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "cliui": { @@ -9551,9 +9634,9 @@ "dev": true }, "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, "color": { @@ -9855,10 +9938,11 @@ "dev": true }, "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "requires": {} }, "deep-is": { "version": "0.1.4", @@ -9910,9 +9994,9 @@ } }, "diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, "domexception": { @@ -9952,9 +10036,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.4.340", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.340.tgz", - "integrity": "sha512-zx8hqumOqltKsv/MF50yvdAlPF9S/4PXbyfzJS6ZGhbddGkRegdwImmfSVqCkEziYzrIGZ/TlrzBND4FysfkDg==", + "version": "1.4.508", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz", + "integrity": "sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==", "dev": true }, "emittery": { @@ -10212,16 +10296,16 @@ "dev": true }, "expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", "dev": true, "requires": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" + "@jest/expect-utils": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" } }, "express": { @@ -10948,9 +11032,9 @@ } }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "requires": { "has": "^1.0.3" @@ -11026,34 +11110,37 @@ "dev": true }, "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", "dev": true, "requires": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" } }, @@ -11086,9 +11173,9 @@ } }, "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -11117,151 +11204,152 @@ "integrity": "sha512-NHuYTuLgwwBrGKNORh+mSqnvJUeXe9OX9HLP60CT4iRQPgi+OPKBjThOLvwc5SA4yfjTQ/RpyhPfczfavS+Gsg==" }, "jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", + "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", "dev": true, "requires": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.6.4", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.5.0" + "jest-cli": "^29.6.4" } }, "jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", + "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", "dev": true, "requires": { "execa": "^5.0.0", + "jest-util": "^29.6.3", "p-limit": "^3.1.0" } }, "jest-circus": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", - "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", + "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", "dev": true, "requires": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "dedent": "^0.7.0", + "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.5.0", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "p-limit": "^3.1.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.6.3", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "jest-cli": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", - "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", + "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", "dev": true, "requires": { - "@jest/core": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-config": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "prompts": "^2.0.1", "yargs": "^17.3.1" } }, "jest-config": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", - "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", + "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", "dev": true, "requires": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", + "@jest/test-sequencer": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.4", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-circus": "^29.6.4", + "jest-environment-node": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" } }, "jest-diff": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", - "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", + "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", + "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", "dev": true, "requires": { "detect-newline": "^3.0.0" } }, "jest-each": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", - "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", + "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", "dev": true, "requires": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.5.0", - "pretty-format": "^29.5.0" + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-environment-node": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", - "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", + "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", "dev": true, "requires": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" } }, "jest-fetch-mock": { @@ -11274,79 +11362,79 @@ } }, "jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true }, "jest-haste-map": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", - "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", + "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", "dev": true, "requires": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.3.2", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "micromatch": "^4.0.4", "walker": "^1.0.8" } }, "jest-leak-detector": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", - "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", + "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", "dev": true, "requires": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-matcher-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", - "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", + "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-message-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", - "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", + "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "jest-mock": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", - "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", + "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", "dev": true, "requires": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.5.0" + "jest-util": "^29.6.3" } }, "jest-pnp-resolver": { @@ -11357,135 +11445,143 @@ "requires": {} }, "jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true }, "jest-resolve": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", - "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", + "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", "dev": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.6.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" } }, "jest-resolve-dependencies": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", - "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", + "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", "dev": true, "requires": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.5.0" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.4" } }, "jest-runner": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", - "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", + "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", "dev": true, "requires": { - "@jest/console": "^29.5.0", - "@jest/environment": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.6.4", + "@jest/environment": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-leak-detector": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-resolve": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-util": "^29.5.0", - "jest-watcher": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.4", + "jest-worker": "^29.6.4", "p-limit": "^3.1.0", "source-map-support": "0.5.13" } }, "jest-runtime": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", - "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", - "dev": true, - "requires": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/globals": "^29.5.0", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", + "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "dev": true, + "requires": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/globals": "^29.6.4", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "slash": "^3.0.0", "strip-bom": "^4.0.0" } }, "jest-snapshot": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", - "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", + "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", "dev": true, "requires": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", + "@jest/expect-utils": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.5.0", + "expect": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "natural-compare": "^1.4.0", - "pretty-format": "^29.5.0", - "semver": "^7.3.5" + "pretty-format": "^29.6.3", + "semver": "^7.5.3" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", + "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", "dev": true, "requires": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -11494,17 +11590,17 @@ } }, "jest-validate": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", - "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", + "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", "dev": true, "requires": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.5.0" + "pretty-format": "^29.6.3" }, "dependencies": { "camelcase": { @@ -11516,29 +11612,29 @@ } }, "jest-watcher": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", - "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", + "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", "dev": true, "requires": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.5.0", + "jest-util": "^29.6.3", "string-length": "^4.0.1" } }, "jest-worker": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", - "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.5.0", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -11660,6 +11756,11 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonpath-plus": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", + "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==" + }, "jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -11688,6 +11789,11 @@ "resolved": "https://registry.npmjs.org/jstat/-/jstat-1.9.6.tgz", "integrity": "sha512-rPBkJbK2TnA8pzs93QcDDPlKcrtZWuuCo2dVR0TFLOJSxhqfWOVCSp8aV3/oSbn+4uY4yw1URtLpHQedtmXfug==" }, + "just-clone": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-6.2.0.tgz", + "integrity": "sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==" + }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -11886,19 +11992,22 @@ } }, "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "requires": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -12196,9 +12305,9 @@ "dev": true }, "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "nodemailer": { @@ -12553,9 +12662,9 @@ } }, "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true }, "pkg-dir": { @@ -12579,12 +12688,12 @@ "dev": true }, "pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", "dev": true, "requires": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -12654,9 +12763,9 @@ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, "pure-rand": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz", - "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", + "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", "dev": true }, "qs": { @@ -12737,6 +12846,17 @@ "@redis/time-series": "1.0.5" } }, + "redis-om": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/redis-om/-/redis-om-0.4.2.tgz", + "integrity": "sha512-sBah+ljGQY4Zm1f9/+l7HjtIuTQH/rzIX3JSLizymbr6VlTZ2ibt+U3xYKNjIA0tv/D9toEMF7HFXHMtT+l+1A==", + "requires": { + "jsonpath-plus": "^7.2.0", + "just-clone": "^6.1.1", + "redis": "^4.6.4", + "ulid": "^2.3.0" + } + }, "require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -12755,12 +12875,12 @@ "dev": true }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -13474,6 +13594,11 @@ "random-bytes": "~1.0.0" } }, + "ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==" + }, "undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -13505,9 +13630,9 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -13821,9 +13946,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { "cliui": "^8.0.1", diff --git a/package.json b/package.json index 20512497..03a90b42 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", - "redis": "^4.6.8", + "redis-om": "^0.4.2", "simple-ldap-search": "^3.1.2", "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", @@ -60,7 +60,7 @@ "express": "^4.18.1", "express-session": "^1.17.3", "jasmine": "^4.3.0", - "jest": "^29.5.0", + "jest": "^29.6.4", "jsdom": "^21.1.1", "jsdom-global": "^3.0.2", "module-alias": "^2.2.3", diff --git a/src/connections/__tests__/redis.test.js b/src/connections/__tests__/redis.test.js new file mode 100644 index 00000000..6a434bb5 --- /dev/null +++ b/src/connections/__tests__/redis.test.js @@ -0,0 +1,29 @@ +const { Client } = require('redis-om') + +const url = process.env.REDIS_URL + +// Jest test suite +describe('Redis Database Tests', () => { + it('should connect to Redis', async () => { + // Create a Redis client and specify the host and port + const client = new Client() + + // Define the Redis connection URL (e.g., 'redis://localhost:6379') + const redisUrl = url || 'redis://localhost:6379' + + console.log('Connecting to Redis using URL:', redisUrl) + + // Attempt to connect to Redis using the specified URL + await client.open(redisUrl) + + // Check if the client is open (connected) + const isOpen = client.isOpen() + console.log('Redis client is open:', isOpen) + + // Close the Redis client to release resources + await client.close() + + // Wait for the connection to be established + expect(isOpen).toBeTruthy() + }) +}) From 2de6aeb3879414dca3ae8a5648daff422029368d Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 17:39:42 -0400 Subject: [PATCH 063/229] ldap connection test added --- src/connections/LDAP_client.js | 6 +++++- src/connections/__tests__/ldap.test.js | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/connections/__tests__/ldap.test.js diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index 2994aac9..ea97c216 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -13,7 +13,11 @@ client.on('connectError', (err) => { }) client.bind(config.ldap.admin.username, config.ldap.admin.password, (err) => { - assert.ifError(err) + if (err) { + assert.ifError(err) + } else { + console.log('Binded to LDAP') + } }) module.exports = client diff --git a/src/connections/__tests__/ldap.test.js b/src/connections/__tests__/ldap.test.js new file mode 100644 index 00000000..c7b19aff --- /dev/null +++ b/src/connections/__tests__/ldap.test.js @@ -0,0 +1,16 @@ +const ldapClient = require('../LDAP_client') +const config = require('../../config/config.js') + +describe('LDAP Connection', () => { + // This test case will check if the LDAP client is connected successfully + it('should connect to LDAP', (done) => { + ldapClient.on('connect', () => { + console.log('Connected to LDAP') + done() + }) + + ldapClient.on('error', (err) => { + done.fail(`Failed to connect to LDAP: ${err}`) + }) + }) +}) From c27768618fc086002a842581fd1e54327ed7cdc5 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 17:45:39 -0400 Subject: [PATCH 064/229] move ldapUtils from helpers to utils --- src/services/group.services.js | 2 +- src/services/user.services.js | 2 +- src/{helpers => utils}/ldapUtils.js | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{helpers => utils}/ldapUtils.js (100%) diff --git a/src/services/group.services.js b/src/services/group.services.js index e3e680aa..a8e4169f 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -1,6 +1,6 @@ require('dotenv').config({ path: __dirname + '/../../.env' }) const config = require('@src/config/config') -const { performLdapSearch } = require('@src/helpers/ldapUtils') +const { performLdapSearch } = require('@src/utils/ldapUtils') const GroupServices = () => { const getGroup = async (group) => { diff --git a/src/services/user.services.js b/src/services/user.services.js index 49a151a3..bb2cc4f3 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -3,7 +3,7 @@ const config = require('@src/config/config') const { performLdapSearch, performLdapUpdate, -} = require('@src/helpers/ldapUtils') +} = require('@src/utils/ldapUtils') const UserServices = () => { const handleFilteredSearch = async ( diff --git a/src/helpers/ldapUtils.js b/src/utils/ldapUtils.js similarity index 100% rename from src/helpers/ldapUtils.js rename to src/utils/ldapUtils.js From 4b565818f695ccc7e23774fd90818331b52f1cf3 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 18:35:28 -0400 Subject: [PATCH 065/229] migrate client and test to redis instead redis-om --- package-lock.json | 1 + package.json | 1 + src/connections/__tests__/redis.test.js | 45 ++++++++++------- src/connections/redis_client.js | 67 +++++++++++++------------ 4 files changed, 63 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69abebc3..8b4d0d7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", + "redis": "^4.6.8", "redis-om": "^0.4.2", "simple-ldap-search": "^3.1.2", "simple-statistics": "^7.8.3", diff --git a/package.json b/package.json index 03a90b42..d7950abd 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", + "redis": "^4.6.8", "redis-om": "^0.4.2", "simple-ldap-search": "^3.1.2", "simple-statistics": "^7.8.3", diff --git a/src/connections/__tests__/redis.test.js b/src/connections/__tests__/redis.test.js index 6a434bb5..3522abe4 100644 --- a/src/connections/__tests__/redis.test.js +++ b/src/connections/__tests__/redis.test.js @@ -1,29 +1,36 @@ -const { Client } = require('redis-om') - -const url = process.env.REDIS_URL +const redisClient = require('../redis_client') // Jest test suite describe('Redis Database Tests', () => { - it('should connect to Redis', async () => { - // Create a Redis client and specify the host and port - const client = new Client() - - // Define the Redis connection URL (e.g., 'redis://localhost:6379') - const redisUrl = url || 'redis://localhost:6379' + beforeAll(async () => { + // Create and connect to the Redis client + await redisClient.client.connect() + console.log(' redisClient.ping()', redisClient.client.ping()) + }) - console.log('Connecting to Redis using URL:', redisUrl) + afterAll(async () => { + // Disconnect the Redis client and release resources + redisClient.client.quit() + }) - // Attempt to connect to Redis using the specified URL - await client.open(redisUrl) + it('should connect to Redis', async () => { + // Check if the Redis client is connected + const isConnected = redisClient.client.isOpen + expect(isConnected).toBeTruthy() + }) - // Check if the client is open (connected) - const isOpen = client.isOpen() - console.log('Redis client is open:', isOpen) + it('should set and get a value from Redis', async () => { + // Set a key-value pair + await redisClient.client.hSet('key', 'field', 'perro') - // Close the Redis client to release resources - await client.close() + // Get the value by key + const value = await redisClient.client.hGetAll('key') + console.log('value', value) - // Wait for the connection to be established - expect(isOpen).toBeTruthy() + expect(value).toEqual({ + field: 'perro', + }) }) + + // Add more tests as needed }) diff --git a/src/connections/redis_client.js b/src/connections/redis_client.js index 6560ac97..8f529cb2 100644 --- a/src/connections/redis_client.js +++ b/src/connections/redis_client.js @@ -1,41 +1,44 @@ -const redis = require('redis') -const config = require('@src/config/config') +const { createClient } = require('redis') +const { promisify } = require('util') -// Create a Redis client -const client = redis.createClient({ - host: config.redis.host, // Docker Desktop container runs on localhost - port: parseInt(config.redis.port), // Default Redis port -}) +// Function to create and configure a Redis client +function createRedisClient() { + const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379' + const client = createClient({ + url: redisUrl, + }) -// Test the connection -client.on('connect', () => { - console.log('Connected to Redis server') -}) + // Promisify Redis client methods for async/await support + const getAsync = promisify(client.get).bind(client) + const setAsync = promisify(client.set).bind(client) -// Handle errors -client.on('error', (err) => { - console.error('Redis Error:', err) -}) + // Connect to Redis + async function connect() { + return new Promise((resolve, reject) => { + client.on('error', (err) => { + reject(err) + }) -// Example: Set a key-value pair -/* client.set('myKey', 'myValue', (err, reply) => { - if (err) { - console.error('Redis Set Error:', err) - } else { - console.log('Set result:', reply) + client.on('connect', () => { + resolve() + }) + }) } -}) -// Example: Get a value by key -client.get('myKey', (err, reply) => { - if (err) { - console.error('Redis Get Error:', err) - } else { - console.log('Get result:', reply) + // Close the Redis client to release resources + function disconnect() { + client.quit() } -}) */ -// Close the Redis connection (when needed) -// client.quit(); + // Return the Redis client and utility functions + return { + client, + connect, + disconnect, + isConnected: client.isOpen, + getAsync, + setAsync, + } +} -module.exports = { client } +module.exports = createRedisClient() From bd9837a7f8e1fc5a5ba0326b25e0a5780f9079eb Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 22:46:57 -0400 Subject: [PATCH 066/229] add auth services test --- src/connections/__tests__/redis.test.js | 2 -- src/services/__tests__/auth.services.test.js | 35 ++++++++++++++++++++ src/services/auth.services.js | 21 ++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/services/__tests__/auth.services.test.js create mode 100644 src/services/auth.services.js diff --git a/src/connections/__tests__/redis.test.js b/src/connections/__tests__/redis.test.js index 3522abe4..d4bee4c8 100644 --- a/src/connections/__tests__/redis.test.js +++ b/src/connections/__tests__/redis.test.js @@ -5,7 +5,6 @@ describe('Redis Database Tests', () => { beforeAll(async () => { // Create and connect to the Redis client await redisClient.client.connect() - console.log(' redisClient.ping()', redisClient.client.ping()) }) afterAll(async () => { @@ -25,7 +24,6 @@ describe('Redis Database Tests', () => { // Get the value by key const value = await redisClient.client.hGetAll('key') - console.log('value', value) expect(value).toEqual({ field: 'perro', diff --git a/src/services/__tests__/auth.services.test.js b/src/services/__tests__/auth.services.test.js new file mode 100644 index 00000000..22c97330 --- /dev/null +++ b/src/services/__tests__/auth.services.test.js @@ -0,0 +1,35 @@ +const redisClient = require('../../connections/redis_client') + +describe('addToBlackList Service', () => { + beforeAll(async () => { + // Create and connect to the Redis client + await redisClient.client.connect() + }) + + it('should connect to Redis', async () => { + // Check if the Redis client is connected + const isConnected = redisClient.client.isOpen + expect(isConnected).toBeTruthy() + }) + + afterAll(async () => { + // Disconnect the Redis client and release resources + redisClient.client.quit() + }) + + it('should add a token to the blacklist when connected', async () => { + const token = 'your_jwt_token_here' + const expirationInSeconds = 3600 // 1 hour + const defaultValue = '1' + + redisClient.client.hSet(token, expirationInSeconds, defaultValue) + + const value = await redisClient.client.hGetAll(token) + + console.log(value) + + expect(value).toEqual({ + 3600: defaultValue, + }) + }) +}) diff --git a/src/services/auth.services.js b/src/services/auth.services.js new file mode 100644 index 00000000..60d56812 --- /dev/null +++ b/src/services/auth.services.js @@ -0,0 +1,21 @@ +const { + client, + connect, + isConnected, + disconnect, +} = require('../connections/redis_client') + +const addToBlackList = async (token, expirationInSeconds) => { + try { + await connect() + if (isConnected) { + client.hSet(token, expirationInSeconds, '1') + } else { + throw new Error('Error connecting to redis DB') + } + } catch (error) {} +} + +module.exports = { + addToBlackList, +} From a0cdb4f31298831d04cbc7f12c4098952af68ff6 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 23:13:21 -0400 Subject: [PATCH 067/229] logout method added --- src/modules/authentication/LdapAuth.js | 30 +++++++++++++++++++++++--- src/services/auth.services.js | 16 ++++++++++---- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 50784306..6373cb6a 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -10,6 +10,8 @@ const UserServices = require('../../services/user.services') const GroupServices = require('../../services/group.services') const User = require('../../schemas/user.schema').User const ProfileServices = require('../../services/profile.services') +const { addToBlackList } = require('@src/services/auth.services') +const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') /* helpers */ const { @@ -49,13 +51,13 @@ var _usernameAttributeName const sessionStore = new Map() function checkLastAuthentication(req, res, next) { - /* const userId = req.body.username + const userId = req.body.username const lastAuthTimestamp = sessionStore.get(userId) if (!lastAuthTimestamp || Date.now() - lastAuthTimestamp >= 15 * 60 * 1000) { next() } else { res.status(401).json({ message: 'Logout first before re-authenticating.' }) - } */ + } next() } var init = function ( @@ -176,7 +178,8 @@ var init = function ( router.use(passport.initialize()) router.use(passport.session()) // login - router.post(_loginUrl, checkLastAuthentication, login) + router.post(_loginUrl, login) + router.post(_logoutUrl, checkAuth, logout) } /** @@ -274,5 +277,26 @@ const login = function (req, res, next) { )(req, res, next) } +const logout = function (req, res, next) { + try { + const token = req.headers.authorization.split(' ')[1] + const isLogout = addToBlackList(token) + if (isLogout) { + res.status(200).json({ + success: true, + message: 'user logged out correctly', + }) + } else { + res.status(500).json({ + success: false, + error: 'Invalid token', + message: `this token hasn't been used yet`, + }) + } + } catch (error) { + res.status(401).json({ success: false, message: 'Invalid token' }) + } +} + module.exports.init = init module.exports.initialize = initialize diff --git a/src/services/auth.services.js b/src/services/auth.services.js index 60d56812..b610ca19 100644 --- a/src/services/auth.services.js +++ b/src/services/auth.services.js @@ -5,15 +5,23 @@ const { disconnect, } = require('../connections/redis_client') -const addToBlackList = async (token, expirationInSeconds) => { +const addToBlackList = async (token, expirationInSeconds = 3600) => { try { - await connect() - if (isConnected) { + await client.connect() + if (client.isOpen) { client.hSet(token, expirationInSeconds, '1') + disconnect() + return true } else { + disconnect() throw new Error('Error connecting to redis DB') } - } catch (error) {} + } catch (err) { + disconnect() + console.log('error', err) + throw new Error('Error connecting to redis DB') + } + return falsef } module.exports = { From 0576f513595427228cee5950a9b5b3b54e98670f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 23:24:59 -0400 Subject: [PATCH 068/229] add method to check is blacklisted token --- src/services/auth.services.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/services/auth.services.js b/src/services/auth.services.js index b610ca19..cdd570d2 100644 --- a/src/services/auth.services.js +++ b/src/services/auth.services.js @@ -9,6 +9,10 @@ const addToBlackList = async (token, expirationInSeconds = 3600) => { try { await client.connect() if (client.isOpen) { + const exists = await client.exists(token) + if (exists > 0) { + throw new Error('Already logged out') + } client.hSet(token, expirationInSeconds, '1') disconnect() return true @@ -21,9 +25,31 @@ const addToBlackList = async (token, expirationInSeconds = 3600) => { console.log('error', err) throw new Error('Error connecting to redis DB') } - return falsef } +const isBlackListed = async (token) => { + try { + await client.connect() + if (client.isOpen) { + const exists = await client.exists(token) + disconnect() + return exists === 0 ? false : true + } else { + disconnect() + throw new Error('Error connecting to redis DB') + } + } catch (error) { + disconnect() + console.log('error', err) + throw new Error('Error connecting to redis DB') + } +} + +isBlackListed( + 'yJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbImFkbWluIiwidXNlciJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOS0wNlQxOTozMToyNS43ODNaIiwibG9naW5JbmZvIjoiOS82LzIwMjMsIDEwOjU0OjM3IFBNIiwiaWF0IjoxNjk0MDU1Mjc4LCJleHAiOjE2OTQwNTc5Nzh9.0WKdygQ10i7I9Tu4yIPhmJZeyJ7RoJ0IYybe2Wlh0SM' +) + module.exports = { addToBlackList, + isBlackListed, } From f3e52af7e5e4aa352d7e47d6369e6b5d56684a40 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 6 Sep 2023 23:47:21 -0400 Subject: [PATCH 069/229] add expired token middleware --- src/middlewares/auth.handler.js | 30 ++++++++++++++ src/modules/authentication/LdapAuth.js | 8 ++-- src/routes/profile.routes.js | 56 +++++++++++++++++--------- src/routes/user.routes.js | 8 +++- src/services/auth.services.js | 12 +++--- 5 files changed, 83 insertions(+), 31 deletions(-) diff --git a/src/middlewares/auth.handler.js b/src/middlewares/auth.handler.js index a0d73335..cc2ff473 100644 --- a/src/middlewares/auth.handler.js +++ b/src/middlewares/auth.handler.js @@ -2,6 +2,7 @@ const passport = require('passport') const { verifyToken } = require('../utils/authentication/tokens/token_verify') const boom = require('@hapi/boom') const { responseError } = require('../schemas/response.schema') +const { isBlackListed } = require('@src/services/auth.services') const checkAuth = (req, res, next) => { const auth = passport.authenticate('jwt', { session: false }) @@ -31,7 +32,36 @@ const checkRoles = (...roles) => { } } +// Middleware to check if a JWT token is blacklisted +const checkBlacklist = async (req, res, next) => { + const token = req.headers.authorization.split(' ')[1] // Assuming the token is in the Authorization header + + if (!token) { + return res.status(401).json({ message: 'Unauthorized' }) + } + + try { + const isBlacklisted = await isBlackListed(token) + console.log('isBlacklisted', isBlacklisted) + + if (isBlacklisted) { + return res.status(401).json({ + success: false, + error: 'Token is expired', + message: 'Try to Log In again', + }) + } + + // Token is not blacklisted, continue with the request + next() + } catch (error) { + console.error('Error checking token blacklist:', error) + return res.status(500).json({ message: 'Internal Server Error' }) + } +} + module.exports = { checkAuth, checkRoles, + checkBlacklist, } diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 6373cb6a..1f17690c 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -277,10 +277,11 @@ const login = function (req, res, next) { )(req, res, next) } -const logout = function (req, res, next) { +const logout = async function (req, res, next) { try { const token = req.headers.authorization.split(' ')[1] - const isLogout = addToBlackList(token) + const isLogout = await addToBlackList(token) + console.log('isLogout', isLogout) if (isLogout) { res.status(200).json({ success: true, @@ -290,10 +291,9 @@ const logout = function (req, res, next) { res.status(500).json({ success: false, error: 'Invalid token', - message: `this token hasn't been used yet`, }) } - } catch (error) { + } catch (err) { res.status(401).json({ success: false, message: 'Invalid token' }) } } diff --git a/src/routes/profile.routes.js b/src/routes/profile.routes.js index 0318bbeb..b0bb7fc6 100644 --- a/src/routes/profile.routes.js +++ b/src/routes/profile.routes.js @@ -3,31 +3,49 @@ const router = express.Router() const ProfileServices = require('../services/profile.services') const { responseSuccess, responseError } = require('../schemas/response.schema') const validateResponse = require('../middlewares/validateResponse') -const { checkAuth, checkRoles } = require('../middlewares/auth.handler') +const { + checkAuth, + checkRoles, + checkBlacklist, +} = require('../middlewares/auth.handler') const service = ProfileServices() //Get all users -router.get('/', checkAuth, checkRoles('user'), validateResponse, (req, res) => { - service - .getProfile(req) - .then((data) => responseSuccess(res, 'data fetched succesfully', data)) - .catch((err) => responseError(res, err.message, err.errors)) -}) - -router.put('/', checkAuth, checkRoles('user'), validateResponse, (req, res) => { - const { email, password, confirmPassword } = req.body - if (!email && !password) { - responseError(res, 'fields cannot be empty', null) - } else if (password) { - if (password !== confirmPassword) { - responseError(res, 'passwords must be the same') - } - } else { +router.get( + '/', + checkAuth, + checkBlacklist, + checkRoles('user'), + validateResponse, + (req, res) => { service - .updateProfile(email, password, req) + .getProfile(req) .then((data) => responseSuccess(res, 'data fetched succesfully', data)) .catch((err) => responseError(res, err.message, err.errors)) } -}) +) + +router.put( + '/', + checkAuth, + checkBlacklist, + checkRoles('user'), + validateResponse, + (req, res) => { + const { email, password, confirmPassword } = req.body + if (!email && !password) { + responseError(res, 'fields cannot be empty', null) + } else if (password) { + if (password !== confirmPassword) { + responseError(res, 'passwords must be the same') + } + } else { + service + .updateProfile(email, password, req) + .then((data) => responseSuccess(res, 'data fetched succesfully', data)) + .catch((err) => responseError(res, err.message, err.errors)) + } + } +) module.exports = router diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 461d8037..1b755c92 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -2,7 +2,11 @@ const express = require('express') const router = express.Router() const UserServices = require('@src/services/user.services.js') const validateResponse = require('@src/middlewares/validateResponse') -const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') +const { + checkAuth, + checkRoles, + checkBlacklist, +} = require('@src/middlewares/auth.handler') const service = UserServices() const config = require('@src/config/config') const { @@ -11,7 +15,7 @@ const { const validateQuery = require('@src/middlewares/queryValidator') // Middleware for routes requiring checkAuth and checkRoles('admin') -router.use(checkAuth, checkRoles('admin')) +router.use(checkAuth, checkBlacklist, checkRoles('admin')) // Middleware to handle common success and error responses router.use(validateResponse) diff --git a/src/services/auth.services.js b/src/services/auth.services.js index cdd570d2..b5e72438 100644 --- a/src/services/auth.services.js +++ b/src/services/auth.services.js @@ -11,7 +11,8 @@ const addToBlackList = async (token, expirationInSeconds = 3600) => { if (client.isOpen) { const exists = await client.exists(token) if (exists > 0) { - throw new Error('Already logged out') + disconnect() + return false } client.hSet(token, expirationInSeconds, '1') disconnect() @@ -28,12 +29,15 @@ const addToBlackList = async (token, expirationInSeconds = 3600) => { } const isBlackListed = async (token) => { + console.log('token', token) try { await client.connect() if (client.isOpen) { const exists = await client.exists(token) + console.log('exists', exists) disconnect() - return exists === 0 ? false : true + if (exists === 0) return false + else return true } else { disconnect() throw new Error('Error connecting to redis DB') @@ -45,10 +49,6 @@ const isBlackListed = async (token) => { } } -isBlackListed( - 'yJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbImFkbWluIiwidXNlciJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOS0wNlQxOTozMToyNS43ODNaIiwibG9naW5JbmZvIjoiOS82LzIwMjMsIDEwOjU0OjM3IFBNIiwiaWF0IjoxNjk0MDU1Mjc4LCJleHAiOjE2OTQwNTc5Nzh9.0WKdygQ10i7I9Tu4yIPhmJZeyJ7RoJ0IYybe2Wlh0SM' -) - module.exports = { addToBlackList, isBlackListed, From ee09835b58ecd995a065e53236167f8b3e58fb33 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 7 Sep 2023 14:05:18 -0400 Subject: [PATCH 070/229] set session expiredTime to 15 min instead 1 day --- src/middlewares/session.handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewares/session.handler.js b/src/middlewares/session.handler.js index f86e1266..41ea6f43 100644 --- a/src/middlewares/session.handler.js +++ b/src/middlewares/session.handler.js @@ -24,7 +24,7 @@ try { unset: 'destroy', cookie: { httpOnly: false, - maxAge: 1000 * 3600 * 24, + maxAge: 900000, // 15 minutes in milliseconds secure: false, // this need to be false if https is not used. Otherwise, cookie will not be sent. }, }) From f7b8960c2d8d493f7057694a597872c06629fbe6 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 7 Sep 2023 14:05:54 -0400 Subject: [PATCH 071/229] forbit users to log in if already has a session --- src/modules/authentication/LdapAuth.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 1f17690c..f880ebc3 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -48,18 +48,6 @@ var _usernameAttributeName * @param {string} [logoutUrl] - path to logout page. Default: /logout */ -const sessionStore = new Map() - -function checkLastAuthentication(req, res, next) { - const userId = req.body.username - const lastAuthTimestamp = sessionStore.get(userId) - if (!lastAuthTimestamp || Date.now() - lastAuthTimestamp >= 15 * 60 * 1000) { - next() - } else { - res.status(401).json({ message: 'Logout first before re-authenticating.' }) - } - next() -} var init = function ( opt, ldapurl, @@ -92,6 +80,10 @@ var init = function ( } const username = req.body.username const password = req.body.password + + if (req.session.passport !== undefined) + throw new Error('log out before logging back in') + const res = await userService.getByUsername(username) const response = res[0] @@ -236,8 +228,6 @@ const login = function (req, res, next) { const rootBaseDN = extractBaseFromDn(ldapDn) const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') - sessionStore.set(user.uid, Date.now()) - const payload = { sub: user.uid, dn: user.dn, From 485ff1f18817ba694f24ab2a87ff77295fd4cc32 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 7 Sep 2023 14:06:05 -0400 Subject: [PATCH 072/229] add session middleware test --- .../__tests__/authMiddleware.test.js | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/modules/authentication/__tests__/authMiddleware.test.js diff --git a/src/modules/authentication/__tests__/authMiddleware.test.js b/src/modules/authentication/__tests__/authMiddleware.test.js new file mode 100644 index 00000000..63d21c3e --- /dev/null +++ b/src/modules/authentication/__tests__/authMiddleware.test.js @@ -0,0 +1,53 @@ +const request = require('supertest') +const express = require('express') + +// Importa tu aplicación Express con la ruta protegida +const app = express() + +// Configura tu aplicación Express con el middleware de sesión +const authMiddleware = (req, res, next) => { + if (req.headers.cookie && req.headers.cookie !== undefined) { + // El usuario está autenticado + next() + } else { + // El usuario no está autenticado + res.status(401).json({ message: 'No autorizado' }) + } +} + +app.use(authMiddleware) + +// Define una ruta de prueba protegida con el middleware de autenticación +app.get('/ruta-protegida', (req, res) => { + res.json({ message: 'Ruta protegida' }) +}) + +describe('Pruebas de autenticación', () => { + it('Debería permitir el acceso a la ruta protegida si el usuario está autenticado', async () => { + // Simula una sesión autenticada + const authenticatedSession = { + passport: { user: 'usuario-autenticado' }, + } + + // Crea una sesión de prueba con el usuario autenticado + const sessionCookie = Buffer.from( + JSON.stringify(authenticatedSession) + ).toString('base64') + + const response = await request(app) + .get('/ruta-protegida') + .set('Cookie', [`session=${sessionCookie}`]) + + // Debería responder con un código 200 si el usuario está autenticado + expect(response.status).toBe(200) + expect(response.body.message).toBe('Ruta protegida') + }) + + it('Debería denegar el acceso a la ruta protegida si el usuario no está autenticado', async () => { + const response = await request(app).get('/ruta-protegida') + + // Debería responder con un código 401 si el usuario no está autenticado + expect(response.status).toBe(401) + expect(response.body.message).toBe('No autorizado') + }) +}) From f21513de97231d933236862350e0dae42268ab4a Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 7 Sep 2023 19:55:56 -0400 Subject: [PATCH 073/229] destroy user session on logout --- src/modules/authentication/LdapAuth.js | 26 ++------------ src/modules/authentication/functions/index.js | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 src/modules/authentication/functions/index.js diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index f880ebc3..e0fa07f7 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -7,11 +7,10 @@ const CustomStrategy = require('passport-custom').Strategy const JwtStrategy = require('../../utils/authentication/strategies/jwtStrategy') const { authenticate } = require('ldap-authentication') const UserServices = require('../../services/user.services') -const GroupServices = require('../../services/group.services') const User = require('../../schemas/user.schema').User const ProfileServices = require('../../services/profile.services') -const { addToBlackList } = require('@src/services/auth.services') const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') +const { logout } = require('./functions/index.js') /* helpers */ const { @@ -246,7 +245,7 @@ const login = function (req, res, next) { } const userObj = { ...user } - const token = signToken(payload, { expiresIn: '45 minutes' }) + const token = signToken(payload, { expiresIn: '15 minutes' }) const refreshToken = signToken(payload, { expiresIn: '1 day' }) req.login(user, (loginErr) => { @@ -267,26 +266,5 @@ const login = function (req, res, next) { )(req, res, next) } -const logout = async function (req, res, next) { - try { - const token = req.headers.authorization.split(' ')[1] - const isLogout = await addToBlackList(token) - console.log('isLogout', isLogout) - if (isLogout) { - res.status(200).json({ - success: true, - message: 'user logged out correctly', - }) - } else { - res.status(500).json({ - success: false, - error: 'Invalid token', - }) - } - } catch (err) { - res.status(401).json({ success: false, message: 'Invalid token' }) - } -} - module.exports.init = init module.exports.initialize = initialize diff --git a/src/modules/authentication/functions/index.js b/src/modules/authentication/functions/index.js new file mode 100644 index 00000000..d20e94ce --- /dev/null +++ b/src/modules/authentication/functions/index.js @@ -0,0 +1,36 @@ +const { addToBlackList } = require('@src/services/auth.services') + +const clearSession = (req) => { + return new Promise((resolve, reject) => { + req.session.destroy((err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) +} + +const logout = async function (req, res, next) { + try { + await clearSession(req) + const token = req.headers.authorization.split(' ')[1] + const isLogout = await addToBlackList(token) + if (isLogout) { + res.status(200).json({ + success: true, + message: 'user logged out correctly', + }) + } else { + res.status(500).json({ + success: false, + error: 'Invalid token', + }) + } + } catch (err) { + res.status(401).json({ success: false, message: 'Invalid token' }) + } +} + +module.exports = { logout } From cce386e94be794d5324fb47f1e19b07231266c0e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 7 Sep 2023 20:45:40 -0400 Subject: [PATCH 074/229] save and delete refreshToken on login and logout --- src/modules/authentication/LdapAuth.js | 8 ++- src/modules/authentication/functions/index.js | 8 ++- src/services/auth.services.js | 70 ++++++++++++++++++- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index e0fa07f7..eb879d67 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -9,8 +9,12 @@ const { authenticate } = require('ldap-authentication') const UserServices = require('../../services/user.services') const User = require('../../schemas/user.schema').User const ProfileServices = require('../../services/profile.services') -const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') +const { checkAuth } = require('@src/middlewares/auth.handler') const { logout } = require('./functions/index.js') +const { + storeRefreshToken, + getRefreshToken, +} = require('@src/services/auth.services') /* helpers */ const { @@ -248,6 +252,8 @@ const login = function (req, res, next) { const token = signToken(payload, { expiresIn: '15 minutes' }) const refreshToken = signToken(payload, { expiresIn: '1 day' }) + await storeRefreshToken(user.uid, refreshToken) + req.login(user, (loginErr) => { if (loginErr) { return next(loginErr) diff --git a/src/modules/authentication/functions/index.js b/src/modules/authentication/functions/index.js index d20e94ce..236aabcb 100644 --- a/src/modules/authentication/functions/index.js +++ b/src/modules/authentication/functions/index.js @@ -1,4 +1,7 @@ -const { addToBlackList } = require('@src/services/auth.services') +const { + addToBlackList, + deleteRefreshToken, +} = require('@src/services/auth.services') const clearSession = (req) => { return new Promise((resolve, reject) => { @@ -14,9 +17,10 @@ const clearSession = (req) => { const logout = async function (req, res, next) { try { - await clearSession(req) const token = req.headers.authorization.split(' ')[1] const isLogout = await addToBlackList(token) + await clearSession(req) + await deleteRefreshToken(req.user.uid) if (isLogout) { res.status(200).json({ success: true, diff --git a/src/services/auth.services.js b/src/services/auth.services.js index b5e72438..d2569e30 100644 --- a/src/services/auth.services.js +++ b/src/services/auth.services.js @@ -29,12 +29,10 @@ const addToBlackList = async (token, expirationInSeconds = 3600) => { } const isBlackListed = async (token) => { - console.log('token', token) try { await client.connect() if (client.isOpen) { const exists = await client.exists(token) - console.log('exists', exists) disconnect() if (exists === 0) return false else return true @@ -49,7 +47,75 @@ const isBlackListed = async (token) => { } } +const storeRefreshToken = async (userId, refreshToken) => { + try { + await client.connect() + if (client.isOpen) { + client.set(`refreshToken:${userId}`, refreshToken, (err) => { + if (err) { + disconnect() + console.error('Error storing refresh token:', err) + } else { + disconnect() + console.log('Refresh token stored successfully.') + } + }) + disconnect() + } else { + disconnect() + throw new Error('Error connecting to redis DB') + } + } catch (error) { + disconnect() + console.error('Error storing refresh token:', error) + } +} + +const deleteRefreshToken = async (userId) => { + try { + await client.connect() + if (client.isOpen) { + const data = await client.del(`refreshToken:${userId}`) + console.log('Refresh token deleted successfully.') + disconnect() + return data + } else { + disconnect() + throw new Error('Error connecting to redis DB') + } + } catch (error) { + disconnect() + console.error('Error deleting refresh token:', error) + throw error // Re-throw the error to propagate it further if needed + } +} + +const getRefreshToken = async (userId, callback) => { + try { + await client.connect() + + client.get(`refreshToken:${userId}`, (err, refreshToken) => { + if (err) { + disconnect() + console.error('Error retrieving refresh token:', err) + callback(err) + } else { + disconnect() + console.log('Refresh token retrieved successfully.') + callback(null, refreshToken) + } + }) + } catch (error) { + disconnect() + console.error('Error retrieving refresh token:', error) + callback(error) + } +} + module.exports = { addToBlackList, isBlackListed, + getRefreshToken, + storeRefreshToken, + deleteRefreshToken, } From b295af875f44845addd6a7a3f09b9b95cb13693f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 8 Sep 2023 19:19:59 -0400 Subject: [PATCH 075/229] logout working ok --- src/modules/authentication/functions/index.js | 26 +++++----------- src/services/auth.services.js | 31 ++++++++----------- 2 files changed, 20 insertions(+), 37 deletions(-) diff --git a/src/modules/authentication/functions/index.js b/src/modules/authentication/functions/index.js index 236aabcb..07fe3813 100644 --- a/src/modules/authentication/functions/index.js +++ b/src/modules/authentication/functions/index.js @@ -15,26 +15,14 @@ const clearSession = (req) => { }) } -const logout = async function (req, res, next) { - try { - const token = req.headers.authorization.split(' ')[1] - const isLogout = await addToBlackList(token) - await clearSession(req) - await deleteRefreshToken(req.user.uid) - if (isLogout) { - res.status(200).json({ - success: true, - message: 'user logged out correctly', - }) - } else { - res.status(500).json({ - success: false, - error: 'Invalid token', - }) - } - } catch (err) { - res.status(401).json({ success: false, message: 'Invalid token' }) +const logout = (req, res) => { + const accessToken = req.body.accessToken // You can adjust this based on how you send the token in the request + clearSession(req) + if (!accessToken) { + return res.status(400).json({ message: 'Access token is required.' }) } + addToBlackList(req.user.uid, accessToken) + return res.status(200).json({ message: 'User logged out successfully.' }) } module.exports = { logout } diff --git a/src/services/auth.services.js b/src/services/auth.services.js index d2569e30..3ae860ca 100644 --- a/src/services/auth.services.js +++ b/src/services/auth.services.js @@ -5,45 +5,40 @@ const { disconnect, } = require('../connections/redis_client') -const addToBlackList = async (token, expirationInSeconds = 3600) => { +const isBlackListed = async (token) => { try { await client.connect() + if (client.isOpen) { - const exists = await client.exists(token) - if (exists > 0) { - disconnect() - return false - } - client.hSet(token, expirationInSeconds, '1') + const userId = await client.get(`blackList:${token}`) disconnect() - return true + + // If userId is not null, it means the token exists in the blacklist + return userId !== null } else { disconnect() throw new Error('Error connecting to redis DB') } - } catch (err) { + } catch (error) { disconnect() - console.log('error', err) - throw new Error('Error connecting to redis DB') + console.error('Error checking token in blacklist:', error) + throw error } } -const isBlackListed = async (token) => { +const addToBlackList = async (userId, token) => { try { await client.connect() if (client.isOpen) { - const exists = await client.exists(token) + await client.set(`blackList:${token}`, userId) disconnect() - if (exists === 0) return false - else return true } else { disconnect() throw new Error('Error connecting to redis DB') } } catch (error) { disconnect() - console.log('error', err) - throw new Error('Error connecting to redis DB') + console.error('Error storing refresh token:', error) } } @@ -114,8 +109,8 @@ const getRefreshToken = async (userId, callback) => { module.exports = { addToBlackList, - isBlackListed, getRefreshToken, storeRefreshToken, deleteRefreshToken, + isBlackListed, } From b975eb87711a0f421edd5272639ef4b9ca103197 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 8 Sep 2023 19:22:45 -0400 Subject: [PATCH 076/229] refactor auth services --- src/services/auth.services.js | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/services/auth.services.js b/src/services/auth.services.js index 3ae860ca..c8f60349 100644 --- a/src/services/auth.services.js +++ b/src/services/auth.services.js @@ -46,15 +46,7 @@ const storeRefreshToken = async (userId, refreshToken) => { try { await client.connect() if (client.isOpen) { - client.set(`refreshToken:${userId}`, refreshToken, (err) => { - if (err) { - disconnect() - console.error('Error storing refresh token:', err) - } else { - disconnect() - console.log('Refresh token stored successfully.') - } - }) + await client.set(`refreshToken:${userId}`, refreshToken) disconnect() } else { disconnect() @@ -88,18 +80,8 @@ const deleteRefreshToken = async (userId) => { const getRefreshToken = async (userId, callback) => { try { await client.connect() - - client.get(`refreshToken:${userId}`, (err, refreshToken) => { - if (err) { - disconnect() - console.error('Error retrieving refresh token:', err) - callback(err) - } else { - disconnect() - console.log('Refresh token retrieved successfully.') - callback(null, refreshToken) - } - }) + const refreshToken = await client.get(`refreshToken:${userId}`) + return refreshToken } catch (error) { disconnect() console.error('Error retrieving refresh token:', error) From 4853a30b3ab076a81a85db6d2d637a6cabd110dd Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 8 Sep 2023 19:58:25 -0400 Subject: [PATCH 077/229] retrieve new token and refreshToken on refresh endpoint --- src/modules/authentication/LdapAuth.js | 5 +- src/modules/authentication/functions/index.js | 74 ++++++++++++++++++- src/services/auth.services.js | 1 + 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index eb879d67..762489a3 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -10,7 +10,7 @@ const UserServices = require('../../services/user.services') const User = require('../../schemas/user.schema').User const ProfileServices = require('../../services/profile.services') const { checkAuth } = require('@src/middlewares/auth.handler') -const { logout } = require('./functions/index.js') +const { logout, refresh } = require('./functions/index.js') const { storeRefreshToken, getRefreshToken, @@ -174,7 +174,8 @@ var init = function ( router.use(passport.session()) // login router.post(_loginUrl, login) - router.post(_logoutUrl, checkAuth, logout) + router.post(_logoutUrl, logout) + router.post('/refresh', refresh) } /** diff --git a/src/modules/authentication/functions/index.js b/src/modules/authentication/functions/index.js index 07fe3813..7e0171b1 100644 --- a/src/modules/authentication/functions/index.js +++ b/src/modules/authentication/functions/index.js @@ -1,8 +1,23 @@ const { addToBlackList, deleteRefreshToken, + getRefreshToken, + storeRefreshToken, } = require('@src/services/auth.services') +const UserServices = require('@src/services/user.services') +const ProfileServices = require('@src/services/profile.services') + +const { + extractBaseFromDn, + extractGroupsFromDn, +} = require('@src/helpers/dnHelper') + +const { signToken } = require('@src/utils/authentication/tokens/token_sign') + +const userService = UserServices() +const profileService = ProfileServices() + const clearSession = (req) => { return new Promise((resolve, reject) => { req.session.destroy((err) => { @@ -25,4 +40,61 @@ const logout = (req, res) => { return res.status(200).json({ message: 'User logged out successfully.' }) } -module.exports = { logout } +const refresh = async (req, res) => { + const { username } = req.body + + const refreshToken = await getRefreshToken(username) + + if (!refreshToken) { + return res.status(401).json({ message: 'Refresh token not found' }) + } + + const response = await userService.getByUsername(username) + const user = response[0] + + if (!user) { + return res.status(401).json({ message: 'User not found' }) + } + + const ldapDn = user.dn + const groups = extractGroupsFromDn(ldapDn) + const rootBaseDN = extractBaseFromDn(ldapDn) + const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') + + const last_time_logged = await profileService.getLastLoginByUsername(user.uid) + const loginInfo = await profileService.updateLastTimeLogged(user.uid) + + const isAdmin = true + + const payload = { + sub: user.uid, + dn: user.dn, + uid: user.uid, + groups: groups, + base: rootBaseDN, + localBase: localBaseDN, + firstname: user.givenName, + lastname: user.sn, + fullname: user.cn, + email: user.mail, + ci: user.CI, + roles: isAdmin ? ['admin', 'user'] : ['user'], + last_time_logged, + loginInfo, + } + + const newToken = signToken(payload, { expiresIn: '15 minutes' }) + const newRefreshToken = signToken(payload, { expiresIn: '1 day' }) + + await deleteRefreshToken(username) + setTimeout(async () => { + storeRefreshToken(user.uid, newRefreshToken) + }, 100) + + res.status(200).json({ + newToken, + newRefreshToken, + }) +} + +module.exports = { logout, refresh } diff --git a/src/services/auth.services.js b/src/services/auth.services.js index c8f60349..ef92bced 100644 --- a/src/services/auth.services.js +++ b/src/services/auth.services.js @@ -81,6 +81,7 @@ const getRefreshToken = async (userId, callback) => { try { await client.connect() const refreshToken = await client.get(`refreshToken:${userId}`) + disconnect() return refreshToken } catch (error) { disconnect() From 05c768bd04cbf2f9cf2681f8805ae7f681b6423b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 8 Sep 2023 20:11:31 -0400 Subject: [PATCH 078/229] check undefined on logger when refresh token --- src/middlewares/logger.handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js index 22c8d5ac..ec6b2ef7 100644 --- a/src/middlewares/logger.handler.js +++ b/src/middlewares/logger.handler.js @@ -31,7 +31,7 @@ const logFormat = (tokens, req, res) => { status: tokens.status(req, res), content_length: tokens.res(req, res, 'content-length'), response_time: tokens['response-time'](req, res), - user: req.user === undefined ? 'anonymous' : req.user.uid, + user: req.user === undefined ? 'anonymous' : req?.user?.uid, } logger.info({ ...log }) From 6ca333819ea25ca0706aea5a78a5812b1e060946 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 10:56:01 -0400 Subject: [PATCH 079/229] add jest.config file --- jest.config.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 jest.config.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..f966e82c --- /dev/null +++ b/jest.config.js @@ -0,0 +1,7 @@ +// jest.config.js +module.exports = { + // ... + moduleNameMapper: { + '^@src/(.*)$': '/src/$1', + }, +} From e4b1ebad78650d788c8ccd03b2994bbf52a8b7c4 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 10:56:52 -0400 Subject: [PATCH 080/229] add get group by CN endpoint --- src/routes/group.routes.js | 23 +++++++++++++++++++++++ src/services/group.services.js | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index 03712780..db6f2d4d 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -24,4 +24,27 @@ router.get('/:group', checkAuth, validateResponse, async (req, res) => { } }) +router.get('/byType/:type', checkAuth, validateResponse, async (req, res) => { + try { + const type = req.params.type + const baseDN = req.body.dn + if (!type) { + throw new Error(`It seems that the type is missing.`) + } + + const response = await service.getGroupByCN(baseDN, type) + + res.json({ + success: true, + data: response, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error fetching group', + error: `It seems that the group does not exist.`, + }) + } +}) + module.exports = router diff --git a/src/services/group.services.js b/src/services/group.services.js index a8e4169f..0c12ac14 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -1,5 +1,5 @@ require('dotenv').config({ path: __dirname + '/../../.env' }) -const config = require('@src/config/config') +const config = require('../config/config') const { performLdapSearch } = require('@src/utils/ldapUtils') const GroupServices = () => { @@ -18,6 +18,38 @@ const GroupServices = () => { throw err } } + + const getAdminGroup = async (baseDN = config.ldap.base) => { + const ldapFilter = `(objectClass=*)` + const dn = `cn=admin,${baseDN}` + try { + const results = await performLdapSearch(dn, ldapFilter) + return results + } catch (error) { + console.error('Error in getAdminGroup:', error) + // Optionally, you can throw the error again to propagate it to the caller + throw error + } + } + + const getGroupByCN = async (baseDN = config.ldap.base, cn = 'admin') => { + const ldapFilter = `(objectClass=*)` + const customDN = `cn=${cn},${baseDN}` + console.log('ldapFilter', ldapFilter) + try { + const results = await performLdapSearch(customDN, ldapFilter) + return results + } catch (error) { + console.error('Error in getGroupByCN:', error) + throw error + } + } + + return { + getAdminGroup, + getGroup, + getGroupByCN, + } } module.exports = GroupServices From d0c89a7c18a0a58c2821d607efc7c20d106f0cb9 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 11:34:42 -0400 Subject: [PATCH 081/229] add admin and superadmin roles fetched from ldap --- src/modules/authentication/LdapAuth.js | 20 +++++++++++------ src/services/auth.services.js | 30 ++++++++++++++++++++++++++ src/services/group.services.js | 1 - 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 762489a3..925c2f26 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -6,9 +6,10 @@ const passport = require('passport') const CustomStrategy = require('passport-custom').Strategy const JwtStrategy = require('../../utils/authentication/strategies/jwtStrategy') const { authenticate } = require('ldap-authentication') -const UserServices = require('../../services/user.services') +const userService = require('../../services/user.services')() +const { isSuperAdmin, isAdmin } = require('@src/services/auth.services') const User = require('../../schemas/user.schema').User -const ProfileServices = require('../../services/profile.services') +const profileService = require('../../services/profile.services')() const { checkAuth } = require('@src/middlewares/auth.handler') const { logout, refresh } = require('./functions/index.js') const { @@ -25,9 +26,6 @@ const { const { signToken } = require('../../utils/authentication/tokens/token_sign') const { responseSuccess } = require('../../schemas/response.schema') -const userService = UserServices() -const profileService = ProfileServices() - var _backwardCompatible = false var _dn var _findFunc @@ -221,7 +219,6 @@ const login = function (req, res, next) { .status(401) .json({ success: false, message: 'User cannot be found' }) } else { - const isAdmin = user.right === 'Todos' const last_time_logged = await profileService.getLastLoginByUsername( user.uid ) @@ -232,6 +229,15 @@ const login = function (req, res, next) { const rootBaseDN = extractBaseFromDn(ldapDn) const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') + let roles = ['user'] + const isSpAdmin = await isSuperAdmin(user.uid) + if (isSpAdmin) { + roles = [...roles, 'admin', 'superadmin'] + } else { + const isAdm = await isAdmin(user.uid, localBaseDN) + isAdm ? (roles = [...roles, 'admin']) : [...roles] + } + const payload = { sub: user.uid, dn: user.dn, @@ -244,7 +250,7 @@ const login = function (req, res, next) { fullname: user.cn, email: user.mail, ci: user.CI, - roles: isAdmin ? ['admin', 'user'] : ['user'], + roles: roles, last_time_logged, loginInfo, } diff --git a/src/services/auth.services.js b/src/services/auth.services.js index ef92bced..7c951266 100644 --- a/src/services/auth.services.js +++ b/src/services/auth.services.js @@ -4,6 +4,8 @@ const { isConnected, disconnect, } = require('../connections/redis_client') +const config = require('@src/config/config') +const { performLdapSearch } = require('@src/utils/ldapUtils') const isBlackListed = async (token) => { try { @@ -90,10 +92,38 @@ const getRefreshToken = async (userId, callback) => { } } +const isSuperAdmin = async (uid) => { + const ldapFilter = `(objectClass=posixGroup)` + const customDN = `cn=admin,${config.ldap.base}` + try { + const response = await performLdapSearch(customDN, ldapFilter) + const adminGroup = response[0] + return adminGroup.memberUid.includes(uid) + } catch (error) { + console.error('Error in isSuperAdmin:', error) + throw error + } +} + +const isAdmin = async (uid, baseDN = config.ldap.base) => { + const ldapFilter = `(objectClass=posixGroup)` + const customDN = `cn=admin,${baseDN.replace('usuarios', 'grupos')}` + try { + const response = await performLdapSearch(customDN, ldapFilter) + const adminGroup = response[0] + return adminGroup.memberUid.includes(uid) + } catch (error) { + console.error('Error in isSuperAdmin:', error) + throw error + } +} + module.exports = { addToBlackList, getRefreshToken, storeRefreshToken, deleteRefreshToken, isBlackListed, + isSuperAdmin, + isAdmin, } diff --git a/src/services/group.services.js b/src/services/group.services.js index 0c12ac14..35903e28 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -35,7 +35,6 @@ const GroupServices = () => { const getGroupByCN = async (baseDN = config.ldap.base, cn = 'admin') => { const ldapFilter = `(objectClass=*)` const customDN = `cn=${cn},${baseDN}` - console.log('ldapFilter', ldapFilter) try { const results = await performLdapSearch(customDN, ldapFilter) return results From 40cd6dca7cf3bfe80743947bf9cb9af9f832cd01 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 12:47:24 -0400 Subject: [PATCH 082/229] update active session error handleling in log in --- src/modules/authentication/LdapAuth.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 925c2f26..dfff2754 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -82,7 +82,9 @@ var init = function ( const username = req.body.username const password = req.body.password - if (req.session.passport !== undefined) + const localSession = { user: username } + + if (localSession.user === req.session?.passport?.user) throw new Error('log out before logging back in') const res = await userService.getByUsername(username) From 23b02dda5c306b611a228d730c6ad5d3c0233b22 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 12:47:50 -0400 Subject: [PATCH 083/229] allow to search only in localBranch --- src/routes/user.routes.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 1b755c92..1748bdde 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -15,7 +15,7 @@ const { const validateQuery = require('@src/middlewares/queryValidator') // Middleware for routes requiring checkAuth and checkRoles('admin') -router.use(checkAuth, checkBlacklist, checkRoles('admin')) +router.use(checkAuth, checkRoles('admin')) // Middleware to handle common success and error responses router.use(validateResponse) @@ -23,7 +23,7 @@ router.use(validateResponse) // Route handler for getting all users router.get('/', async (req, res) => { try { - const baseDN = `${config.ldap.base}` + const { localBase } = req.user const isValid = validateQuery(req.query) const queryFilter = createLdapFilterFromQuery(req.query) const ldapFilter = `(&(objectClass=person)${queryFilter})` @@ -33,7 +33,7 @@ router.get('/', async (req, res) => { // Call the performLdapSearch function to retrieve users matching the group filters const searchResults = await service.handleFilteredSearch( - baseDN, + localBase, ldapFilter, attributes, req.query.page, From 021bbf297a1b131231309ff90ff046b9600da068 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 15:29:47 -0400 Subject: [PATCH 084/229] add mongoose connection file --- src/connections/mongoose.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/connections/mongoose.js diff --git a/src/connections/mongoose.js b/src/connections/mongoose.js new file mode 100644 index 00000000..d362cb48 --- /dev/null +++ b/src/connections/mongoose.js @@ -0,0 +1,15 @@ +const mongoose = require('mongoose') +const config = require('@src/config/config') + +mongoose.connect(config.mongodb.url, { + useNewUrlParser: true, + useUnifiedTopology: true, +}) + +const db = mongoose.connection + +// Handle MongoDB connection events +db.on('error', console.error.bind(console, 'MongoDB connection error:')) +db.once('open', () => { + console.log('Connected to MongoDB (ldapDB)') +}) From 87001247b3d557d5c28b703df9cf77628676d772 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 15:30:00 -0400 Subject: [PATCH 085/229] add aditional dependencies --- package-lock.json | 96 +++++++++++++++++++++++++++++++++++++++-------- package.json | 4 ++ 2 files changed, 84 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8b4d0d7a..01f85073 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,8 @@ "express-paginate": "^1.0.2", "express-rate-limit": "^6.7.0", "express-status-monitor": "^1.3.4", + "express-validator": "^7.0.1", + "fs": "^0.0.1-security", "handlebars": "^4.7.7", "helmet": "^6.1.3", "jerzy": "^0.2.1", @@ -41,11 +43,13 @@ "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", + "password-validator": "^5.3.0", "redis": "^4.6.8", "redis-om": "^0.4.2", "simple-ldap-search": "^3.1.2", "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", + "ssha": "^1.0.1", "winston": "^3.8.2", "winston-mongodb": "^5.1.1" }, @@ -3391,6 +3395,18 @@ } } }, + "node_modules/express-validator": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.0.1.tgz", + "integrity": "sha512-oB+z9QOzQIE8FnlINqyIFA8eIckahC6qc8KtqLdLJcU3/phVyuhXH3bA4qzcrhme+1RYaCSwrq+TlZ/kAKIARA==", + "dependencies": { + "lodash": "^4.17.21", + "validator": "^13.9.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/express/node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -3620,6 +3636,11 @@ "node": ">= 0.6" } }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5072,11 +5093,6 @@ "npm": ">=6" } }, - "node_modules/jsonwebtoken/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, "node_modules/jsonwebtoken/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5251,9 +5267,9 @@ } }, "node_modules/lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha512-6X37Sq9KCpLSXEh8uM12AKYlviHPNNk4RxiGBn4cmKGJinbXBneWIV7iE/nXkM928O7ytHcHb6+X6Svl0f4hXg==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.assign": { "version": "4.2.0", @@ -6113,6 +6129,14 @@ "node": ">= 0.4.0" } }, + "node_modules/password-validator": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/password-validator/-/password-validator-5.3.0.tgz", + "integrity": "sha512-Q+bSEM5pjokZqzWGoQaoylkeWeH4+9uMYlVImiPD0EOJClQ2RPBhrJ5h0OjhMKtwOmu5rRcLaTZo5Gk9RBl0ig==", + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6964,6 +6988,14 @@ "es5-ext": "^0.10.53" } }, + "node_modules/ssha": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ssha/-/ssha-1.0.1.tgz", + "integrity": "sha512-Ugp2lmTmtTScqZHTFw2qayc0td0bzGwnttOONmcZOcM1wpq1YV+1P3U+oeJgu2/JBDTy/Qc+d+9blh5L13bI5Q==", + "engines": { + "node": "*" + } + }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -7505,6 +7537,14 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -10558,6 +10598,15 @@ } } }, + "express-validator": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.0.1.tgz", + "integrity": "sha512-oB+z9QOzQIE8FnlINqyIFA8eIckahC6qc8KtqLdLJcU3/phVyuhXH3bA4qzcrhme+1RYaCSwrq+TlZ/kAKIARA==", + "requires": { + "lodash": "^4.17.21", + "validator": "^13.9.0" + } + }, "ext": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", @@ -10689,6 +10738,11 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -11773,11 +11827,6 @@ "semver": "^7.3.8" }, "dependencies": { - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -11931,9 +11980,9 @@ } }, "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha512-6X37Sq9KCpLSXEh8uM12AKYlviHPNNk4RxiGBn4cmKGJinbXBneWIV7iE/nXkM928O7ytHcHb6+X6Svl0f4hXg==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.assign": { "version": "4.2.0", @@ -12608,6 +12657,11 @@ "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" }, + "password-validator": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/password-validator/-/password-validator-5.3.0.tgz", + "integrity": "sha512-Q+bSEM5pjokZqzWGoQaoylkeWeH4+9uMYlVImiPD0EOJClQ2RPBhrJ5h0OjhMKtwOmu5rRcLaTZo5Gk9RBl0ig==" + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -13282,6 +13336,11 @@ "es5-ext": "^0.10.53" } }, + "ssha": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ssha/-/ssha-1.0.1.tgz", + "integrity": "sha512-Ugp2lmTmtTScqZHTFw2qayc0td0bzGwnttOONmcZOcM1wpq1YV+1P3U+oeJgu2/JBDTy/Qc+d+9blh5L13bI5Q==" + }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -13679,6 +13738,11 @@ } } }, + "validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index d7950abd..9704e783 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "express-paginate": "^1.0.2", "express-rate-limit": "^6.7.0", "express-status-monitor": "^1.3.4", + "express-validator": "^7.0.1", + "fs": "^0.0.1-security", "handlebars": "^4.7.7", "helmet": "^6.1.3", "jerzy": "^0.2.1", @@ -48,11 +50,13 @@ "passport": "^0.6.0", "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", + "password-validator": "^5.3.0", "redis": "^4.6.8", "redis-om": "^0.4.2", "simple-ldap-search": "^3.1.2", "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", + "ssha": "^1.0.1", "winston": "^3.8.2", "winston-mongodb": "^5.1.1" }, From 61c477a3c9c08a2430e43e5c4939fd0f5e7900f1 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 15:30:11 -0400 Subject: [PATCH 086/229] add email template --- .../templates/reset-password-email.html | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/modules/forgot-password/templates/reset-password-email.html diff --git a/src/modules/forgot-password/templates/reset-password-email.html b/src/modules/forgot-password/templates/reset-password-email.html new file mode 100644 index 00000000..61081223 --- /dev/null +++ b/src/modules/forgot-password/templates/reset-password-email.html @@ -0,0 +1,44 @@ + + + + + Restablecimiento de Contraseña + + + + + + + + + + + + +
+

Restablecimiento de Contraseña

+
+

Hola, {{name}}!

+

+ Has solicitado restablecer tu contraseña. Haz clic en el siguiente + enlace para restablecer tu contraseña: +

+

+ Restablecer Contraseña +

+

Si no solicitaste esto, puedes ignorar este correo.

+
+

Equipo de Administración LDAP Cujae

+
+ + From b448a3f21bb5b4d6303e21cde8d6559cfd0ebc7d Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 19:18:16 -0400 Subject: [PATCH 087/229] remove fynamic link fomr html template --- src/modules/forgot-password/templates/reset-password-email.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/forgot-password/templates/reset-password-email.html b/src/modules/forgot-password/templates/reset-password-email.html index 61081223..046c0ec3 100644 --- a/src/modules/forgot-password/templates/reset-password-email.html +++ b/src/modules/forgot-password/templates/reset-password-email.html @@ -20,7 +20,7 @@

Restablecimiento de Contraseña

Hola, {{name}}!

- Has solicitado restablecer tu contraseña. Haz clic en el siguiente - enlace para restablecer tu contraseña: -

-

- Restablecer Contraseña + Has solicitado restablecer tu contraseña. Copia el siguiente código + en el formulario que te enviamos para restablecer tu contraseña:

+

{{code}}

Si no solicitaste esto, puedes ignorar este correo.

From 9783a2962a71c30008f26f2eec93e1a3e582360d Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 20:07:56 -0400 Subject: [PATCH 095/229] add password validation middleware --- .../schemas/passwordValidation.schema.js | 19 +++--- .../forgot-password/utils/passwordUtils.js | 63 +++++++++++++++++++ 2 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 src/modules/forgot-password/utils/passwordUtils.js diff --git a/src/modules/forgot-password/schemas/passwordValidation.schema.js b/src/modules/forgot-password/schemas/passwordValidation.schema.js index e32efd31..5985bf3e 100644 --- a/src/modules/forgot-password/schemas/passwordValidation.schema.js +++ b/src/modules/forgot-password/schemas/passwordValidation.schema.js @@ -1,14 +1,12 @@ -var passwordValidator = require('password-validator') +const passwordValidator = require('password-validator') -// Create a schema -var schema = new passwordValidator() +const passwordSchema = new passwordValidator() -// Add properties to it -schema +passwordSchema .is() - .min(8) // Minimum length 8 + .min(8) .is() - .max(100) // Maximum length 100 + .max(100) // You can adjust the maximum length as needed .has() .uppercase() // Must have uppercase letters .has() @@ -16,8 +14,15 @@ schema .has() .digits(2) // Must have at least 2 digits .has() + .symbols(1) // Must have at least 1 special symbol + .has() .not() .spaces() // Should not have spaces .is() .not() .oneOf(['Passw0rd', 'Password123', '123', 'admin', 'admin1234', '1234']) // Blacklist these values + .is() + .not() + .oneOf(['AnotherWeakPassword', '12345678', 'qwerty']) // Add more weak passwords here + +module.exports = passwordSchema diff --git a/src/modules/forgot-password/utils/passwordUtils.js b/src/modules/forgot-password/utils/passwordUtils.js new file mode 100644 index 00000000..7e854333 --- /dev/null +++ b/src/modules/forgot-password/utils/passwordUtils.js @@ -0,0 +1,63 @@ +const passwordSchema = require('../schemas/passwordValidation.schema') + +const passwordValidationMiddleware = (req, res, next) => { + // Retrieve the new password from the request body + const newPassword = req.body.newPassword + + // Validate the new password against the schema + const isPasswordValid = passwordSchema.validate(newPassword) + + if (!isPasswordValid) { + // Password doesn't meet the requirements + const validationErrors = passwordSchema.validate(newPassword, { + list: true, + }) + + // Construct an error message based on the validation specifics + const errorMessages = [] + + if (validationErrors.includes('min')) { + errorMessages.push('Password must be at least 8 characters long.') + } + + if (validationErrors.includes('max')) { + errorMessages.push('Password cannot exceed 100 characters.') + } + + if (validationErrors.includes('uppercase')) { + errorMessages.push('Password must contain at least one uppercase letter.') + } + + if (validationErrors.includes('lowercase')) { + errorMessages.push('Password must contain at least one lowercase letter.') + } + + if (validationErrors.includes('digits')) { + errorMessages.push('Password must contain at least 2 digits.') + } + + if (validationErrors.includes('symbols')) { + errorMessages.push('Password must contain at least 1 special symbol.') + } + + if (validationErrors.includes('spaces')) { + errorMessages.push('Password should not contain spaces.') + } + + if (validationErrors.includes('oneOf')) { + errorMessages.push('Password cannot be one of the common weak passwords.') + } + + // Respond with error messages and suggestions + return res.status(400).json({ + message: 'Invalid password format.', + errors: errorMessages, + suggestion: 'Suggested password format: Sample@Pass123', // Customize as needed + }) + } + + // Password is valid; proceed to the next middleware or route handler + next() +} + +module.exports = { passwordValidationMiddleware } From 6b63cdcab1e56da9362bb7ffe875191fc9e604f7 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 20:08:14 -0400 Subject: [PATCH 096/229] validate password before update --- .../routes/recovery_password.routes.js | 35 +++---------------- .../services/restore-password.service.js | 2 +- .../utils/{index.js => emailUtils.js} | 3 ++ 3 files changed, 8 insertions(+), 32 deletions(-) rename src/modules/forgot-password/utils/{index.js => emailUtils.js} (99%) diff --git a/src/modules/forgot-password/routes/recovery_password.routes.js b/src/modules/forgot-password/routes/recovery_password.routes.js index 1ccadd9a..b399632b 100644 --- a/src/modules/forgot-password/routes/recovery_password.routes.js +++ b/src/modules/forgot-password/routes/recovery_password.routes.js @@ -6,8 +6,8 @@ const { generateRecoveryCode, sendRecoveryPasswordEmailTo, } = require('../services/restore-password.service') -const { validationResult } = require('express-validator') -const { body } = require('express-validator') +const { validationResult, body } = require('express-validator') +const { passwordValidationMiddleware } = require('../utils/passwordUtils') // Validation rules for the email or username const validateEmailOrUsername = [ @@ -46,39 +46,12 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { res.status(500).json({ message: 'Internal Server Error' }) } }) -router.post('/reset-password', (req, res) => { +router.post('/reset-password', passwordValidationMiddleware, (req, res) => { // Retrieve the reset token and new password from the request body - const { token, newPassword } = req.body + const { recoveryCode, newPassword } = req.body try { - // Verify the reset token - const decodedToken = jwt.verify(token, secretKey) - - // Check if the token is still valid - redisClient.get(token, (error, storedEmailOrUsername) => { - if (error) { - throw new Error('Error retrieving token from Redis.') - } - - if (storedEmailOrUsername === decodedToken.emailOrUsername) { - // Token is valid, update the user's password in your database - // Replace this with your password update logic - // For example, you might hash the new password and update it in the database - // const hashedPassword = hashPassword(newPassword); - // updateUserPassword(decodedToken.emailOrUsername, hashedPassword); - - // Respond with a success message - res.json({ message: 'Password reset successful.' }) - - // Delete the token from Redis (optional) - redisClient.del(token) - } else { - // Token does not match the stored value - throw new Error('Invalid or expired token.') - } - }) } catch (error) { - // Handle token verification errors console.error('Error resetting password:', error) res.status(400).json({ message: 'Invalid or expired token.' }) } diff --git a/src/modules/forgot-password/services/restore-password.service.js b/src/modules/forgot-password/services/restore-password.service.js index 2ab1adbe..091f93b6 100644 --- a/src/modules/forgot-password/services/restore-password.service.js +++ b/src/modules/forgot-password/services/restore-password.service.js @@ -6,7 +6,7 @@ const fs = require('fs') const userServices = require('@src/services/user.services')() const path = require('path') const UserAndRecoveryCode = require('../schemas/user&recoveryCode.schema') -const { generateRandomSixDigitNumber, readHTMLFile } = require('../utils/index') +const { generateRandomSixDigitNumber, readHTMLFile } = require('../utils/emailUtils') const generateRecoveryCode = async (user, expiration) => { try { diff --git a/src/modules/forgot-password/utils/index.js b/src/modules/forgot-password/utils/emailUtils.js similarity index 99% rename from src/modules/forgot-password/utils/index.js rename to src/modules/forgot-password/utils/emailUtils.js index 3de7047b..f55e0fe9 100644 --- a/src/modules/forgot-password/utils/index.js +++ b/src/modules/forgot-password/utils/emailUtils.js @@ -20,4 +20,7 @@ function readHTMLFile(path, callback) { }) } + + + module.exports = { generateRandomSixDigitNumber, readHTMLFile } From 859750ab58f5cd86e275e270e533a200d5945255 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 20:15:55 -0400 Subject: [PATCH 097/229] send token on forgot password --- .../routes/recovery_password.routes.js | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/modules/forgot-password/routes/recovery_password.routes.js b/src/modules/forgot-password/routes/recovery_password.routes.js index b399632b..46de37e0 100644 --- a/src/modules/forgot-password/routes/recovery_password.routes.js +++ b/src/modules/forgot-password/routes/recovery_password.routes.js @@ -8,6 +8,8 @@ const { } = require('../services/restore-password.service') const { validationResult, body } = require('express-validator') const { passwordValidationMiddleware } = require('../utils/passwordUtils') +const { signToken } = require('@src/utils/authentication/tokens/token_sign') +const { checkAuth, checkBlacklist } = require('@src/middlewares/auth.handler') // Validation rules for the email or username const validateEmailOrUsername = [ @@ -37,24 +39,39 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { return res.status(404).json({ message: 'User not found.' }) } + const payload = { + username: user.uid, + email: user.maildrop, + } + + const token = signToken(payload, { expiresIn: '15 minutes' }) + const recoveryCode = await generateRecoveryCode(user, new Date(900000)) //15 min await sendRecoveryPasswordEmailTo(user, recoveryCode) - res.json({ message: 'Password reset email sent successfully.' }) + res.json({ + message: 'Password reset email sent successfully.', + token: token, + }) } catch (error) { console.error('Error in /forgot-password:', error) res.status(500).json({ message: 'Internal Server Error' }) } }) -router.post('/reset-password', passwordValidationMiddleware, (req, res) => { - // Retrieve the reset token and new password from the request body - const { recoveryCode, newPassword } = req.body +router.post( + '/reset-password', + checkAuth, + passwordValidationMiddleware, + (req, res) => { + // Retrieve the reset token and new password from the request body + const { recoveryCode, newPassword } = req.body - try { - } catch (error) { - console.error('Error resetting password:', error) - res.status(400).json({ message: 'Invalid or expired token.' }) + try { + } catch (error) { + console.error('Error resetting password:', error) + res.status(400).json({ message: 'Invalid or expired token.' }) + } } -}) +) module.exports = router From 6b9917cf0374e977de5831d2649b5b13095843d9 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 20:38:34 -0400 Subject: [PATCH 098/229] add check recovery code validation --- .../routes/recovery_password.routes.js | 32 ++++++++++--- .../services/restore-password.service.js | 46 ++++++++++++++++++- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/modules/forgot-password/routes/recovery_password.routes.js b/src/modules/forgot-password/routes/recovery_password.routes.js index 46de37e0..fa3a407b 100644 --- a/src/modules/forgot-password/routes/recovery_password.routes.js +++ b/src/modules/forgot-password/routes/recovery_password.routes.js @@ -5,11 +5,14 @@ const userService = require('@src/services/user.services')() const { generateRecoveryCode, sendRecoveryPasswordEmailTo, + checkRecoveryCode, } = require('../services/restore-password.service') const { validationResult, body } = require('express-validator') const { passwordValidationMiddleware } = require('../utils/passwordUtils') const { signToken } = require('@src/utils/authentication/tokens/token_sign') +const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') const { checkAuth, checkBlacklist } = require('@src/middlewares/auth.handler') +const boom = require('@hapi/boom') // Validation rules for the email or username const validateEmailOrUsername = [ @@ -46,7 +49,10 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { const token = signToken(payload, { expiresIn: '15 minutes' }) - const recoveryCode = await generateRecoveryCode(user, new Date(900000)) //15 min + const currentTime = new Date() + const expiration = new Date(currentTime.getTime() + 15 * 60 * 1000) // 15 minutes in milliseconds + + const recoveryCode = await generateRecoveryCode(user, expiration) await sendRecoveryPasswordEmailTo(user, recoveryCode) res.json({ @@ -62,14 +68,28 @@ router.post( '/reset-password', checkAuth, passwordValidationMiddleware, - (req, res) => { - // Retrieve the reset token and new password from the request body - const { recoveryCode, newPassword } = req.body - + async (req, res) => { try { + const { recoveryCode, newPassword } = req.body + const payload = verifyToken(req.headers.authorization.split(' ')[1]) + console.log('payload', payload) + + if (!payload) { + const error = boom.unauthorized('Token is not valid') + throw error + } + + const checkedCode = await checkRecoveryCode( + payload.username, + recoveryCode + ) + if (!checkedCode.isValid) { + const error = boom.unauthorized(checkedCode.isValid.message) + throw error + } } catch (error) { console.error('Error resetting password:', error) - res.status(400).json({ message: 'Invalid or expired token.' }) + res.status(400).json({ message: 'Invalid or expired recovery code.' }) } } ) diff --git a/src/modules/forgot-password/services/restore-password.service.js b/src/modules/forgot-password/services/restore-password.service.js index 091f93b6..df341f7b 100644 --- a/src/modules/forgot-password/services/restore-password.service.js +++ b/src/modules/forgot-password/services/restore-password.service.js @@ -6,7 +6,10 @@ const fs = require('fs') const userServices = require('@src/services/user.services')() const path = require('path') const UserAndRecoveryCode = require('../schemas/user&recoveryCode.schema') -const { generateRandomSixDigitNumber, readHTMLFile } = require('../utils/emailUtils') +const { + generateRandomSixDigitNumber, + readHTMLFile, +} = require('../utils/emailUtils') const generateRecoveryCode = async (user, expiration) => { try { @@ -95,4 +98,43 @@ const sendRecoveryPasswordEmailTo = async (user, code) => { } } -module.exports = { generateRecoveryCode, sendRecoveryPasswordEmailTo } +async function checkRecoveryCode(username, recoveryCode) { + try { + // Find a user by username + const user = await UserAndRecoveryCode.findOne({ username }) + + // Check if the user exists and has a recovery code set + if (!user || !user.recoveryCode || !user.recoveryCode.code) { + return { + isValid: false, + message: 'User not found or no recovery code set.', + } + } + + // Check if the recovery code matches + if (user.recoveryCode.code !== recoveryCode) { + return { isValid: false, message: 'Invalid recovery code.' } + } + + // Check if the recovery code has expired + const currentTime = new Date() + if ( + user.recoveryCode.expiresAt && + currentTime > user.recoveryCode.expiresAt + ) { + return { isValid: false, message: 'Recovery code has expired.' } + } + + // Recovery code is valid + return { isValid: true, message: 'Recovery code is valid.' } + } catch (error) { + console.error('Error checking recovery code:', error) + throw error + } +} + +module.exports = { + generateRecoveryCode, + sendRecoveryPasswordEmailTo, + checkRecoveryCode, +} From 2758c002144336e1ca19a8d193c243a3b3fc8185 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 21:06:35 -0400 Subject: [PATCH 099/229] update password method finished --- .../routes/recovery_password.routes.js | 17 +++++++++++++++-- .../forgot-password/utils/passwordUtils.js | 13 ++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/modules/forgot-password/routes/recovery_password.routes.js b/src/modules/forgot-password/routes/recovery_password.routes.js index fa3a407b..c85289f6 100644 --- a/src/modules/forgot-password/routes/recovery_password.routes.js +++ b/src/modules/forgot-password/routes/recovery_password.routes.js @@ -8,7 +8,11 @@ const { checkRecoveryCode, } = require('../services/restore-password.service') const { validationResult, body } = require('express-validator') -const { passwordValidationMiddleware } = require('../utils/passwordUtils') +const { + passwordValidationMiddleware, + hashPassword, + verifyPassword, +} = require('../utils/passwordUtils') const { signToken } = require('@src/utils/authentication/tokens/token_sign') const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') const { checkAuth, checkBlacklist } = require('@src/middlewares/auth.handler') @@ -72,7 +76,6 @@ router.post( try { const { recoveryCode, newPassword } = req.body const payload = verifyToken(req.headers.authorization.split(' ')[1]) - console.log('payload', payload) if (!payload) { const error = boom.unauthorized('Token is not valid') @@ -87,6 +90,16 @@ router.post( const error = boom.unauthorized(checkedCode.isValid.message) throw error } + + const encriptedPassword = hashPassword(newPassword) + const updatedUser = await userService.updateUser( + payload.username, + 'userPassword', + encriptedPassword + ) + if (!!updatedUser) { + res.status(200).json({ success: true, message: 'updated user' }) + } } catch (error) { console.error('Error resetting password:', error) res.status(400).json({ message: 'Invalid or expired recovery code.' }) diff --git a/src/modules/forgot-password/utils/passwordUtils.js b/src/modules/forgot-password/utils/passwordUtils.js index 7e854333..c5828ab7 100644 --- a/src/modules/forgot-password/utils/passwordUtils.js +++ b/src/modules/forgot-password/utils/passwordUtils.js @@ -1,4 +1,6 @@ const passwordSchema = require('../schemas/passwordValidation.schema') +const assert = require('assert') +const ssha = require('ssha') const passwordValidationMiddleware = (req, res, next) => { // Retrieve the new password from the request body @@ -60,4 +62,13 @@ const passwordValidationMiddleware = (req, res, next) => { next() } -module.exports = { passwordValidationMiddleware } +const hashPassword = (password) => ssha.create(password) + +const verifyPassword = (password, encriptedPassword) => + ssha.verify(password, encriptedPassword) + +module.exports = { + passwordValidationMiddleware, + hashPassword, + verifyPassword, +} From e7d827a42ef26274d5be9069b102903fccef053f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 21:26:43 -0400 Subject: [PATCH 100/229] add email notification to password change --- .../forgot-password/images/favicon.png | Bin 0 -> 4193 bytes .../routes/recovery_password.routes.js | 3 + .../services/restore-password.service.js | 51 +++++++++++ .../templates/reset-password-email.html | 86 +++++++++++++----- .../templates/successful-password-change.html | 61 +++++++++++++ 5 files changed, 177 insertions(+), 24 deletions(-) create mode 100644 src/modules/forgot-password/images/favicon.png create mode 100644 src/modules/forgot-password/templates/successful-password-change.html diff --git a/src/modules/forgot-password/images/favicon.png b/src/modules/forgot-password/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..6007eb10435bb7ae321cf002f8ec4ccf34ec9152 GIT binary patch literal 4193 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 { const payload = { username: user.uid, + name: user.name, email: user.maildrop, } @@ -98,6 +100,7 @@ router.post( encriptedPassword ) if (!!updatedUser) { + await sendSuccessPasswordEmailTo(payload.name, payload.email) res.status(200).json({ success: true, message: 'updated user' }) } } catch (error) { diff --git a/src/modules/forgot-password/services/restore-password.service.js b/src/modules/forgot-password/services/restore-password.service.js index df341f7b..896c05cc 100644 --- a/src/modules/forgot-password/services/restore-password.service.js +++ b/src/modules/forgot-password/services/restore-password.service.js @@ -133,8 +133,59 @@ async function checkRecoveryCode(username, recoveryCode) { } } +const sendSuccessPasswordEmailTo = async (username, email) => { + try { + const transporter = nodemailer.createTransport( + smtpTransport({ + service: process.env.EMAIL_SERVICE, + host: process.env.EMAIL_HOST, + secure: true, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASSWORD, + }, + }) + ) + + readHTMLFile( + path.join(__dirname, '../templates/successful-password-change.html'), + function (err, html) { + if (err) { + console.log('error reading file', err) + return + } + + const template = handlebars.compile(html) + const replacements = { + username, + } + const htmlToSend = template(replacements) + + const mailOptions = { + from: process.env.EMAIL_USER, + to: email, + subject: 'Reestablecimiento de Contrasena', + html: htmlToSend, + } + + transporter.sendMail(mailOptions, function (error, info) { + if (error) { + console.log(error) + } else { + console.log('Email sent: ' + info.response) + } + }) + } + ) + } catch (error) { + console.error(error) + throw new Error('Error on sending email') + } +} + module.exports = { generateRecoveryCode, sendRecoveryPasswordEmailTo, checkRecoveryCode, + sendSuccessPasswordEmailTo, } diff --git a/src/modules/forgot-password/templates/reset-password-email.html b/src/modules/forgot-password/templates/reset-password-email.html index 4e280e0d..1763b64e 100644 --- a/src/modules/forgot-password/templates/reset-password-email.html +++ b/src/modules/forgot-password/templates/reset-password-email.html @@ -1,32 +1,70 @@ - Restablecimiento de Contraseña + + - - - - - - - - - - -
-

Restablecimiento de Contraseña

-
-

Hola, {{name}}!

-

- Has solicitado restablecer tu contraseña. Copia el siguiente código - en el formulario que te enviamos para restablecer tu contraseña: -

-

{{code}}

-

Si no solicitaste esto, puedes ignorar este correo.

-
-

Equipo de Administración LDAP Cujae

-
+
+ +
+ Company Logo +

Restablecimiento de Contraseña

+
+ +
+

Hola, {{name}}!

+

+ Has solicitado restablecer tu contraseña. Copia el siguiente código en + el formulario que te enviamos para restablecer tu contraseña: +

+

{{code}}

+

Si no solicitaste esto, puedes ignorar este correo.

+
+ +
+

Equipo de Administración LDAP Cujae

+
+
diff --git a/src/modules/forgot-password/templates/successful-password-change.html b/src/modules/forgot-password/templates/successful-password-change.html new file mode 100644 index 00000000..e425cabe --- /dev/null +++ b/src/modules/forgot-password/templates/successful-password-change.html @@ -0,0 +1,61 @@ + + + + Cambio de Contraseña Exitoso + + +
+ +
+ Cujae Logo +

Cambio de Contraseña Exitoso

+
+ +
+

Tu contraseña ha sido cambiada exitosamente.

+

+ Si no solicitaste este cambio, por favor, contacta a nuestro equipo de + soporte de inmediato. +

+
+ +
+

© 2023 Tu Empresa. Todos los derechos reservados.

+
+
+ + From 2cec98337dd993a76d1c031c262bd44a3b99e531 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 11 Sep 2023 21:27:16 -0400 Subject: [PATCH 101/229] change blue color for cujae green --- .../forgot-password/templates/reset-password-email.html | 4 ++-- .../forgot-password/templates/successful-password-change.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/forgot-password/templates/reset-password-email.html b/src/modules/forgot-password/templates/reset-password-email.html index 1763b64e..5065dec0 100644 --- a/src/modules/forgot-password/templates/reset-password-email.html +++ b/src/modules/forgot-password/templates/reset-password-email.html @@ -20,7 +20,7 @@ } header { - background-color: #007bff; + background-color: #006633; color: #fff; text-align: center; padding: 20px; @@ -36,7 +36,7 @@ } footer { - background-color: #007bff; + background-color: #006633; color: #fff; text-align: center; padding: 10px; diff --git a/src/modules/forgot-password/templates/successful-password-change.html b/src/modules/forgot-password/templates/successful-password-change.html index e425cabe..d6a4a507 100644 --- a/src/modules/forgot-password/templates/successful-password-change.html +++ b/src/modules/forgot-password/templates/successful-password-change.html @@ -24,7 +24,7 @@
Date: Mon, 11 Sep 2023 21:41:13 -0400 Subject: [PATCH 102/229] delete unnecessary dependencies --- package-lock.json | 1655 +-------------------------------------------- package.json | 13 - 2 files changed, 2 insertions(+), 1666 deletions(-) diff --git a/package-lock.json b/package-lock.json index 01f85073..40b78ffa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,7 @@ "dependencies": { "@hapi/boom": "^10.0.1", "axios": "^1.3.5", - "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", - "cluster": "^0.7.7", "compression": "^1.7.4", "cors": "^2.8.5", "dotenv": "^16.0.3", @@ -25,18 +23,13 @@ "fs": "^0.0.1-security", "handlebars": "^4.7.7", "helmet": "^6.1.3", - "jerzy": "^0.2.1", - "jest-fetch-mock": "^3.0.3", "joi": "^13.0.2", "jsonwebtoken": "^9.0.0", - "jstat": "^1.9.6", "LDAP": "^1.2.1", "ldap-authentication": "^2.3.1", "ldapjs": "^3.0.1", - "lodash": "^4.17.4", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", - "node-cache": "^5.1.2", "nodemailer": "^6.9.1", "nodemailer-smtp-transport": "^2.7.4", "os": "^0.1.2", @@ -45,9 +38,6 @@ "passport-jwt": "^4.0.1", "password-validator": "^5.3.0", "redis": "^4.6.8", - "redis-om": "^0.4.2", - "simple-ldap-search": "^3.1.2", - "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", "ssha": "^1.0.1", "winston": "^3.8.2", @@ -59,12 +49,9 @@ "express-session": "^1.17.3", "jasmine": "^4.3.0", "jest": "^29.6.4", - "jsdom": "^21.1.1", - "jsdom-global": "^3.0.2", "module-alias": "^2.2.3", "mongoose": "^6.5.2", "nodemon": "^2.0.22", - "superagent": "^8.0.0", "supertest": "^6.3.3" } }, @@ -1329,15 +1316,6 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/@types/babel__core": { "version": "7.20.1", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", @@ -1472,12 +1450,6 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1501,77 +1473,11 @@ "node": ">= 0.6" } }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==" }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1887,11 +1793,6 @@ "node": ">= 0.8" } }, - "node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2209,26 +2110,6 @@ "node": ">=12" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/cluster": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/cluster/-/cluster-0.7.7.tgz", - "integrity": "sha512-16LzEZSoBUgRHSN7NA46ntGnI9tf0NnxTm3KCDvHd6aj4EsBqUbYDN3ImCfjYegBXJZiutULF5rWkcRusMIozQ==", - "dependencies": { - "log": ">= 1.2.0", - "mkdirp": ">= 0.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -2511,14 +2392,6 @@ "node": ">= 0.10" } }, - "node_modules/cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", - "dependencies": { - "node-fetch": "2.6.7" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2533,71 +2406,6 @@ "node": ">= 8" } }, - "node_modules/cssstyle": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", - "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", - "dev": true, - "dependencies": { - "rrweb-cssom": "^0.6.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/d/node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "node_modules/data-urls": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", - "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", - "dev": true, - "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/data-urls/node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dev": true, - "dependencies": { - "punycode": "^2.3.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", - "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", - "dev": true, - "dependencies": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2606,12 +2414,6 @@ "ms": "2.0.0" } }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, "node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -2626,12 +2428,6 @@ } } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -2703,18 +2499,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "dev": true, - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -2723,15 +2507,6 @@ "node": ">=12" } }, - "node_modules/duration": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", - "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.46" - } - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -2917,18 +2692,6 @@ } } }, - "node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2938,39 +2701,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2994,28 +2724,6 @@ "node": ">=8" } }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -3029,24 +2737,6 @@ "node": ">=4" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -3055,15 +2745,6 @@ "node": ">= 0.6" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "node_modules/event-loop-stats": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/event-loop-stats/-/event-loop-stats-1.2.0.tgz", @@ -3463,14 +3144,6 @@ } ] }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dependencies": { - "type": "^2.7.2" - } - }, "node_modules/extsprintf": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", @@ -3485,12 +3158,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -3868,18 +3535,6 @@ "node": ">=8.9.0" } }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3901,43 +3556,6 @@ "node": ">= 0.8" } }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/httpntlm": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz", @@ -3958,42 +3576,6 @@ "node": ">= 6.15.1" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4180,12 +3762,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -4342,12 +3918,6 @@ "integrity": "sha512-qybtBUesniQdW6n+QIHMng2vDOHscIC/dEXjW+JzO9+LoAZMb03RCUC5xFOv/btSKPm1xL42fn+RjlU4oB42Lg==", "dev": true }, - "node_modules/jerzy": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/jerzy/-/jerzy-0.2.1.tgz", - "integrity": "sha512-NHuYTuLgwwBrGKNORh+mSqnvJUeXe9OX9HLP60CT4iRQPgi+OPKBjThOLvwc5SA4yfjTQ/RpyhPfczfavS+Gsg==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." - }, "node_modules/jest": { "version": "29.6.4", "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", @@ -4558,15 +4128,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-fetch-mock": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", - "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", - "dependencies": { - "cross-fetch": "^3.0.4", - "promise-polyfill": "^8.1.3" - } - }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", @@ -4961,85 +4522,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsdom": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.1.1.tgz", - "integrity": "sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==", - "dev": true, - "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.2", - "acorn-globals": "^7.0.0", - "cssstyle": "^3.0.0", - "data-urls": "^4.0.0", - "decimal.js": "^10.4.3", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.6.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.1", - "ws": "^8.13.0", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom-global": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsdom-global/-/jsdom-global-3.0.2.tgz", - "integrity": "sha512-t1KMcBkz/pT5JrvcJbpUR2u/w1kO9jXctaaGJ0vZDzwFnIvGWw9IDSRciT83kIs8Bnw4qpOl8bQK08V01YgMPg==", - "dev": true, - "peerDependencies": { - "jsdom": ">=10.0.0" - } - }, - "node_modules/jsdom/node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dev": true, - "dependencies": { - "punycode": "^2.3.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/jsdom/node_modules/whatwg-url": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", - "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", - "dev": true, - "dependencies": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -5070,14 +4552,6 @@ "node": ">=6" } }, - "node_modules/jsonpath-plus": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", - "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==", - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -5098,16 +4572,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/jstat": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/jstat/-/jstat-1.9.6.tgz", - "integrity": "sha512-rPBkJbK2TnA8pzs93QcDDPlKcrtZWuuCo2dVR0TFLOJSxhqfWOVCSp8aV3/oSbn+4uY4yw1URtLpHQedtmXfug==" - }, - "node_modules/just-clone": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-6.2.0.tgz", - "integrity": "sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==" - }, "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -5235,19 +4699,6 @@ "node": ">=6" } }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -5286,20 +4737,6 @@ "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" }, - "node_modules/log": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/log/-/log-6.3.1.tgz", - "integrity": "sha512-McG47rJEWOkXTDioZzQNydAVvZNeEkSyLJ1VWkFwfW+o1knW+QSi8D1KjPn/TnctV+q99lkvJNe1f0E1IjfY2A==", - "dependencies": { - "d": "^1.0.1", - "duration": "^0.2.2", - "es5-ext": "^0.10.53", - "event-emitter": "^0.3.5", - "sprintf-kit": "^2.0.1", - "type": "^2.5.0", - "uni-global": "^1.0.0" - } - }, "node_modules/logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -5669,60 +5106,6 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "node_modules/node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "dependencies": { - "clone": "2.x" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5880,12 +5263,6 @@ "node": ">=8" } }, - "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", - "dev": true - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5963,23 +5340,6 @@ "node": ">=4" } }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/os": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", @@ -6048,22 +5408,10 @@ "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "dependencies": { - "entities": "^4.4.0" + "node": ">=8" }, "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parseqs": { @@ -6238,15 +5586,6 @@ "node": ">= 0.6" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/pretty-format": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", @@ -6283,11 +5622,6 @@ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.1.0.tgz", "integrity": "sha512-9C20RLxrZU/rFnxWncDkuF6O999NdIf3E1ws4B0ZeY3sRVPzWBMsYDE2lxjxhiXxg464cQTgKUGm8/i6y2YGXg==" }, - "node_modules/promise-polyfill": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", - "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==" - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -6318,12 +5652,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -6368,12 +5696,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -6450,20 +5772,6 @@ "@redis/time-series": "1.0.5" } }, - "node_modules/redis-om": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/redis-om/-/redis-om-0.4.2.tgz", - "integrity": "sha512-sBah+ljGQY4Zm1f9/+l7HjtIuTQH/rzIX3JSLizymbr6VlTZ2ibt+U3xYKNjIA0tv/D9toEMF7HFXHMtT+l+1A==", - "dependencies": { - "jsonpath-plus": "^7.2.0", - "just-clone": "^6.1.1", - "redis": "^4.6.4", - "ulid": "^2.3.0" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -6481,12 +5789,6 @@ "node": ">=0.10.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, "node_modules/resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", @@ -6534,12 +5836,6 @@ "node": ">=10" } }, - "node_modules/rrweb-cssom": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", - "dev": true - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6570,18 +5866,6 @@ "node": ">=6" } }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, "node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -6689,43 +5973,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/simple-ldap-search": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/simple-ldap-search/-/simple-ldap-search-3.1.2.tgz", - "integrity": "sha512-/9b5f4VJhFiI2V2ZtZbFbTv+3PCamV7MGKgMLea/1mKvtY1GSVbZQKclOnqaoxkZ3i/yAPTF1eURykBNFKi36g==", - "dependencies": { - "ldapjs": "^2.3.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/simple-ldap-search/node_modules/ldapjs": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz", - "integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==", - "dependencies": { - "abstract-logging": "^2.0.0", - "asn1": "^0.2.4", - "assert-plus": "^1.0.0", - "backoff": "^2.5.0", - "ldap-filter": "^0.3.3", - "once": "^1.4.0", - "vasync": "^2.2.0", - "verror": "^1.8.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/simple-statistics": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.3.tgz", - "integrity": "sha512-JFvMY00t6SBGtwMuJ+nqgsx9ylkMiJ5JlK9bkj8AdvniIe5615wWQYkKHXe84XtSuc40G/tlrPu0A5/NlJvv8A==", - "engines": { - "node": "*" - } - }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -6980,14 +6227,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/sprintf-kit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sprintf-kit/-/sprintf-kit-2.0.1.tgz", - "integrity": "sha512-2PNlcs3j5JflQKcg4wpdqpZ+AjhQJ2OZEo34NXDtlB0tIPG84xaaXhpA8XFacFiwjKA4m49UOYG83y3hbMn/gQ==", - "dependencies": { - "es5-ext": "^0.10.53" - } - }, "node_modules/ssha": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ssha/-/ssha-1.0.1.tgz", @@ -7217,12 +6456,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -7309,21 +6542,6 @@ "nodetouch": "bin/nodetouch.js" } }, - "node_modules/tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", - "dev": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/tr46": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", @@ -7341,23 +6559,6 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, - "node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -7420,14 +6621,6 @@ "node": ">= 0.8" } }, - "node_modules/ulid": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", - "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", - "bin": { - "ulid": "bin/cli.js" - } - }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -7439,23 +6632,6 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==" }, - "node_modules/uni-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/uni-global/-/uni-global-1.0.0.tgz", - "integrity": "sha512-WWM3HP+siTxzIWPNUg7hZ4XO8clKi6NoCAJJWnuRL+BAqyFXF8gC03WNyTefGoUXYc47uYgXxpKLIEvo65PEHw==", - "dependencies": { - "type": "^2.5.0" - } - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -7494,16 +6670,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7590,18 +6756,6 @@ "node": ">=0.6.0" } }, - "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "dev": true, - "dependencies": { - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -7620,39 +6774,6 @@ "node": ">=12" } }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding/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==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/whatwg-url": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", @@ -7810,15 +6931,6 @@ "node": ">= 6" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -7859,42 +6971,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, "node_modules/xmlhttprequest-ssl": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", @@ -8973,12 +8049,6 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, - "@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true - }, "@types/babel__core": { "version": "7.20.1", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", @@ -9113,12 +8183,6 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -9139,59 +8203,11 @@ "negotiator": "0.6.3" } }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true - }, - "acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, - "requires": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, "after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==" }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -9435,11 +8451,6 @@ "safe-buffer": "5.1.2" } }, - "bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" - }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -9649,20 +8660,6 @@ "wrap-ansi": "^7.0.0" } }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" - }, - "cluster": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/cluster/-/cluster-0.7.7.tgz", - "integrity": "sha512-16LzEZSoBUgRHSN7NA46ntGnI9tf0NnxTm3KCDvHd6aj4EsBqUbYDN3ImCfjYegBXJZiutULF5rWkcRusMIozQ==", - "requires": { - "log": ">= 1.2.0", - "mkdirp": ">= 0.0.1" - } - }, "cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -9888,14 +8885,6 @@ "vary": "^1" } }, - "cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", - "requires": { - "node-fetch": "2.6.7" - } - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -9907,63 +8896,6 @@ "which": "^2.0.1" } }, - "cssstyle": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", - "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", - "dev": true, - "requires": { - "rrweb-cssom": "^0.6.0" - } - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - }, - "dependencies": { - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - } - } - }, - "data-urls": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", - "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", - "dev": true, - "requires": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.0" - }, - "dependencies": { - "tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dev": true, - "requires": { - "punycode": "^2.3.0" - } - }, - "whatwg-url": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", - "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", - "dev": true, - "requires": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" - } - } - } - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -9972,12 +8904,6 @@ "ms": "2.0.0" } }, - "decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, "dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -9985,12 +8911,6 @@ "dev": true, "requires": {} }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, "deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -10040,29 +8960,11 @@ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, - "domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "dev": true, - "requires": { - "webidl-conversions": "^7.0.0" - } - }, "dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" }, - "duration": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", - "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", - "requires": { - "d": "1", - "es5-ext": "~0.10.46" - } - }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -10198,12 +9100,6 @@ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==" }, - "entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", - "dev": true - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -10213,35 +9109,6 @@ "is-arrayish": "^0.2.1" } }, - "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -10259,51 +9126,17 @@ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, - "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "event-loop-stats": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/event-loop-stats/-/event-loop-stats-1.2.0.tgz", @@ -10607,14 +9440,6 @@ "validator": "^13.9.0" } }, - "ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "requires": { - "type": "^2.7.2" - } - }, "extsprintf": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", @@ -10626,12 +9451,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, "fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -10902,15 +9721,6 @@ "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" }, - "html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "requires": { - "whatwg-encoding": "^2.0.0" - } - }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -10929,34 +9739,6 @@ "toidentifier": "1.0.1" } }, - "http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "httpntlm": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz", @@ -10971,33 +9753,6 @@ "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.5.2.tgz", "integrity": "sha512-2Jm+x9WkExDOeFRrdBCBSpLPT5SokTcRHkunV3pjKmX/cx6av8zQ0WtHUMDrYb6O4hBFzNU6sxJEypvRUVYKnw==" }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -11128,12 +9883,6 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -11253,11 +10002,6 @@ "integrity": "sha512-qybtBUesniQdW6n+QIHMng2vDOHscIC/dEXjW+JzO9+LoAZMb03RCUC5xFOv/btSKPm1xL42fn+RjlU4oB42Lg==", "dev": true }, - "jerzy": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/jerzy/-/jerzy-0.2.1.tgz", - "integrity": "sha512-NHuYTuLgwwBrGKNORh+mSqnvJUeXe9OX9HLP60CT4iRQPgi+OPKBjThOLvwc5SA4yfjTQ/RpyhPfczfavS+Gsg==" - }, "jest": { "version": "29.6.4", "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", @@ -11407,15 +10151,6 @@ "jest-util": "^29.6.3" } }, - "jest-fetch-mock": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", - "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", - "requires": { - "cross-fetch": "^3.0.4", - "promise-polyfill": "^8.1.3" - } - }, "jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", @@ -11731,68 +10466,6 @@ "esprima": "^4.0.0" } }, - "jsdom": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.1.1.tgz", - "integrity": "sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==", - "dev": true, - "requires": { - "abab": "^2.0.6", - "acorn": "^8.8.2", - "acorn-globals": "^7.0.0", - "cssstyle": "^3.0.0", - "data-urls": "^4.0.0", - "decimal.js": "^10.4.3", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.6.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.1", - "ws": "^8.13.0", - "xml-name-validator": "^4.0.0" - }, - "dependencies": { - "tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "dev": true, - "requires": { - "punycode": "^2.3.0" - } - }, - "whatwg-url": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", - "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", - "dev": true, - "requires": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" - } - } - } - }, - "jsdom-global": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsdom-global/-/jsdom-global-3.0.2.tgz", - "integrity": "sha512-t1KMcBkz/pT5JrvcJbpUR2u/w1kO9jXctaaGJ0vZDzwFnIvGWw9IDSRciT83kIs8Bnw4qpOl8bQK08V01YgMPg==", - "dev": true, - "requires": {} - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -11811,11 +10484,6 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, - "jsonpath-plus": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", - "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==" - }, "jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -11834,16 +10502,6 @@ } } }, - "jstat": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/jstat/-/jstat-1.9.6.tgz", - "integrity": "sha512-rPBkJbK2TnA8pzs93QcDDPlKcrtZWuuCo2dVR0TFLOJSxhqfWOVCSp8aV3/oSbn+4uY4yw1URtLpHQedtmXfug==" - }, - "just-clone": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-6.2.0.tgz", - "integrity": "sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==" - }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -11954,16 +10612,6 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -11999,20 +10647,6 @@ "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" }, - "log": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/log/-/log-6.3.1.tgz", - "integrity": "sha512-McG47rJEWOkXTDioZzQNydAVvZNeEkSyLJ1VWkFwfW+o1knW+QSi8D1KjPn/TnctV+q99lkvJNe1f0E1IjfY2A==", - "requires": { - "d": "^1.0.1", - "duration": "^0.2.2", - "es5-ext": "^0.10.53", - "event-emitter": "^0.3.5", - "sprintf-kit": "^2.0.1", - "type": "^2.5.0", - "uni-global": "^1.0.0" - } - }, "logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -12306,48 +10940,6 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "requires": { - "clone": "2.x" - } - }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } - } - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -12473,12 +11065,6 @@ "path-key": "^3.0.0" } }, - "nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -12535,20 +11121,6 @@ "require-at": "^1.0.6" } }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, "os": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", @@ -12601,15 +11173,6 @@ "lines-and-columns": "^1.1.6" } }, - "parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "requires": { - "entities": "^4.4.0" - } - }, "parseqs": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", @@ -12736,12 +11299,6 @@ "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==" }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true - }, "pretty-format": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", @@ -12771,11 +11328,6 @@ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.1.0.tgz", "integrity": "sha512-9C20RLxrZU/rFnxWncDkuF6O999NdIf3E1ws4B0ZeY3sRVPzWBMsYDE2lxjxhiXxg464cQTgKUGm8/i6y2YGXg==" }, - "promise-polyfill": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", - "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==" - }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -12800,12 +11352,6 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, "pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -12831,12 +11377,6 @@ "side-channel": "^1.0.4" } }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -12901,17 +11441,6 @@ "@redis/time-series": "1.0.5" } }, - "redis-om": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/redis-om/-/redis-om-0.4.2.tgz", - "integrity": "sha512-sBah+ljGQY4Zm1f9/+l7HjtIuTQH/rzIX3JSLizymbr6VlTZ2ibt+U3xYKNjIA0tv/D9toEMF7HFXHMtT+l+1A==", - "requires": { - "jsonpath-plus": "^7.2.0", - "just-clone": "^6.1.1", - "redis": "^4.6.4", - "ulid": "^2.3.0" - } - }, "require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -12923,12 +11452,6 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, "resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", @@ -12961,12 +11484,6 @@ "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true }, - "rrweb-cssom": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", - "dev": true - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -12991,15 +11508,6 @@ "sparse-bitfield": "^3.0.3" } }, - "saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, "semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -13088,36 +11596,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "simple-ldap-search": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/simple-ldap-search/-/simple-ldap-search-3.1.2.tgz", - "integrity": "sha512-/9b5f4VJhFiI2V2ZtZbFbTv+3PCamV7MGKgMLea/1mKvtY1GSVbZQKclOnqaoxkZ3i/yAPTF1eURykBNFKi36g==", - "requires": { - "ldapjs": "^2.3.1" - }, - "dependencies": { - "ldapjs": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz", - "integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==", - "requires": { - "abstract-logging": "^2.0.0", - "asn1": "^0.2.4", - "assert-plus": "^1.0.0", - "backoff": "^2.5.0", - "ldap-filter": "^0.3.3", - "once": "^1.4.0", - "vasync": "^2.2.0", - "verror": "^1.8.1" - } - } - } - }, - "simple-statistics": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.3.tgz", - "integrity": "sha512-JFvMY00t6SBGtwMuJ+nqgsx9ylkMiJ5JlK9bkj8AdvniIe5615wWQYkKHXe84XtSuc40G/tlrPu0A5/NlJvv8A==" - }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -13328,14 +11806,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "sprintf-kit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sprintf-kit/-/sprintf-kit-2.0.1.tgz", - "integrity": "sha512-2PNlcs3j5JflQKcg4wpdqpZ+AjhQJ2OZEo34NXDtlB0tIPG84xaaXhpA8XFacFiwjKA4m49UOYG83y3hbMn/gQ==", - "requires": { - "es5-ext": "^0.10.53" - } - }, "ssha": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ssha/-/ssha-1.0.1.tgz", @@ -13496,12 +11966,6 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -13573,18 +12037,6 @@ "nopt": "~1.0.10" } }, - "tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - } - }, "tr46": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", @@ -13599,20 +12051,6 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, - "type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -13654,11 +12092,6 @@ "random-bytes": "~1.0.0" } }, - "ulid": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", - "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==" - }, "undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -13670,20 +12103,6 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==" }, - "uni-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/uni-global/-/uni-global-1.0.0.tgz", - "integrity": "sha512-WWM3HP+siTxzIWPNUg7hZ4XO8clKi6NoCAJJWnuRL+BAqyFXF8gC03WNyTefGoUXYc47uYgXxpKLIEvo65PEHw==", - "requires": { - "type": "^2.5.0" - } - }, - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -13699,16 +12118,6 @@ "picocolors": "^1.0.0" } }, - "url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -13778,15 +12187,6 @@ "extsprintf": "^1.2.0" } }, - "w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "dev": true, - "requires": { - "xml-name-validator": "^4.0.0" - } - }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -13802,32 +12202,6 @@ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true }, - "whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "requires": { - "iconv-lite": "0.6.3" - }, - "dependencies": { - "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==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true - }, "whatwg-url": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", @@ -13933,12 +12307,6 @@ } } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -13970,25 +12338,6 @@ "signal-exit": "^3.0.7" } }, - "ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "dev": true, - "requires": {} - }, - "xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, "xmlhttprequest-ssl": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", diff --git a/package.json b/package.json index 9704e783..1de14938 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,7 @@ "dependencies": { "@hapi/boom": "^10.0.1", "axios": "^1.3.5", - "bcryptjs": "^2.4.3", "body-parser": "^1.20.2", - "cluster": "^0.7.7", "compression": "^1.7.4", "cors": "^2.8.5", "dotenv": "^16.0.3", @@ -32,18 +30,13 @@ "fs": "^0.0.1-security", "handlebars": "^4.7.7", "helmet": "^6.1.3", - "jerzy": "^0.2.1", - "jest-fetch-mock": "^3.0.3", "joi": "^13.0.2", "jsonwebtoken": "^9.0.0", - "jstat": "^1.9.6", "LDAP": "^1.2.1", "ldap-authentication": "^2.3.1", "ldapjs": "^3.0.1", - "lodash": "^4.17.4", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", - "node-cache": "^5.1.2", "nodemailer": "^6.9.1", "nodemailer-smtp-transport": "^2.7.4", "os": "^0.1.2", @@ -52,9 +45,6 @@ "passport-jwt": "^4.0.1", "password-validator": "^5.3.0", "redis": "^4.6.8", - "redis-om": "^0.4.2", - "simple-ldap-search": "^3.1.2", - "simple-statistics": "^7.8.3", "socket.io": "^4.6.1", "ssha": "^1.0.1", "winston": "^3.8.2", @@ -66,12 +56,9 @@ "express-session": "^1.17.3", "jasmine": "^4.3.0", "jest": "^29.6.4", - "jsdom": "^21.1.1", - "jsdom-global": "^3.0.2", "module-alias": "^2.2.3", "mongoose": "^6.5.2", "nodemon": "^2.0.22", - "superagent": "^8.0.0", "supertest": "^6.3.3" }, "scripts": { From c3f1dab516f7a8a4819f22dda989a479ad6cd09f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 15:26:49 -0400 Subject: [PATCH 103/229] rename password module --- .../images/favicon.png | Bin .../routes/recovery_password.routes.js | 0 .../schemas/passwordValidation.schema.js | 0 .../schemas/user&recoveryCode.schema.js | 0 .../services/restore-password.service.js | 0 .../templates/reset-password-email.html | 0 .../templates/successful-password-change.html | 0 .../utils/emailUtils.js | 0 .../utils/passwordUtils.js | 0 src/routes/routes.js | 5 +++-- 10 files changed, 3 insertions(+), 2 deletions(-) rename src/modules/{forgot-password => passwordManagement}/images/favicon.png (100%) rename src/modules/{forgot-password => passwordManagement}/routes/recovery_password.routes.js (100%) rename src/modules/{forgot-password => passwordManagement}/schemas/passwordValidation.schema.js (100%) rename src/modules/{forgot-password => passwordManagement}/schemas/user&recoveryCode.schema.js (100%) rename src/modules/{forgot-password => passwordManagement}/services/restore-password.service.js (100%) rename src/modules/{forgot-password => passwordManagement}/templates/reset-password-email.html (100%) rename src/modules/{forgot-password => passwordManagement}/templates/successful-password-change.html (100%) rename src/modules/{forgot-password => passwordManagement}/utils/emailUtils.js (100%) rename src/modules/{forgot-password => passwordManagement}/utils/passwordUtils.js (100%) diff --git a/src/modules/forgot-password/images/favicon.png b/src/modules/passwordManagement/images/favicon.png similarity index 100% rename from src/modules/forgot-password/images/favicon.png rename to src/modules/passwordManagement/images/favicon.png diff --git a/src/modules/forgot-password/routes/recovery_password.routes.js b/src/modules/passwordManagement/routes/recovery_password.routes.js similarity index 100% rename from src/modules/forgot-password/routes/recovery_password.routes.js rename to src/modules/passwordManagement/routes/recovery_password.routes.js diff --git a/src/modules/forgot-password/schemas/passwordValidation.schema.js b/src/modules/passwordManagement/schemas/passwordValidation.schema.js similarity index 100% rename from src/modules/forgot-password/schemas/passwordValidation.schema.js rename to src/modules/passwordManagement/schemas/passwordValidation.schema.js diff --git a/src/modules/forgot-password/schemas/user&recoveryCode.schema.js b/src/modules/passwordManagement/schemas/user&recoveryCode.schema.js similarity index 100% rename from src/modules/forgot-password/schemas/user&recoveryCode.schema.js rename to src/modules/passwordManagement/schemas/user&recoveryCode.schema.js diff --git a/src/modules/forgot-password/services/restore-password.service.js b/src/modules/passwordManagement/services/restore-password.service.js similarity index 100% rename from src/modules/forgot-password/services/restore-password.service.js rename to src/modules/passwordManagement/services/restore-password.service.js diff --git a/src/modules/forgot-password/templates/reset-password-email.html b/src/modules/passwordManagement/templates/reset-password-email.html similarity index 100% rename from src/modules/forgot-password/templates/reset-password-email.html rename to src/modules/passwordManagement/templates/reset-password-email.html diff --git a/src/modules/forgot-password/templates/successful-password-change.html b/src/modules/passwordManagement/templates/successful-password-change.html similarity index 100% rename from src/modules/forgot-password/templates/successful-password-change.html rename to src/modules/passwordManagement/templates/successful-password-change.html diff --git a/src/modules/forgot-password/utils/emailUtils.js b/src/modules/passwordManagement/utils/emailUtils.js similarity index 100% rename from src/modules/forgot-password/utils/emailUtils.js rename to src/modules/passwordManagement/utils/emailUtils.js diff --git a/src/modules/forgot-password/utils/passwordUtils.js b/src/modules/passwordManagement/utils/passwordUtils.js similarity index 100% rename from src/modules/forgot-password/utils/passwordUtils.js rename to src/modules/passwordManagement/utils/passwordUtils.js diff --git a/src/routes/routes.js b/src/routes/routes.js index ef9c39af..4e001862 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -2,14 +2,15 @@ const UserRoutes = require('./user.routes') const GroupRoutes = require('./group.routes') const ProfileRoutes = require('./profile.routes') const LogsRoutes = require('./logs.routes') -const RecoveryPassword = require('@src/modules/forgot-password/routes/recovery_password.routes') +const RecoveryPassword = require('@src/modules/passwordManagement/routes/recovery_password.routes') +const UpdatePassword = require('@src/modules/passwordManagement/routes/updated_password.routes') const addRoutes = (app) => { app.use('/users', UserRoutes) app.use('/groups', GroupRoutes) app.use('/profile', ProfileRoutes) app.use('/logs', LogsRoutes) - app.use('/', RecoveryPassword) + app.use('/', RecoveryPassword, UpdatePassword) } module.exports = addRoutes From a92374bdc84b9f8a8f8f0b8a3a9c213703dfdf96 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 15:35:42 -0400 Subject: [PATCH 104/229] update password endpoint added --- .../routes/updated_password.routes.js | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/modules/passwordManagement/routes/updated_password.routes.js diff --git a/src/modules/passwordManagement/routes/updated_password.routes.js b/src/modules/passwordManagement/routes/updated_password.routes.js new file mode 100644 index 00000000..dd3e124f --- /dev/null +++ b/src/modules/passwordManagement/routes/updated_password.routes.js @@ -0,0 +1,73 @@ +const express = require('express') +const router = express.Router() +const userService = require('@src/services/user.services')() +const { + sendSuccessPasswordEmailTo, +} = require('../services/restore-password.service') +const { validationResult, body } = require('express-validator') +const { + passwordValidationMiddleware, + hashPassword, + verifyPassword, +} = require('../utils/passwordUtils') +const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') +const { checkAuth, checkBlacklist } = require('@src/middlewares/auth.handler') +const boom = require('@hapi/boom') + +router.post( + '/update-password', + checkAuth, + passwordValidationMiddleware, + async (req, res) => { + try { + const { newPassword, confirmPassword, oldPassword } = req.body + + if (!newPassword || !confirmPassword) { + throw boom.unauthorized( + 'New password and confirm password are required' + ) + } + if (!oldPassword) { + throw boom.unauthorized('Password is required') + } + + if (newPassword !== confirmPassword) { + throw boom.unauthorized('Passwords must be equal') + } + + const token = req.headers.authorization.split(' ')[1] + const payload = verifyToken(token) + + if (!payload) { + throw boom.unauthorized('Token is not valid') + } + + const user = await userService.getByUsername(payload.uid) + + if (!user) { + throw boom.unauthorized('User not found') + } + + if (!verifyPassword(oldPassword, user.userPassword)) { + throw boom.unauthorized('Invalid password') + } + + const encryptedPassword = hashPassword(newPassword) + const updatedUser = await userService.updateUser( + payload.uid, + 'userPassword', + encryptedPassword + ) + + if (!!updatedUser) { + await sendSuccessPasswordEmailTo(payload.name, payload.email) + res.status(200).json({ success: true, message: 'Updated user' }) + } + } catch (error) { + console.error('Error updating password:', error) + res.status(500).json({ message: 'Error updating password' }) + } + } +) + +module.exports = router From 8ad8e35ffb2163495af04affbdcae352e5642a71 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 15:40:31 -0400 Subject: [PATCH 105/229] add email notification to update password endpoint --- .../passwordManagement/routes/updated_password.routes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/passwordManagement/routes/updated_password.routes.js b/src/modules/passwordManagement/routes/updated_password.routes.js index dd3e124f..d803938b 100644 --- a/src/modules/passwordManagement/routes/updated_password.routes.js +++ b/src/modules/passwordManagement/routes/updated_password.routes.js @@ -60,7 +60,7 @@ router.post( ) if (!!updatedUser) { - await sendSuccessPasswordEmailTo(payload.name, payload.email) + await sendSuccessPasswordEmailTo(user.name, user.maildrop) res.status(200).json({ success: true, message: 'Updated user' }) } } catch (error) { From 85e5b0bfe33b650d749eac78b7e58f8ecfe3477b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 15:40:55 -0400 Subject: [PATCH 106/229] update password change template --- .../templates/successful-password-change.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/passwordManagement/templates/successful-password-change.html b/src/modules/passwordManagement/templates/successful-password-change.html index d6a4a507..30972fe6 100644 --- a/src/modules/passwordManagement/templates/successful-password-change.html +++ b/src/modules/passwordManagement/templates/successful-password-change.html @@ -33,7 +33,8 @@ Cujae Logo

Cambio de Contraseña Exitoso

@@ -54,7 +55,7 @@

Cambio de Contraseña Exitoso

padding: 10px; " > -

© 2023 Tu Empresa. Todos los derechos reservados.

+

Equipo de Administración LDAP Cujae

From 007dcbc9a7c07a147dd0b75ed44e3f68cdf7d560 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:07:12 -0400 Subject: [PATCH 107/229] add filter by lastName --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 9865d29a..4c734ecf 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -6,6 +6,7 @@ const attributeFilters = { username: (value) => `uid=${value}`, ci: (value) => `ci=${value}`, email: (value) => `maildrop=${value}`, + lastName: (value) => `lastName=${value}`, } const userTypeFilters = { From 7f7ca711b00241fe7e98167cba4a0aec757acf16 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:07:24 -0400 Subject: [PATCH 108/229] add filter by sex --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 4c734ecf..2c9baa80 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -7,6 +7,7 @@ const attributeFilters = { ci: (value) => `ci=${value}`, email: (value) => `maildrop=${value}`, lastName: (value) => `lastName=${value}`, + sex: (value) => `sex=${value}`, } const userTypeFilters = { From cfd5363b1d396a573ec910857c29e48ed5a89056 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:07:45 -0400 Subject: [PATCH 109/229] add filter by area --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 2c9baa80..db53a0bb 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -8,6 +8,7 @@ const attributeFilters = { email: (value) => `maildrop=${value}`, lastName: (value) => `lastName=${value}`, sex: (value) => `sex=${value}`, + area: (value) => `area=${value}`, } const userTypeFilters = { From 91745b3adf79e98ad4e50b4e27ab53378e5d570f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:08:13 -0400 Subject: [PATCH 110/229] add filter by userCondition --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index db53a0bb..d196895e 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -9,6 +9,7 @@ const attributeFilters = { lastName: (value) => `lastName=${value}`, sex: (value) => `sex=${value}`, area: (value) => `area=${value}`, + userCondition: (value) => `userCondition=${value}`, } const userTypeFilters = { From d892d6e7b3d1ee3999496b8e403c4317d43e1788 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:08:40 -0400 Subject: [PATCH 111/229] add filter by userStatus --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index d196895e..50294205 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -10,6 +10,7 @@ const attributeFilters = { sex: (value) => `sex=${value}`, area: (value) => `area=${value}`, userCondition: (value) => `userCondition=${value}`, + userStatus: (value) => `userStatus=${value}`, } const userTypeFilters = { From 2a7b2117cac2f25b592a657c5f409d85aa278b9e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:09:07 -0400 Subject: [PATCH 112/229] add filter by sedeMunicipio --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 50294205..fc78fec6 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -11,6 +11,7 @@ const attributeFilters = { area: (value) => `area=${value}`, userCondition: (value) => `userCondition=${value}`, userStatus: (value) => `userStatus=${value}`, + sedeMunicipio: (value) => `sedeMunicipio=${value}`, } const userTypeFilters = { From fd6a3bec9618cfbb58dd6dc0570687657136a2c9 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:09:46 -0400 Subject: [PATCH 113/229] add filter by userInformation --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index fc78fec6..afaabed5 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -12,6 +12,7 @@ const attributeFilters = { userCondition: (value) => `userCondition=${value}`, userStatus: (value) => `userStatus=${value}`, sedeMunicipio: (value) => `sedeMunicipio=${value}`, + userInformation: (value) => `userInformation=${value}`, } const userTypeFilters = { From ea00cdaa318961f10bfffce8ad450845b6d834dd Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:10:18 -0400 Subject: [PATCH 114/229] add filter by career --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index afaabed5..23073627 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -13,6 +13,7 @@ const attributeFilters = { userStatus: (value) => `userStatus=${value}`, sedeMunicipio: (value) => `sedeMunicipio=${value}`, userInformation: (value) => `userInformation=${value}`, + career: (value) => `career=${value}`, } const userTypeFilters = { From fff7aff52ecb2b6bbd9584e247ce4bfe2ead0eea Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:10:40 -0400 Subject: [PATCH 115/229] add filter by studentClassGroup --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 23073627..1387a895 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -14,6 +14,7 @@ const attributeFilters = { sedeMunicipio: (value) => `sedeMunicipio=${value}`, userInformation: (value) => `userInformation=${value}`, career: (value) => `career=${value}`, + studentClassGroup: (value) => `studentClassGroup=${value}`, } const userTypeFilters = { From 1be7ad3389f5051614c7aa6a7bba08f329140a0b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:11:07 -0400 Subject: [PATCH 116/229] add filter by studentYear --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 1387a895..0839bde1 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -15,6 +15,7 @@ const attributeFilters = { userInformation: (value) => `userInformation=${value}`, career: (value) => `career=${value}`, studentClassGroup: (value) => `studentClassGroup=${value}`, + studentYear: (value) => `studentYear=${value}`, } const userTypeFilters = { From 84e956b1886dc15897a2555ab039f5455b994e42 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:11:26 -0400 Subject: [PATCH 117/229] add filter by country --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 0839bde1..2c6d5d5a 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -16,6 +16,7 @@ const attributeFilters = { career: (value) => `career=${value}`, studentClassGroup: (value) => `studentClassGroup=${value}`, studentYear: (value) => `studentYear=${value}`, + country: (value) => `country=${value}`, } const userTypeFilters = { From cfdae85f83bf58b62fe7830ff2dfcea28cf49298 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:11:48 -0400 Subject: [PATCH 118/229] add filter by UJC --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 2c6d5d5a..51222821 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -17,6 +17,7 @@ const attributeFilters = { studentClassGroup: (value) => `studentClassGroup=${value}`, studentYear: (value) => `studentYear=${value}`, country: (value) => `country=${value}`, + UJC: (value) => `UJC=${value}`, } const userTypeFilters = { From 5b028b49d23b27a1d88f74865683aeca199ee7e9 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:12:15 -0400 Subject: [PATCH 119/229] add filter by skinColor --- src/helpers/convertQueryToFilter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 51222821..2e8aff21 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -18,6 +18,7 @@ const attributeFilters = { studentYear: (value) => `studentYear=${value}`, country: (value) => `country=${value}`, UJC: (value) => `UJC=${value}`, + skinColor: (value) => `skinColor=${value}`, } const userTypeFilters = { From 8219dec66cc6d260c084fc043d26c48ff16ed587 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:13:03 -0400 Subject: [PATCH 120/229] add filter by sn and displayName --- src/helpers/convertQueryToFilter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 2e8aff21..ce2f4b63 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -19,6 +19,8 @@ const attributeFilters = { country: (value) => `country=${value}`, UJC: (value) => `UJC=${value}`, skinColor: (value) => `skinColor=${value}`, + sn: (value) => `sn=${value}`, + displayName: (value) => `displayName=${value}`, } const userTypeFilters = { From aaf9e22249461e2bbcec21120553ca627d547923 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:13:44 -0400 Subject: [PATCH 121/229] add filter by mail and maildrop --- src/helpers/convertQueryToFilter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index ce2f4b63..a009604c 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -21,6 +21,8 @@ const attributeFilters = { skinColor: (value) => `skinColor=${value}`, sn: (value) => `sn=${value}`, displayName: (value) => `displayName=${value}`, + mail: (value) => `mail=${value}`, + maildrop: (value) => `maildrop=${value}`, } const userTypeFilters = { From 7c294ec8e07fb4088053676247182960f7cb24ca Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:14:39 -0400 Subject: [PATCH 122/229] add filter by dn and objectName --- src/helpers/convertQueryToFilter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index a009604c..9a6ca635 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -23,6 +23,8 @@ const attributeFilters = { displayName: (value) => `displayName=${value}`, mail: (value) => `mail=${value}`, maildrop: (value) => `maildrop=${value}`, + objectName: (value) => `objectName=${value}`, + dn: (value) => `dn=${value}`, } const userTypeFilters = { From e57d32e139b7c325eda0842a4ad3be50353fbcf8 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:19:05 -0400 Subject: [PATCH 123/229] add employees att filters --- src/helpers/convertQueryToFilter.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 9a6ca635..d8784a96 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -25,6 +25,15 @@ const attributeFilters = { maildrop: (value) => `maildrop=${value}`, objectName: (value) => `objectName=${value}`, dn: (value) => `dn=${value}`, + workerID: (value) => `workerID=${value}`, + workArea: (value) => `workArea=${value}`, + nameInstitution: (value) => `nameInstitution=${value}`, + workercontract: (value) => `workercontract=${value}`, + userYears: (value) => `userYears=${value}`, + schoolLevel: (value) => `schoolLevel=${value}`, + orgRole: (value) => `orgRole=${value}`, + educationalCategory: (value) => `educationalCategory=${value}`, + scientificCategory: (value) => `scientificCategory=${value}`, } const userTypeFilters = { From c0ec6160057b3a1f4855655e923b3cbbced8681a Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 17:42:15 -0400 Subject: [PATCH 124/229] get groups by DN --- src/routes/group.routes.js | 47 +++++++++++++++++++++++++++++++--- src/services/group.services.js | 12 +++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index db6f2d4d..717daebc 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -3,14 +3,53 @@ const router = express.Router() const GroupServices = require('../services/group.services') const { responseSuccess, responseError } = require('../schemas/response.schema') const validateResponse = require('../middlewares/validateResponse') -const { checkAuth } = require('../middlewares/auth.handler') +const { checkAuth, checkRoles } = require('../middlewares/auth.handler') +const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') +const config = require('@src/config/config') const service = GroupServices() //Get all users -router.get('/:group', checkAuth, validateResponse, async (req, res) => { +/* router.get( + '/:group', + checkAuth, + checkRoles('admin'), + validateResponse, + async (req, res) => { + try { + const group = req.params.group + const response = await service.getGroup(group) + res.json({ + success: true, + data: response, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error fetching group', + error: `It seems that the group does not exist.`, + }) + } + } +) + */ +router.get('/', checkAuth, validateResponse, async (req, res) => { try { - const group = req.params.group - const response = await service.getGroup(group) + const payload = verifyToken(req.headers.authorization.split(' ')[1]) + + if (!payload) { + throw new Error(`Invalid token.`) + } + + const baseDN = payload.roles.includes('superadmin') + ? (req.body.baseDN = config.ldap.base) + : payload.localBase.replace('ou=usuarios,', '') + + if (!baseDN) { + throw new Error(`Invalid token.`) + } + + const response = await service.getGroupsInBaseDN(baseDN) + res.json({ success: true, data: response, diff --git a/src/services/group.services.js b/src/services/group.services.js index 35903e28..c80d5784 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -44,10 +44,22 @@ const GroupServices = () => { } } + const getGroupsInBaseDN = async (baseDN = config.ldap.base) => { + const ldapFilter = `(objectClass=organizationalUnit)` + try { + const results = await performLdapSearch(baseDN, ldapFilter) + return results + } catch (error) { + console.error('Error in getGroupByCN:', error) + throw error + } + } + return { getAdminGroup, getGroup, getGroupByCN, + getGroupsInBaseDN, } } From cacad8e563ff0a955c376d50b8bc756cfd75734e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Tue, 12 Sep 2023 23:29:59 -0400 Subject: [PATCH 125/229] get groups in baseDN --- src/constants/nodeTypes.js | 5 ++++ src/routes/group.routes.js | 55 +++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 src/constants/nodeTypes.js diff --git a/src/constants/nodeTypes.js b/src/constants/nodeTypes.js new file mode 100644 index 00000000..074c8067 --- /dev/null +++ b/src/constants/nodeTypes.js @@ -0,0 +1,5 @@ +const nodeTypes = ['groups', 'users', 'poxisGroups'] + +module.exports = { + nodeTypes, +} diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index 717daebc..b4854809 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -7,31 +7,8 @@ const { checkAuth, checkRoles } = require('../middlewares/auth.handler') const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') const config = require('@src/config/config') const service = GroupServices() +const { nodeTypes } = require('@src/constants/nodeTypes') -//Get all users -/* router.get( - '/:group', - checkAuth, - checkRoles('admin'), - validateResponse, - async (req, res) => { - try { - const group = req.params.group - const response = await service.getGroup(group) - res.json({ - success: true, - data: response, - }) - } catch (error) { - res.status(500).json({ - success: false, - message: 'Error fetching group', - error: `It seems that the group does not exist.`, - }) - } - } -) - */ router.get('/', checkAuth, validateResponse, async (req, res) => { try { const payload = verifyToken(req.headers.authorization.split(' ')[1]) @@ -63,6 +40,36 @@ router.get('/', checkAuth, validateResponse, async (req, res) => { } }) +router.get( + '/getChilds/:nodeType', + checkAuth, + checkRoles('admin'), + validateResponse, + async (req, res) => { + try { + const nodeType = req.params.nodeType + const baseDN = req + + if (!nodeTypes.includes(nodeType)) { + throw new Error(`Invalid node type.`) + } + + if (nodeType === nodeTypes[0]) { + const response = await service.getGroupsInBaseDN(baseDN) + res.json({ + success: true, + data: response, + }) + } + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error fetching group', + error: `It seems that the group does not exist.`, + }) + } + } +) router.get('/byType/:type', checkAuth, validateResponse, async (req, res) => { try { const type = req.params.type From 06400e80ad230ce5b5e076bc1b343cc5109b8c8b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 13 Sep 2023 00:02:08 -0400 Subject: [PATCH 126/229] add scopedSearch method --- src/utils/ldapUtils.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index 7679d098..ec4f66f8 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -42,7 +42,6 @@ const transform = (entry) => { // Perform a search using the provided filter and return the results const performLdapSearch = async (baseDn, filter, attributes) => { - console.log('filter', filter) return new Promise((resolve, reject) => { try { bindLdapClient() // Bind before search @@ -76,6 +75,40 @@ const performLdapSearch = async (baseDn, filter, attributes) => { }) } +const performScopedLdapSearch = async (baseDn, filter, attributes) => { + return new Promise((resolve, reject) => { + try { + bindLdapClient() // Bind before search + + const searchOptions = { + filter: filter, + scope: 'one', + attributes, + timeLimit: config.ldap.timeLimit, + } + + const searchResults = [] + + ldapClient.search(baseDn, searchOptions, (err, searchResponse) => { + if (err) { + reject(err) + return // Exit the function early in case of error + } + + searchResponse.on('searchEntry', (entry) => { + searchResults.push(transform(entry)) + }) + + searchResponse.on('end', () => { + resolve(searchResults) + }) + }) + } catch (err) { + reject(err) + } + }) +} + const performLdapUpdate = async (userDN, att, value) => { console.log('userDN', userDN) return new Promise((resolve, reject) => { @@ -109,4 +142,5 @@ module.exports = { performLdapSearch, unbindLdapClient, performLdapUpdate, + performScopedLdapSearch, } From ac585525f8bc5ffdb9ecdedcf374dfcdce559572 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 13 Sep 2023 00:05:00 -0400 Subject: [PATCH 127/229] add get Childrens --- src/routes/group.routes.js | 21 +++++++++------------ src/services/group.services.js | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index b4854809..d33f85ad 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -41,26 +41,23 @@ router.get('/', checkAuth, validateResponse, async (req, res) => { }) router.get( - '/getChilds/:nodeType', + '/getChilds', checkAuth, checkRoles('admin'), validateResponse, async (req, res) => { try { - const nodeType = req.params.nodeType - const baseDN = req + const { baseDN } = req.body - if (!nodeTypes.includes(nodeType)) { - throw new Error(`Invalid node type.`) + if (!baseDN) { + throw new Error(`Base DN required.`) } - if (nodeType === nodeTypes[0]) { - const response = await service.getGroupsInBaseDN(baseDN) - res.json({ - success: true, - data: response, - }) - } + const response = await service.getChildrensBaseDN(baseDN) + res.json({ + success: true, + data: response, + }) } catch (error) { res.status(500).json({ success: false, diff --git a/src/services/group.services.js b/src/services/group.services.js index c80d5784..850d548c 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -1,6 +1,9 @@ require('dotenv').config({ path: __dirname + '/../../.env' }) const config = require('../config/config') -const { performLdapSearch } = require('@src/utils/ldapUtils') +const { + performLdapSearch, + performScopedLdapSearch, +} = require('@src/utils/ldapUtils') const GroupServices = () => { const getGroup = async (group) => { @@ -55,11 +58,23 @@ const GroupServices = () => { } } + const getChildrensBaseDN = async (baseDN = config.ldap.base) => { + const ldapFilter = `(objectClass=*)` + try { + const results = await performScopedLdapSearch(baseDN, ldapFilter) + return results + } catch (error) { + console.error('Error in getChildrens', error) + throw error + } + } + return { getAdminGroup, getGroup, getGroupByCN, getGroupsInBaseDN, + getChildrensBaseDN, } } From 6665cbf5d9597911101ddcde64f2972bd15495a0 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 13 Sep 2023 00:26:11 -0400 Subject: [PATCH 128/229] add search by DN --- src/routes/dn.routes.js | 39 +++++++++++++++++++++++++++++++++++++ src/routes/group.routes.js | 1 - src/routes/routes.js | 3 ++- src/services/dn.services.js | 16 +++++++++++++++ src/utils/ldapUtils.js | 35 +++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/routes/dn.routes.js create mode 100644 src/services/dn.services.js diff --git a/src/routes/dn.routes.js b/src/routes/dn.routes.js new file mode 100644 index 00000000..07245d6a --- /dev/null +++ b/src/routes/dn.routes.js @@ -0,0 +1,39 @@ +const express = require('express') +const router = express.Router() +const { searchByDN } = require('../services/dn.services') +const { responseSuccess, responseError } = require('../schemas/response.schema') +const validateResponse = require('../middlewares/validateResponse') +const { checkAuth, checkRoles } = require('../middlewares/auth.handler') +const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') +const config = require('@src/config/config') + +router.get( + '/searchByDN', + checkAuth, + checkRoles('admin'), + validateResponse, + async (req, res) => { + try { + const { baseDN } = req.body + + if (baseDN === undefined) { + throw new Error(`Base DN required.`) + } + + const response = await searchByDN(baseDN) + + res.json({ + success: true, + data: response, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error fetching group', + error: error.message, + }) + } + } +) + +module.exports = router diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index d33f85ad..7defb0ad 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -7,7 +7,6 @@ const { checkAuth, checkRoles } = require('../middlewares/auth.handler') const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') const config = require('@src/config/config') const service = GroupServices() -const { nodeTypes } = require('@src/constants/nodeTypes') router.get('/', checkAuth, validateResponse, async (req, res) => { try { diff --git a/src/routes/routes.js b/src/routes/routes.js index 4e001862..23dedf58 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -4,13 +4,14 @@ const ProfileRoutes = require('./profile.routes') const LogsRoutes = require('./logs.routes') const RecoveryPassword = require('@src/modules/passwordManagement/routes/recovery_password.routes') const UpdatePassword = require('@src/modules/passwordManagement/routes/updated_password.routes') +const DNRoutes = require('@src/routes/dn.routes') const addRoutes = (app) => { app.use('/users', UserRoutes) app.use('/groups', GroupRoutes) app.use('/profile', ProfileRoutes) app.use('/logs', LogsRoutes) - app.use('/', RecoveryPassword, UpdatePassword) + app.use('/', RecoveryPassword, UpdatePassword, DNRoutes) } module.exports = addRoutes diff --git a/src/services/dn.services.js b/src/services/dn.services.js new file mode 100644 index 00000000..466f67da --- /dev/null +++ b/src/services/dn.services.js @@ -0,0 +1,16 @@ +const config = require('@src/config/config') +const { performBaseLdapSearch } = require('@src/utils/ldapUtils') + +const searchByDN = async (dn = '') => { + const ldapFilter = `(objectClass=*)` + try { + const results = await performBaseLdapSearch(dn, ldapFilter) + return results + } catch (error) { + console.error('Error in getChildrens', error) + throw error + } +} +module.exports = { + searchByDN, +} diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index ec4f66f8..d6f7c0bb 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -109,6 +109,40 @@ const performScopedLdapSearch = async (baseDn, filter, attributes) => { }) } +const performBaseLdapSearch = async (baseDn, filter, attributes) => { + return new Promise((resolve, reject) => { + try { + bindLdapClient() // Bind before search + + const searchOptions = { + filter: filter, + scope: 'base', + attributes, + timeLimit: config.ldap.timeLimit, + } + + const searchResults = [] + + ldapClient.search(baseDn, searchOptions, (err, searchResponse) => { + if (err) { + reject(err) + return // Exit the function early in case of error + } + + searchResponse.on('searchEntry', (entry) => { + searchResults.push(transform(entry)) + }) + + searchResponse.on('end', () => { + resolve(searchResults) + }) + }) + } catch (err) { + reject(err) + } + }) +} + const performLdapUpdate = async (userDN, att, value) => { console.log('userDN', userDN) return new Promise((resolve, reject) => { @@ -143,4 +177,5 @@ module.exports = { unbindLdapClient, performLdapUpdate, performScopedLdapSearch, + performBaseLdapSearch, } From ca026cbace0881524e333cc2ba02ffc88b1726b0 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 16 Sep 2023 17:58:58 -0400 Subject: [PATCH 129/229] add mongoose error connection on logger --- src/middlewares/logger.handler.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js index ec6b2ef7..8c41a86f 100644 --- a/src/middlewares/logger.handler.js +++ b/src/middlewares/logger.handler.js @@ -2,6 +2,7 @@ const winston = require('winston') const { MongoDB } = require('winston-mongodb') const config = require('../config/config') const morgan = require('morgan') +const mongoose = require('mongoose') // Import Mongoose const logger = winston.createLogger({ level: 'info', @@ -49,10 +50,24 @@ const logFormat = (tokens, req, res) => { const addLoggerMiddleware = (app) => { try { // Attempt to establish the database connection and configure logger - winston.add(logger.transports[0]) + mongoose.connect(config.mongodb.url, { + useNewUrlParser: true, + useUnifiedTopology: true, + }) + + const db = mongoose.connection + + db.on('error', (error) => { + console.error('Error connecting to the database:', error.message) + }) + + db.once('open', () => { + console.log('Connected to MongoDB (ldapDB)') + winston.add(logger.transports[0]) - // Register the morgan middleware - app.use(morgan(logFormat)) + // Register the morgan middleware + app.use(morgan(logFormat)) + }) // Add error connection handler app.on('error', (err) => { From 9d30f79637b9de13748a40cfe6b592df372c0745 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 16 Sep 2023 17:59:34 -0400 Subject: [PATCH 130/229] add mongoose error connection --- src/connections/mongoose.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/connections/mongoose.js b/src/connections/mongoose.js index d362cb48..be12f9ba 100644 --- a/src/connections/mongoose.js +++ b/src/connections/mongoose.js @@ -1,15 +1,26 @@ -const mongoose = require('mongoose') -const config = require('@src/config/config') +const mongoose = require('mongoose'); +const config = require('@src/config/config'); mongoose.connect(config.mongodb.url, { useNewUrlParser: true, useUnifiedTopology: true, -}) +}); -const db = mongoose.connection +const db = mongoose.connection; // Handle MongoDB connection events -db.on('error', console.error.bind(console, 'MongoDB connection error:')) +db.on('error', (error) => { + console.error('MongoDB connection error:', error); +}); + db.once('open', () => { - console.log('Connected to MongoDB (ldapDB)') -}) + console.log('Connected to MongoDB (ldapDB)'); +}); + +// Handle closing the connection on application termination +process.on('SIGINT', () => { + mongoose.connection.close(() => { + console.log('MongoDB connection disconnected through app termination'); + process.exit(0); + }); +}); From 522c54463927fc16fdb710c6ee03d9549eba33a2 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 16 Sep 2023 18:02:46 -0400 Subject: [PATCH 131/229] add try catch to ldap initialization --- src/utils/ldap_initialization.js | 50 ++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/utils/ldap_initialization.js b/src/utils/ldap_initialization.js index cd621bb1..8e64af7d 100644 --- a/src/utils/ldap_initialization.js +++ b/src/utils/ldap_initialization.js @@ -3,25 +3,39 @@ const { usernameAttr, userOptions } = require('./../constants/ldap_options') const User = require('../schemas/user.schema').User const ldap_initialization = (app) => { - //LDAP initialization - LdapAuth.initialize( - userOptions, - app, - (id) => User.findOne({ username: id }).exec(), - async (user) => { - console.log(`${user[usernameAttr]} has logged in`) - let foundUser = await User.findOneAndUpdate( - { username: user[usernameAttr] }, - user, - { - upsert: true, - new: true, + try { + // LDAP initialization + LdapAuth.initialize(userOptions, app, async (id) => { + try { + const user = await User.findOne({ username: id }).exec() + if (!user) { + throw new Error('User not found in the database') } - ).exec() - console.log(`${foundUser.username} is retrieved from database`) - return foundUser - } - ) + console.log(`${user[usernameAttr]} has logged in`) + let foundUser = await User.findOneAndUpdate( + { username: user[usernameAttr] }, + user, + { + upsert: true, + new: true, + } + ).exec() + if (!foundUser) { + throw new Error('Failed to update/retrieve user from the database') + } + console.log(`${foundUser.username} is retrieved from the database`) + return foundUser + } catch (error) { + console.error( + 'Error while querying/updating user in the database:', + error.message + ) + throw error // Re-throw the error to be caught by the outer catch block + } + }) + } catch (error) { + console.error('Error during LDAP initialization:', error.message) + } } module.exports = ldap_initialization From d591d726e24a3684155d231f5f04e113bf77b15b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 16 Sep 2023 18:04:36 -0400 Subject: [PATCH 132/229] add try catch to ldap client --- src/connections/LDAP_client.js | 45 +++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index ea97c216..4035eea7 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -1,23 +1,34 @@ const ldap = require('ldapjs') const config = require('../config/config') -var assert = require('assert') +const assert = require('assert') -const client = ldap.createClient({ - url: [`${config.ldap.url}:${config.ldap.port}`], - connectTimeout: 60000, - reconnect: true, -}) +try { + const client = ldap.createClient({ + url: [`${config.ldap.url}:${config.ldap.port}`], + connectTimeout: 60000, + reconnect: true, + }) -client.on('connectError', (err) => { - console.log('Error trying to connect to LDAP', err) -}) + client.on('connectError', (err) => { + console.log('Error trying to connect to LDAP', err) + }) -client.bind(config.ldap.admin.username, config.ldap.admin.password, (err) => { - if (err) { - assert.ifError(err) - } else { - console.log('Binded to LDAP') - } -}) + client.bind(config.ldap.admin.username, config.ldap.admin.password, (err) => { + try { + if (err) { + console.error('Error binding to LDAP:', err) + // Handle the error gracefully here, e.g., log it or perform other actions + } else { + console.log('Binded to LDAP') + } + } catch (bindError) { + console.error('Error during LDAP binding:', bindError) + // Handle the binding error gracefully here + } + }) -module.exports = client + module.exports = client +} catch (clientError) { + console.error('Error creating LDAP client:', clientError) + // Handle the LDAP client creation error gracefully here +} From 4bd6ad030d1aac1c77a9dd703bf38f88a8247916 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 16 Sep 2023 18:12:24 -0400 Subject: [PATCH 133/229] add catch error validations --- src/connections/mongoose.js | 48 ++++++++++++++------------ src/middlewares/logger.handler.js | 57 +++++++++++++++---------------- 2 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/connections/mongoose.js b/src/connections/mongoose.js index be12f9ba..e4760010 100644 --- a/src/connections/mongoose.js +++ b/src/connections/mongoose.js @@ -1,26 +1,32 @@ -const mongoose = require('mongoose'); -const config = require('@src/config/config'); +const mongoose = require('mongoose') +const config = require('@src/config/config') -mongoose.connect(config.mongodb.url, { - useNewUrlParser: true, - useUnifiedTopology: true, -}); +try { + mongoose.connect(config.mongodb.url, { + useNewUrlParser: true, + useUnifiedTopology: true, + }) -const db = mongoose.connection; + const db = mongoose.connection -// Handle MongoDB connection events -db.on('error', (error) => { - console.error('MongoDB connection error:', error); -}); + // Handle MongoDB connection events + db.on('error', (error) => { + console.error('MongoDB connection error:', error) + // Handle the MongoDB connection error gracefully here + }) -db.once('open', () => { - console.log('Connected to MongoDB (ldapDB)'); -}); + db.once('open', () => { + console.log('Connected to MongoDB (ldapDB)') + }) -// Handle closing the connection on application termination -process.on('SIGINT', () => { - mongoose.connection.close(() => { - console.log('MongoDB connection disconnected through app termination'); - process.exit(0); - }); -}); + // Handle closing the connection on application termination + process.on('SIGINT', () => { + mongoose.connection.close(() => { + console.log('MongoDB connection disconnected through app termination') + process.exit(0) + }) + }) +} catch (mongoError) { + console.error('Error connecting to MongoDB:', mongoError) + // Handle the MongoDB connection error gracefully here +} diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js index 8c41a86f..e33103d8 100644 --- a/src/middlewares/logger.handler.js +++ b/src/middlewares/logger.handler.js @@ -2,7 +2,7 @@ const winston = require('winston') const { MongoDB } = require('winston-mongodb') const config = require('../config/config') const morgan = require('morgan') -const mongoose = require('mongoose') // Import Mongoose +const mongoose = require('mongoose') const logger = winston.createLogger({ level: 'info', @@ -48,39 +48,38 @@ const logFormat = (tokens, req, res) => { } const addLoggerMiddleware = (app) => { - try { - // Attempt to establish the database connection and configure logger - mongoose.connect(config.mongodb.url, { - useNewUrlParser: true, - useUnifiedTopology: true, - }) + // Attempt to establish the database connection and configure logger + mongoose.connect(config.mongodb.url, { + useNewUrlParser: true, + useUnifiedTopology: true, + }) - const db = mongoose.connection + const db = mongoose.connection - db.on('error', (error) => { - console.error('Error connecting to the database:', error.message) - }) + db.on('error', (error) => { + // Handle the MongoDB connection error gracefully + console.log('Error connecting to the database:', error.message) + // You can choose to continue with the app, but logging to MongoDB won't work + }) - db.once('open', () => { - console.log('Connected to MongoDB (ldapDB)') - winston.add(logger.transports[0]) + db.once('open', () => { + console.log('Connected to MongoDB (ldapDB)') + winston.add(logger.transports[0]) - // Register the morgan middleware - app.use(morgan(logFormat)) - }) + // Register the morgan middleware + app.use(morgan(logFormat)) + }) - // Add error connection handler - app.on('error', (err) => { - if ( - err.name === 'ServerSelectionError' && - err.message === 'connection timed out' - ) { - console.error('Error connecting to the database:', err.message) - } - }) - } catch (error) { - console.error('Error connecting to the database:', error.message) - } + // Add error connection handler + app.on('error', (err) => { + if ( + err.name === 'ServerSelectionError' && + err.message === 'connection timed out' + ) { + console.log('Error connecting to the database:', err.message) + // Handle the MongoDB connection error gracefully + } + }) } module.exports = addLoggerMiddleware From b41e49e2571adda5b84e5e684be3294bcaf124eb Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 22 Sep 2023 11:30:45 -0400 Subject: [PATCH 134/229] fix login bug --- src/modules/authentication/LdapAuth.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index bcac8936..9b2a397e 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -230,8 +230,6 @@ const login = function (req, res, next) { const rootBaseDN = extractBaseFromDn(ldapDn) const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') - console.log('user', user) - let roles = ['user'] const isSpAdmin = await isSuperAdmin(user.uid) if (isSpAdmin) { @@ -262,20 +260,21 @@ const login = function (req, res, next) { const token = signToken(payload, { expiresIn: '15 minutes' }) const refreshToken = signToken(payload, { expiresIn: '1 day' }) - await storeRefreshToken(user.uid, refreshToken) + /* await storeRefreshToken(user.uid, refreshToken) */ req.login(user, (loginErr) => { if (loginErr) { return next(loginErr) } - _insertFunc(user).then((user) => { + console.log('user', user) + if (!!user) { const data = { token: token, refreshToken: refreshToken, user: userObj, } return responseSuccess(res, 'authentication succeeded', data) - }) + } }) } } From f32465c82f319e57c631d4de3a04d61445dff5f0 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 22 Sep 2023 18:36:47 -0400 Subject: [PATCH 135/229] fix undefined bug on user.uid --- .../passwordManagement/routes/recovery_password.routes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/passwordManagement/routes/recovery_password.routes.js b/src/modules/passwordManagement/routes/recovery_password.routes.js index 360426ad..83f35a70 100644 --- a/src/modules/passwordManagement/routes/recovery_password.routes.js +++ b/src/modules/passwordManagement/routes/recovery_password.routes.js @@ -43,7 +43,7 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { ? await userService.getByEmail(emailOrUsername) : await userService.getByUsername(emailOrUsername) - if (user.uid === undefined) { + if (user === undefined) { return res.status(404).json({ message: 'User not found.' }) } From c6db4e56ceff87920f6642e68c1b8b558cfebf28 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 22 Sep 2023 23:08:44 -0400 Subject: [PATCH 136/229] validate role before fecth users from ldap --- src/routes/user.routes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 1748bdde..1f237e61 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -23,7 +23,7 @@ router.use(validateResponse) // Route handler for getting all users router.get('/', async (req, res) => { try { - const { localBase } = req.user + const { localBase, roles } = req.user const isValid = validateQuery(req.query) const queryFilter = createLdapFilterFromQuery(req.query) const ldapFilter = `(&(objectClass=person)${queryFilter})` @@ -33,12 +33,13 @@ router.get('/', async (req, res) => { // Call the performLdapSearch function to retrieve users matching the group filters const searchResults = await service.handleFilteredSearch( - localBase, + roles.includes('superadmin') ? config.ldap.base : localBase, ldapFilter, attributes, req.query.page, req.query.limit ) + // Send the search results res.json({ success: true, From 64ea7a9cc3951e1cf40bce3d48e5bb9915586221 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 22 Sep 2023 23:08:57 -0400 Subject: [PATCH 137/229] clean logs on login --- src/modules/authentication/LdapAuth.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 9b2a397e..459476c9 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -266,7 +266,6 @@ const login = function (req, res, next) { if (loginErr) { return next(loginErr) } - console.log('user', user) if (!!user) { const data = { token: token, From d5bfdc5ae4e5b4b31906e5ce4227b7b820a6a3c5 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 22 Sep 2023 23:09:22 -0400 Subject: [PATCH 138/229] refactor logger and add more transports --- index.js | 5 +- src/middlewares/logger.handler.js | 85 ----------------------------- src/middlewares/morganMiddleware.js | 61 +++++++++++++++++++++ src/utils/logger.js | 51 +++++++++++++++++ 4 files changed, 115 insertions(+), 87 deletions(-) delete mode 100644 src/middlewares/logger.handler.js create mode 100644 src/middlewares/morganMiddleware.js create mode 100644 src/utils/logger.js diff --git a/index.js b/index.js index 44c620ad..5238767f 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ const bodyParser = require('body-parser') const express = require('express') const addRoutes = require('@src/routes/routes.js') const sessionMiddleWare = require('@src/middlewares/session.handler.js') -const addLoggerMiddleware = require('@src/middlewares/logger.handler.js') +const addLoggerMiddleware = require('@src/middlewares/morganMiddleware') const ldap_initialization = require('@src/utils/ldap_initialization.js') const path = require('path') const cors = require('cors') @@ -15,6 +15,7 @@ const limiter = require('@src/middlewares/rate_limiter.handler.js') const paginate = require('express-paginate') const compression = require('compression') const cache = require('express-cache-ctrl') +const logger = require('@src/utils/logger') //app initialization const app = express() @@ -45,7 +46,7 @@ ldap_initialization(app) app.use(paginate.middleware(100, CONFIG.ldap.sizeLimit)) // Middleware de caché -app.use(cache.private(3600)) +app.use(cache.private(3600)) //add routes to application addRoutes(app) diff --git a/src/middlewares/logger.handler.js b/src/middlewares/logger.handler.js deleted file mode 100644 index e33103d8..00000000 --- a/src/middlewares/logger.handler.js +++ /dev/null @@ -1,85 +0,0 @@ -const winston = require('winston') -const { MongoDB } = require('winston-mongodb') -const config = require('../config/config') -const morgan = require('morgan') -const mongoose = require('mongoose') - -const logger = winston.createLogger({ - level: 'info', - format: winston.format.combine(winston.format.timestamp()), - transports: [ - new MongoDB({ - db: config.mongodb.url, - options: { useUnifiedTopology: true }, - collection: 'logs', - level: 'info', - capped: true, - cappedSize: 1000000, - cappedMax: 1000, - metaKey: 'meta', - }), - ], -}) - -morgan.token('user', (req) => { - return req.user ? req.user.uid : 'anonymous' -}) - -const logFormat = (tokens, req, res) => { - const log = { - method: tokens.method(req, res), - url: tokens.url(req, res), - status: tokens.status(req, res), - content_length: tokens.res(req, res, 'content-length'), - response_time: tokens['response-time'](req, res), - user: req.user === undefined ? 'anonymous' : req?.user?.uid, - } - - logger.info({ ...log }) - - return [ - `method:${log.method}`, - `url:${log.url}`, - `status:${log.status}`, - `content-length:${log.content_length}`, - `response-time:${log.response_time}ms`, - `user_uid:${log.user}`, - ].join(' ') -} - -const addLoggerMiddleware = (app) => { - // Attempt to establish the database connection and configure logger - mongoose.connect(config.mongodb.url, { - useNewUrlParser: true, - useUnifiedTopology: true, - }) - - const db = mongoose.connection - - db.on('error', (error) => { - // Handle the MongoDB connection error gracefully - console.log('Error connecting to the database:', error.message) - // You can choose to continue with the app, but logging to MongoDB won't work - }) - - db.once('open', () => { - console.log('Connected to MongoDB (ldapDB)') - winston.add(logger.transports[0]) - - // Register the morgan middleware - app.use(morgan(logFormat)) - }) - - // Add error connection handler - app.on('error', (err) => { - if ( - err.name === 'ServerSelectionError' && - err.message === 'connection timed out' - ) { - console.log('Error connecting to the database:', err.message) - // Handle the MongoDB connection error gracefully - } - }) -} - -module.exports = addLoggerMiddleware diff --git a/src/middlewares/morganMiddleware.js b/src/middlewares/morganMiddleware.js new file mode 100644 index 00000000..9dbd6978 --- /dev/null +++ b/src/middlewares/morganMiddleware.js @@ -0,0 +1,61 @@ +const morgan = require('morgan') +const winston = require('winston') +const mongoose = require('mongoose') // Import mongoose +const { MongoDB } = require('winston-mongodb') // Import MongoDB transport +const config = require('@src/config/config') + +const logger = require('@src/utils/logger') + +morgan.token('user', (req) => { + return req.user ? req.user.uid : 'anonymous' +}) + +const logFormat = (tokens, req, res) => { + const log = { + method: tokens.method(req, res), + url: tokens.url(req, res), + status: tokens.status(req, res), + content_length: tokens.res(req, res, 'content-length'), + response_time: tokens['response-time'](req, res), + user: req.user === undefined ? 'anonymous' : req?.user?.uid, + } + + logger.info({ ...log }) +} + +const addLoggerMiddleware = (app) => { + mongoose.connect(config.mongodb.url, { + useNewUrlParser: true, + useUnifiedTopology: true, + }) + + const db = mongoose.connection + + db.on('error', (error) => { + console.error('Error connecting to the database:', error.message) + }) + + winston.add(logger.transports[0]) + + // Configure MongoDB transport for Winston + logger.add( + new MongoDB({ + db: config.mongodb.url, // Change this to your MongoDB URL + level: 'info', // Adjust the level as needed + useUnifiedTopology: true, + }) + ) + + app.use(morgan(logFormat)) + + app.on('error', (err) => { + if ( + err.name === 'ServerSelectionError' && + err.message === 'connection timed out' + ) { + console.error('Error connecting to the database:', err.message) + } + }) +} + +module.exports = addLoggerMiddleware diff --git a/src/utils/logger.js b/src/utils/logger.js new file mode 100644 index 00000000..e8938afe --- /dev/null +++ b/src/utils/logger.js @@ -0,0 +1,51 @@ +const winston = require('winston') +const { MongoDB } = require('winston-mongodb') +const config = require('@src/config/config') + +const level = () => { + const env = process.env.NODE_ENV || 'development' + const isDevelopment = env === 'development' + return isDevelopment ? 'debug' : 'warn' +} + +const colors = { + error: 'red', + warn: 'yellow', + info: 'green', + http: 'magenta', + debug: 'white', +} + +winston.addColors(colors) + +const format = winston.format.combine( + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), + winston.format.colorize({ all: true }), + winston.format.printf( + (info) => `${info.timestamp} ${info.level}: ${info.message}` + ) +) + +const transports = [ + new winston.transports.Console({}), + new winston.transports.File({ filename: 'logs/all.log', level: 'error' }), + new winston.transports.File({ filename: 'logs/all.log' }), + new MongoDB({ + db: config.mongodb.url, + options: { useUnifiedTopology: true }, + collection: 'logs', + level: 'info', + capped: true, + cappedSize: 1000000, + cappedMax: 1000, + metaKey: 'meta', + }), +] + +const logger = winston.createLogger({ + level: 'info', + format: format, + transports: transports, +}) + +module.exports = logger From c80b1d7f2ecc8682b9169e227feecc5b7be76b85 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 22 Sep 2023 23:45:29 -0400 Subject: [PATCH 139/229] print uid from jwt instead of req.user --- package-lock.json | 11 +++++++++ package.json | 1 + src/middlewares/morganMiddleware.js | 25 ++++++++++++++++++--- src/utils/authentication/tokens/jwtUtils.js | 10 +++++++++ 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 src/utils/authentication/tokens/jwtUtils.js diff --git a/package-lock.json b/package-lock.json index 40b78ffa..6ec122ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "helmet": "^6.1.3", "joi": "^13.0.2", "jsonwebtoken": "^9.0.0", + "jwt-decode": "^3.1.2", "LDAP": "^1.2.1", "ldap-authentication": "^2.3.1", "ldapjs": "^3.0.1", @@ -4591,6 +4592,11 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, "node_modules/kareem": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.4.1.tgz", @@ -10521,6 +10527,11 @@ "safe-buffer": "^5.0.1" } }, + "jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, "kareem": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.4.1.tgz", diff --git a/package.json b/package.json index 1de14938..bae23bae 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "helmet": "^6.1.3", "joi": "^13.0.2", "jsonwebtoken": "^9.0.0", + "jwt-decode": "^3.1.2", "LDAP": "^1.2.1", "ldap-authentication": "^2.3.1", "ldapjs": "^3.0.1", diff --git a/src/middlewares/morganMiddleware.js b/src/middlewares/morganMiddleware.js index 9dbd6978..dbe11ce4 100644 --- a/src/middlewares/morganMiddleware.js +++ b/src/middlewares/morganMiddleware.js @@ -3,24 +3,43 @@ const winston = require('winston') const mongoose = require('mongoose') // Import mongoose const { MongoDB } = require('winston-mongodb') // Import MongoDB transport const config = require('@src/config/config') +const { decodeJWT } = require('@src/utils/authentication/tokens/jwtUtils') const logger = require('@src/utils/logger') +const { validate } = require('@src/schemas/logs.schema') morgan.token('user', (req) => { return req.user ? req.user.uid : 'anonymous' }) const logFormat = (tokens, req, res) => { + const status = tokens.status(req, res) + const payload = + req.headers.authorization !== undefined + ? decodeJWT(req.headers.authorization.split(' ')[1]) + : undefined + const log = { method: tokens.method(req, res), url: tokens.url(req, res), status: tokens.status(req, res), content_length: tokens.res(req, res, 'content-length'), response_time: tokens['response-time'](req, res), - user: req.user === undefined ? 'anonymous' : req?.user?.uid, + user: + payload !== undefined + ? payload.uid + : req.body.username !== undefined + ? req.body.username + : 'anonymus', + } + if (status < 400) { + logger.info({ ...log }) + } + if (status >= 400) { + logger.error({ + ...log, + }) } - - logger.info({ ...log }) } const addLoggerMiddleware = (app) => { diff --git a/src/utils/authentication/tokens/jwtUtils.js b/src/utils/authentication/tokens/jwtUtils.js new file mode 100644 index 00000000..45e948b5 --- /dev/null +++ b/src/utils/authentication/tokens/jwtUtils.js @@ -0,0 +1,10 @@ +const jwt_decode = require('jwt-decode') + +const decodeJWT = (jwt) => { + const decodedToken = jwt_decode(jwt) + return decodedToken +} + +module.exports = { + decodeJWT, +} From dcd97e2724f705dd3d3a86ed1122faea69ee8fd8 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 22 Sep 2023 23:47:07 -0400 Subject: [PATCH 140/229] save error logs into a separate log file --- src/utils/logger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/logger.js b/src/utils/logger.js index e8938afe..2c68d371 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -28,7 +28,7 @@ const format = winston.format.combine( const transports = [ new winston.transports.Console({}), - new winston.transports.File({ filename: 'logs/all.log', level: 'error' }), + new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), new winston.transports.File({ filename: 'logs/all.log' }), new MongoDB({ db: config.mongodb.url, From 6c2c5d9b1279c11604e15a2284601b08b64d4b52 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 22 Sep 2023 23:59:13 -0400 Subject: [PATCH 141/229] saving level correctly on db --- src/utils/logger.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/utils/logger.js b/src/utils/logger.js index 2c68d371..55bcd905 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -20,14 +20,35 @@ winston.addColors(colors) const format = winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), - winston.format.colorize({ all: true }), winston.format.printf( - (info) => `${info.timestamp} ${info.level}: ${info.message}` + (info) => + `${info.timestamp} ${info.level}: ${ + typeof info.message === 'string' + ? info.message + : JSON.stringify(info.message) + }` + ) +) + +const colorizedFormat = winston.format.combine( + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), + winston.format.colorize({ + all: true, + }), + winston.format.printf( + (info) => + `${info.timestamp} ${info.level}: ${ + typeof info.message === 'string' + ? info.message + : JSON.stringify(info.message) + }` ) ) const transports = [ - new winston.transports.Console({}), + new winston.transports.Console({ + format: colorizedFormat, + }), new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), new winston.transports.File({ filename: 'logs/all.log' }), new MongoDB({ From 3a5aa430a387eb40168a541e21bc8de9abe8ba80 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 00:07:25 -0400 Subject: [PATCH 142/229] add dn and localBase to logs --- src/middlewares/morganMiddleware.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/middlewares/morganMiddleware.js b/src/middlewares/morganMiddleware.js index dbe11ce4..323726b0 100644 --- a/src/middlewares/morganMiddleware.js +++ b/src/middlewares/morganMiddleware.js @@ -19,6 +19,8 @@ const logFormat = (tokens, req, res) => { ? decodeJWT(req.headers.authorization.split(' ')[1]) : undefined + console.log(payload) + const log = { method: tokens.method(req, res), url: tokens.url(req, res), @@ -31,6 +33,8 @@ const logFormat = (tokens, req, res) => { : req.body.username !== undefined ? req.body.username : 'anonymus', + dn: payload !== undefined ? payload.dn : 'unknown', + branch: payload !== undefined ? payload.localBase : 'unknown', } if (status < 400) { logger.info({ ...log }) From 3838995a9f541306da1f6406b4c27d3ae96e1591 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 00:11:56 -0400 Subject: [PATCH 143/229] clean logs --- src/middlewares/morganMiddleware.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/middlewares/morganMiddleware.js b/src/middlewares/morganMiddleware.js index 323726b0..13dee393 100644 --- a/src/middlewares/morganMiddleware.js +++ b/src/middlewares/morganMiddleware.js @@ -19,8 +19,6 @@ const logFormat = (tokens, req, res) => { ? decodeJWT(req.headers.authorization.split(' ')[1]) : undefined - console.log(payload) - const log = { method: tokens.method(req, res), url: tokens.url(req, res), @@ -36,6 +34,7 @@ const logFormat = (tokens, req, res) => { dn: payload !== undefined ? payload.dn : 'unknown', branch: payload !== undefined ? payload.localBase : 'unknown', } + if (status < 400) { logger.info({ ...log }) } From b4c63fc4348f8f6ffc9e0e4d1881dbd12ec7f460 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 12:32:19 -0400 Subject: [PATCH 144/229] add filters logs endpoint inside logs module --- .../logsManagement/routes/logs.routes.js | 46 +++++++++++++++++++ src/routes/logs.routes.js | 31 ------------- src/routes/routes.js | 4 +- 3 files changed, 48 insertions(+), 33 deletions(-) create mode 100644 src/modules/logsManagement/routes/logs.routes.js delete mode 100644 src/routes/logs.routes.js diff --git a/src/modules/logsManagement/routes/logs.routes.js b/src/modules/logsManagement/routes/logs.routes.js new file mode 100644 index 00000000..57f9cd50 --- /dev/null +++ b/src/modules/logsManagement/routes/logs.routes.js @@ -0,0 +1,46 @@ +const fs = require('fs') +const express = require('express') +const router = express.Router() + +// Read and parse the log file +const parseLogFile = () => { + const logData = fs.readFileSync('logs/all.log', 'utf-8') + const logs = logData + .split('\n') + .filter(Boolean) + .map((line) => { + const splittedLine = line.split(' ') + const message = splittedLine[3].replace(/^"(.*)"$/, '$1') + const parsedMessage = JSON.parse(message) + + const log = { + date: splittedLine[0], + time: splittedLine[1], + level: splittedLine[2], + message: parsedMessage, + } + return log + }) + + return logs +} +router.get('/logs', (req, res) => { + const queryParams = req.query + const logs = parseLogFile() + + const filteredLogs = logs.filter((log) => { + return Object.entries(queryParams).every(([key, value]) => { + if (key === 'level' && !log.level.startsWith(value)) { + return false + } + if (key in log.message && log.message[key] !== value) { + return false + } + return true + }) + }) + + res.json(filteredLogs) +}) + +module.exports = router diff --git a/src/routes/logs.routes.js b/src/routes/logs.routes.js deleted file mode 100644 index 447a7f8c..00000000 --- a/src/routes/logs.routes.js +++ /dev/null @@ -1,31 +0,0 @@ -const express = require('express') -const LogService = require('../services/logs.services') -const router = express.Router() -const { responseSuccess, responseError } = require('../schemas/response.schema') -const validateResponse = require('../middlewares/validateResponse') -const { checkAuth, checkRoles } = require('../middlewares/auth.handler') - -// Controller function to get logs filtered by method, url, status, and user -router.get( - '/', - checkAuth, - checkRoles('user'), - validateResponse, - async (req, res) => { - try { - const filters = { - method: req.query.method, - url: req.query.url, - status: req.query.status, - user: req.query.user, - } - const logs = await LogService.filterLogs(filters) - res.json(logs) - } catch (error) { - console.error('Error fetching filtered logs:', error) - res.status(500).json({ error: 'Internal Server Error' }) - } - } -) - -module.exports = router diff --git a/src/routes/routes.js b/src/routes/routes.js index 23dedf58..7b5558d3 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -1,17 +1,17 @@ const UserRoutes = require('./user.routes') const GroupRoutes = require('./group.routes') const ProfileRoutes = require('./profile.routes') -const LogsRoutes = require('./logs.routes') const RecoveryPassword = require('@src/modules/passwordManagement/routes/recovery_password.routes') const UpdatePassword = require('@src/modules/passwordManagement/routes/updated_password.routes') const DNRoutes = require('@src/routes/dn.routes') +const LogsRoutes = require('../modules/logsManagement/routes/logs.routes') const addRoutes = (app) => { app.use('/users', UserRoutes) app.use('/groups', GroupRoutes) app.use('/profile', ProfileRoutes) - app.use('/logs', LogsRoutes) app.use('/', RecoveryPassword, UpdatePassword, DNRoutes) + app.use('/', LogsRoutes) } module.exports = addRoutes From 0a0e4f603c64b8b032e7d6f9eb451c7aedc4f346 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 12:41:51 -0400 Subject: [PATCH 145/229] validate admin scope to access logs --- src/modules/logsManagement/routes/logs.routes.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/modules/logsManagement/routes/logs.routes.js b/src/modules/logsManagement/routes/logs.routes.js index 57f9cd50..16c5d126 100644 --- a/src/modules/logsManagement/routes/logs.routes.js +++ b/src/modules/logsManagement/routes/logs.routes.js @@ -1,6 +1,8 @@ const fs = require('fs') const express = require('express') const router = express.Router() +const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') +const { decodeJWT } = require('@src/utils/authentication/tokens/jwtUtils') // Read and parse the log file const parseLogFile = () => { @@ -24,7 +26,10 @@ const parseLogFile = () => { return logs } -router.get('/logs', (req, res) => { +router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { + const payload = decodeJWT(req.headers.authorization.split(' ')[1]) + const isSuperAdmin = payload.roles.includes('superadmin') + const queryParams = req.query const logs = parseLogFile() @@ -33,9 +38,15 @@ router.get('/logs', (req, res) => { if (key === 'level' && !log.level.startsWith(value)) { return false } + + if (!isSuperAdmin) { + return log.message.branch === payload.localBase + } + if (key in log.message && log.message[key] !== value) { return false } + return true }) }) From 73f75b5375e7fe6893e4946fa86bdc52971eb8aa Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 13:25:24 -0400 Subject: [PATCH 146/229] sending logs by web socket --- package-lock.json | 88 ++++++++++++++++++- package.json | 4 +- .../__tests__/integration.test.js | 57 ++++++++++++ .../logsManagement/routes/logs.routes.js | 63 +++++++++++++ 4 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 src/modules/logsManagement/__tests__/integration.test.js diff --git a/package-lock.json b/package-lock.json index 6ec122ab..7b5c1f8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@hapi/boom": "^10.0.1", "axios": "^1.3.5", + "axios-mock-adapter": "^1.22.0", "body-parser": "^1.20.2", "compression": "^1.7.4", "cors": "^2.8.5", @@ -42,7 +43,8 @@ "socket.io": "^4.6.1", "ssha": "^1.0.1", "winston": "^3.8.2", - "winston-mongodb": "^5.1.1" + "winston-mongodb": "^5.1.1", + "ws": "^8.14.2" }, "devDependencies": { "connect-mongo": "^4.6.0", @@ -1609,6 +1611,18 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-mock-adapter": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz", + "integrity": "sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "is-buffer": "^2.0.5" + }, + "peerDependencies": { + "axios": ">= 0.17.0" + } + }, "node_modules/babel-jest": { "version": "29.6.4", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", @@ -3153,6 +3167,11 @@ "node >=0.6.0" ] }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3703,6 +3722,28 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, "node_modules/is-core-module": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", @@ -6977,6 +7018,26 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xmlhttprequest-ssl": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", @@ -8323,6 +8384,15 @@ "proxy-from-env": "^1.1.0" } }, + "axios-mock-adapter": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz", + "integrity": "sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==", + "requires": { + "fast-deep-equal": "^3.1.3", + "is-buffer": "^2.0.5" + } + }, "babel-jest": { "version": "29.6.4", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", @@ -9451,6 +9521,11 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==" }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -9847,6 +9922,11 @@ "binary-extensions": "^2.0.0" } }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, "is-core-module": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", @@ -12349,6 +12429,12 @@ "signal-exit": "^3.0.7" } }, + "ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "requires": {} + }, "xmlhttprequest-ssl": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", diff --git a/package.json b/package.json index bae23bae..f3879560 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "dependencies": { "@hapi/boom": "^10.0.1", "axios": "^1.3.5", + "axios-mock-adapter": "^1.22.0", "body-parser": "^1.20.2", "compression": "^1.7.4", "cors": "^2.8.5", @@ -49,7 +50,8 @@ "socket.io": "^4.6.1", "ssha": "^1.0.1", "winston": "^3.8.2", - "winston-mongodb": "^5.1.1" + "winston-mongodb": "^5.1.1", + "ws": "^8.14.2" }, "devDependencies": { "connect-mongo": "^4.6.0", diff --git a/src/modules/logsManagement/__tests__/integration.test.js b/src/modules/logsManagement/__tests__/integration.test.js new file mode 100644 index 00000000..a16e3376 --- /dev/null +++ b/src/modules/logsManagement/__tests__/integration.test.js @@ -0,0 +1,57 @@ +const axios = require('axios') +const axiosMockAdapter = require('axios-mock-adapter') +const WebSocket = require('ws') + +// Import your REST API server module here. +// Import your WebSocket server module here. + +describe('Integration Test', () => { + let axiosMock + let ws + let wsMessages + + beforeAll(() => { + // Start your REST API server (if it's not already running) + // Start your WebSocket server (if it's not already running) + + // Create a WebSocket connection + ws = new WebSocket('ws://localhost:8080') + wsMessages = [] + + // Handle messages from the WebSocket server + ws.on('message', (message) => { + wsMessages.push(message) + }) + + // Create an instance of the axios mock adapter + axiosMock = new axiosMockAdapter(axios) + + // Mock the HTTP request to your REST API + axiosMock + .onGet('http://localhost:3000/logs') + .reply(200, { data: 'Mocked data from REST API' }) + }) + + afterAll(() => { + // Close the WebSocket connection and perform any necessary cleanup + ws.close() + axiosMock.restore() + }) + + it('should perform HTTP and WebSocket communication', async () => { + // Make an HTTP request to your REST API + const response = await axios.get('http://localhost:3000/logs') + + expect(response.status).toBe(200) + expect(response.data).toEqual({ data: 'Mocked data from REST API' }) + + // Send a message to the WebSocket server + ws.send('Hello, WebSocket server!') + + // Wait for a moment to allow the WebSocket server to process the message + await new Promise((resolve) => setTimeout(resolve, 100)) + + // Assertions for WebSocket messages + expect(wsMessages).toContain('Hello, WebSocket server!') + }) +}) diff --git a/src/modules/logsManagement/routes/logs.routes.js b/src/modules/logsManagement/routes/logs.routes.js index 16c5d126..316cfb2b 100644 --- a/src/modules/logsManagement/routes/logs.routes.js +++ b/src/modules/logsManagement/routes/logs.routes.js @@ -3,6 +3,7 @@ const express = require('express') const router = express.Router() const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') const { decodeJWT } = require('@src/utils/authentication/tokens/jwtUtils') +const { WebSocketServer, OPEN } = require('ws') // Read and parse the log file const parseLogFile = () => { @@ -26,6 +27,68 @@ const parseLogFile = () => { return logs } + +const wss = new WebSocketServer({ + port: 5005, + perMessageDeflate: { + zlibDeflateOptions: { + // See zlib defaults. + chunkSize: 1024, + memLevel: 7, + level: 3, + }, + zlibInflateOptions: { + chunkSize: 10 * 1024, + }, + // Other options settable: + clientNoContextTakeover: true, // Defaults to negotiated value. + serverNoContextTakeover: true, // Defaults to negotiated value. + serverMaxWindowBits: 10, // Defaults to negotiated value. + // Below options specified as default values. + concurrencyLimit: 10, // Limits zlib concurrency for perf. + threshold: 1024, // Size (in bytes) below which messages + // should not be compressed if context takeover is disabled. + }, +}) + +// WebSocket connection handler +wss.on('error', console.error) + +wss.on('open', function open() { + wss.send('something') +}) + +wss.on('message', function message(data) { + console.log('received: %s', data) +}) + +// Function to send logs to connected WebSocket clients +const sendLogsToClients = (clients, logs) => { + clients.forEach((client) => { + if (client.readyState === OPEN) { + client.send(JSON.stringify(logs)) + } + }) +} + +// Watch for changes in the log file and notify connected clients +fs.watch('logs/all.log', (eventType, filename) => { + if (eventType === 'change') { + const logs = parseLogFile() + sendLogsToClients([...wss.clients], logs) + } +}) + +// WebSocket upgrade handler +router.server = (server) => { + // Upgrade HTTP request to WebSocket + server.on('upgrade', (request, socket, head) => { + wss.handleUpgrade(request, socket, head, (client) => { + wss.emit('connection', client, request) + }) + }) +} + router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { const payload = decodeJWT(req.headers.authorization.split(' ')[1]) const isSuperAdmin = payload.roles.includes('superadmin') From 09b581ecd0e4503a890be0f9d2b337c2325abde3 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 14:24:06 -0400 Subject: [PATCH 147/229] send logs on streaming --- package-lock.json | 14 +++ package.json | 1 + src/middlewares/morganMiddleware.js | 4 +- .../logsManagement/routes/logs.routes.js | 107 +++++++++--------- 4 files changed, 73 insertions(+), 53 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7b5c1f8c..40db5057 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "redis": "^4.6.8", "socket.io": "^4.6.1", "ssha": "^1.0.1", + "tail": "^2.2.6", "winston": "^3.8.2", "winston-mongodb": "^5.1.1", "ws": "^8.14.2" @@ -6503,6 +6504,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tail": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/tail/-/tail-2.2.6.tgz", + "integrity": "sha512-IQ6G4wK/t8VBauYiGPLx+d3fA5XjSVagjWV5SIYzvEvglbQjwEcukeYI68JOPpdydjxhZ9sIgzRlSmwSpphHyw==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -12057,6 +12066,11 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "tail": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/tail/-/tail-2.2.6.tgz", + "integrity": "sha512-IQ6G4wK/t8VBauYiGPLx+d3fA5XjSVagjWV5SIYzvEvglbQjwEcukeYI68JOPpdydjxhZ9sIgzRlSmwSpphHyw==" + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", diff --git a/package.json b/package.json index f3879560..5056aec6 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "redis": "^4.6.8", "socket.io": "^4.6.1", "ssha": "^1.0.1", + "tail": "^2.2.6", "winston": "^3.8.2", "winston-mongodb": "^5.1.1", "ws": "^8.14.2" diff --git a/src/middlewares/morganMiddleware.js b/src/middlewares/morganMiddleware.js index 13dee393..5a0b6f2d 100644 --- a/src/middlewares/morganMiddleware.js +++ b/src/middlewares/morganMiddleware.js @@ -22,9 +22,9 @@ const logFormat = (tokens, req, res) => { const log = { method: tokens.method(req, res), url: tokens.url(req, res), - status: tokens.status(req, res), + status: parseInt(tokens.status(req, res)), content_length: tokens.res(req, res, 'content-length'), - response_time: tokens['response-time'](req, res), + response_time: parseFloat(tokens['response-time'](req, res)), user: payload !== undefined ? payload.uid diff --git a/src/modules/logsManagement/routes/logs.routes.js b/src/modules/logsManagement/routes/logs.routes.js index 316cfb2b..f4afcce4 100644 --- a/src/modules/logsManagement/routes/logs.routes.js +++ b/src/modules/logsManagement/routes/logs.routes.js @@ -4,32 +4,37 @@ const router = express.Router() const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') const { decodeJWT } = require('@src/utils/authentication/tokens/jwtUtils') const { WebSocketServer, OPEN } = require('ws') +const Tail = require('tail').Tail + +// Initialize the tail instance to monitor the log file +const tail = new Tail('logs/all.log', { + fromBeginning: true, // Start reading from the beginning of the file + follow: true, // Continue monitoring the file for new lines +}) + +// Create a set to store connected WebSocket clients +const clients = new Set() + +// Function to send logs to connected WebSocket clients +const sendLogsToClients = (logs) => { + const logEntries = logs.split('\n').filter(Boolean) + clients.forEach((client) => { + if (client.readyState === OPEN) { + logEntries.forEach((log) => { + client.send(log) + }) + } + }) +} // Read and parse the log file const parseLogFile = () => { const logData = fs.readFileSync('logs/all.log', 'utf-8') - const logs = logData - .split('\n') - .filter(Boolean) - .map((line) => { - const splittedLine = line.split(' ') - const message = splittedLine[3].replace(/^"(.*)"$/, '$1') - const parsedMessage = JSON.parse(message) - - const log = { - date: splittedLine[0], - time: splittedLine[1], - level: splittedLine[2], - message: parsedMessage, - } - return log - }) - - return logs + return logData } const wss = new WebSocketServer({ - port: 5005, + port: 5006, perMessageDeflate: { zlibDeflateOptions: { // See zlib defaults. @@ -51,32 +56,29 @@ const wss = new WebSocketServer({ }, }) -// WebSocket connection handler +tail.on('line', (data) => { + sendLogsToClients(data) +}) + wss.on('error', console.error) wss.on('open', function open() { wss.send('something') }) -wss.on('message', function message(data) { +wss.on('message', function message(client, data) { console.log('received: %s', data) }) -// Function to send logs to connected WebSocket clients -const sendLogsToClients = (clients, logs) => { - clients.forEach((client) => { - if (client.readyState === OPEN) { - client.send(JSON.stringify(logs)) - } - }) -} +// Serve the last log entry to new WebSocket clients +wss.on('connection', (client) => { + clients.add(client) // Add the client to the set + client.send(parseLogFile()) // Send the current log content to the new client +}) -// Watch for changes in the log file and notify connected clients -fs.watch('logs/all.log', (eventType, filename) => { - if (eventType === 'change') { - const logs = parseLogFile() - sendLogsToClients([...wss.clients], logs) - } +// Remove clients from the set when they close the connection +wss.on('close', (client) => { + clients.delete(client) }) // WebSocket upgrade handler @@ -96,23 +98,26 @@ router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { const queryParams = req.query const logs = parseLogFile() - const filteredLogs = logs.filter((log) => { - return Object.entries(queryParams).every(([key, value]) => { - if (key === 'level' && !log.level.startsWith(value)) { - return false - } - - if (!isSuperAdmin) { - return log.message.branch === payload.localBase - } - - if (key in log.message && log.message[key] !== value) { - return false - } - - return true + const filteredLogs = logs + .split('\n') + .filter(Boolean) + .filter((log) => { + return Object.entries(queryParams).every(([key, value]) => { + if (key === 'level' && !log.includes(`"level":"${value}"`)) { + return false + } + + if (!isSuperAdmin) { + return log.includes(`"branch":"${payload.localBase}"`) + } + + if (log.includes(`"${key}":"${value}"`)) { + return false + } + + return true + }) }) - }) res.json(filteredLogs) }) From 1c0fd1b78152149e61703e2868b44a3d6e3f7024 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 14:33:01 -0400 Subject: [PATCH 148/229] add log file endpoint --- package-lock.json | 60 +++++++++++++++++++ package.json | 1 + .../logsManagement/routes/logs.routes.js | 13 ++++ 3 files changed, 74 insertions(+) diff --git a/package-lock.json b/package-lock.json index 40db5057..303d7935 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,7 @@ "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", "password-validator": "^5.3.0", + "path": "^0.12.7", "redis": "^4.6.8", "socket.io": "^4.6.1", "ssha": "^1.0.1", @@ -5533,6 +5534,15 @@ "node": ">=8.10.0" } }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5660,6 +5670,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -6726,11 +6744,24 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -11325,6 +11356,15 @@ "resolved": "https://registry.npmjs.org/password-validator/-/password-validator-5.3.0.tgz", "integrity": "sha512-Q+bSEM5pjokZqzWGoQaoylkeWeH4+9uMYlVImiPD0EOJClQ2RPBhrJ5h0OjhMKtwOmu5rRcLaTZo5Gk9RBl0ig==" }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -11418,6 +11458,11 @@ } } }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -12223,6 +12268,21 @@ "picocolors": "^1.0.0" } }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + } + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 5056aec6..0bd81b3c 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "passport-custom": "^1.1.1", "passport-jwt": "^4.0.1", "password-validator": "^5.3.0", + "path": "^0.12.7", "redis": "^4.6.8", "socket.io": "^4.6.1", "ssha": "^1.0.1", diff --git a/src/modules/logsManagement/routes/logs.routes.js b/src/modules/logsManagement/routes/logs.routes.js index f4afcce4..c5f2cff8 100644 --- a/src/modules/logsManagement/routes/logs.routes.js +++ b/src/modules/logsManagement/routes/logs.routes.js @@ -5,6 +5,7 @@ const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') const { decodeJWT } = require('@src/utils/authentication/tokens/jwtUtils') const { WebSocketServer, OPEN } = require('ws') const Tail = require('tail').Tail +const path = require('path') // Initialize the tail instance to monitor the log file const tail = new Tail('logs/all.log', { @@ -122,4 +123,16 @@ router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { res.json(filteredLogs) }) +// Define a route to retrieve the log file +router.get('/log-file', (req, res) => { + const logFilePath = path.join(__dirname, '../../../../logs/all.log'); // Adjust the path as needed + const fileStream = fs.createReadStream(logFilePath); + + // Set response headers for downloading the log file + res.setHeader('Content-Disposition', 'attachment; filename="all.log"'); + res.setHeader('Content-Type', 'text/plain'); + + fileStream.pipe(res); +}); + module.exports = router From 7520fc8cc6da6eab8d74044095f8f4811b80d305 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 15:33:38 -0400 Subject: [PATCH 149/229] add jwt validation to the file endpoint --- src/modules/logsManagement/routes/logs.routes.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/logsManagement/routes/logs.routes.js b/src/modules/logsManagement/routes/logs.routes.js index c5f2cff8..784ec89a 100644 --- a/src/modules/logsManagement/routes/logs.routes.js +++ b/src/modules/logsManagement/routes/logs.routes.js @@ -124,15 +124,15 @@ router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { }) // Define a route to retrieve the log file -router.get('/log-file', (req, res) => { - const logFilePath = path.join(__dirname, '../../../../logs/all.log'); // Adjust the path as needed - const fileStream = fs.createReadStream(logFilePath); +router.get('/log-file', checkAuth, checkRoles('superadmin'), (req, res) => { + const logFilePath = path.join(__dirname, '../../../../logs/all.log') // Adjust the path as needed + const fileStream = fs.createReadStream(logFilePath) // Set response headers for downloading the log file - res.setHeader('Content-Disposition', 'attachment; filename="all.log"'); - res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Content-Disposition', 'attachment; filename="all.log"') + res.setHeader('Content-Type', 'text/plain') - fileStream.pipe(res); -}); + fileStream.pipe(res) +}) module.exports = router From 178dd3647f0901c20f0009e72bd6e411b2ee0c6c Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 16:01:23 -0400 Subject: [PATCH 150/229] return json throw petition --- .../logsManagement/routes/logs.routes.js | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/src/modules/logsManagement/routes/logs.routes.js b/src/modules/logsManagement/routes/logs.routes.js index 784ec89a..c388afee 100644 --- a/src/modules/logsManagement/routes/logs.routes.js +++ b/src/modules/logsManagement/routes/logs.routes.js @@ -57,6 +57,28 @@ const wss = new WebSocketServer({ }, }) +const parseLogFileToJson = () => { + const logData = fs.readFileSync('logs/all.log', 'utf-8') + const logs = logData + .split('\n') + .filter(Boolean) + .map((line) => { + const splittedLine = line.split(' ') + const message = splittedLine[3].replace(/^"(.*)"$/, '$1') + const parsedMessage = JSON.parse(message) + const log = { + date: splittedLine[0], + time: splittedLine[1], + level: splittedLine[2], + message: parsedMessage, + } + + return log + }) + + return logs +} + tail.on('line', (data) => { sendLogsToClients(data) }) @@ -97,28 +119,25 @@ router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { const isSuperAdmin = payload.roles.includes('superadmin') const queryParams = req.query - const logs = parseLogFile() + const logs = parseLogFileToJson() - const filteredLogs = logs - .split('\n') - .filter(Boolean) - .filter((log) => { - return Object.entries(queryParams).every(([key, value]) => { - if (key === 'level' && !log.includes(`"level":"${value}"`)) { - return false - } + const filteredLogs = logs.filter((log) => { + return Object.entries(queryParams).every(([key, value]) => { + if (key === 'level' && !log.level.startsWith(value)) { + return false + } - if (!isSuperAdmin) { - return log.includes(`"branch":"${payload.localBase}"`) - } + if (!isSuperAdmin) { + return log.message.branch === payload.localBase + } - if (log.includes(`"${key}":"${value}"`)) { - return false - } + if (key in log.message && log.message[key] !== value) { + return false + } - return true - }) + return true }) + }) res.json(filteredLogs) }) From 330b0f3614bd4de8d9141dcc36b51bac44f8605d Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 23 Sep 2023 16:07:38 -0400 Subject: [PATCH 151/229] add filters logs by daily, weekly and monthly --- .../logsManagement/routes/logs.routes.js | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/modules/logsManagement/routes/logs.routes.js b/src/modules/logsManagement/routes/logs.routes.js index c388afee..3edae5d8 100644 --- a/src/modules/logsManagement/routes/logs.routes.js +++ b/src/modules/logsManagement/routes/logs.routes.js @@ -117,7 +117,6 @@ router.server = (server) => { router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { const payload = decodeJWT(req.headers.authorization.split(' ')[1]) const isSuperAdmin = payload.roles.includes('superadmin') - const queryParams = req.query const logs = parseLogFileToJson() @@ -139,7 +138,58 @@ router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { }) }) - res.json(filteredLogs) + // Function to filter logs by date range + const filterLogsByDateRange = (logs, startDate, endDate) => { + return logs.filter((log) => { + const logDate = new Date(log.date) + return logDate >= startDate && logDate <= endDate + }) + } + + const period = req.query.period + if (period === 'daily') { + const currentDate = new Date() + const startDate = new Date( + currentDate.getFullYear(), + currentDate.getMonth(), + currentDate.getDate() + ) + const endDate = new Date( + startDate.getFullYear(), + startDate.getMonth(), + startDate.getDate() + 1 + ) + const dailyLogs = filterLogsByDateRange(filteredLogs, startDate, endDate) + res.json(dailyLogs) + } else if (period === 'weekly') { + const currentDate = new Date() + const startDate = new Date( + currentDate.getFullYear(), + currentDate.getMonth(), + currentDate.getDate() - 7 + ) + const weeklyLogs = filterLogsByDateRange( + filteredLogs, + startDate, + currentDate + ) + res.json(weeklyLogs) + } else if (period === 'monthly') { + const currentDate = new Date() + const startDate = new Date( + currentDate.getFullYear(), + currentDate.getMonth(), + currentDate.getDate() - 30 + ) + const monthlyLogs = filterLogsByDateRange( + filteredLogs, + startDate, + currentDate + ) + res.json(monthlyLogs) + } else { + res.json(filteredLogs) // Return filtered logs without date filtering if no period is specified + } }) // Define a route to retrieve the log file From cf7e845d2feeccf521efed8c3e6337a0f9b28dfb Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 24 Sep 2023 14:55:18 -0400 Subject: [PATCH 152/229] add search by group --- src/routes/group.routes.js | 9 +++++++-- src/services/group.services.js | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index 7defb0ad..a1549914 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -8,9 +8,11 @@ const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') const config = require('@src/config/config') const service = GroupServices() -router.get('/', checkAuth, validateResponse, async (req, res) => { +router.get('/:group', checkAuth, validateResponse, async (req, res) => { try { const payload = verifyToken(req.headers.authorization.split(' ')[1]) + const group = req.params.group + const { withChildrens = true } = req.query if (!payload) { throw new Error(`Invalid token.`) @@ -24,7 +26,10 @@ router.get('/', checkAuth, validateResponse, async (req, res) => { throw new Error(`Invalid token.`) } - const response = await service.getGroupsInBaseDN(baseDN) + const response = + withChildrens === true + ? await service.getGroupsInBaseDN(baseDN, withChildrens) + : await service.getGroup(group) res.json({ success: true, diff --git a/src/services/group.services.js b/src/services/group.services.js index 850d548c..1230509b 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -8,7 +8,7 @@ const { const GroupServices = () => { const getGroup = async (group) => { try { - const baseDN = config.ldap.base + const baseDN = 'dc=cu' const ldapFilter = `(ou=${group})` const results = await performLdapSearch(baseDN, ldapFilter) if (results[0] === undefined) { From 5f6f302c23c62216ae4f59c5e3b34c17ea879ab0 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 24 Sep 2023 15:34:18 -0400 Subject: [PATCH 153/229] add getGroups endpoint --- src/helpers/convertQueryToFilter.js | 2 ++ src/routes/group.routes.js | 34 ++++++++++++++++++++++++----- src/services/group.services.js | 19 ++++++++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index d8784a96..06854d7c 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -34,6 +34,7 @@ const attributeFilters = { orgRole: (value) => `orgRole=${value}`, educationalCategory: (value) => `educationalCategory=${value}`, scientificCategory: (value) => `scientificCategory=${value}`, + ou: (value) => `ou=${value}`, } const userTypeFilters = { @@ -44,6 +45,7 @@ const userTypeFilters = { const createLdapFilterFromQuery = (query) => { const filters = [] + console.log('query', query) for (const key in query) { if (attributeFilters[key] && query[key]) { diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index a1549914..2b32c1d2 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -8,11 +8,10 @@ const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') const config = require('@src/config/config') const service = GroupServices() -router.get('/:group', checkAuth, validateResponse, async (req, res) => { +router.get('/byName/:group', checkAuth, validateResponse, async (req, res) => { try { const payload = verifyToken(req.headers.authorization.split(' ')[1]) const group = req.params.group - const { withChildrens = true } = req.query if (!payload) { throw new Error(`Invalid token.`) @@ -26,10 +25,33 @@ router.get('/:group', checkAuth, validateResponse, async (req, res) => { throw new Error(`Invalid token.`) } - const response = - withChildrens === true - ? await service.getGroupsInBaseDN(baseDN, withChildrens) - : await service.getGroup(group) + const response = await service.getGroup(group) + + res.json({ + success: true, + data: response, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: 'Error fetching group', + error: `It seems that the group does not exist.`, + }) + } +}) + +router.get('/', checkAuth, validateResponse, async (req, res) => { + try { + const { baseDN = 'dc=cu' } = req.body + const { withChildrens = true } = req.query + + const ldapFilter = `(&(objectClass=organizationalUnit))` + + if (!baseDN) { + throw new Error(`Invalid token.`) + } + + const response = await service.getGroups(baseDN, ldapFilter, withChildrens) res.json({ success: true, diff --git a/src/services/group.services.js b/src/services/group.services.js index 1230509b..de7b8fc8 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -3,6 +3,7 @@ const config = require('../config/config') const { performLdapSearch, performScopedLdapSearch, + performBaseLdapSearch, } = require('@src/utils/ldapUtils') const GroupServices = () => { @@ -22,6 +23,23 @@ const GroupServices = () => { } } + const getGroups = async ( + baseDN = 'dc=cu', + ldapFilter, + withChildrens = true + ) => { + try { + const results = + withChildrens === true + ? await performLdapSearch(baseDN, ldapFilter) + : await performBaseLdapSearch(baseDN, ldapFilter) + return results + } catch (error) { + console.error('Error in getChildrens', error) + throw error + } + } + const getAdminGroup = async (baseDN = config.ldap.base) => { const ldapFilter = `(objectClass=*)` const dn = `cn=admin,${baseDN}` @@ -72,6 +90,7 @@ const GroupServices = () => { return { getAdminGroup, getGroup, + getGroups, getGroupByCN, getGroupsInBaseDN, getChildrensBaseDN, From 02f3d031a3d1ea37c8a87657057bf6f41a97330b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 24 Sep 2023 15:42:26 -0400 Subject: [PATCH 154/229] filter groups by scope in querys --- src/routes/group.routes.js | 4 ++-- src/services/group.services.js | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index 2b32c1d2..1f38e1e5 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -43,7 +43,7 @@ router.get('/byName/:group', checkAuth, validateResponse, async (req, res) => { router.get('/', checkAuth, validateResponse, async (req, res) => { try { const { baseDN = 'dc=cu' } = req.body - const { withChildrens = true } = req.query + const { scope = 'sub' } = req.query const ldapFilter = `(&(objectClass=organizationalUnit))` @@ -51,7 +51,7 @@ router.get('/', checkAuth, validateResponse, async (req, res) => { throw new Error(`Invalid token.`) } - const response = await service.getGroups(baseDN, ldapFilter, withChildrens) + const response = await service.getGroups(baseDN, ldapFilter, scope) res.json({ success: true, diff --git a/src/services/group.services.js b/src/services/group.services.js index de7b8fc8..feb279b0 100644 --- a/src/services/group.services.js +++ b/src/services/group.services.js @@ -23,16 +23,14 @@ const GroupServices = () => { } } - const getGroups = async ( - baseDN = 'dc=cu', - ldapFilter, - withChildrens = true - ) => { + const getGroups = async (baseDN = 'dc=cu', ldapFilter, scope = 'sub') => { try { const results = - withChildrens === true + scope === 'sub' ? await performLdapSearch(baseDN, ldapFilter) - : await performBaseLdapSearch(baseDN, ldapFilter) + : scope === 'base' + ? await performBaseLdapSearch(baseDN, ldapFilter) + : await performScopedLdapSearch(baseDN, ldapFilter) return results } catch (error) { console.error('Error in getChildrens', error) From f341ad47f82dc571e86e1dadbb9630d804a315ec Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 24 Sep 2023 15:46:23 -0400 Subject: [PATCH 155/229] change get groups to post --- src/routes/group.routes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index 1f38e1e5..b186f2d5 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -40,7 +40,7 @@ router.get('/byName/:group', checkAuth, validateResponse, async (req, res) => { } }) -router.get('/', checkAuth, validateResponse, async (req, res) => { +router.post('/', checkAuth, validateResponse, async (req, res) => { try { const { baseDN = 'dc=cu' } = req.body const { scope = 'sub' } = req.query From ea445454949559e3602525ea6fa0ea72c48daccf Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 28 Sep 2023 18:58:11 -0400 Subject: [PATCH 156/229] fix CI query endpoint --- src/helpers/convertQueryToFilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 06854d7c..91c44603 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -4,7 +4,7 @@ const attributeFilters = { uid: (value) => `uid=${value}`, cn: (value) => `cn=${value}`, username: (value) => `uid=${value}`, - ci: (value) => `ci=${value}`, + CI: (value) => `CI=${value}`, email: (value) => `maildrop=${value}`, lastName: (value) => `lastName=${value}`, sex: (value) => `sex=${value}`, From 723ace7fc88f313f9364506ed2a340d93dc6de0e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 29 Sep 2023 13:11:57 -0400 Subject: [PATCH 157/229] add ldap update multiples atts --- src/routes/user.routes.js | 47 ++++++++++++++++++++++++++++++++++++++- src/utils/ldapUtils.js | 1 - 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 1f237e61..10de63e0 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -13,12 +13,13 @@ const { createLdapFilterFromQuery, } = require('@src/helpers/convertQueryToFilter') const validateQuery = require('@src/middlewares/queryValidator') +const ldap = require('ldapjs') +const ldapClient = require('@src/connections/LDAP_client') // Middleware for routes requiring checkAuth and checkRoles('admin') router.use(checkAuth, checkRoles('admin')) // Middleware to handle common success and error responses -router.use(validateResponse) // Route handler for getting all users router.get('/', async (req, res) => { @@ -154,4 +155,48 @@ router.put('/:username', async (req, res) => { } }) +router.post('/modify-ldap', async (req, res) => { + const dn = req.body.dn + const attributes = req.body.attributes + + const modifications = [] + + // Loop through the updated attributes and create modification objects + for (const attributeName in attributes) { + if (attributes.hasOwnProperty(attributeName)) { + const attributeValue = attributes[attributeName] + + // Create a modification object to replace the attribute value + const modification = new ldap.Change({ + operation: 'replace', // Use 'replace' to replace the attribute value + modification: { + type: attributeName, + values: [attributeValue], + }, + }) + + modifications.push(modification) + } + } + + let errorOccurred = false // Track if any modification failed + + // Perform the LDAP modify operation with all modifications + for (const modification of modifications) { + ldapClient.modify(dn, modification, (err) => { + if (err) { + console.error('Error modifying attributes:', err) + errorOccurred = true + } + }) + } + + if (errorOccurred) { + res.status(500).json({ error: 'Error modifying attributes' }) + } else { + console.log('Attributes modified successfully') + res.json({ message: 'Attributes modified successfully' }) + } +}) + module.exports = router diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index d6f7c0bb..2f3bb1c9 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -144,7 +144,6 @@ const performBaseLdapSearch = async (baseDn, filter, attributes) => { } const performLdapUpdate = async (userDN, att, value) => { - console.log('userDN', userDN) return new Promise((resolve, reject) => { try { bindLdapClient() // Bind before search From 92295f7221cb18f017ad39ee035412eebf8d138e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 11 Oct 2023 21:34:51 -0400 Subject: [PATCH 158/229] increase the expiration time of the jwt --- src/modules/authentication/LdapAuth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 459476c9..6ce7edd8 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -257,7 +257,7 @@ const login = function (req, res, next) { } const userObj = { ...user } - const token = signToken(payload, { expiresIn: '15 minutes' }) + const token = signToken(payload, { expiresIn: '45 minutes' }) const refreshToken = signToken(payload, { expiresIn: '1 day' }) /* await storeRefreshToken(user.uid, refreshToken) */ From 082e01dda6760f7d34e093e2d4f9b208158cf960 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 11 Oct 2023 21:35:13 -0400 Subject: [PATCH 159/229] update get methods to post --- src/routes/group.routes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/group.routes.js b/src/routes/group.routes.js index b186f2d5..5aa836b9 100644 --- a/src/routes/group.routes.js +++ b/src/routes/group.routes.js @@ -66,7 +66,7 @@ router.post('/', checkAuth, validateResponse, async (req, res) => { } }) -router.get( +router.post( '/getChilds', checkAuth, checkRoles('admin'), @@ -93,7 +93,7 @@ router.get( } } ) -router.get('/byType/:type', checkAuth, validateResponse, async (req, res) => { +router.post('/byType/:type', checkAuth, validateResponse, async (req, res) => { try { const type = req.params.type const baseDN = req.body.dn From 9e310380b2a43bfd68b22579687aeeff0c9a79f8 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 15 Oct 2023 16:01:28 -0400 Subject: [PATCH 160/229] add error and end listeners on ldap client --- src/connections/LDAP_client.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/connections/LDAP_client.js b/src/connections/LDAP_client.js index 4035eea7..9341960e 100644 --- a/src/connections/LDAP_client.js +++ b/src/connections/LDAP_client.js @@ -13,6 +13,14 @@ try { console.log('Error trying to connect to LDAP', err) }) + client.on('error', (err) => { + console.error('LDAP Error:', err.message) + }) + + client.on('end', (result) => { + console.log('LDAP Result:', result.status) + }) + client.bind(config.ldap.admin.username, config.ldap.admin.password, (err) => { try { if (err) { From 36cf1eafdf17ebfdfc8a5714c9357f7055c628fc Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 15 Oct 2023 16:02:48 -0400 Subject: [PATCH 161/229] add ldap function to add user --- src/utils/ldapUtils.js | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index 2f3bb1c9..75fb4a6e 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -171,10 +171,82 @@ const performLdapUpdate = async (userDN, att, value) => { }) } +const performLdapAddition = async (dn, entry) => { + return new Promise((resolve, reject) => { + try { + bindLdapClient() // Bind before search + + /* const entry = { + CI: '85100804881', + middleName: 'Lorenzo', + lastName: 'Neyra', + name: 'Javier', + homeAddress: 'Ave. 31 B # 4208', + telephoneNumber: '(7) 202-73-19', + dayRegister: '2019-10-19', + sex: 'M', + area: 'null', + userCondition: 'Externo', + userStatus: 'Activo', + sedeMunicipio: 'PLAYA', + userType: 'Estudiante', + userInformation: 'Curso por Encuentros', + career: 'Ingeniería Informática', + studentClassGroup: '23', + studentYear: '2', + country: 'Cuba', + UJC: 'no', + skinColor: 'Blanco', + nameInstitution: 'Filial 10 de Octubre', + right: 'Todos', + hash: '44dacf000071e4b68d826bc96ff9909d4b8e25e1', + lastTimeChange: '13-Jul-2023-19:21:41', + uid: 'tommy.test', + homeDirectory: '/home/tommy.test', + givenName: 'tommy.test', + cn: 'Tommy', + sn: 'Lorenzo Neyra', + displayName: 'Javier', + uidNumber: '1000', + userPassword: '{SHA}qlPBpNoITztMtAoTi+qrhqSXK+88', + mail: ['tommy.test@cujae.edu.cu'], + maildrop: ['tommy.test@cujae.edu.cu'], + gidNumber: '1000842', + sambaSID: 'sambaSID', + objectClass: [ + 'top', + 'person', + 'posixAccount', + 'shadowAccount', + 'inetOrgPerson', + 'iesServices', + 'sambaSamAccount', + 'radiusprofile', + 'CourierMailAlias', + 'iesMember', + ], + } */ + + ldapClient.add(dn, entry, (err) => { + if (err) { + console.log('error', err) + assert.ifError(err) + } else { + console.log('created user') + resolve('created User') + } + }) + } catch (err) { + reject(err) + } + }) +} + module.exports = { performLdapSearch, unbindLdapClient, performLdapUpdate, performScopedLdapSearch, performBaseLdapSearch, + performLdapAddition, } From 49bea92fc90d29fd240fc6b553e0b4f6d2926144 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 15 Oct 2023 17:43:57 -0400 Subject: [PATCH 162/229] add user endpoint created --- src/routes/user.routes.js | 32 ++++++++++++- src/schemas/ldapEntry.schema.js | 3 ++ src/services/user.services.js | 80 ++++++++++++++++++++++++++++++++- src/utils/ldapUtils.js | 51 --------------------- 4 files changed, 113 insertions(+), 53 deletions(-) create mode 100644 src/schemas/ldapEntry.schema.js diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 10de63e0..735733c1 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -1,7 +1,6 @@ const express = require('express') const router = express.Router() const UserServices = require('@src/services/user.services.js') -const validateResponse = require('@src/middlewares/validateResponse') const { checkAuth, checkRoles, @@ -199,4 +198,35 @@ router.post('/modify-ldap', async (req, res) => { } }) +router.post('/newUser', async (req, res) => { + try { + const { newUser, userDN } = req.body + + if (!newUser || !userDN) { + throw new Error('Missing atts') + } + + if (newUser && newUser.hasOwnProperty('dn')) { + delete newUser.dn + } + + if (newUser && newUser.hasOwnProperty('controls')) { + delete newUser.controls + } + + // Call the service function with the required parameters + const response = await service.addUser(userDN, newUser) // Replace 'YourDNHere' with the appropriate DN. + + if (response) { + res.status(200).json({ message: 'New user added successfully.' }) + } else { + res + .status(500) + .json({ error: 'Failed to add the new user to the LDAP directory.' }) + } + } catch (error) { + console.error('Error in route:', error) + res.status(500).json({ error: error.message }) + } +}) module.exports = router diff --git a/src/schemas/ldapEntry.schema.js b/src/schemas/ldapEntry.schema.js new file mode 100644 index 00000000..fa135103 --- /dev/null +++ b/src/schemas/ldapEntry.schema.js @@ -0,0 +1,3 @@ + + +module.export = ldapEntrySchema diff --git a/src/services/user.services.js b/src/services/user.services.js index 5d1817c0..b8970b48 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -1,6 +1,50 @@ require('dotenv').config({ path: __dirname + '/../../.env' }) const config = require('@src/config/config') -const { performLdapSearch, performLdapUpdate } = require('@src/utils/ldapUtils') +const { + performLdapSearch, + performLdapUpdate, + performLdapAddition, +} = require('@src/utils/ldapUtils') +const Joi = require('joi') +const ldapEntrySchema = Joi.object({ + CI: Joi.string().required(), + middleName: Joi.string().required(), + lastName: Joi.string().required(), + name: Joi.string().required(), + homeAddress: Joi.string().required(), + telephoneNumber: Joi.string().required(), + dayRegister: Joi.date().iso().required(), + sex: Joi.string().valid('M', 'F').required(), + area: Joi.string().allow(null).required(), + userCondition: Joi.string().required(), + userStatus: Joi.string().required(), + sedeMunicipio: Joi.string().required(), + userType: Joi.string().required(), + userInformation: Joi.string().required(), + career: Joi.string().required(), + studentClassGroup: Joi.string().required(), + studentYear: Joi.string().required(), + country: Joi.string().required(), + UJC: Joi.string().required(), + skinColor: Joi.string().required(), + nameInstitution: Joi.string().required(), + right: Joi.string().required(), + hash: Joi.string().required(), + lastTimeChange: Joi.string().required(), + uid: Joi.string().required(), + homeDirectory: Joi.string().required(), + givenName: Joi.string().required(), + cn: Joi.string().required(), + sn: Joi.string().required(), + displayName: Joi.string().required(), + uidNumber: Joi.string().required(), + userPassword: Joi.string().required(), + mail: Joi.array().items(Joi.string()).required(), + maildrop: Joi.array().items(Joi.string()).required(), + gidNumber: Joi.string().required(), + sambaSID: Joi.string().required(), + objectClass: Joi.array().items(Joi.string()).required(), +}) const UserServices = () => { const handleFilteredSearch = async ( @@ -59,11 +103,45 @@ const UserServices = () => { return updatedUser } + const addUser = async (dn, newUser) => { + try { + // Input validation + if (!dn || !newUser) { + throw new Error('Missing required parameters.') + } + + const { error, value } = ldapEntrySchema.validate(newUser) + + if (error) { + throw new Error(`Validation failed: ${error.details[0].message}`) + } + + // Check if the user already exists + const alreadyExistingUser = await handleFilteredSearch( + config.ldap.base, + `(|(uid=${newUser.uid})(CI=${newUser.CI})(email=${newUser.email}))` + ) + + if (!!alreadyExistingUser && alreadyExistingUser.length !== 0) { + throw new Error(`User already exists with the given DN.`) + } + + // Perform LDAP addition + const addedUser = await performLdapAddition(dn, newUser) + + return addedUser + } catch (error) { + console.error('Error in addUser:', error) + throw error + } + } + return { getByUsername, handleFilteredSearch, updateUser, getByEmail, + addUser, } } diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index 75fb4a6e..57314fcc 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -176,57 +176,6 @@ const performLdapAddition = async (dn, entry) => { try { bindLdapClient() // Bind before search - /* const entry = { - CI: '85100804881', - middleName: 'Lorenzo', - lastName: 'Neyra', - name: 'Javier', - homeAddress: 'Ave. 31 B # 4208', - telephoneNumber: '(7) 202-73-19', - dayRegister: '2019-10-19', - sex: 'M', - area: 'null', - userCondition: 'Externo', - userStatus: 'Activo', - sedeMunicipio: 'PLAYA', - userType: 'Estudiante', - userInformation: 'Curso por Encuentros', - career: 'Ingeniería Informática', - studentClassGroup: '23', - studentYear: '2', - country: 'Cuba', - UJC: 'no', - skinColor: 'Blanco', - nameInstitution: 'Filial 10 de Octubre', - right: 'Todos', - hash: '44dacf000071e4b68d826bc96ff9909d4b8e25e1', - lastTimeChange: '13-Jul-2023-19:21:41', - uid: 'tommy.test', - homeDirectory: '/home/tommy.test', - givenName: 'tommy.test', - cn: 'Tommy', - sn: 'Lorenzo Neyra', - displayName: 'Javier', - uidNumber: '1000', - userPassword: '{SHA}qlPBpNoITztMtAoTi+qrhqSXK+88', - mail: ['tommy.test@cujae.edu.cu'], - maildrop: ['tommy.test@cujae.edu.cu'], - gidNumber: '1000842', - sambaSID: 'sambaSID', - objectClass: [ - 'top', - 'person', - 'posixAccount', - 'shadowAccount', - 'inetOrgPerson', - 'iesServices', - 'sambaSamAccount', - 'radiusprofile', - 'CourierMailAlias', - 'iesMember', - ], - } */ - ldapClient.add(dn, entry, (err) => { if (err) { console.log('error', err) From 52c16060eeb4e7363931d8c760b65fa745ba203a Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 16 Oct 2023 21:14:45 -0400 Subject: [PATCH 163/229] add validations to studet and employees --- src/schemas/ldapEntry.schema.js | 71 ++++++++++++++++++++++++++++++++- src/services/user.services.js | 57 +++++++------------------- 2 files changed, 84 insertions(+), 44 deletions(-) diff --git a/src/schemas/ldapEntry.schema.js b/src/schemas/ldapEntry.schema.js index fa135103..f17e3a53 100644 --- a/src/schemas/ldapEntry.schema.js +++ b/src/schemas/ldapEntry.schema.js @@ -1,3 +1,72 @@ +const Joi = require('joi') +const { userTypes } = require('@src/constants/userTypes') +const userSchema = Joi.object({ + CI: Joi.string().required(), + middleName: Joi.string().required(), + lastName: Joi.string().required(), + name: Joi.string().required(), + homeAddress: Joi.string().required(), + telephoneNumber: Joi.string().required(), + dayRegister: Joi.date().iso().required(), + sex: Joi.string().valid('M', 'F').required(), + area: Joi.string().allow(null).required(), + userCondition: Joi.string().required(), + userStatus: Joi.string().required(), + sedeMunicipio: Joi.string().required(), + userType: Joi.string().required(), + userInformation: Joi.string().required(), + career: Joi.string().required(), + studentClassGroup: Joi.string().required(), + studentYear: Joi.string().required(), + country: Joi.string().required(), + UJC: Joi.string().required(), + skinColor: Joi.string().required(), + nameInstitution: Joi.string().required(), + right: Joi.string().required(), + hash: Joi.string().required(), + lastTimeChange: Joi.string().required(), + uid: Joi.string().required(), + homeDirectory: Joi.string().required(), + givenName: Joi.string().required(), + cn: Joi.string().required(), + sn: Joi.string().required(), + displayName: Joi.string().required(), + uidNumber: Joi.string().required(), + userPassword: Joi.string().required(), + mail: Joi.array().items(Joi.string()).required(), + maildrop: Joi.array().items(Joi.string()).required(), + gidNumber: Joi.string().required(), + sambaSID: Joi.string().required(), + objectClass: Joi.array().items(Joi.string()).required(), +}) -module.export = ldapEntrySchema +const studentSchema = Joi.object({ + career: Joi.string().required(), + studentYear: Joi.string().required(), + studentClassGroup: Joi.string().required(), + userInformation: Joi.string().required(), + userCondition: Joi.string().required(), + userStatus: Joi.string().required(), +}) + +const employeeSchema = Joi.object({ + dateContract: Joi.date().required(), + dateEndContract: Joi.date().required(), + educationalCategory: Joi.string().required(), + orgRole: Joi.string().required(), + schoolLevel: Joi.string().required(), + scientificCategory: Joi.string().required(), + userYears: Joi.string().required(), + workerContract: Joi.string().required(), + workArea: Joi.string().required(), + workerID: Joi.string().required(), +}) + +const ldapEntrySchema = userSchema.when('userType', { + is: userTypes[0], + then: studentSchema, + otherwise: employeeSchema, +}) + +module.exports = { ldapEntrySchema, studentSchema, employeeSchema, userSchema } diff --git a/src/services/user.services.js b/src/services/user.services.js index b8970b48..9d62a881 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -5,46 +5,12 @@ const { performLdapUpdate, performLdapAddition, } = require('@src/utils/ldapUtils') -const Joi = require('joi') -const ldapEntrySchema = Joi.object({ - CI: Joi.string().required(), - middleName: Joi.string().required(), - lastName: Joi.string().required(), - name: Joi.string().required(), - homeAddress: Joi.string().required(), - telephoneNumber: Joi.string().required(), - dayRegister: Joi.date().iso().required(), - sex: Joi.string().valid('M', 'F').required(), - area: Joi.string().allow(null).required(), - userCondition: Joi.string().required(), - userStatus: Joi.string().required(), - sedeMunicipio: Joi.string().required(), - userType: Joi.string().required(), - userInformation: Joi.string().required(), - career: Joi.string().required(), - studentClassGroup: Joi.string().required(), - studentYear: Joi.string().required(), - country: Joi.string().required(), - UJC: Joi.string().required(), - skinColor: Joi.string().required(), - nameInstitution: Joi.string().required(), - right: Joi.string().required(), - hash: Joi.string().required(), - lastTimeChange: Joi.string().required(), - uid: Joi.string().required(), - homeDirectory: Joi.string().required(), - givenName: Joi.string().required(), - cn: Joi.string().required(), - sn: Joi.string().required(), - displayName: Joi.string().required(), - uidNumber: Joi.string().required(), - userPassword: Joi.string().required(), - mail: Joi.array().items(Joi.string()).required(), - maildrop: Joi.array().items(Joi.string()).required(), - gidNumber: Joi.string().required(), - sambaSID: Joi.string().required(), - objectClass: Joi.array().items(Joi.string()).required(), -}) +const { + ldapEntrySchema, + studentSchema, + employeeSchema, +} = require('@src/schemas/ldapEntry.schema') +const { userTypes } = require('@src/constants/userTypes') const UserServices = () => { const handleFilteredSearch = async ( @@ -110,11 +76,16 @@ const UserServices = () => { throw new Error('Missing required parameters.') } + if (!userTypes.includes(newUser.userType)) { + throw new Error(`Invalid userType`) + } + const { error, value } = ldapEntrySchema.validate(newUser) - if (error) { - throw new Error(`Validation failed: ${error.details[0].message}`) - } + if (newUser.userType === userTypes) + if (error) { + throw new Error(`Validation failed: ${error.details[0].message}`) + } // Check if the user already exists const alreadyExistingUser = await handleFilteredSearch( From 9e80f9c404f81ab6355f6aba3790819022ed2c1e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Wed, 18 Oct 2023 12:12:43 -0400 Subject: [PATCH 164/229] add user correctly --- src/constants/ldap_options.js | 3 +-- src/constants/user_objectClasses.js | 14 ++++++++++++++ src/routes/user.routes.js | 13 ++++++------- src/services/user.services.js | 5 +++++ src/utils/ldapUtils.js | 21 ++++++++++++++++++++- 5 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 src/constants/user_objectClasses.js diff --git a/src/constants/ldap_options.js b/src/constants/ldap_options.js index 91907fd5..295d801f 100644 --- a/src/constants/ldap_options.js +++ b/src/constants/ldap_options.js @@ -25,5 +25,4 @@ let userOptions = { //starttls: true } - -module.exports = {usernameAttr, searchBase, admOptions, userOptions} \ No newline at end of file +module.exports = { usernameAttr, searchBase, admOptions, userOptions } diff --git a/src/constants/user_objectClasses.js b/src/constants/user_objectClasses.js new file mode 100644 index 00000000..14a6aace --- /dev/null +++ b/src/constants/user_objectClasses.js @@ -0,0 +1,14 @@ +const objectClasses = [ + 'top', + 'person', + 'posixAccount', + 'shadowAccount', + 'inetOrgPerson', + 'iesServices', + 'sambaSamAccount', + 'radiusprofile', + 'CourierMailAlias', + 'iesMember', +] + +module.exports = {objectClasses} diff --git a/src/routes/user.routes.js b/src/routes/user.routes.js index 735733c1..3e8eed7d 100644 --- a/src/routes/user.routes.js +++ b/src/routes/user.routes.js @@ -201,18 +201,17 @@ router.post('/modify-ldap', async (req, res) => { router.post('/newUser', async (req, res) => { try { const { newUser, userDN } = req.body + const propertiesToDelete = ['dn', 'controls', 'objectClass', 'objectclass'] if (!newUser || !userDN) { throw new Error('Missing atts') } - if (newUser && newUser.hasOwnProperty('dn')) { - delete newUser.dn - } - - if (newUser && newUser.hasOwnProperty('controls')) { - delete newUser.controls - } + propertiesToDelete.map((att) => { + if (newUser && newUser.hasOwnProperty(att)) { + delete newUser[att] + } + }) // Call the service function with the required parameters const response = await service.addUser(userDN, newUser) // Replace 'YourDNHere' with the appropriate DN. diff --git a/src/services/user.services.js b/src/services/user.services.js index 9d62a881..550db3d0 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -11,6 +11,7 @@ const { employeeSchema, } = require('@src/schemas/ldapEntry.schema') const { userTypes } = require('@src/constants/userTypes') +const { objectClasses } = require('@src/constants/user_objectClasses') const UserServices = () => { const handleFilteredSearch = async ( @@ -97,6 +98,10 @@ const UserServices = () => { throw new Error(`User already exists with the given DN.`) } + /* Add necessary atts */ + newUser.objectclass = objectClasses + newUser.homeDirectory = `/home/${newUser.uid}` + // Perform LDAP addition const addedUser = await performLdapAddition(dn, newUser) diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index 57314fcc..bca6d749 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -40,6 +40,13 @@ const transform = (entry) => { return data } +function generateUniqueUID() { + // Generate a random 4-digit number + const randomPart = Math.floor(1000 + Math.random() * 9000) + + return `${randomPart}` +} + // Perform a search using the provided filter and return the results const performLdapSearch = async (baseDn, filter, attributes) => { return new Promise((resolve, reject) => { @@ -172,6 +179,14 @@ const performLdapUpdate = async (userDN, att, value) => { } const performLdapAddition = async (dn, entry) => { + entry.uidNumber = generateUniqueUID() + entry.gidNumber = '1000' + entry.right = 'Todos' + entry.lastTimeChange = new Date().toISOString() + entry.sambaSID = 'S-1-5-21-1255719363-1350762778-3568053751-513' + + console.log('entry', entry) + return new Promise((resolve, reject) => { try { bindLdapClient() // Bind before search @@ -179,7 +194,10 @@ const performLdapAddition = async (dn, entry) => { ldapClient.add(dn, entry, (err) => { if (err) { console.log('error', err) - assert.ifError(err) + reject({ + success: true, + message: err, + }) } else { console.log('created user') resolve('created User') @@ -198,4 +216,5 @@ module.exports = { performScopedLdapSearch, performBaseLdapSearch, performLdapAddition, + generateUniqueUID, } From 1710978c0c2cc5531fef2d68ed42246d61912a32 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 17:03:14 -0400 Subject: [PATCH 165/229] install swagger dependencies --- package-lock.json | 4000 ++++++++++++++++++++++++++++++++++++++++++--- package.json | 3 + 2 files changed, 3793 insertions(+), 210 deletions(-) diff --git a/package-lock.json b/package-lock.json index 303d7935..d5536c07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,9 @@ "redis": "^4.6.8", "socket.io": "^4.6.1", "ssha": "^1.0.1", + "swagger-jsdoc": "^6.2.8", + "swagger-ui": "^5.9.0", + "swagger-ui-express": "^5.0.0", "tail": "^2.2.6", "winston": "^3.8.2", "winston-mongodb": "^5.1.1", @@ -73,6 +76,62 @@ "node": ">=6.0.0" } }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -702,6 +761,29 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.2.tgz", + "integrity": "sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==", + "dependencies": { + "core-js-pure": "^3.30.2", + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -780,6 +862,11 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -798,6 +885,14 @@ "kuler": "^2.0.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", + "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", + "engines": { + "node": ">=14" + } + }, "node_modules/@hapi/boom": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", @@ -1162,6 +1257,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, "node_modules/@ldapjs/asn1": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-2.0.0.tgz", @@ -1321,6 +1421,380 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, + "node_modules/@swagger-api/apidom-ast": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-0.78.0.tgz", + "integrity": "sha512-mEXmRmkFlmO6dcBuakFkc2gevN4mC6incPAQE1UciaX4hLuJpiv/5DTH9gVWTR0CWUFw/dXROTD/x6ETV0y03A==", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2", + "unraw": "^3.0.0" + } + }, + "node_modules/@swagger-api/apidom-core": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-0.78.0.tgz", + "integrity": "sha512-Qx9m+1u6H4Bsa38s73ANtGn8zFGqK0peguM+SFuUR5HirjpoFB8JB7IG5E8+ymUlpWhlU43q9QnJjcaYJw9MTg==", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "minim": "~0.23.8", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "short-unique-id": "^5.0.2", + "stampit": "^4.3.2" + } + }, + "node_modules/@swagger-api/apidom-error": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-0.78.0.tgz", + "integrity": "sha512-P0enIK3XymxCPHlhGtqc4TU5H+cHf7L0yDFmfjZEcsjDzGDv5A+m5tf429Pr/R+e51DzpT5/xIcPKTnti0gIOw==", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-json-pointer": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-0.78.0.tgz", + "integrity": "sha512-Ly4ZfUGxxbNoHHc9vR814mU96ZLGsjaJflCW0jdZnMVfVv20fDCoDoOOmXat6ajxUbS2YKimgxPvdBth3K/CRQ==", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-ns-api-design-systems": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-0.78.0.tgz", + "integrity": "sha512-WoWE6w1P3qsokG3Qyc5F3xpz+e/WablE0EHGSgiYxk+MQJLqYmz5UhS5LxYGT9d6o9XUs24ykSbKrYWYwkpp4w==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-1": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "node_modules/@swagger-api/apidom-ns-asyncapi-2": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-0.78.0.tgz", + "integrity": "sha512-QWZohCtXf5UX/I9bnc4MQh16X9jGPdGrByWM93xRvh8X8rIF0BtF9S7lIx028aX3AHYIu4SwYr7JZlqEaZ92Kw==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-json-schema-draft-7": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "node_modules/@swagger-api/apidom-ns-json-schema-draft-4": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-0.78.0.tgz", + "integrity": "sha512-19NR9lTHMOQTIEV4tJq+FlHQAYnjyH+DgI4mmRu6UMFSZjRjutYF7B8lCGogSus9Uwy8YpUk00prLFTld00wgA==", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-core": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "node_modules/@swagger-api/apidom-ns-json-schema-draft-6": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-0.78.0.tgz", + "integrity": "sha512-pHyCPU3OWDiPuLepo03rBpi2n+SCH6PZAgguqAB3lDJ2ymitrT2SNpmZ6CcHvPGR9Y7h4/fR5vAypVZfdNr/WQ==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-ns-json-schema-draft-4": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "node_modules/@swagger-api/apidom-ns-json-schema-draft-7": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-0.78.0.tgz", + "integrity": "sha512-ScUiNNAdwnikH3Fo2rUsDmXOjV7zXfQ6CGE+QkY5Wj3t1M6siw2HpDjrBaaCyp6w/bemvogsh280GrzAnxKLIw==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-ns-json-schema-draft-6": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "node_modules/@swagger-api/apidom-ns-openapi-3-0": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-0.78.0.tgz", + "integrity": "sha512-GRmUOknEzMG37y5sStvjEsk30RLVg5E7iZuougK1rEf+wzzX5XhorSgMx2NQmka5rb814BgzyiqGRmvKQErDBw==", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-ns-json-schema-draft-4": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "node_modules/@swagger-api/apidom-ns-openapi-3-1": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-0.78.0.tgz", + "integrity": "sha512-hHpUZLjIiaLK+99cAPiYNV9QzZQxFoMLqBNYo+GQwqizaVOjxQRi5y/hPkfFALqqufZ1L6XWeyjQrtli0ftqBQ==", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-json": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-0.78.0.tgz", + "integrity": "sha512-g7VlfOrpTzbVV30Ugab0qAJITavLo39apvyFFv2cN2jfuIQa8MlzDP0mZmVtCGQy3IoT4Auns/qWeGcZX0li9w==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-api-design-systems": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-yaml": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-0.78.0.tgz", + "integrity": "sha512-ZueYoHOJARRm84ntCggUZLKNwUHz2U0eG9KHIzw75UW43pyvQVbxAE2ELdyP5f8vr51wMuMp6XYRcFOsNi/oeQ==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-api-design-systems": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-json-2": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-0.78.0.tgz", + "integrity": "sha512-Jm0hbNXWOH2QJIiF+5QgY+ioVSOBqV3WlhTeyrF5kSxHinah16nR1jUkz5tMsSc9sxTZHzWYVLneyBMW3VSHrw==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-asyncapi-2": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-0.78.0.tgz", + "integrity": "sha512-zpP8gQBXhrR/t91Z/Jl0nD/cUSzmYjzhE5qWHkfhbGvzaWatiLrNY+CnFS9RcgF4pb2LSqS5cjDVAExBbjdLdQ==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-asyncapi-2": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-json": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-0.78.0.tgz", + "integrity": "sha512-d/8gFj5cc+pnCo7ORGN5dJPGWzTleYkIwGfsyFuLZNjb4KlrOrKlPl0LKQ/t7MSEbVpSStxbgezoUtfdVhGscw==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2", + "tree-sitter": "=0.20.4", + "tree-sitter-json": "=0.20.1", + "web-tree-sitter": "=0.20.3" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-0": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-0.78.0.tgz", + "integrity": "sha512-MjXkPAiEyTZIljzjEgvAmqaZel0jpKBBqdtC8nWH/9C2ugkKHetKMSgYu+5wvFh//ixJZZE7dM1QHEIBoPl9nA==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-1": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-0.78.0.tgz", + "integrity": "sha512-k+rT6kwu1jAN1lYIP1wVshQdaLu9M+jjCfpvMXXkL/2VpZqq1yP6daFm0ExiHllVUcHWeqRXhubFV3wWkFm6eA==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-0.78.0.tgz", + "integrity": "sha512-RzcqL0kvUl5G75H4qOFSi9FTaVfBtRnjzEcjd8SOKVLg3JJsCv3vrk68laRm8HXocyWgGstU51UzBqkMStXy4A==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-0.78.0.tgz", + "integrity": "sha512-1hB+mcEJd14RJC8lH3yJsoQRDhA8TNNKl3EyQ17eFY0dK29JlluDEbDHIRQpLT1l2jCK/NfqAk2hc37yIwydfw==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-0.78.0.tgz", + "integrity": "sha512-L37X+nRNp+2PyJkAwMdSQjP8tb3xoc6FVk2QXLHogghe1Phrmfaal3TPu2rWJNn7NSBcvSyiTAR7gEIULitugA==", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2", + "tree-sitter": "=0.20.4", + "tree-sitter-yaml": "=0.5.0", + "web-tree-sitter": "=0.20.3" + } + }, + "node_modules/@swagger-api/apidom-reference": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-0.78.0.tgz", + "integrity": "sha512-IiOaMgy+CzpQe5fFwyge4B/lkHQnBhiuNGPgIJELYXJMZle+pN6K/V4muLCG6JjAXllucbCqMpW/KLmPxGAXaw==", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@types/ramda": "~0.29.6", + "axios": "^1.4.0", + "minimatch": "^7.4.3", + "process": "^0.11.10", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + }, + "optionalDependencies": { + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-json-pointer": "^0.78.0", + "@swagger-api/apidom-ns-asyncapi-2": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^0.78.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^0.78.0", + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^0.78.0", + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^0.78.0", + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "^0.78.0", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0" + } + }, + "node_modules/@swagger-api/apidom-reference/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@swagger-api/apidom-reference/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@types/babel__core": { "version": "7.20.1", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", @@ -1384,6 +1858,23 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.7.tgz", + "integrity": "sha512-EVLigw5zInURhzfXUM65eixfadfsHKomGKUakToXo84t8gGIJuTcD2xooM2See7GyQ7DRtYjhCHnSUQez8JaLw==", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.4.tgz", + "integrity": "sha512-ZchYkbieA+7tnxwX/SCBySx9WwvWR8TaP5tb2jRAzwvLb/rWchGw3v0w3pqUbUvj0GCwW2Xz/AVPSk6kUGctXQ==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -1408,11 +1899,44 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/json-schema": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==" + }, "node_modules/@types/node": { "version": "17.0.28", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.28.tgz", "integrity": "sha512-UYmIeBnB0On70dN1iGCinsL1qH5JmIEJwa+3KX0Xw4HQJ8KA16ULlyTCNmnzfyzj/BlxZKmZLqp4TYdssnov1w==" }, + "node_modules/@types/prop-types": { + "version": "15.7.9", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz", + "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==" + }, + "node_modules/@types/ramda": { + "version": "0.29.7", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.29.7.tgz", + "integrity": "sha512-IUl6U95qwlQtVvZkSX4ODj08oJVtPyWMFRtPVNqhxc2rt+Bh7lCzTrGMYMZ7dmRKcAjtot3xrPnYGwsjdt8gzQ==", + "dependencies": { + "types-ramda": "^0.29.5" + } + }, + "node_modules/@types/react": { + "version": "18.2.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.31.tgz", + "integrity": "sha512-c2UnPv548q+5DFh03y8lEDeMfDwBn9G3dRwfkrxQMo/dOtRHUUO57k6pHvBIfH/VF4Nh+98mZ5aaSe+2echD5g==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.5.tgz", + "integrity": "sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==" + }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -1424,6 +1948,16 @@ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" }, + "node_modules/@types/unist": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.9.tgz", + "integrity": "sha512-zC0iXxAv1C1ERURduJueYzkzZ2zaGyc+P2c95hgkikHPr3z8EdUZOlgEQ5X0DRmwDZn+hekycQnoeiiRVrmilQ==" + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/webidl-conversions": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", @@ -1455,6 +1989,11 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1511,7 +2050,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1544,7 +2082,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -1603,10 +2140,26 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autolinker": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.16.2.tgz", + "integrity": "sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==", + "dependencies": { + "tslib": "^2.3.0" + } + }, "node_modules/axios": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz", - "integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -1760,8 +2313,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-arraybuffer": { "version": "0.1.4", @@ -1775,7 +2327,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -1866,7 +2417,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1876,7 +2426,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -1941,7 +2490,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -2002,6 +2551,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2044,7 +2598,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2065,6 +2618,33 @@ "node": ">=10" } }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -2092,11 +2672,16 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true + }, "node_modules/ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true, "funding": [ { "type": "github", @@ -2113,6 +2698,11 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -2164,7 +2754,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2219,6 +2808,23 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "engines": { + "node": ">= 6" + } + }, "node_modules/component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -2273,8 +2879,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "node_modules/concat-stream": { "version": "1.6.2", @@ -2392,6 +2997,24 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/core-js-pure": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.33.1.tgz", + "integrity": "sha512-wCXGbLjnsP10PlK/thHSQlOLlLKNEkaWbTzVvHHZ79fZNeN1gUmw2gBlpItxPv/pvqldevEXFh/d5stdNvl6EQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -2413,7 +3036,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2423,6 +3045,16 @@ "node": ">= 8" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2431,6 +3063,21 @@ "ms": "2.0.0" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -2445,11 +3092,18 @@ } } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2488,6 +3142,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -2516,6 +3179,22 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dompurify": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", + "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" + }, "node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -2524,6 +3203,14 @@ "node": ">=12" } }, + "node_modules/drange": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", + "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", + "engines": { + "node": ">=4" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -2574,6 +3261,15 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/engine.io": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", @@ -2754,6 +3450,14 @@ "node": ">=4" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2807,6 +3511,15 @@ "node": ">= 0.8.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/expect": { "version": "29.6.4", "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", @@ -3174,6 +3887,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3186,6 +3904,18 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "dev": true }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -3204,7 +3934,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -3242,6 +3971,14 @@ "node": ">=8" } }, + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dependencies": { + "micromatch": "^4.0.2" + } + }, "node_modules/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -3279,6 +4016,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/formidable": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", @@ -3330,11 +4075,30 @@ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "optional": true + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/fsevents": { "version": "2.3.2", @@ -3415,11 +4179,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "optional": true + }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3459,8 +4228,7 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/handlebars": { "version": "4.7.7", @@ -3515,7 +4283,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -3531,6 +4298,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/helmet": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.1.3.tgz", @@ -3548,6 +4340,14 @@ "node": ">=8" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/hoek": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", @@ -3557,6 +4357,19 @@ "node": ">=8.9.0" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3622,7 +4435,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -3644,6 +4456,14 @@ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, + "node_modules/immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -3681,7 +4501,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3692,6 +4511,20 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -3706,6 +4539,28 @@ "node": ">= 0.10" } }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -3758,6 +4613,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3797,15 +4675,31 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -3817,6 +4711,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -3836,8 +4741,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", @@ -3864,21 +4768,6 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -4423,21 +5312,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jest-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", @@ -4547,11 +5421,15 @@ "node": ">=8.9.0" } }, + "node_modules/js-file-download": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.12.tgz", + "integrity": "sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "3.14.1", @@ -4584,6 +5462,17 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-stable-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz", + "integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==", + "dependencies": { + "jsonify": "^0.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -4596,6 +5485,25 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -4646,6 +5554,14 @@ "integrity": "sha512-aJ9opVoXroQUPfovYP5kaj2lM7Jn02Gw13bL0lg9v0V7SaUc0qavPs0Eue7d2DcC3NjqI6QAUElXNsuZSeM+EA==", "dev": true }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -4781,11 +5697,31 @@ "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", "integrity": "sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, "node_modules/logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -4804,6 +5740,30 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4830,21 +5790,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -4891,7 +5836,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -4939,6 +5883,29 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minim": { + "version": "0.23.8", + "resolved": "https://registry.npmjs.org/minim/-/minim-0.23.8.tgz", + "integrity": "sha512-bjdr2xW1dBCMsMGGsUeqM4eFI60m94+szhxWys+B1ztIt6gWSfeGBdSVCIawezeHYLYn0j6zrsXdQS/JllBzww==", + "dependencies": { + "lodash": "^4.15.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -4949,7 +5916,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4976,6 +5942,12 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "optional": true + }, "node_modules/module-alias": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", @@ -5131,9 +6103,15 @@ } }, "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "optional": true + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "optional": true }, "node_modules/natural-compare": { @@ -5155,6 +6133,57 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/node-abi": { + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.51.0.tgz", + "integrity": "sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "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-commonjs": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch-commonjs/-/node-fetch-commonjs-3.3.2.tgz", + "integrity": "sha512-VBlAiynj3VMLrotgwOS3OyECFxas5y7ltLcK4t41lMUZeaK15Ym4QRkqN0EQKAFL42q9i21EPKjzLUPfltR72A==", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5378,6 +6407,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "peer": true + }, "node_modules/optional-require": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", @@ -5394,6 +6444,14 @@ "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==" }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5445,6 +6503,23 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -5534,8 +6609,53 @@ "node": ">=8.10.0" } }, - "node_modules/path": { - "version": "0.12.7", + "node_modules/patch-package": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^2.2.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=14", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/patch-package/node_modules/yaml": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/path": { + "version": "0.12.7", "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", "dependencies": { @@ -5556,7 +6676,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5565,7 +6684,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -5596,7 +6714,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -5636,6 +6753,32 @@ "node": ">=8" } }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/precond": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", @@ -5670,6 +6813,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -5701,6 +6852,33 @@ "node": ">= 6" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -5724,6 +6902,16 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -5762,6 +6950,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/ramda": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", + "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/ramda-adjunct": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ramda-adjunct/-/ramda-adjunct-4.1.1.tgz", + "integrity": "sha512-BnCGsZybQZMDGram9y7RiryoRHS5uwx8YeGuUeDKuZuvK38XO6JJfmK85BwRWAKFA6pZ5nZBO/HBFtExVaf31w==", + "engines": { + "node": ">=0.10.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda-adjunct" + }, + "peerDependencies": { + "ramda": ">= 0.29.0" + } + }, + "node_modules/randexp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", + "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", + "dependencies": { + "drange": "^1.0.2", + "ret": "^0.2.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -5771,6 +7000,14 @@ "node": ">= 0.8" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -5793,11 +7030,165 @@ "node": ">= 0.8" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-copy-to-clipboard": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", + "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", + "dependencies": { + "copy-to-clipboard": "^3.3.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^15.3.0 || 16 || 17 || 18" + } + }, + "node_modules/react-debounce-input": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.3.0.tgz", + "integrity": "sha512-VEqkvs8JvY/IIZvh71Z0TC+mdbxERvYF33RcebnodlsUZ8RSgyKe2VWaHXv4+/8aoOgXLxWrdsYs2hDhcwbUgA==", + "dependencies": { + "lodash.debounce": "^4", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^15.3.0 || 16 || 17 || 18" + } + }, + "node_modules/react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/react-immutable-proptypes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.2.0.tgz", + "integrity": "sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ==", + "dependencies": { + "invariant": "^2.2.2" + }, + "peerDependencies": { + "immutable": ">=3.6.2" + } + }, + "node_modules/react-immutable-pure-component": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-immutable-pure-component/-/react-immutable-pure-component-2.2.2.tgz", + "integrity": "sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A==", + "peerDependencies": { + "immutable": ">= 2 || >= 4.0.0-rc", + "react": ">= 16.6", + "react-dom": ">= 16.6" + } + }, + "node_modules/react-inspector": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.2.tgz", + "integrity": "sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==", + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-syntax-highlighter": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", + "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.27.0", + "refractor": "^3.6.0" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } }, "node_modules/readable-stream": { "version": "2.3.8", @@ -5838,6 +7229,72 @@ "@redis/time-series": "1.0.5" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz", + "integrity": "sha512-SchSn/DWfGb3oAejd+1hhHx01xUoxY+V7TeK0BKqpkLKiQPVFf7DYzEaKmrEVxsWxielKfSK9/Xq66YyxgR1cg==", + "peerDependencies": { + "immutable": "^3.8.1 || ^4.0.0-rc.1" + } + }, + "node_modules/refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "node_modules/remarkable": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-2.0.1.tgz", + "integrity": "sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==", + "dependencies": { + "argparse": "^1.0.10", + "autolinker": "^3.11.0" + }, + "bin": { + "remarkable": "bin/remarkable.js" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -5855,6 +7312,16 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "node_modules/resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", @@ -5902,6 +7369,25 @@ "node": ">=10" } }, + "node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -5932,10 +7418,19 @@ "node": ">=6" } }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5974,6 +7469,31 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -5993,11 +7513,22 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -6009,11 +7540,19 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } }, + "node_modules/short-unique-id": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.0.3.tgz", + "integrity": "sha512-yhniEILouC0s4lpH0h7rJsfylZdca10W9mDJRAFh3EpcSUanCHGb0R7kcFOIUCZYSAPo0PUD5ZxWQdW0T4xaug==", + "bin": { + "short-unique-id": "bin/short-unique-id", + "suid": "bin/short-unique-id" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -6039,6 +7578,51 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -6278,6 +7862,15 @@ "source-map": "^0.6.0" } }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -6290,8 +7883,7 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/ssha": { "version": "1.0.1", @@ -6321,6 +7913,11 @@ "node": ">=10" } }, + "node_modules/stampit": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stampit/-/stampit-4.3.2.tgz", + "integrity": "sha512-pE2org1+ZWQBnIxRPrBM2gVupkuDD0TTNIo1H6GdT/vO82NXli2z8lRE8cu/nBIHrcOCXFBAHpb9ZldrB2/qOA==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -6495,31 +8092,177 @@ "superagent": "^8.0.5" }, "engines": { - "node": ">=6.4.0" + "node": ">=6.4.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swagger-client": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.23.1.tgz", + "integrity": "sha512-ecRJsoGozhGvEUmim2kIc/pH9BllnPVuajuEXVm49EDbwbwbp7P+i5EW+8w5FLaqmGrx9eio51G9bvJV/XC+YQ==", + "dependencies": { + "@babel/runtime-corejs3": "^7.22.15", + "@swagger-api/apidom-core": ">=0.77.0 <1.0.0", + "@swagger-api/apidom-json-pointer": ">=0.77.0 <1.0.0", + "@swagger-api/apidom-ns-openapi-3-1": ">=0.77.0 <1.0.0", + "@swagger-api/apidom-reference": ">=0.77.0 <1.0.0", + "cookie": "~0.5.0", + "deepmerge": "~4.3.0", + "fast-json-patch": "^3.0.0-1", + "is-plain-object": "^5.0.0", + "js-yaml": "^4.1.0", + "node-abort-controller": "^3.1.1", + "node-fetch-commonjs": "^3.3.1", + "qs": "^6.10.2", + "traverse": "~0.6.6", + "undici": "^5.24.0" + } + }, + "node_modules/swagger-client/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/swagger-client/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "dependencies": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "bin": { + "swagger-jsdoc": "bin/swagger-jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", "dependencies": { - "has-flag": "^4.0.0" + "@apidevtools/swagger-parser": "10.0.3" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, + "node_modules/swagger-ui": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/swagger-ui/-/swagger-ui-5.9.0.tgz", + "integrity": "sha512-x+FB8V7RtFaXdwWx0dNbI1nqaDCQI1yhJ5Db0obh8Fu3zr832VEXLbMi9hixQCRWv7FcbWy0baQA0x/4oHhqyw==", + "hasInstallScript": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.23.1", + "@braintree/sanitize-url": "=6.0.4", + "base64-js": "^1.5.1", + "classnames": "^2.3.1", + "css.escape": "1.5.1", + "deep-extend": "0.6.0", + "dompurify": "=3.0.6", + "ieee754": "^1.2.1", + "immutable": "^3.x.x", + "js-file-download": "^0.4.12", + "js-yaml": "=4.1.0", + "lodash": "^4.17.21", + "patch-package": "^8.0.0", + "prop-types": "^15.8.1", + "randexp": "^0.5.3", + "randombytes": "^2.1.0", + "react": "=17.0.2", + "react-copy-to-clipboard": "5.1.0", + "react-debounce-input": "=3.3.0", + "react-dom": "=17.0.2", + "react-immutable-proptypes": "2.2.0", + "react-immutable-pure-component": "^2.2.0", + "react-inspector": "^6.0.1", + "react-redux": "^8.1.2", + "react-syntax-highlighter": "^15.5.0", + "redux": "^4.1.2", + "redux-immutable": "^4.0.0", + "remarkable": "^2.0.1", + "reselect": "^4.1.8", + "serialize-error": "^8.1.0", + "sha.js": "^2.4.11", + "swagger-client": "^3.22.3", + "url-parse": "^1.5.10", + "xml": "=1.0.1", + "xml-but-prettier": "^1.0.1", + "zenscroll": "^4.0.2" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.0.tgz", + "integrity": "sha512-NUHSYoe5XRTk/Are8jPJ6phzBh3l9l33nEyXosM17QInoV95/jng8+PuSGtbD407QoPf93MH3Bkh773OgesJpA==" + }, + "node_modules/swagger-ui-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", + "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">= v0.10.32" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, + "node_modules/swagger-ui/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/swagger-ui/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, "node_modules/tail": { @@ -6530,6 +8273,59 @@ "node": ">= 6.0.0" } }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6549,6 +8345,17 @@ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -6573,7 +8380,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -6581,6 +8387,11 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -6628,11 +8439,72 @@ "node": ">=12" } }, + "node_modules/traverse": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", + "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tree-sitter": { + "version": "0.20.4", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.20.4.tgz", + "integrity": "sha512-rjfR5dc4knG3jnJNN/giJ9WOoN1zL/kZyrS0ILh+eqq8RNcIbiXA63JsMEgluug0aNvfQvK4BfCErN1vIzvKog==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.17.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/tree-sitter-json": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/tree-sitter-json/-/tree-sitter-json-0.20.1.tgz", + "integrity": "sha512-482hf7J+aBwhksSw8yWaqI8nyP1DrSwnS4IMBShsnkFWD3SE8oalHnsEik59fEVi3orcTCUtMzSjZx+0Tpa6Vw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.18.0" + } + }, + "node_modules/tree-sitter-yaml": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/tree-sitter-yaml/-/tree-sitter-yaml-0.5.0.tgz", + "integrity": "sha512-POJ4ZNXXSWIG/W4Rjuyg36MkUD4d769YRUGKRqN+sVaj/VCo6Dh6Pkssn1Rtewd5kybx+jT1BWMyWN0CijXnMA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.14.0" + } + }, "node_modules/triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "node_modules/ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==" + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -6671,6 +8543,14 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "node_modules/types-ramda": { + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.5.tgz", + "integrity": "sha512-u+bAYXHDPJR+amB0qMrMU/NXRB2PG8QqpO2v6j7yK/0mPZhlaaZj++ynYjnVpkPEpCkZEGxNpWY3X7qyLCGE3w==", + "dependencies": { + "ts-toolbelt": "^9.6.0" + } + }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -6706,6 +8586,25 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==" }, + "node_modules/undici": { + "version": "5.26.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.26.4.tgz", + "integrity": "sha512-OG+QOf0fTLtazL9P9X7yqWxQ+Z0395Wk6DSkyTxtaq3wQEjIroVe7Y4asCX/vcCxYpNGMnwz8F0qbRYUoaQVMw==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -6714,6 +8613,11 @@ "node": ">= 0.8" } }, + "node_modules/unraw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unraw/-/unraw-3.0.0.tgz", + "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==" + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", @@ -6744,6 +8648,23 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -6852,6 +8773,20 @@ "makeerror": "1.0.12" } }, + "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" + } + }, + "node_modules/web-tree-sitter": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.20.3.tgz", + "integrity": "sha512-zKGJW9r23y3BcJusbgvnOH2OYAW40MXAOi9bi3Gcc7T4Gms9WWgXF8m6adsJWpGJEhgOzCrfiz1IzKowJWrtYw==", + "optional": true + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -6878,7 +8813,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -7078,6 +9012,19 @@ } } }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==" + }, + "node_modules/xml-but-prettier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-but-prettier/-/xml-but-prettier-1.0.1.tgz", + "integrity": "sha512-C2CJaadHrZTqESlH03WOyw0oZTtoy2uEg6dSDF6YRg+9GnYNub53RRemLpnvtbHDFelxMx4LajiFsYeR6XJHgQ==", + "dependencies": { + "repeat-string": "^1.5.2" + } + }, "node_modules/xmlhttprequest-ssl": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", @@ -7108,6 +9055,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", + "engines": { + "node": ">= 6" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -7151,6 +9106,39 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/zenscroll": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zenscroll/-/zenscroll-4.0.2.tgz", + "integrity": "sha512-jEA1znR7b4C/NnaycInCU6h/d15ZzCd1jmsruqOKnZP6WXQSMH3W2GL+OXbkruslU4h+Tzuos0HdswzRUk/Vgg==" } }, "dependencies": { @@ -7164,6 +9152,55 @@ "@jridgewell/trace-mapping": "^0.3.9" } }, + "@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "requires": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==" + }, + "@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "requires": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + } + }, "@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -7639,6 +9676,23 @@ "@babel/helper-plugin-utils": "^7.22.5" } }, + "@babel/runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "@babel/runtime-corejs3": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.2.tgz", + "integrity": "sha512-54cIh74Z1rp4oIjsHjqN+WM4fMyCBYe+LpZ9jWm51CZ1fbH3SkAzQD/3XLoNkjbJ7YEmjobLXyvQrFypRHOrXw==", + "requires": { + "core-js-pure": "^3.30.2", + "regenerator-runtime": "^0.14.0" + } + }, "@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -7702,6 +9756,11 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" + }, "@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -7717,6 +9776,11 @@ "kuler": "^2.0.0" } }, + "@fastify/busboy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", + "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==" + }, "@hapi/boom": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", @@ -8008,6 +10072,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, "@ldapjs/asn1": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-2.0.0.tgz", @@ -8156,6 +10225,374 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, + "@swagger-api/apidom-ast": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-0.78.0.tgz", + "integrity": "sha512-mEXmRmkFlmO6dcBuakFkc2gevN4mC6incPAQE1UciaX4hLuJpiv/5DTH9gVWTR0CWUFw/dXROTD/x6ETV0y03A==", + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2", + "unraw": "^3.0.0" + } + }, + "@swagger-api/apidom-core": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-0.78.0.tgz", + "integrity": "sha512-Qx9m+1u6H4Bsa38s73ANtGn8zFGqK0peguM+SFuUR5HirjpoFB8JB7IG5E8+ymUlpWhlU43q9QnJjcaYJw9MTg==", + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "minim": "~0.23.8", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "short-unique-id": "^5.0.2", + "stampit": "^4.3.2" + } + }, + "@swagger-api/apidom-error": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-0.78.0.tgz", + "integrity": "sha512-P0enIK3XymxCPHlhGtqc4TU5H+cHf7L0yDFmfjZEcsjDzGDv5A+m5tf429Pr/R+e51DzpT5/xIcPKTnti0gIOw==", + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-json-pointer": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-0.78.0.tgz", + "integrity": "sha512-Ly4ZfUGxxbNoHHc9vR814mU96ZLGsjaJflCW0jdZnMVfVv20fDCoDoOOmXat6ajxUbS2YKimgxPvdBth3K/CRQ==", + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-ns-api-design-systems": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-0.78.0.tgz", + "integrity": "sha512-WoWE6w1P3qsokG3Qyc5F3xpz+e/WablE0EHGSgiYxk+MQJLqYmz5UhS5LxYGT9d6o9XUs24ykSbKrYWYwkpp4w==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-1": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "@swagger-api/apidom-ns-asyncapi-2": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-0.78.0.tgz", + "integrity": "sha512-QWZohCtXf5UX/I9bnc4MQh16X9jGPdGrByWM93xRvh8X8rIF0BtF9S7lIx028aX3AHYIu4SwYr7JZlqEaZ92Kw==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-json-schema-draft-7": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "@swagger-api/apidom-ns-json-schema-draft-4": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-0.78.0.tgz", + "integrity": "sha512-19NR9lTHMOQTIEV4tJq+FlHQAYnjyH+DgI4mmRu6UMFSZjRjutYF7B8lCGogSus9Uwy8YpUk00prLFTld00wgA==", + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-core": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "@swagger-api/apidom-ns-json-schema-draft-6": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-0.78.0.tgz", + "integrity": "sha512-pHyCPU3OWDiPuLepo03rBpi2n+SCH6PZAgguqAB3lDJ2ymitrT2SNpmZ6CcHvPGR9Y7h4/fR5vAypVZfdNr/WQ==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-ns-json-schema-draft-4": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "@swagger-api/apidom-ns-json-schema-draft-7": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-0.78.0.tgz", + "integrity": "sha512-ScUiNNAdwnikH3Fo2rUsDmXOjV7zXfQ6CGE+QkY5Wj3t1M6siw2HpDjrBaaCyp6w/bemvogsh280GrzAnxKLIw==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-ns-json-schema-draft-6": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "@swagger-api/apidom-ns-openapi-3-0": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-0.78.0.tgz", + "integrity": "sha512-GRmUOknEzMG37y5sStvjEsk30RLVg5E7iZuougK1rEf+wzzX5XhorSgMx2NQmka5rb814BgzyiqGRmvKQErDBw==", + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-ns-json-schema-draft-4": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "@swagger-api/apidom-ns-openapi-3-1": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-0.78.0.tgz", + "integrity": "sha512-hHpUZLjIiaLK+99cAPiYNV9QzZQxFoMLqBNYo+GQwqizaVOjxQRi5y/hPkfFALqqufZ1L6XWeyjQrtli0ftqBQ==", + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + } + }, + "@swagger-api/apidom-parser-adapter-api-design-systems-json": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-0.78.0.tgz", + "integrity": "sha512-g7VlfOrpTzbVV30Ugab0qAJITavLo39apvyFFv2cN2jfuIQa8MlzDP0mZmVtCGQy3IoT4Auns/qWeGcZX0li9w==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-api-design-systems": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-0.78.0.tgz", + "integrity": "sha512-ZueYoHOJARRm84ntCggUZLKNwUHz2U0eG9KHIzw75UW43pyvQVbxAE2ELdyP5f8vr51wMuMp6XYRcFOsNi/oeQ==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-api-design-systems": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-0.78.0.tgz", + "integrity": "sha512-Jm0hbNXWOH2QJIiF+5QgY+ioVSOBqV3WlhTeyrF5kSxHinah16nR1jUkz5tMsSc9sxTZHzWYVLneyBMW3VSHrw==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-asyncapi-2": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-0.78.0.tgz", + "integrity": "sha512-zpP8gQBXhrR/t91Z/Jl0nD/cUSzmYjzhE5qWHkfhbGvzaWatiLrNY+CnFS9RcgF4pb2LSqS5cjDVAExBbjdLdQ==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-asyncapi-2": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-parser-adapter-json": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-0.78.0.tgz", + "integrity": "sha512-d/8gFj5cc+pnCo7ORGN5dJPGWzTleYkIwGfsyFuLZNjb4KlrOrKlPl0LKQ/t7MSEbVpSStxbgezoUtfdVhGscw==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2", + "tree-sitter": "=0.20.4", + "tree-sitter-json": "=0.20.1", + "web-tree-sitter": "=0.20.3" + } + }, + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-0.78.0.tgz", + "integrity": "sha512-MjXkPAiEyTZIljzjEgvAmqaZel0jpKBBqdtC8nWH/9C2ugkKHetKMSgYu+5wvFh//ixJZZE7dM1QHEIBoPl9nA==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-0.78.0.tgz", + "integrity": "sha512-k+rT6kwu1jAN1lYIP1wVshQdaLu9M+jjCfpvMXXkL/2VpZqq1yP6daFm0ExiHllVUcHWeqRXhubFV3wWkFm6eA==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-0.78.0.tgz", + "integrity": "sha512-RzcqL0kvUl5G75H4qOFSi9FTaVfBtRnjzEcjd8SOKVLg3JJsCv3vrk68laRm8HXocyWgGstU51UzBqkMStXy4A==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-0.78.0.tgz", + "integrity": "sha512-1hB+mcEJd14RJC8lH3yJsoQRDhA8TNNKl3EyQ17eFY0dK29JlluDEbDHIRQpLT1l2jCK/NfqAk2hc37yIwydfw==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.0.0" + } + }, + "@swagger-api/apidom-parser-adapter-yaml-1-2": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-0.78.0.tgz", + "integrity": "sha512-L37X+nRNp+2PyJkAwMdSQjP8tb3xoc6FVk2QXLHogghe1Phrmfaal3TPu2rWJNn7NSBcvSyiTAR7gEIULitugA==", + "optional": true, + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-ast": "^0.78.0", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@types/ramda": "~0.29.6", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2", + "tree-sitter": "=0.20.4", + "tree-sitter-yaml": "=0.5.0", + "web-tree-sitter": "=0.20.3" + } + }, + "@swagger-api/apidom-reference": { + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-0.78.0.tgz", + "integrity": "sha512-IiOaMgy+CzpQe5fFwyge4B/lkHQnBhiuNGPgIJELYXJMZle+pN6K/V4muLCG6JjAXllucbCqMpW/KLmPxGAXaw==", + "requires": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^0.78.0", + "@swagger-api/apidom-error": "^0.78.0", + "@swagger-api/apidom-json-pointer": "^0.78.0", + "@swagger-api/apidom-ns-asyncapi-2": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.78.0", + "@swagger-api/apidom-ns-openapi-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^0.78.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^0.78.0", + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^0.78.0", + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^0.78.0", + "@swagger-api/apidom-parser-adapter-json": "^0.78.0", + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^0.78.0", + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "^0.78.0", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "^0.78.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.78.0", + "@types/ramda": "~0.29.6", + "axios": "^1.4.0", + "minimatch": "^7.4.3", + "process": "^0.11.10", + "ramda": "~0.29.0", + "ramda-adjunct": "^4.1.1", + "stampit": "^4.3.2" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "@types/babel__core": { "version": "7.20.1", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", @@ -8219,6 +10656,23 @@ "@types/node": "*" } }, + "@types/hast": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.7.tgz", + "integrity": "sha512-EVLigw5zInURhzfXUM65eixfadfsHKomGKUakToXo84t8gGIJuTcD2xooM2See7GyQ7DRtYjhCHnSUQez8JaLw==", + "requires": { + "@types/unist": "^2" + } + }, + "@types/hoist-non-react-statics": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.4.tgz", + "integrity": "sha512-ZchYkbieA+7tnxwX/SCBySx9WwvWR8TaP5tb2jRAzwvLb/rWchGw3v0w3pqUbUvj0GCwW2Xz/AVPSk6kUGctXQ==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -8243,11 +10697,44 @@ "@types/istanbul-lib-report": "*" } }, + "@types/json-schema": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==" + }, "@types/node": { "version": "17.0.28", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.28.tgz", "integrity": "sha512-UYmIeBnB0On70dN1iGCinsL1qH5JmIEJwa+3KX0Xw4HQJ8KA16ULlyTCNmnzfyzj/BlxZKmZLqp4TYdssnov1w==" }, + "@types/prop-types": { + "version": "15.7.9", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz", + "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==" + }, + "@types/ramda": { + "version": "0.29.7", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.29.7.tgz", + "integrity": "sha512-IUl6U95qwlQtVvZkSX4ODj08oJVtPyWMFRtPVNqhxc2rt+Bh7lCzTrGMYMZ7dmRKcAjtot3xrPnYGwsjdt8gzQ==", + "requires": { + "types-ramda": "^0.29.5" + } + }, + "@types/react": { + "version": "18.2.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.31.tgz", + "integrity": "sha512-c2UnPv548q+5DFh03y8lEDeMfDwBn9G3dRwfkrxQMo/dOtRHUUO57k6pHvBIfH/VF4Nh+98mZ5aaSe+2echD5g==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/scheduler": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.5.tgz", + "integrity": "sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==" + }, "@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -8259,6 +10746,16 @@ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" }, + "@types/unist": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.9.tgz", + "integrity": "sha512-zC0iXxAv1C1ERURduJueYzkzZ2zaGyc+P2c95hgkikHPr3z8EdUZOlgEQ5X0DRmwDZn+hekycQnoeiiRVrmilQ==" + }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "@types/webidl-conversions": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", @@ -8290,6 +10787,11 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -8334,7 +10836,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -8358,7 +10859,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -8414,10 +10914,23 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "autolinker": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.16.2.tgz", + "integrity": "sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==", + "requires": { + "tslib": "^2.3.0" + } + }, "axios": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz", - "integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -8540,8 +11053,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-arraybuffer": { "version": "0.1.4", @@ -8551,8 +11063,7 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "base64id": { "version": "2.0.0", @@ -8616,7 +11127,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8626,7 +11136,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -8665,7 +11174,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, + "devOptional": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -8703,6 +11212,11 @@ "get-intrinsic": "^1.0.2" } }, + "call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -8725,7 +11239,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8737,6 +11250,21 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -8753,11 +11281,16 @@ "readdirp": "~3.6.0" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true + }, "ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==" }, "cjs-module-lexer": { "version": "1.2.3", @@ -8765,6 +11298,11 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -8821,7 +11359,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -8857,6 +11394,16 @@ "delayed-stream": "~1.0.0" } }, + "comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" + }, + "commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==" + }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -8904,8 +11451,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", @@ -8987,6 +11533,19 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, + "copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, + "core-js-pure": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.33.1.tgz", + "integrity": "sha512-wCXGbLjnsP10PlK/thHSQlOLlLKNEkaWbTzVvHHZ79fZNeN1gUmw2gBlpItxPv/pvqldevEXFh/d5stdNvl6EQ==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -9005,13 +11564,22 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + }, + "csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -9020,6 +11588,15 @@ "ms": "2.0.0" } }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "optional": true, + "requires": { + "mimic-response": "^3.1.0" + } + }, "dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -9027,11 +11604,15 @@ "dev": true, "requires": {} }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, "deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" }, "delayed-stream": { "version": "1.0.0", @@ -9054,6 +11635,12 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "optional": true + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -9076,11 +11663,29 @@ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "dompurify": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz", + "integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==" + }, "dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" }, + "drange": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", + "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==" + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -9122,6 +11727,15 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "optional": true, + "requires": { + "once": "^1.4.0" + } + }, "engine.io": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", @@ -9248,6 +11862,11 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -9285,6 +11904,12 @@ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "optional": true + }, "expect": { "version": "29.6.4", "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", @@ -9566,6 +12191,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==" + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -9578,6 +12208,14 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "dev": true }, + "fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "requires": { + "format": "^0.2.0" + } + }, "fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -9596,7 +12234,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -9625,6 +12262,14 @@ "path-exists": "^4.0.0" } }, + "find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "requires": { + "micromatch": "^4.0.2" + } + }, "fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -9645,6 +12290,11 @@ "mime-types": "^2.1.12" } }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" + }, "formidable": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", @@ -9683,11 +12333,27 @@ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "optional": true + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.3.2", @@ -9740,11 +12406,16 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "optional": true + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -9772,8 +12443,7 @@ "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "handlebars": { "version": "4.7.7", @@ -9818,14 +12488,30 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, + "hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" + }, + "hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "requires": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + } + }, "helmet": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.1.3.tgz", @@ -9837,11 +12523,31 @@ "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", "dev": true }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, "hoek": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -9891,8 +12597,7 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore-by-default": { "version": "1.0.1", @@ -9900,6 +12605,11 @@ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, + "immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==" + }, "import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -9925,7 +12635,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -9936,6 +12645,20 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -9947,6 +12670,20 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -9976,6 +12713,16 @@ "has": "^1.0.3" } }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -10003,17 +12750,34 @@ "is-extglob": "^2.1.1" } }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -10030,8 +12794,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "istanbul-lib-coverage": { "version": "3.2.0", @@ -10050,17 +12813,6 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "istanbul-lib-report": { @@ -10478,17 +13230,6 @@ "natural-compare": "^1.4.0", "pretty-format": "^29.6.3", "semver": "^7.5.3" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "jest-util": { @@ -10576,11 +13317,15 @@ "topo": "3.x.x" } }, + "js-file-download": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.12.tgz", + "integrity": "sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.14.1", @@ -10604,12 +13349,34 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "json-stable-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz", + "integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==", + "requires": { + "jsonify": "^0.0.1" + } + }, "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==" + }, "jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -10658,6 +13425,14 @@ "integrity": "sha512-aJ9opVoXroQUPfovYP5kaj2lM7Jn02Gw13bL0lg9v0V7SaUc0qavPs0Eue7d2DcC3NjqI6QAUElXNsuZSeM+EA==", "dev": true }, + "klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "requires": { + "graceful-fs": "^4.1.11" + } + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -10773,11 +13548,31 @@ "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", "integrity": "sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==" }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.isobject": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==" }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, "logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -10798,6 +13593,23 @@ } } }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "requires": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -10813,17 +13625,6 @@ "dev": true, "requires": { "semver": "^7.5.3" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "makeerror": { @@ -10866,7 +13667,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "requires": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -10896,6 +13696,20 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "optional": true + }, + "minim": { + "version": "0.23.8", + "resolved": "https://registry.npmjs.org/minim/-/minim-0.23.8.tgz", + "integrity": "sha512-bjdr2xW1dBCMsMGGsUeqM4eFI60m94+szhxWys+B1ztIt6gWSfeGBdSVCIawezeHYLYn0j6zrsXdQS/JllBzww==", + "requires": { + "lodash": "^4.15.0" + } + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -10906,7 +13720,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -10924,6 +13737,12 @@ "minimist": "^1.2.6" } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "optional": true + }, "module-alias": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", @@ -11050,9 +13869,15 @@ } }, "nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "optional": true + }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "optional": true }, "natural-compare": { @@ -11071,6 +13896,34 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node-abi": { + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.51.0.tgz", + "integrity": "sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==", + "optional": true, + "requires": { + "semver": "^7.3.5" + } + }, + "node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "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==" + }, + "node-fetch-commonjs": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch-commonjs/-/node-fetch-commonjs-3.3.2.tgz", + "integrity": "sha512-VBlAiynj3VMLrotgwOS3OyECFxas5y7ltLcK4t41lMUZeaK15Ym4QRkqN0EQKAFL42q9i21EPKjzLUPfltR72A==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -11244,6 +14097,21 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "peer": true + }, "optional-require": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", @@ -11257,6 +14125,11 @@ "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==" }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -11292,6 +14165,19 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -11356,6 +14242,40 @@ "resolved": "https://registry.npmjs.org/password-validator/-/password-validator-5.3.0.tgz", "integrity": "sha512-Q+bSEM5pjokZqzWGoQaoylkeWeH4+9uMYlVImiPD0EOJClQ2RPBhrJ5h0OjhMKtwOmu5rRcLaTZo5Gk9RBl0ig==" }, + "patch-package": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "requires": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^2.2.2" + }, + "dependencies": { + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + }, + "yaml": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==" + } + } + }, "path": { "version": "0.12.7", "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", @@ -11374,14 +14294,12 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.7", @@ -11407,9 +14325,8 @@ }, "picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "pidusage": { "version": "2.0.18", @@ -11434,6 +14351,26 @@ "find-up": "^4.0.0" } }, + "prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "optional": true, + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + }, "precond": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", @@ -11458,6 +14395,11 @@ } } }, + "prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -11483,6 +14425,31 @@ "sisteransi": "^1.0.5" } }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "requires": { + "xtend": "^4.0.0" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -11503,6 +14470,16 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "optional": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -11522,12 +14499,45 @@ "side-channel": "^1.0.4" } }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "ramda": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", + "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==" + }, + "ramda-adjunct": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ramda-adjunct/-/ramda-adjunct-4.1.1.tgz", + "integrity": "sha512-BnCGsZybQZMDGram9y7RiryoRHS5uwx8YeGuUeDKuZuvK38XO6JJfmK85BwRWAKFA6pZ5nZBO/HBFtExVaf31w==", + "requires": {} + }, + "randexp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", + "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", + "requires": { + "drange": "^1.0.2", + "ret": "^0.2.0" + } + }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -11544,11 +14554,112 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "optional": true + } + } + }, + "react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "react-copy-to-clipboard": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", + "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", + "requires": { + "copy-to-clipboard": "^3.3.1", + "prop-types": "^15.8.1" + } + }, + "react-debounce-input": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.3.0.tgz", + "integrity": "sha512-VEqkvs8JvY/IIZvh71Z0TC+mdbxERvYF33RcebnodlsUZ8RSgyKe2VWaHXv4+/8aoOgXLxWrdsYs2hDhcwbUgA==", + "requires": { + "lodash.debounce": "^4", + "prop-types": "^15.8.1" + } + }, + "react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + } + }, + "react-immutable-proptypes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.2.0.tgz", + "integrity": "sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ==", + "requires": { + "invariant": "^2.2.2" + } + }, + "react-immutable-pure-component": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-immutable-pure-component/-/react-immutable-pure-component-2.2.2.tgz", + "integrity": "sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A==", + "requires": {} + }, + "react-inspector": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.2.tgz", + "integrity": "sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==", + "requires": {} + }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + } + }, + "react-syntax-highlighter": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", + "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", + "requires": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.27.0", + "refractor": "^3.6.0" + } }, "readable-stream": { "version": "2.3.8", @@ -11586,6 +14697,56 @@ "@redis/time-series": "1.0.5" } }, + "redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "redux-immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz", + "integrity": "sha512-SchSn/DWfGb3oAejd+1hhHx01xUoxY+V7TeK0BKqpkLKiQPVFf7DYzEaKmrEVxsWxielKfSK9/Xq66YyxgR1cg==", + "requires": {} + }, + "refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "requires": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "dependencies": { + "prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==" + } + } + }, + "regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "remarkable": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-2.0.1.tgz", + "integrity": "sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==", + "requires": { + "argparse": "^1.0.10", + "autolinker": "^3.11.0" + } + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" + }, "require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -11597,6 +14758,16 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", @@ -11629,6 +14800,19 @@ "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true }, + "ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -11653,10 +14837,19 @@ "sparse-bitfield": "^3.0.3" } }, + "scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -11688,6 +14881,21 @@ } } }, + "serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "requires": { + "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + } + } + }, "serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -11704,11 +14912,19 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -11716,8 +14932,12 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "short-unique-id": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.0.3.tgz", + "integrity": "sha512-yhniEILouC0s4lpH0h7rJsfylZdca10W9mDJRAFh3EpcSUanCHGb0R7kcFOIUCZYSAPo0PUD5ZxWQdW0T4xaug==" }, "side-channel": { "version": "1.0.4", @@ -11741,6 +14961,23 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "optional": true + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "optional": true, + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -11936,6 +15173,11 @@ "source-map": "^0.6.0" } }, + "space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" + }, "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -11948,8 +15190,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "ssha": { "version": "1.0.1", @@ -11970,6 +15211,11 @@ "escape-string-regexp": "^2.0.0" } }, + "stampit": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stampit/-/stampit-4.3.2.tgz", + "integrity": "sha512-pE2org1+ZWQBnIxRPrBM2gVupkuDD0TTNIo1H6GdT/vO82NXli2z8lRE8cu/nBIHrcOCXFBAHpb9ZldrB2/qOA==" + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -12100,7 +15346,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -12111,11 +15356,189 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "swagger-client": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.23.1.tgz", + "integrity": "sha512-ecRJsoGozhGvEUmim2kIc/pH9BllnPVuajuEXVm49EDbwbwbp7P+i5EW+8w5FLaqmGrx9eio51G9bvJV/XC+YQ==", + "requires": { + "@babel/runtime-corejs3": "^7.22.15", + "@swagger-api/apidom-core": ">=0.77.0 <1.0.0", + "@swagger-api/apidom-json-pointer": ">=0.77.0 <1.0.0", + "@swagger-api/apidom-ns-openapi-3-1": ">=0.77.0 <1.0.0", + "@swagger-api/apidom-reference": ">=0.77.0 <1.0.0", + "cookie": "~0.5.0", + "deepmerge": "~4.3.0", + "fast-json-patch": "^3.0.0-1", + "is-plain-object": "^5.0.0", + "js-yaml": "^4.1.0", + "node-abort-controller": "^3.1.1", + "node-fetch-commonjs": "^3.3.1", + "qs": "^6.10.2", + "traverse": "~0.6.6", + "undici": "^5.24.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "requires": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + } + }, + "swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "requires": { + "@apidevtools/swagger-parser": "10.0.3" + } + }, + "swagger-ui": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/swagger-ui/-/swagger-ui-5.9.0.tgz", + "integrity": "sha512-x+FB8V7RtFaXdwWx0dNbI1nqaDCQI1yhJ5Db0obh8Fu3zr832VEXLbMi9hixQCRWv7FcbWy0baQA0x/4oHhqyw==", + "requires": { + "@babel/runtime-corejs3": "^7.23.1", + "@braintree/sanitize-url": "=6.0.4", + "base64-js": "^1.5.1", + "classnames": "^2.3.1", + "css.escape": "1.5.1", + "deep-extend": "0.6.0", + "dompurify": "=3.0.6", + "ieee754": "^1.2.1", + "immutable": "^3.x.x", + "js-file-download": "^0.4.12", + "js-yaml": "=4.1.0", + "lodash": "^4.17.21", + "patch-package": "^8.0.0", + "prop-types": "^15.8.1", + "randexp": "^0.5.3", + "randombytes": "^2.1.0", + "react": "=17.0.2", + "react-copy-to-clipboard": "5.1.0", + "react-debounce-input": "=3.3.0", + "react-dom": "=17.0.2", + "react-immutable-proptypes": "2.2.0", + "react-immutable-pure-component": "^2.2.0", + "react-inspector": "^6.0.1", + "react-redux": "^8.1.2", + "react-syntax-highlighter": "^15.5.0", + "redux": "^4.1.2", + "redux-immutable": "^4.0.0", + "remarkable": "^2.0.1", + "reselect": "^4.1.8", + "serialize-error": "^8.1.0", + "sha.js": "^2.4.11", + "swagger-client": "^3.22.3", + "url-parse": "^1.5.10", + "xml": "=1.0.1", + "xml-but-prettier": "^1.0.1", + "zenscroll": "^4.0.2" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "swagger-ui-dist": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.0.tgz", + "integrity": "sha512-NUHSYoe5XRTk/Are8jPJ6phzBh3l9l33nEyXosM17QInoV95/jng8+PuSGtbD407QoPf93MH3Bkh773OgesJpA==" + }, + "swagger-ui-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", + "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", + "requires": { + "swagger-ui-dist": ">=5.0.0" + } + }, "tail": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/tail/-/tail-2.2.6.tgz", "integrity": "sha512-IQ6G4wK/t8VBauYiGPLx+d3fA5XjSVagjWV5SIYzvEvglbQjwEcukeYI68JOPpdydjxhZ9sIgzRlSmwSpphHyw==" }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "optional": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "optional": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "optional": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -12132,6 +15555,14 @@ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -12153,11 +15584,15 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -12196,11 +15631,63 @@ "punycode": "^2.1.1" } }, + "traverse": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", + "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==" + }, + "tree-sitter": { + "version": "0.20.4", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.20.4.tgz", + "integrity": "sha512-rjfR5dc4knG3jnJNN/giJ9WOoN1zL/kZyrS0ILh+eqq8RNcIbiXA63JsMEgluug0aNvfQvK4BfCErN1vIzvKog==", + "optional": true, + "requires": { + "nan": "^2.17.0", + "prebuild-install": "^7.1.1" + } + }, + "tree-sitter-json": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/tree-sitter-json/-/tree-sitter-json-0.20.1.tgz", + "integrity": "sha512-482hf7J+aBwhksSw8yWaqI8nyP1DrSwnS4IMBShsnkFWD3SE8oalHnsEik59fEVi3orcTCUtMzSjZx+0Tpa6Vw==", + "optional": true, + "requires": { + "nan": "^2.18.0" + } + }, + "tree-sitter-yaml": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/tree-sitter-yaml/-/tree-sitter-yaml-0.5.0.tgz", + "integrity": "sha512-POJ4ZNXXSWIG/W4Rjuyg36MkUD4d769YRUGKRqN+sVaj/VCo6Dh6Pkssn1Rtewd5kybx+jT1BWMyWN0CijXnMA==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "optional": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -12227,6 +15714,14 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "types-ramda": { + "version": "0.29.5", + "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.5.tgz", + "integrity": "sha512-u+bAYXHDPJR+amB0qMrMU/NXRB2PG8QqpO2v6j7yK/0mPZhlaaZj++ynYjnVpkPEpCkZEGxNpWY3X7qyLCGE3w==", + "requires": { + "ts-toolbelt": "^9.6.0" + } + }, "uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -12253,11 +15748,29 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==" }, + "undici": { + "version": "5.26.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.26.4.tgz", + "integrity": "sha512-OG+QOf0fTLtazL9P9X7yqWxQ+Z0395Wk6DSkyTxtaq3wQEjIroVe7Y4asCX/vcCxYpNGMnwz8F0qbRYUoaQVMw==", + "requires": { + "@fastify/busboy": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "unraw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unraw/-/unraw-3.0.0.tgz", + "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==" + }, "update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", @@ -12268,6 +15781,21 @@ "picocolors": "^1.0.0" } }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -12361,6 +15889,17 @@ "makeerror": "1.0.12" } }, + "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==" + }, + "web-tree-sitter": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.20.3.tgz", + "integrity": "sha512-zKGJW9r23y3BcJusbgvnOH2OYAW40MXAOi9bi3Gcc7T4Gms9WWgXF8m6adsJWpGJEhgOzCrfiz1IzKowJWrtYw==", + "optional": true + }, "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -12381,7 +15920,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -12509,6 +16047,19 @@ "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", "requires": {} }, + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==" + }, + "xml-but-prettier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-but-prettier/-/xml-but-prettier-1.0.1.tgz", + "integrity": "sha512-C2CJaadHrZTqESlH03WOyw0oZTtoy2uEg6dSDF6YRg+9GnYNub53RRemLpnvtbHDFelxMx4LajiFsYeR6XJHgQ==", + "requires": { + "repeat-string": "^1.5.2" + } + }, "xmlhttprequest-ssl": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", @@ -12530,6 +16081,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==" + }, "yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -12561,6 +16117,30 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "requires": { + "commander": "^9.4.1", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "dependencies": { + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true + } + } + }, + "zenscroll": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zenscroll/-/zenscroll-4.0.2.tgz", + "integrity": "sha512-jEA1znR7b4C/NnaycInCU6h/d15ZzCd1jmsruqOKnZP6WXQSMH3W2GL+OXbkruslU4h+Tzuos0HdswzRUk/Vgg==" } } } diff --git a/package.json b/package.json index 0bd81b3c..37469b40 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,9 @@ "redis": "^4.6.8", "socket.io": "^4.6.1", "ssha": "^1.0.1", + "swagger-jsdoc": "^6.2.8", + "swagger-ui": "^5.9.0", + "swagger-ui-express": "^5.0.0", "tail": "^2.2.6", "winston": "^3.8.2", "winston-mongodb": "^5.1.1", From 301d69cb3cf9adb8373ad4b11e727168acb6869d Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 17:09:19 -0400 Subject: [PATCH 166/229] swagger added and updating api folder structure --- index.js | 23 +++++---- src/api/v1/routes/routes.js | 19 +++++++ src/api/v1/swagger.js | 51 +++++++++++++++++++ .../dn.controller.js} | 0 .../group.controller.js} | 0 .../profile.controller.js} | 0 .../user.controller.js} | 0 .../logs.controller.js} | 0 .../recovery_password.controller.js} | 0 .../updated_password.controller.js} | 0 src/routes/routes.js | 17 ------- 11 files changed, 84 insertions(+), 26 deletions(-) create mode 100644 src/api/v1/routes/routes.js create mode 100644 src/api/v1/swagger.js rename src/{routes/dn.routes.js => controllers/dn.controller.js} (100%) rename src/{routes/group.routes.js => controllers/group.controller.js} (100%) rename src/{routes/profile.routes.js => controllers/profile.controller.js} (100%) rename src/{routes/user.routes.js => controllers/user.controller.js} (100%) rename src/modules/logsManagement/{routes/logs.routes.js => controllers/logs.controller.js} (100%) rename src/modules/passwordManagement/{routes/recovery_password.routes.js => controllers/recovery_password.controller.js} (100%) rename src/modules/passwordManagement/{routes/updated_password.routes.js => controllers/updated_password.controller.js} (100%) delete mode 100644 src/routes/routes.js diff --git a/index.js b/index.js index 5238767f..8f9791c0 100644 --- a/index.js +++ b/index.js @@ -1,21 +1,24 @@ /* jshint node:true */ /* global require */ require('module-alias/register') -const CONFIG = require('@src/config/config.js') const bodyParser = require('body-parser') const express = require('express') -const addRoutes = require('@src/routes/routes.js') -const sessionMiddleWare = require('@src/middlewares/session.handler.js') -const addLoggerMiddleware = require('@src/middlewares/morganMiddleware') -const ldap_initialization = require('@src/utils/ldap_initialization.js') + const path = require('path') const cors = require('cors') const helmet = require('helmet') -const limiter = require('@src/middlewares/rate_limiter.handler.js') const paginate = require('express-paginate') const compression = require('compression') const cache = require('express-cache-ctrl') + +const CONFIG = require('@src/config/config.js') const logger = require('@src/utils/logger') +const limiter = require('@src/middlewares/rate_limiter.handler.js') +const addRoutes = require('@src/api/v1/routes/routes.js') +const sessionMiddleWare = require('@src/middlewares/session.handler.js') +const addLoggerMiddleware = require('@src/middlewares/morganMiddleware') +const ldap_initialization = require('@src/utils/ldap_initialization.js') +const { swaggerDocs: v1SwaggerDocs } = require('@src/api/v1/swagger.js') //app initialization const app = express() @@ -58,8 +61,10 @@ app.set('~', path.join(__dirname, 'src')) app.use(express.static('./src/public')) // Start server -let port = CONFIG.server.port || 4000 -console.log(`server listen on port ${port}`) -app.listen(port, CONFIG.server.host || '127.0.0.1') +let PORT = CONFIG.server.port || 4000 +app.listen(PORT, () => { + logger.info(`Server started on port ${PORT}`) + v1SwaggerDocs(app, PORT) +}) module.exports = app diff --git a/src/api/v1/routes/routes.js b/src/api/v1/routes/routes.js new file mode 100644 index 00000000..0f883f25 --- /dev/null +++ b/src/api/v1/routes/routes.js @@ -0,0 +1,19 @@ +const userController = require('@src/controllers/user.controller') +const groupController = require('@src/controllers/group.controller') +const profileController = require('@src/controllers/profile.controller') +const recoveryPasswordController = require('@src/modules/passwordManagement/controllers/recovery_password.controller') +const updatePasswordController = require('@src/modules/passwordManagement/controllers/updated_password.controller') +const dnController = require('@src/controllers/dn.controller') +const logsController = require('@src/modules/logsManagement/controllers/logs.controller') + +const addRoutes = (app) => { + app.use('/v1/users', userController) + app.use('/v1/groups', groupController) + app.use('/v1/profile', profileController) + app.use('/v1/recovery-password', recoveryPasswordController) + app.use('/v1/update-password', updatePasswordController) + app.use('/v1/dn', dnController) + app.use('/v1/logs', logsController) +} + +module.exports = addRoutes diff --git a/src/api/v1/swagger.js b/src/api/v1/swagger.js new file mode 100644 index 00000000..b35fa88e --- /dev/null +++ b/src/api/v1/swagger.js @@ -0,0 +1,51 @@ +/* Swagger Imports */ + +const swaggerJsdoc = require('swagger-jsdoc') +const swaggerUi = require('swagger-ui-express') +const config = require('@src/config/config') + +// Swagger definition +const options = { + swaggerDefinition: { + openapi: '3.0.0', + info: { + title: 'LDAP API', + version: '1.0.0', + description: 'LDAP API', + contact: { + name: 'LDAP API', + email: 'ahmediglez@gmail.com', + }, + }, + servers: [ + { + url: 'http://localhost:4005', + }, + ], + }, + apis: ['./routes/*.js', '../connections/LDAP_client.js'], +} + +// Initialize swagger-jsdoc -> returns validated swagger spec in json format +const specs = swaggerJsdoc(options) + +//Function to setup swagger +const swaggerDocs = (app, port) => { + app.use( + '/api/v1/docs', + swaggerUi.serve, + swaggerUi.setup(specs, { explorer: true }) + ) + + app.get('/api/v1/docs.json', (req, res) => { + res.setHeader('Content-Type', 'application/json') + res.send(specs) + }) + + console.log('Swagger initialized') + console.log( + `Swagger docs available at http://localhost:${port}/api/v1/docs` + ) +} + +module.exports = { swaggerDocs } diff --git a/src/routes/dn.routes.js b/src/controllers/dn.controller.js similarity index 100% rename from src/routes/dn.routes.js rename to src/controllers/dn.controller.js diff --git a/src/routes/group.routes.js b/src/controllers/group.controller.js similarity index 100% rename from src/routes/group.routes.js rename to src/controllers/group.controller.js diff --git a/src/routes/profile.routes.js b/src/controllers/profile.controller.js similarity index 100% rename from src/routes/profile.routes.js rename to src/controllers/profile.controller.js diff --git a/src/routes/user.routes.js b/src/controllers/user.controller.js similarity index 100% rename from src/routes/user.routes.js rename to src/controllers/user.controller.js diff --git a/src/modules/logsManagement/routes/logs.routes.js b/src/modules/logsManagement/controllers/logs.controller.js similarity index 100% rename from src/modules/logsManagement/routes/logs.routes.js rename to src/modules/logsManagement/controllers/logs.controller.js diff --git a/src/modules/passwordManagement/routes/recovery_password.routes.js b/src/modules/passwordManagement/controllers/recovery_password.controller.js similarity index 100% rename from src/modules/passwordManagement/routes/recovery_password.routes.js rename to src/modules/passwordManagement/controllers/recovery_password.controller.js diff --git a/src/modules/passwordManagement/routes/updated_password.routes.js b/src/modules/passwordManagement/controllers/updated_password.controller.js similarity index 100% rename from src/modules/passwordManagement/routes/updated_password.routes.js rename to src/modules/passwordManagement/controllers/updated_password.controller.js diff --git a/src/routes/routes.js b/src/routes/routes.js deleted file mode 100644 index 7b5558d3..00000000 --- a/src/routes/routes.js +++ /dev/null @@ -1,17 +0,0 @@ -const UserRoutes = require('./user.routes') -const GroupRoutes = require('./group.routes') -const ProfileRoutes = require('./profile.routes') -const RecoveryPassword = require('@src/modules/passwordManagement/routes/recovery_password.routes') -const UpdatePassword = require('@src/modules/passwordManagement/routes/updated_password.routes') -const DNRoutes = require('@src/routes/dn.routes') -const LogsRoutes = require('../modules/logsManagement/routes/logs.routes') - -const addRoutes = (app) => { - app.use('/users', UserRoutes) - app.use('/groups', GroupRoutes) - app.use('/profile', ProfileRoutes) - app.use('/', RecoveryPassword, UpdatePassword, DNRoutes) - app.use('/', LogsRoutes) -} - -module.exports = addRoutes From 9d1aafda65ec9d86ba6c9b08c6096692588c1d7f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 18:55:49 -0400 Subject: [PATCH 167/229] update login and logout method to the new baseUrl --- src/modules/authentication/LdapAuth.js | 51 +++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 6ce7edd8..f548df9c 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -68,8 +68,8 @@ var init = function ( } _findFunc = findFunc _insertFunc = insertFunc - _loginUrl = loginUrl || '/login' - _logoutUrl = logoutUrl || '/logout' + _loginUrl = loginUrl || '/api/v1/login' + _logoutUrl = logoutUrl || '/api/v1/logout' _usernameAttributeName = '' passport.use( @@ -172,7 +172,54 @@ var init = function ( router.use(passport.initialize()) router.use(passport.session()) // login + + /** + * @openapi + * /api/v1/workouts: + * get: + * tags: + * - Workouts + * responses: + * 200: + * description: OK + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: OK + * data: + * type: array + * items: + * type: object + */ router.post(_loginUrl, login) + + /** + * @openapi + * /api/v1/workouts: + * get: + * tags: + * - Workouts + * responses: + * 200: + * description: OK + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: OK + * data: + * type: array + * items: + * type: object + */ + router.post(_logoutUrl, logout) router.post('/refresh', refresh) } From e0cf74dc7588a4e1839dae62c516d16d337865a9 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 18:56:04 -0400 Subject: [PATCH 168/229] fix json typo on jsonconfig --- jsconfig.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/jsconfig.json b/jsconfig.json index acb5b353..8c77443d 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,9 +1,10 @@ { - "compilerOptions": { - "baseUrl": "./", - "paths": { - "@src/*": ["src/*"] - } + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@src/*": [ + "src/*" + ] } } - \ No newline at end of file +} \ No newline at end of file From 38b8b92ec28560738b00a570c287f793e9db8f1b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 18:56:17 -0400 Subject: [PATCH 169/229] add swagger dep --- package-lock.json | 36 ++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 37 insertions(+) diff --git a/package-lock.json b/package-lock.json index d5536c07..462a3a3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "socket.io": "^4.6.1", "ssha": "^1.0.1", "swagger-jsdoc": "^6.2.8", + "swagger-node-express": "^2.1.3", "swagger-ui": "^5.9.0", "swagger-ui-express": "^5.0.0", "tail": "^2.2.6", @@ -8175,6 +8176,26 @@ "node": ">=12.0.0" } }, + "node_modules/swagger-node-express": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/swagger-node-express/-/swagger-node-express-2.1.3.tgz", + "integrity": "sha512-XvDxo2Jk7dt83UF9EdRzL6EpfPy+9Wp5p8ITV0zeKFoblg2DqcSHccdiwBWuMr68H6g4wJOtHJ8aBC19oKFZyA==", + "dependencies": { + "lodash": "1.3.1" + }, + "engines": { + "node": ">= 0.8.x" + } + }, + "node_modules/swagger-node-express/node_modules/lodash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.3.1.tgz", + "integrity": "sha512-F7AB8u+6d00CCgnbjWzq9fFLpzOMCgq6mPjOW4+8+dYbrnc0obRrC+IHctzfZ1KKTQxX0xo/punrlpOWcf4gpw==", + "engines": [ + "node", + "rhino" + ] + }, "node_modules/swagger-parser": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", @@ -15406,6 +15427,21 @@ "yaml": "2.0.0-1" } }, + "swagger-node-express": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/swagger-node-express/-/swagger-node-express-2.1.3.tgz", + "integrity": "sha512-XvDxo2Jk7dt83UF9EdRzL6EpfPy+9Wp5p8ITV0zeKFoblg2DqcSHccdiwBWuMr68H6g4wJOtHJ8aBC19oKFZyA==", + "requires": { + "lodash": "1.3.1" + }, + "dependencies": { + "lodash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.3.1.tgz", + "integrity": "sha512-F7AB8u+6d00CCgnbjWzq9fFLpzOMCgq6mPjOW4+8+dYbrnc0obRrC+IHctzfZ1KKTQxX0xo/punrlpOWcf4gpw==" + } + } + }, "swagger-parser": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", diff --git a/package.json b/package.json index 37469b40..e6ab7d18 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "socket.io": "^4.6.1", "ssha": "^1.0.1", "swagger-jsdoc": "^6.2.8", + "swagger-node-express": "^2.1.3", "swagger-ui": "^5.9.0", "swagger-ui-express": "^5.0.0", "tail": "^2.2.6", From df88bd694e1a95764c89401c1a124b8061a634ba Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 18:56:35 -0400 Subject: [PATCH 170/229] update routes baseUrl --- src/api/v1/routes/routes.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api/v1/routes/routes.js b/src/api/v1/routes/routes.js index 0f883f25..bbce3c05 100644 --- a/src/api/v1/routes/routes.js +++ b/src/api/v1/routes/routes.js @@ -7,13 +7,13 @@ const dnController = require('@src/controllers/dn.controller') const logsController = require('@src/modules/logsManagement/controllers/logs.controller') const addRoutes = (app) => { - app.use('/v1/users', userController) - app.use('/v1/groups', groupController) - app.use('/v1/profile', profileController) - app.use('/v1/recovery-password', recoveryPasswordController) - app.use('/v1/update-password', updatePasswordController) - app.use('/v1/dn', dnController) - app.use('/v1/logs', logsController) + app.use('/api/v1/users', userController) + app.use('/api/v1/groups', groupController) + app.use('/api/v1/profile', profileController) + app.use('/api/v1/recovery-password', recoveryPasswordController) + app.use('/api/v1/update-password', updatePasswordController) + app.use('/api/v1/dn', dnController) + app.use('/api/v1/logs', logsController) } module.exports = addRoutes From 4ef879f2cb27a75540244360a875ef05ccfd2a05 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:12:22 -0400 Subject: [PATCH 171/229] add swagger config --- src/api/v1/swagger.js | 85 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/src/api/v1/swagger.js b/src/api/v1/swagger.js index b35fa88e..51fe7612 100644 --- a/src/api/v1/swagger.js +++ b/src/api/v1/swagger.js @@ -2,6 +2,7 @@ const swaggerJsdoc = require('swagger-jsdoc') const swaggerUi = require('swagger-ui-express') +const { allowAllMiddleware } = require('@src/middlewares/auth.handler') const config = require('@src/config/config') // Swagger definition @@ -9,21 +10,91 @@ const options = { swaggerDefinition: { openapi: '3.0.0', info: { - title: 'LDAP API', + title: 'CUJAE LDAP API', version: '1.0.0', - description: 'LDAP API', + description: 'API para la administración de usuarios LDAP de la CUJAE', contact: { - name: 'LDAP API', + name: 'Ahmed González', email: 'ahmediglez@gmail.com', }, }, servers: [ { - url: 'http://localhost:4005', + url: `http://localhost:${config.server.port}/api/v1`, }, ], + tags: [ + { + name: 'Users', + description: 'API para la administración de usuarios LDAP', + }, + ], + + components: { + schemas: { + User: { + type: 'object', + properties: { + uid: { + type: 'string', + description: 'El uid del usuario', + example: 'ahmediglez', + }, + cn: { + type: 'string', + description: 'El nombre del usuario', + example: 'Ahmed González', + }, + sn: { + type: 'string', + description: 'El apellido del usuario', + example: 'González', + }, + mail: { + type: 'string', + description: 'El correo electrónico del usuario', + example: 'ahmediglez@gmaul.com', + }, + password: { + type: 'string', + description: 'La contraseña del usuario', + example: '123456', + }, + roles: { + type: 'array', + description: 'Los roles del usuario', + items: { + type: 'string', + example: 'admin', + }, + }, + }, + }, + UserResponse: { + type: 'object', + properties: { + status: { + type: 'string', + description: 'El estado de la respuesta', + example: 'OK', + }, + data: { + type: 'object', + description: 'El usuario', + $ref: '#/components/schemas/User', + }, + }, + }, + }, + }, }, - apis: ['./routes/*.js', '../connections/LDAP_client.js'], + apis: [ + './src/api/v1/routes/*.js', + './src/controllers/*.js', + './src/modules/logsManagement/controllers/*.js', + './src/modules/passwordManagement/controllers/*.js', + './src/api/v1/swagger.js', + ], } // Initialize swagger-jsdoc -> returns validated swagger spec in json format @@ -43,9 +114,7 @@ const swaggerDocs = (app, port) => { }) console.log('Swagger initialized') - console.log( - `Swagger docs available at http://localhost:${port}/api/v1/docs` - ) + console.log(`Swagger docs available at http://localhost:${port}/api/v1/docs`) } module.exports = { swaggerDocs } From 8d1e06a02041ff47e43ee245841c43d72e9479f5 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:16:08 -0400 Subject: [PATCH 172/229] users endpoint documented --- src/controllers/user.controller.js | 393 ++++++++++++++++++++++++++++- 1 file changed, 379 insertions(+), 14 deletions(-) diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 3e8eed7d..d81b400e 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -1,11 +1,7 @@ const express = require('express') const router = express.Router() const UserServices = require('@src/services/user.services.js') -const { - checkAuth, - checkRoles, - checkBlacklist, -} = require('@src/middlewares/auth.handler') +const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') const service = UserServices() const config = require('@src/config/config') const { @@ -15,12 +11,202 @@ const validateQuery = require('@src/middlewares/queryValidator') const ldap = require('ldapjs') const ldapClient = require('@src/connections/LDAP_client') -// Middleware for routes requiring checkAuth and checkRoles('admin') -router.use(checkAuth, checkRoles('admin')) - -// Middleware to handle common success and error responses - -// Route handler for getting all users +/** + * @openapi + * /api/v1/users: + * get: + * summary: Get a list of users. + * description: Retrieve a list of users based on query parameters. You can filter the results by providing one or more of the following query parameters. + * operationId: getUsers + * parameters: + * - in: query + * name: page + * schema: + * type: integer + * default: 1 + * description: The page number. + * - in: query + * name: limit + * schema: + * type: integer + * default: 10 + * description: The number of items per page. + * - in: query + * name: uid + * schema: + * type: string + * description: Filter users by UID. + * - in: query + * name: cn + * schema: + * type: string + * description: Filter users by CN. + * - in: query + * name: username + * schema: + * type: string + * description: Filter users by username. + * - in: query + * name: CI + * schema: + * type: string + * description: Filter users by CI. + * - in: query + * name: email + * schema: + * type: string + * description: Filter users by email. + * - in: query + * name: lastName + * schema: + * type: string + * description: Filter users by last name. + * - in: query + * name: sex + * schema: + * type: string + * description: Filter users by sex. + * - in: query + * name: area + * schema: + * type: string + * description: Filter users by area. + * - in: query + * name: userCondition + * schema: + * type: string + * description: Filter users by condition. + * - in: query + * name: userStatus + * schema: + * type: string + * description: Filter users by status. + * - in: query + * name: sedeMunicipio + * schema: + * type: string + * description: Filter users by municipality. + * - in: query + * name: userInformation + * schema: + * type: string + * description: Filter users by information. + * - in: query + * name: career + * schema: + * type: string + * description: Filter users by career. + * - in: query + * name: studentClassGroup + * schema: + * type: string + * description: Filter users by class group. + * - in: query + * name: studentYear + * schema: + * type: string + * description: Filter users by student year. + * - in: query + * name: country + * schema: + * type: string + * description: Filter users by country. + * - in: query + * name: UJC + * schema: + * type: string + * description: Filter users by UJC. + * - in: query + * name: skinColor + * schema: + * type: string + * description: Filter users by skin color. + * - in: query + * name: sn + * schema: + * type: string + * description: Filter users by SN. + * - in: query + * name: displayName + * schema: + * type: string + * description: Filter users by display name. + * - in: query + * name: mail + * schema: + * type: string + * description: Filter users by mail. + * - in: query + * name: maildrop + * schema: + * type: string + * description: Filter users by maildrop. + * - in: query + * name: objectName + * schema: + * type: string + * description: Filter users by object name. + * - in: query + * name: dn + * schema: + * type: string + * description: Filter users by DN. + * - in: query + * name: workerID + * schema: + * type: string + * description: Filter users by worker ID. + * - in: query + * name: workArea + * schema: + * type: string + * description: Filter users by work area. + * - in: query + * name: nameInstitution + * schema: + * type: string + * description: Filter users by institution name. + * - in: query + * name: workercontract + * schema: + * type: string + * description: Filter users by worker contract. + * - in: query + * name: userYears + * schema: + * type: string + * description: Filter users by years of service. + * - in: query + * name: schoolLevel + * schema: + * type: string + * description: Filter users by school level. + * - in: query + * name: orgRole + * schema: + * type: string + * description: Filter users by organizational role. + * - in: query + * name: educationalCategory + * schema: + * type: string + * description: Filter users by educational category. + * - in: query + * name: scientificCategory + * schema: + * type: string + * description: Filter users by scientific category. + * - in: query + * name: ou + * schema: + * type: string + * description: Filter users by organizational unit. + * responses: + * 200: + * description: A list of users. + * 500: + * description: An error occurred. + */ router.get('/', async (req, res) => { try { const { localBase, roles } = req.user @@ -41,9 +227,9 @@ router.get('/', async (req, res) => { ) // Send the search results - res.json({ + res.status(200).json({ success: true, - length: searchResults.length, + message: 'List of users retrieved successfully', data: searchResults, }) } catch (error) { @@ -55,7 +241,37 @@ router.get('/', async (req, res) => { } }) -// Get users by groups +/** + * @openapi + * /api/v1/group/{group}: + * get: + * summary: Get a list of users in a specific group. + * description: Retrieve a list of users in the specified group based on query parameters. + * parameters: + * - in: path + * name: group + * required: true + * schema: + * type: string + * description: The group name for which to retrieve users. + * - in: query + * name: page + * schema: + * type: integer + * default: 1 + * description: The page number. + * - in: query + * name: limit + * schema: + * type: integer + * default: 10 + * description: The number of items per page. + * responses: + * 200: + * description: A list of users in the group. + * 500: + * description: An error occurred. + */ router.get('/group/:group', async (req, res) => { try { const baseDN = `ou=usuarios,ou=${req.params.group},${config.ldap.base}` @@ -96,6 +312,46 @@ router.get('/group/:group', async (req, res) => { } }) +/** + * @openapi + * /api/v1/users/baseDN: + * post: + * tags: [Users] + * summary: Get a list of users based on a custom baseDN. + * description: Retrieve a list of users based on a custom baseDN. You can specify the baseDN in the request body to filter the results. Optionally, you can provide additional query parameters to further refine the search. + * operationId: getUsersByBaseDN + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * baseDN: + * type: string + * description: The custom baseDN to search for users. + * example: "ou=example,dc=example,dc=com" + * parameters: + * - in: query + * name: page + * schema: + * type: integer + * default: 1 + * description: The page number. + * - in: query + * name: limit + * schema: + * type: integer + * default: 10 + * description: The number of items per page. + * responses: + * 200: + * description: A list of users based on the custom baseDN. + * 400: + * description: Bad request. The `baseDN` parameter is missing in the request body. + * 500: + * description: An error occurred. + */ router.post('/baseDN', async (req, res) => { try { const baseDN = req.body.baseDN @@ -132,6 +388,46 @@ router.post('/baseDN', async (req, res) => { } }) +/** + * @openapi + * /api/v1/users/{username}: + * put: + * tags: [Users] + * summary: Update a user's attributes. + * description: Update a user's attributes by specifying the username in the URL path. You can provide the attribute name (`att`) and the new value (`value`) in the request body to perform the update. + * operationId: updateUserAttributes + * parameters: + * - in: path + * name: username + * required: true + * schema: + * type: string + * description: The username of the user to update. + * example: johndoe + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * att: + * type: string + * description: The attribute name to update. + * example: email + * value: + * type: string + * description: The new value for the attribute. + * example: newemail@example.com + * responses: + * 200: + * description: User attributes updated successfully. + * 400: + * description: Bad request. The `username` parameter is missing or invalid. + * 500: + * description: An error occurred while updating user attributes. + */ + router.put('/:username', async (req, res) => { try { const { att, value } = req.body @@ -154,6 +450,39 @@ router.put('/:username', async (req, res) => { } }) +/** + * @openapi + * /api/v1/users/modify-ldap: + * post: + * tags: [Users] + * summary: Modify LDAP user attributes. + * description: Modify LDAP user attributes by specifying the DN (Distinguished Name) and the new attribute values. You can provide a DN in the request body and an object containing attributes to modify. The attributes are provided as key-value pairs, where the key is the attribute name and the value is the new value to set. All specified attributes will be replaced with the new values. + * operationId: modifyLdapUserAttributes + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * dn: + * type: string + * description: The Distinguished Name (DN) of the user whose attributes you want to modify. + * example: cn=johndoe,ou=people,dc=example,dc=com + * attributes: + * type: object + * description: A key-value object containing attributes to modify. + * example: + * email: newemail@example.com + * telephoneNumber: +1 123-456-7890 + * responses: + * 200: + * description: LDAP user attributes modified successfully. + * 400: + * description: Bad request. The `dn` or `attributes` parameter is missing or invalid. + * 500: + * description: An error occurred while modifying LDAP user attributes. + */ router.post('/modify-ldap', async (req, res) => { const dn = req.body.dn const attributes = req.body.attributes @@ -198,6 +527,42 @@ router.post('/modify-ldap', async (req, res) => { } }) +/** + * @openapi + * /api/v1/users/newUser: + * post: + * tags: [Users] + * summary: Add a new user to LDAP. + * description: Add a new user to the LDAP directory. Specify the DN (Distinguished Name) where the new user will be created and provide user attributes as a JSON object. The request body should contain the `newUser` object with user attributes and the `userDN` string that defines the DN of the new user. + * operationId: addNewUser + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * newUser: + * type: object + * description: User attributes for the new LDAP user. + * example: + * cn: johndoe + * sn: Doe + * givenName: John + * mail: johndoe@example.com + * userDN: + * type: string + * description: The Distinguished Name (DN) of the new user. + * example: cn=johndoe,ou=people,dc=example,dc=com + * responses: + * 200: + * description: New user added to LDAP successfully. + * 400: + * description: Bad request. The `newUser` or `userDN` parameter is missing or invalid. + * 500: + * description: An error occurred while adding the new user to LDAP. + */ + router.post('/newUser', async (req, res) => { try { const { newUser, userDN } = req.body From fe94f102d3fd21b8d424634dfb6de192ffcd1f84 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:17:57 -0400 Subject: [PATCH 173/229] profile endpoints documented --- src/controllers/profile.controller.js | 69 ++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/controllers/profile.controller.js b/src/controllers/profile.controller.js index b0bb7fc6..5f1483bc 100644 --- a/src/controllers/profile.controller.js +++ b/src/controllers/profile.controller.js @@ -10,7 +10,27 @@ const { } = require('../middlewares/auth.handler') const service = ProfileServices() -//Get all users +/** + * @openapi + * /api/v1/profile: + * get: + * tags: [Profile] + * summary: Get the user profile. + * description: Retrieve the user profile based on the UID (User ID) contained in the JSON Web Token (JWT) passed in the request headers. The endpoint requires authentication and user role permissions to access the profile information. + * operationId: getUserProfile + * security: + * - BearerAuth: [] + * responses: + * 200: + * description: User profile retrieved successfully. + * 401: + * description: Unauthorized. The user is not authenticated. + * 403: + * description: Forbidden. The user does not have the required permissions to access the profile. + * 500: + * description: An error occurred while retrieving the user profile. + */ + router.get( '/', checkAuth, @@ -25,6 +45,53 @@ router.get( } ) +/** + * @openapi + * /api/v1/profile: + * put: + * tags: [Profile] + * summary: Update the user profile. + * description: Update the user profile based on the provided information in the request body. This endpoint requires authentication and user role permissions to access and modify the profile information. + * operationId: updateUserProfile + * security: + * - BearerAuth: [] + * requestBody: + * description: Profile update information. + * content: + * application/json: + * schema: + * type: object + * properties: + * email: + * type: string + * description: New email address for the user. + * password: + * type: string + * description: New password for the user. + * confirmPassword: + * type: string + * description: Confirm the new password (required if password is provided). + * required: + * - email + * examples: + * UpdateProfileExample: + * value: + * email: new.email@example.com + * password: newPassword123 + * confirmPassword: newPassword123 + * responses: + * 200: + * description: User profile updated successfully. + * 400: + * description: Bad request. Invalid or missing parameters in the request body. + * 401: + * description: Unauthorized. The user is not authenticated. + * 403: + * description: Forbidden. The user does not have the required permissions to update the profile. + * 500: + * description: An error occurred while updating the user profile. + */ + router.put( '/', checkAuth, From c1b438dbd05d08cbc5a87b9073d3dd5d44eb78b5 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:22:09 -0400 Subject: [PATCH 174/229] add tags to endpoint swagger paths --- src/controllers/user.controller.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index d81b400e..a2e2995b 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -15,6 +15,7 @@ const ldapClient = require('@src/connections/LDAP_client') * @openapi * /api/v1/users: * get: + * tags: [Users] * summary: Get a list of users. * description: Retrieve a list of users based on query parameters. You can filter the results by providing one or more of the following query parameters. * operationId: getUsers @@ -245,6 +246,7 @@ router.get('/', async (req, res) => { * @openapi * /api/v1/group/{group}: * get: + * tags: [Users] * summary: Get a list of users in a specific group. * description: Retrieve a list of users in the specified group based on query parameters. * parameters: From e5ce13f6796d9dfe38bc6e17bba3961d468b43f9 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:22:28 -0400 Subject: [PATCH 175/229] groups controller documented --- src/controllers/group.controller.js | 126 ++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/controllers/group.controller.js b/src/controllers/group.controller.js index 5aa836b9..c0681d4e 100644 --- a/src/controllers/group.controller.js +++ b/src/controllers/group.controller.js @@ -8,6 +8,32 @@ const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') const config = require('@src/config/config') const service = GroupServices() +/** + * @openapi + * /api/v1/groups/byName/{group}: + * get: + * tags: [Groups] + * summary: Get a LDAP group by its name. + * description: Retrieve information about an LDAP group by specifying its name in the URL path. This endpoint requires authentication to access group information. + * operationId: getGroupByName + * security: + * - BearerAuth: [] + * parameters: + * - in: path + * name: group + * required: true + * schema: + * type: string + * description: The name of the LDAP group to retrieve. + * responses: + * 200: + * description: LDAP group information retrieved successfully. + * 401: + * description: Unauthorized. The user is not authenticated. + * 500: + * description: An error occurred while fetching group information. + */ + router.get('/byName/:group', checkAuth, validateResponse, async (req, res) => { try { const payload = verifyToken(req.headers.authorization.split(' ')[1]) @@ -40,6 +66,40 @@ router.get('/byName/:group', checkAuth, validateResponse, async (req, res) => { } }) +/** + * @openapi + * /api/v1/groups: + * post: + * tags: [Groups] + * summary: Get LDAP groups. + * description: Retrieve a list of LDAP groups based on specified parameters. This endpoint requires authentication to access group information. + * operationId: getGroups + * security: + * - BearerAuth: [] + * parameters: + * - in: query + * name: scope + * schema: + * type: string + * description: The scope of the LDAP search operation (default: 'sub'). + * - in: body + * name: baseDN + * required: false + * description: The base DN (Distinguished Name) for the LDAP search (default: 'dc=cu'). + * schema: + * type: object + * properties: + * baseDN: + * type: string + * responses: + * 200: + * description: List of LDAP groups retrieved successfully. + * 401: + * description: Unauthorized. The user is not authenticated. + * 500: + * description: An error occurred while fetching group information. + */ + router.post('/', checkAuth, validateResponse, async (req, res) => { try { const { baseDN = 'dc=cu' } = req.body @@ -66,6 +126,37 @@ router.post('/', checkAuth, validateResponse, async (req, res) => { } }) +/** + * @openapi + * /api/v1/groups/getChilds: + * post: + * tags: [Groups] + * summary: Get child groups of a specified base DN. + * description: Retrieve child groups of a specified base DN. This endpoint is restricted to administrators and requires authentication. + * operationId: getChildGroups + * security: + * - BearerAuth: [] + * parameters: + * - in: body + * name: baseDN + * required: true + * description: The base DN (Distinguished Name) for which to retrieve child groups. + * schema: + * type: object + * properties: + * baseDN: + * type: string + * responses: + * 200: + * description: Child groups retrieved successfully. + * 401: + * description: Unauthorized. The user is not authenticated. + * 403: + * description: Forbidden. The user does not have sufficient privileges to access this endpoint. + * 500: + * description: An error occurred while fetching child groups. + */ + router.post( '/getChilds', checkAuth, @@ -93,6 +184,41 @@ router.post( } } ) + +/** + * @openapi + * /api/v1/groups/byType/{type}: + * post: + * tags: [Groups] + * summary: Get a group by type. + * description: Retrieve a group by its type using the specified DN. This endpoint requires authentication. + * operationId: getGroupByType + * security: + * - BearerAuth: [] + * parameters: + * - in: path + * name: type + * required: true + * description: The type of the group to retrieve. + * schema: + * type: string + * - in: body + * name: dn + * description: The DN (Distinguished Name) to search for the group. + * schema: + * type: object + * properties: + * dn: + * type: string + * responses: + * 200: + * description: Group retrieved successfully by type. + * 401: + * description: Unauthorized. The user is not authenticated. + * 500: + * description: An error occurred while fetching the group by type. + */ + router.post('/byType/:type', checkAuth, validateResponse, async (req, res) => { try { const type = req.params.type From c103d85a1d56adcab0c412a9301c1111f60984ad Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:28:06 -0400 Subject: [PATCH 176/229] documented authentication --- src/modules/authentication/LdapAuth.js | 106 ++++++++++++++++++------- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index f548df9c..c3d2a01a 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -171,56 +171,102 @@ var init = function ( router.use(passport.initialize()) router.use(passport.session()) - // login /** * @openapi - * /api/v1/workouts: - * get: - * tags: - * - Workouts + * /api/v1/login: + * post: + * tags: [Authentication] + * summary: User login. + * description: Authenticate a user using LDAP credentials. + * operationId: loginUser + * requestBody: + * content: + * application/json: + * schema: + * type: object + * properties: + * username: + * type: string + * password: + * type: string + * required: + * - username + * - password * responses: * 200: - * description: OK - * content: - * application/json: - * schema: - * type: object - * properties: - * status: - * type: string - * example: OK - * data: - * type: array - * items: - * type: object + * description: User authenticated successfully. + * 401: + * description: Authentication failed. Invalid credentials or user not found. + * 500: + * description: An error occurred during authentication. */ + router.post(_loginUrl, login) /** * @openapi - * /api/v1/workouts: - * get: - * tags: - * - Workouts + * /api/v1/logout: + * post: + * tags: [Authentication] + * summary: User logout. + * description: Log out a user and invalidate their access token. + * operationId: logoutUser + * requestBody: + * content: + * application/json: + * schema: + * type: object + * properties: + * accessToken: + * type: string + * required: + * - accessToken * responses: * 200: - * description: OK + * description: User logged out successfully. + * 400: + * description: Bad request. Access token is required. + */ + + router.post(_logoutUrl, logout) + + /** + * @openapi + * /api/v1/refresh: + * post: + * tags: [Authentication] + * summary: Refresh access token. + * description: Refresh the user's access token using a refresh token. + * operationId: refreshAccessToken + * requestBody: + * content: + * application/json: + * schema: + * type: object + * properties: + * username: + * type: string + * required: + * - username + * responses: + * 200: + * description: Access token successfully refreshed. * content: * application/json: * schema: * type: object * properties: - * status: + * newToken: * type: string - * example: OK - * data: - * type: array - * items: - * type: object + * description: The new access token. + * newRefreshToken: + * type: string + * description: The new refresh token. + * 401: + * description: User not found or refresh token not found. */ - router.post(_logoutUrl, logout) router.post('/refresh', refresh) } From 917bbcc0d77331b257726202a2db4e71f1fe754a Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:36:14 -0400 Subject: [PATCH 177/229] logs documentation --- .../controllers/logs.controller.js | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/modules/logsManagement/controllers/logs.controller.js b/src/modules/logsManagement/controllers/logs.controller.js index 3edae5d8..a44a1c19 100644 --- a/src/modules/logsManagement/controllers/logs.controller.js +++ b/src/modules/logsManagement/controllers/logs.controller.js @@ -114,6 +114,34 @@ router.server = (server) => { }) } +/** + * @openapi + * /api/v1/logs: + * get: + * tags: [Logs] + * summary: Get log entries. + * description: Retrieve log entries based on query parameters. + * operationId: getLogEntries + * security: + * - BearerAuth: [] + * parameters: + * - in: query + * name: level + * schema: + * type: string + * description: Filter log entries by level. + * - in: query + * name: period + * schema: + * type: string + * description: Filter log entries by period (daily, weekly, monthly). + * responses: + * 200: + * description: A list of log entries. + * 401: + * description: Unauthorized or insufficient permissions. + */ + router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { const payload = decodeJWT(req.headers.authorization.split(' ')[1]) const isSuperAdmin = payload.roles.includes('superadmin') @@ -192,7 +220,23 @@ router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { } }) -// Define a route to retrieve the log file +/** + * @openapi + * /api/v1/log-file: + * get: + * tags: [Logs] + * summary: Download log file. + * description: Download the log file containing all log entries. + * operationId: downloadLogFile + * security: + * - BearerAuth: [] + * responses: + * 200: + * description: Log file download. + * 401: + * description: Unauthorized or insufficient permissions. + */ + router.get('/log-file', checkAuth, checkRoles('superadmin'), (req, res) => { const logFilePath = path.join(__dirname, '../../../../logs/all.log') // Adjust the path as needed const fileStream = fs.createReadStream(logFilePath) From 9c6b986fb43f91a5d515f782a6295756c263a2e2 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:36:59 -0400 Subject: [PATCH 178/229] password module documentation --- .../recovery_password.controller.js | 90 ++++++++++++++++++- .../updated_password.controller.js | 44 +++++++++ 2 files changed, 131 insertions(+), 3 deletions(-) diff --git a/src/modules/passwordManagement/controllers/recovery_password.controller.js b/src/modules/passwordManagement/controllers/recovery_password.controller.js index 83f35a70..73f6bcde 100644 --- a/src/modules/passwordManagement/controllers/recovery_password.controller.js +++ b/src/modules/passwordManagement/controllers/recovery_password.controller.js @@ -28,7 +28,36 @@ const validateEmailOrUsername = [ .escape(), ] -// Endpoint for initiating the password reset process +/** + * @openapi + * /api/v1/forgot-password: + * post: + * tags: [Password] + * summary: Request a password reset. + * description: Request a password reset for a user. + * operationId: requestPasswordReset + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * emailOrUsername: + * type: string + * required: + * - emailOrUsername + * responses: + * 200: + * description: Password reset email sent successfully. + * 400: + * description: Bad request. Invalid input data. + * 404: + * description: User not found. + * 500: + * description: Internal Server Error. + */ + router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { const errors = validationResult(req) @@ -70,13 +99,68 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { res.status(500).json({ message: 'Internal Server Error' }) } }) + +/** + * @openapi + * /api/v1/update-password: + * post: + * tags: + * - User + * summary: Update user password + * description: Update a user's password. The user must provide the old password, a new password, and confirm the new password. + * operationId: updateUserPassword + * security: + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * oldPassword: + * type: string + * description: The user's old password. + * newPassword: + * type: string + * description: The new password. + * confirmPassword: + * type: string + * description: Confirmation of the new password. + * required: + * - oldPassword + * - newPassword + * - confirmPassword + * responses: + * 200: + * description: Password updated successfully. + * 400: + * description: Bad Request. The request is missing required fields or the passwords do not match. + * 401: + * description: Unauthorized. The user is not authenticated. + * 404: + * description: Not Found. User not found. + * 500: + * description: Internal Server Error. + */ router.post( '/reset-password', checkAuth, passwordValidationMiddleware, async (req, res) => { try { - const { recoveryCode, newPassword } = req.body + const { newPassword, confirmPassword, oldPassword } = req.body + + if (!newPassword || !confirmPassword) { + throw boom.unauthorized( + 'New password and confirm password are required' + ) + } + + if (newPassword !== confirmPassword) { + throw boom.unauthorized('Passwords must be equal') + } + const payload = verifyToken(req.headers.authorization.split(' ')[1]) if (!payload) { @@ -105,7 +189,7 @@ router.post( } } catch (error) { console.error('Error resetting password:', error) - res.status(400).json({ message: 'Invalid or expired recovery code.' }) + res.status(500).json({ message: 'Error updating password' }) } } ) diff --git a/src/modules/passwordManagement/controllers/updated_password.controller.js b/src/modules/passwordManagement/controllers/updated_password.controller.js index d803938b..c74a40d0 100644 --- a/src/modules/passwordManagement/controllers/updated_password.controller.js +++ b/src/modules/passwordManagement/controllers/updated_password.controller.js @@ -14,6 +14,50 @@ const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') const { checkAuth, checkBlacklist } = require('@src/middlewares/auth.handler') const boom = require('@hapi/boom') +/** + * @openapi + * /api/v1/update-password: + * post: + * tags: + * - User + * summary: Update user password + * description: Update a user's password. The user must provide the old password, a new password, and confirm the new password. + * operationId: updateUserPassword + * security: + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * oldPassword: + * type: string + * description: The user's old password. + * newPassword: + * type: string + * description: The new password. + * confirmPassword: + * type: string + * description: Confirmation of the new password. + * required: + * - oldPassword + * - newPassword + * - confirmPassword + * responses: + * 200: + * description: Password updated successfully. + * 400: + * description: Bad Request. The request is missing required fields or the passwords do not match. + * 401: + * description: Unauthorized. The user is not authenticated. + * 404: + * description: Not Found. User not found. + * 500: + * description: Internal Server Error. + */ + router.post( '/update-password', checkAuth, From ab507c519154ed004744cd859a2db41e6521fb3f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:37:13 -0400 Subject: [PATCH 179/229] add tags to swager.js --- src/api/v1/swagger.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/api/v1/swagger.js b/src/api/v1/swagger.js index 51fe7612..f440a84a 100644 --- a/src/api/v1/swagger.js +++ b/src/api/v1/swagger.js @@ -24,10 +24,39 @@ const options = { }, ], tags: [ + { + name: 'Authentication', + description: 'API para la autenticación de usuarios', + }, + { name: 'Users', description: 'API para la administración de usuarios LDAP', }, + { + name: 'Groups', + description: 'API para la administración de grupos LDAP', + }, + { + name: 'Profile', + description: 'API para la administración del perfil de usuario', + }, + { + name: 'Recovery Password', + description: 'API para la recuperación de contraseña', + }, + { + name: 'Update Password', + description: 'API para la actualización de contraseña', + }, + { + name: 'Logs', + description: 'API para la administración de logs', + }, + { + name: 'DN', + description: 'API para la administración de DNs', + }, ], components: { From 8c6ed2156f5e1ec9777220c0b1e31d970b57854b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:49:15 -0400 Subject: [PATCH 180/229] add schemas to swagger.js --- src/api/v1/swagger.js | 238 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 224 insertions(+), 14 deletions(-) diff --git a/src/api/v1/swagger.js b/src/api/v1/swagger.js index f440a84a..0ea59cd1 100644 --- a/src/api/v1/swagger.js +++ b/src/api/v1/swagger.js @@ -64,41 +64,251 @@ const options = { User: { type: 'object', properties: { + CI: { + type: 'string', + description: 'Carnet de identidad del usuario', + }, + middleName: { + type: 'string', + description: 'Segundo nombre del usuario', + }, + lastName: { + type: 'string', + description: 'Apellido del usuario', + }, + name: { + type: 'string', + description: 'Nombre del usuario', + }, + homeAddress: { + type: 'string', + description: 'Dirección del usuario', + }, + telephoneNumber: { + type: 'string', + description: 'Número de teléfono del usuario', + }, + dayRegister: { + type: 'string', + format: 'date-time', + description: 'Fecha de registro del usuario', + }, + sex: { + type: 'string', + enum: ['M', 'F'], + description: 'Sexo del usuario', + }, + area: { + type: 'string', + nullable: true, + description: 'Área del usuario', + }, + userCondition: { + type: 'string', + description: 'Condición del usuario', + }, + userStatus: { + type: 'string', + description: 'Estado del usuario', + enum: ['Activo', 'Inactivo'], + }, + sedeMunicipio: { + type: 'string', + description: 'Municipio de la sede del usuario', + }, + userType: { + type: 'string', + description: 'Tipo de usuario', + enum: ['Estudiante', 'Trabajador', 'Trabajador Docente'], + }, + userInformation: { + type: 'string', + description: 'Información del usuario acerca del curso', + }, + career: { + type: 'string', + description: 'Carrera del usuario', + }, + studentClassGroup: { + type: 'string', + description: 'Grupo del usuario', + }, + studentYear: { + type: 'string', + description: 'Año del usuario', + }, + country: { + type: 'string', + description: 'País del usuario', + default: 'Cuba', + }, + UJC: { + type: 'string', + description: 'Pertenece a la UJC', + }, + skinColor: { + type: 'string', + description: 'Color de piel del usuario', + }, + nameInstitution: { + type: 'string', + description: 'Nombre de la institución del usuario', + default: 'CUJAE', + }, + right: { + type: 'string', + description: 'Derecho del usuario', + default: 'Todos', + }, + hash: { + type: 'string', + description: 'Hash de la contraseña del usuario', + }, + lastTimeChange: { + type: 'string', + format: 'date-time', + description: 'Fecha de la última actualización de la contraseña', + }, uid: { type: 'string', - description: 'El uid del usuario', - example: 'ahmediglez', + description: 'UID del usuario. Formato: nombre.apellido', + required: true, + }, + homeDirectory: { + type: 'string', + description: 'Directorio del usuario. Formato: /home/uid', + }, + givenName: { + type: 'string', + description: 'Nombre del usuario', }, cn: { type: 'string', - description: 'El nombre del usuario', - example: 'Ahmed González', + description: 'Nombre completo del usuario', }, sn: { type: 'string', - description: 'El apellido del usuario', - example: 'González', + description: 'Apellido del usuario', + }, + displayName: { + type: 'string', + description: 'Nombre de visualización del usuario', + }, + uidNumber: { + type: 'string', + description: 'Número de UID del usuario. Formato: 1000', + }, + userPassword: { + type: 'string', + description: 'Contraseña del usuario. Formato: {SSHA}hash', }, mail: { + type: 'array', + items: { + type: 'string', + }, + description: 'Correo electrónico del usuario', + }, + maildrop: { + type: 'array', + items: { + type: 'string', + }, + description: 'Correo electrónico del usuario', + }, + gidNumber: { type: 'string', - description: 'El correo electrónico del usuario', - example: 'ahmediglez@gmaul.com', + description: 'Número de GID del usuario. Formato: 1000', }, - password: { + sambaSID: { type: 'string', - description: 'La contraseña del usuario', - example: '123456', + description: + 'SID del usuario. Formato: S-1-5-21-1004336348-1177238915-682003330-1000', }, - roles: { + objectClass: { type: 'array', - description: 'Los roles del usuario', items: { type: 'string', - example: 'admin', }, + description: + 'Clases del usuario. Formato: top, person, organizationalPerson, inetOrgPerson, posixAccount, shadowAccount, sambaSamAccount', }, }, }, + + Student: { + type: 'object', + properties: { + career: { + type: 'string', + }, + studentYear: { + type: 'string', + }, + studentClassGroup: { + type: 'string', + }, + userInformation: { + type: 'string', + }, + userCondition: { + type: 'string', + }, + userStatus: { + type: 'string', + }, + }, + }, + + Employee: { + type: 'object', + properties: { + dateContract: { + type: 'string', + format: 'date-time', + }, + dateEndContract: { + type: 'string', + format: 'date-time', + }, + educationalCategory: { + type: 'string', + }, + orgRole: { + type: 'string', + }, + schoolLevel: { + type: 'string', + }, + scientificCategory: { + type: 'string', + }, + userYears: { + type: 'string', + }, + workerContract: { + type: 'string', + }, + workArea: { + type: 'string', + }, + workerID: { + type: 'string', + }, + }, + }, + + LdapEntry: { + oneOf: [ + { + $ref: '#/components/schemas/Student', + }, + { + $ref: '#/components/schemas/Employee', + }, + ], + }, + UserResponse: { type: 'object', properties: { From 03c20a770aa2d32b37b5564893de1424b57f7b21 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 22:52:34 -0400 Subject: [PATCH 181/229] update tags for password module --- .../controllers/recovery_password.controller.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/passwordManagement/controllers/recovery_password.controller.js b/src/modules/passwordManagement/controllers/recovery_password.controller.js index 73f6bcde..c9151336 100644 --- a/src/modules/passwordManagement/controllers/recovery_password.controller.js +++ b/src/modules/passwordManagement/controllers/recovery_password.controller.js @@ -32,7 +32,7 @@ const validateEmailOrUsername = [ * @openapi * /api/v1/forgot-password: * post: - * tags: [Password] + * tags: [Recovery Password] * summary: Request a password reset. * description: Request a password reset for a user. * operationId: requestPasswordReset @@ -104,8 +104,7 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { * @openapi * /api/v1/update-password: * post: - * tags: - * - User + * tags: [Update Password] * summary: Update user password * description: Update a user's password. The user must provide the old password, a new password, and confirm the new password. * operationId: updateUserPassword From bb99269c31909c2d7ec1473a8ff3c2f4845c85e2 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 23:05:56 -0400 Subject: [PATCH 182/229] update tags for password module 2 --- .../controllers/updated_password.controller.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/passwordManagement/controllers/updated_password.controller.js b/src/modules/passwordManagement/controllers/updated_password.controller.js index c74a40d0..ce479fd2 100644 --- a/src/modules/passwordManagement/controllers/updated_password.controller.js +++ b/src/modules/passwordManagement/controllers/updated_password.controller.js @@ -18,8 +18,7 @@ const boom = require('@hapi/boom') * @openapi * /api/v1/update-password: * post: - * tags: - * - User + * tags: [Update Password] * summary: Update user password * description: Update a user's password. The user must provide the old password, a new password, and confirm the new password. * operationId: updateUserPassword From a2127b9252476b01bf9fe52947ff1841bc652b54 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 23:06:12 -0400 Subject: [PATCH 183/229] remove comment from login method --- src/modules/authentication/LdapAuth.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index c3d2a01a..b0f461db 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -291,10 +291,6 @@ const initialize = function ( return init(opt, '', router, findFunc, insertFunc, loginUrl, logoutUrl) } -/** - * Customized login authentication handler to send {success: true} - * on successful authenticate, or {success: false} on failed authenticate - */ const login = function (req, res, next) { passport.authenticate( 'ldap', From 51b3471a6d6d98c8680fd66c0568970d3b3fa5cc Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 23:06:24 -0400 Subject: [PATCH 184/229] dn endpoint documentation --- src/controllers/dn.controller.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/controllers/dn.controller.js b/src/controllers/dn.controller.js index 07245d6a..188c4c40 100644 --- a/src/controllers/dn.controller.js +++ b/src/controllers/dn.controller.js @@ -7,6 +7,38 @@ const { checkAuth, checkRoles } = require('../middlewares/auth.handler') const { verifyToken } = require('@src/utils/authentication/tokens/token_verify') const config = require('@src/config/config') +/** + * @openapi + * /api/v1/searchByDN: + * get: + * tags: [DN] + * summary: Search by Distinguished Name (DN). + * description: Retrieve information by providing a Distinguished Name (DN). + * operationId: searchByDN + * security: + * - BearerAuth: [] + * parameters: + * - in: query + * name: baseDN + * required: true + * schema: + * type: string + * description: The Distinguished Name (DN) to search for. + * responses: + * 200: + * description: Successfully retrieved information. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/LDAPResponse' + * 400: + * description: Bad Request. Base DN is missing. + * 401: + * description: Unauthorized. Requires admin role. + * 500: + * description: Internal Server Error. Failed to fetch information. + */ + router.get( '/searchByDN', checkAuth, From 9e98e75e521ffd609ab7f3c9f5e9e1f0787587e2 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 23:06:42 -0400 Subject: [PATCH 185/229] add tags --- src/api/v1/swagger.js | 86 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/api/v1/swagger.js b/src/api/v1/swagger.js index 0ea59cd1..0100fc8d 100644 --- a/src/api/v1/swagger.js +++ b/src/api/v1/swagger.js @@ -49,6 +49,10 @@ const options = { name: 'Update Password', description: 'API para la actualización de contraseña', }, + { + name: 'Reset Password', + description: 'API para el reseteo de contraseña', + }, { name: 'Logs', description: 'API para la administración de logs', @@ -309,6 +313,87 @@ const options = { ], }, + JWTPayload: { + type: 'object', + properties: { + sub: { + type: 'string', + description: 'Subject (sub) del usuario.', + }, + dn: { + type: 'string', + description: 'Distinguished Name (DN) del usuario.', + }, + uid: { + type: 'string', + description: 'Identificador único del usuario.', + }, + groups: { + type: 'array', + items: { + type: 'string', + }, + description: 'Grupos a los que pertenece el usuario.', + }, + base: { + type: 'string', + description: 'Base DN principal.', + }, + localBase: { + type: 'string', + description: 'Base DN local.', + }, + firstname: { + type: 'string', + description: 'Nombre del usuario.', + }, + lastname: { + type: 'string', + description: 'Apellido del usuario.', + }, + fullname: { + type: 'string', + description: 'Nombre completo del usuario.', + }, + email: { + type: 'string', + format: 'email', + description: 'Dirección de correo electrónico del usuario.', + }, + ci: { + type: 'string', + description: 'Número de identidad del usuario.', + }, + roles: { + type: 'array', + items: { + type: 'string', + }, + enum: ['user', 'admin', 'superadmin'], + description: 'Roles del usuario.', + }, + last_time_logged: { + type: 'string', + format: 'date-time', + description: 'Fecha y hora de la última sesión iniciada.', + }, + loginInfo: { + type: 'string', + description: 'Información de inicio de sesión.', + }, + iat: { + type: 'integer', + format: 'int64', + description: 'Tiempo de emisión del token JWT.', + }, + exp: { + type: 'integer', + format: 'int64', + description: 'Tiempo de expiración del token JWT.', + }, + }, + }, + UserResponse: { type: 'object', properties: { @@ -332,6 +417,7 @@ const options = { './src/controllers/*.js', './src/modules/logsManagement/controllers/*.js', './src/modules/passwordManagement/controllers/*.js', + './src/modules/authentication/LdapAuth.js', './src/api/v1/swagger.js', ], } From c8fc8bdfdaef561771430ec3b57a187e32be6c44 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 23:12:04 -0400 Subject: [PATCH 186/229] add postman collection --- LDAP_INF.postman_collection.json | 696 ++++++++++++++++++ .../recovery_password.controller.js | 2 +- 2 files changed, 697 insertions(+), 1 deletion(-) create mode 100644 LDAP_INF.postman_collection.json diff --git a/LDAP_INF.postman_collection.json b/LDAP_INF.postman_collection.json new file mode 100644 index 00000000..456371f3 --- /dev/null +++ b/LDAP_INF.postman_collection.json @@ -0,0 +1,696 @@ +{ + "info": { + "_postman_id": "0f9807c7-756a-4575-829a-7614776ae8ce", + "name": "LDAP_INF", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "14986624" + }, + "item": [ + { + "name": "auth", + "item": [ + { + "name": "Log In", + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": " {\r\n \"username\": \"javier.lorenzo\",\r\n \"password\": \"85100804886\"\r\n} \r\n\r\n\r\n\r\n/* {\r\n \"username\": \"ahmedivan.gonzalez\",\r\n \"password\": \"Ba12345678#\"\r\n} */", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/login", + "host": [ + "{{server}}" + ], + "path": [ + "login" + ] + } + }, + "response": [] + }, + { + "name": "Log out", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYXZpZXIubG9yZW56byIsImRuIjoidWlkPWphdmllci5sb3JlbnpvLG91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsInVpZCI6Imphdmllci5sb3JlbnpvIiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiYXJlYUNlbnRyYWwiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6Imphdmllci5sb3JlbnpvIiwibGFzdG5hbWUiOiJMb3JlbnpvIE5leXJhIiwiZnVsbG5hbWUiOiJKYXZpZXIiLCJlbWFpbCI6Imphdmllci5sb3JlbnpvQGN1amFlLmVkdS5jdSIsImNpIjoiODUxMDA4MDQ4ODYiLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMDktMDhUMjI6MjQ6MjAuNjU2WiIsImxvZ2luSW5mbyI6IjkvOC8yMDIzLCA2OjMzOjUwIFBNIiwiaWF0IjoxNjk0MjEyNDMwLCJleHAiOjE2OTQyMTMzMzB9.V9h8gWJq6ARY_wvZ715lvGMK1lsKb4LhWPB5nNqU45c", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYXZpZXIubG9yZW56byIsImRuIjoidWlkPWphdmllci5sb3JlbnpvLG91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsInVpZCI6Imphdmllci5sb3JlbnpvIiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiYXJlYUNlbnRyYWwiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6Imphdmllci5sb3JlbnpvIiwibGFzdG5hbWUiOiJMb3JlbnpvIE5leXJhIiwiZnVsbG5hbWUiOiJKYXZpZXIiLCJlbWFpbCI6Imphdmllci5sb3JlbnpvQGN1amFlLmVkdS5jdSIsImNpIjoiODUxMDA4MDQ4ODYiLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMDktMDhUMjI6MzM6NTAuOTQ3WiIsImxvZ2luSW5mbyI6IjkvOC8yMDIzLCA2OjQ0OjIxIFBNIiwiaWF0IjoxNjk0MjEzMDYxLCJleHAiOjE2OTQyMTM5NjF9.WDLIWhLc_Sa_fWOa5uPwJeSodfo7bDKMtU2m-GWr4H8\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/logout", + "host": [ + "{{server}}" + ], + "path": [ + "logout" + ] + } + }, + "response": [] + }, + { + "name": "refresh Token", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYXZpZXIubG9yZW56byIsImRuIjoidWlkPWphdmllci5sb3JlbnpvLG91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsInVpZCI6Imphdmllci5sb3JlbnpvIiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiYXJlYUNlbnRyYWwiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6Imphdmllci5sb3JlbnpvIiwibGFzdG5hbWUiOiJMb3JlbnpvIE5leXJhIiwiZnVsbG5hbWUiOiJKYXZpZXIiLCJlbWFpbCI6Imphdmllci5sb3JlbnpvQGN1amFlLmVkdS5jdSIsImNpIjoiODUxMDA4MDQ4ODYiLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMDktMDhUMjM6MTc6NDMuMDU1WiIsImxvZ2luSW5mbyI6IjkvOC8yMDIzLCA3OjMyOjA5IFBNIiwiaWF0IjoxNjk0MjE1OTI5LCJleHAiOjE2OTQyMTY4Mjl9.E2gw4olFGmZYWHZDVegbxG_NvEIO7M5I-dJ0GKC3iW8", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"username\":\"javier.lorenzo\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/refresh", + "host": [ + "{{server}}" + ], + "path": [ + "refresh" + ] + } + }, + "response": [] + }, + { + "name": "recover-password", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImVtYWlsIjoiYWhtZWRpZ2xlekBnbWFpbC5jb20iLCJpYXQiOjE2OTQ0NzgyMjksImV4cCI6MTY5NDQ3OTEyOX0.GvWGo7QdMh1QnSmih5v0FR3QeBYm3rbhWeYEwLcEuWw", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"recoveryCode\": '385268,\r\n \"newPassword\": \"A1222234a#\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/reset-password", + "host": [ + "{{server}}" + ], + "path": [ + "reset-password" + ] + } + }, + "response": [] + }, + { + "name": "forgot-password", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{server}}/forgot-password", + "host": [ + "{{server}}" + ], + "path": [ + "forgot-password" + ] + } + }, + "response": [] + }, + { + "name": "update-password", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbInVzZXIiLCJhZG1pbiJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOS0xMlQwMTowNjowNy4zMzVaIiwibG9naW5JbmZvIjoiOS8xMi8yMDIzLCAzOjE1OjQyIFBNIiwiaWF0IjoxNjk0NTQ2MTQyLCJleHAiOjE2OTQ1NDcwNDJ9.6GG2b2hRGD6NEC4-UcxzV1Z5grlvc9ieqW91UlM68-U", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"oldPassword\":\"abc\",\r\n \"newPassword\": \"Aa12345678#\",\r\n \"confirmPassword\": \"\"\r\n\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/update-password", + "host": [ + "{{server}}" + ], + "path": [ + "update-password" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "groups", + "item": [ + { + "name": "get all", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbInVzZXIiLCJhZG1pbiJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOS0yNFQxOToxNDo1NC4zMjVaIiwibG9naW5JbmZvIjoiOS8yNC8yMDIzLCAzOjMwOjA3IFBNIiwiaWF0IjoxNjk1NTgzODA3LCJleHAiOjE2OTU1ODQ3MDd9.i9wKhiqpdOLRQBUZ3gMgK3vxR8RxygHSDf88DEASFQ4", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"baseDN\" : \"dc=cujae,dc=edu,dc=cu\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/groups?scope=one", + "host": [ + "{{server}}" + ], + "path": [ + "groups" + ], + "query": [ + { + "key": "scope", + "value": "one" + } + ] + } + }, + "response": [] + }, + { + "name": "get admins groups", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYXZpZXIubG9yZW56byIsImRuIjoidWlkPWphdmllci5sb3JlbnpvLG91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsInVpZCI6Imphdmllci5sb3JlbnpvIiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiYXJlYUNlbnRyYWwiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6Imphdmllci5sb3JlbnpvIiwibGFzdG5hbWUiOiJMb3JlbnpvIE5leXJhIiwiZnVsbG5hbWUiOiJKYXZpZXIiLCJlbWFpbCI6Imphdmllci5sb3JlbnpvQGN1amFlLmVkdS5jdSIsImNpIjoiODUxMDA4MDQ4ODYiLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMDktMDlUMDA6MDY6NTQuNzMxWiIsImxvZ2luSW5mbyI6IjkvMTEvMjAyMywgMTA6MTI6NDUgQU0iLCJpYXQiOjE2OTQ0NDE1NjYsImV4cCI6MTY5NDQ0MjQ2Nn0.VftMhpDtwnJ3qvqYwJyL9XhKCDdwwllumW_nxf8ifOI", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"dn\":\"dc=cujae,dc=edu,dc=cu\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/groups/type/admin", + "host": [ + "{{server}}" + ], + "path": [ + "groups", + "type", + "admin" + ] + } + }, + "response": [] + }, + { + "name": "get childrens", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJHb256YWxleiBCZXQiLCJmdWxsbmFtZSI6IkFobWVkY2l0byIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMTAtMDRUMDI6NTc6MjMuNTAwWiIsImxvZ2luSW5mbyI6IjEwLzQvMjAyMywgNzoyNzo0MSBQTSIsImlhdCI6MTY5NjQ2MjA2MiwiZXhwIjoxNjk2NDYyOTYyfQ.sVwLQr0KuvXQ4LQn-FGkkJ8K2BLNbJe1nJ9udzdNe8k", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"baseDN\": \"ou=grupos,ou=informatica,dc=cujae,dc=edu,dc=cu\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/groups/getChilds", + "host": [ + "{{server}}" + ], + "path": [ + "groups", + "getChilds" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "users", + "item": [ + { + "name": "get all", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbImFkbWluIiwidXNlciJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOC0zMFQwMjo0MDoyMC4wNzdaIiwibG9naW5JbmZvIjoiOC8yOS8yMDIzLCAxMToyMToxOCBQTSIsImlhdCI6MTY5MzM2NTY3OCwiZXhwIjoxNjkzMzY4Mzc4fQ.IRKbK1YSm43BFTXvCXGoGJUBBtafDnfPIeiM0ACGvYc", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{server}}/users?all=true", + "host": [ + "{{server}}" + ], + "path": [ + "users" + ], + "query": [ + { + "key": "all", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "get by groups", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbImFkbWluIiwidXNlciJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOC0yOVQxOTo0MjowNC40NTRaIiwibG9naW5JbmZvIjoiOC8yOS8yMDIzLCA1OjM1OjI3IFBNIiwiaWF0IjoxNjkzMzQ0OTMzLCJleHAiOjE2OTMzNDc2MzN9.xOUoO0HTFmTHJ8h-dharjm2PKRKxMrTUlwJLmrTCvs0", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"groups\":[\"usuarios,informatica\"]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/users/getByGroups", + "host": [ + "{{server}}" + ], + "path": [ + "users", + "getByGroups" + ] + } + }, + "response": [] + }, + { + "name": "get by baseDN", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbInVzZXIiLCJhZG1pbiJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOS0yNFQyMDoyMTozNC4zMDVaIiwibG9naW5JbmZvIjoiOS8yNC8yMDIzLCA0OjI0OjIyIFBNIiwiaWF0IjoxNjk1NTg3MDYzLCJleHAiOjE2OTU1ODc5NjN9.gsRDQvmVXtzIlCreZ9OG7fFZfR2ml31wPUj1HLfVeRE", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"baseDN\": \"ou=informatica,cn=cujae,cn=edu,cn=cu\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/users/baseDN", + "host": [ + "{{server}}" + ], + "path": [ + "users", + "baseDN" + ] + } + }, + "response": [] + }, + { + "name": "add new user", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJHb256YWxleiBCZXRhbmNvdXJ0IiwiZnVsbG5hbWUiOiJBaG1lZCIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMTAtMjJUMTk6MTI6NTEuNDExWiIsImxvZ2luSW5mbyI6IjEwLzIyLzIwMjMsIDM6MTM6MzggUE0iLCJpYXQiOjE2OTgwMDIwMTgsImV4cCI6MTY5ODAwNDcxOH0.jmFuZ0u8SeW0nAAHjiIVPyUHGkuLIIhT1_dXUqCFjuM", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"newUser\": {\r\n \"middleName\": \"González\",\r\n \"lastName\": \"Betancourt\",\r\n \"name\": \"Ahmed Iván\",\r\n \"homeAddress\": \"Calle 15 # 272 APTO A E/Calle J y Calle I\",\r\n \"telephoneNumber\": \"(7) 879-32-68\",\r\n \"sex\": \"M\",\r\n \"area\": \"INGENIERIA INFORMATICA\",\r\n \"userStatus\": \"Activo\",\r\n \"sedeMunicipio\": \"PLAZA DE LA REVOLUCION\",\r\n \"userType\": \"Estudiante\",\r\n \"country\": \"Cuba\",\r\n \"UJC\": \"no\",\r\n \"skinColor\": \"Blanco\",\r\n \"nameInstitution\": \"null\", \r\n \"uid\": \"ahmedtest6.gonzalez\", \r\n \"mail\": [\"ahmedtest6@cujae.edu.cu\"],\r\n \"objectClass\": [\r\n \"top\",\r\n \"person\",\r\n \"posixAccount\",\r\n \"shadowAccount\",\r\n \"inetOrgPerson\",\r\n \"iesServices\",\r\n \"sambaSamAccount\",\r\n \"radiusprofile\",\r\n \"CourierMailAlias\",\r\n \"iesMember\"\r\n ],\r\n \"userPassword\": \"{SSHA}xyot2WiSiFVKmM2q0xkOMREVN3CyN3F+uKrIqrAqWiZsniG3KN3JzB8eVhvU/Gm+MSKuOg==\",\r\n \"displayName\": \"Ahmed Ivan\",\r\n \"givenName\": \"Ahmed\",\r\n \"maildrop\": [\"ahmedtest6@gmail.com\"],\r\n \"CI\": \"00092068439\",\r\n \"cn\": \"Ahmed\",\r\n \"sn\": \"Gonzalez Betancourt\"\r\n },\r\n \"userDN\":\"uid=ahmedtest6.gonzalez,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/users/newUser", + "host": [ + "{{server}}" + ], + "path": [ + "users", + "newUser" + ] + } + }, + "response": [] + }, + { + "name": "update user", + "request": { + "method": "PUT", + "header": [], + "url": { + "raw": "{{server}}/users/ahmedivan.gonzalez", + "host": [ + "{{server}}" + ], + "path": [ + "users", + "ahmedivan.gonzalez" + ] + } + }, + "response": [] + }, + { + "name": "update ldap user", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJBaG1lZCBJdmFuIiwiZnVsbG5hbWUiOiJBaG1lZCBJdsOhbiIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMDktMjlUMTY6NDc6NDYuNTYzWiIsImxvZ2luSW5mbyI6IjkvMjkvMjAyMywgMTowMzo1NiBQTSIsImlhdCI6MTY5NjAwNzAzNiwiZXhwIjoxNjk2MDA3OTM2fQ.RKHzZp9mvqotxPb3oGoDtmDnYeI1vzn3aWrYn0TDEI8", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"dn\": \"uid=ahmedivan.gonzalez,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu\",\r\n \"attributes\": {\r\n \"displayName\": \"Ahmed Ivan\",\r\n \"sn\": \"Ahmed Ivan\",\r\n \"maildrop\": \"ahmedglez.dev@gmail.com\",\r\n \"givenName\":\"Ahmed\"\r\n }\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/users/modify-ldap", + "host": [ + "{{server}}" + ], + "path": [ + "users", + "modify-ldap" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "profile", + "item": [ + { + "name": "me", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{server}}/profile", + "host": [ + "{{server}}" + ], + "path": [ + "profile" + ] + } + }, + "response": [] + }, + { + "name": "update profile", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZ29uemFsZXpiIiwiZG4iOiJ1aWQ9YWdvbnphbGV6YixvdT11c3VhcmlvcyxvdT1pbmZvcm1hdGljYSxkYz1jdWphZSxkYz1lZHUsZGM9Y3UiLCJmaXJzdG5hbWUiOiJBaG1lZCBJdsOhbiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIEdvbnrDoWxleiBCZXRhbmNvdXJ0IiwiZW1haWwiOiJhZ29uemFsZXpiQGNlaXMuY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbImFkbWluIiwidXNlciJdLCJpYXQiOjE2ODI0NzAxMzcsImV4cCI6MTY4MjQ3MjgzN30.vxHLbykyqTYBeOGbHcEUHrqt14i-J0THxoyYiDxynBs", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"email\": \"ahmedglez@gmail.com\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/profile", + "host": [ + "{{server}}" + ], + "path": [ + "profile" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "logs", + "item": [ + { + "name": "test", + "request": { + "method": "GET", + "header": [] + }, + "response": [] + }, + { + "name": "get log file", + "request": { + "method": "GET", + "header": [] + }, + "response": [] + } + ] + }, + { + "name": "email", + "item": [] + }, + { + "name": "searchByDN", + "item": [ + { + "name": "searchByDN", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{server}}/searchByDN", + "host": [ + "{{server}}" + ], + "path": [ + "searchByDN" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "New Request", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"Ahmed\",\r\n \"email\": \"ahmedglez@gmail.com\",\r\n \"password\": \"1234\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/users/artillery", + "host": [ + "{{server}}" + ], + "path": [ + "users", + "artillery" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "server", + "value": "http://127.0.0.1:4000", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/src/modules/passwordManagement/controllers/recovery_password.controller.js b/src/modules/passwordManagement/controllers/recovery_password.controller.js index c9151336..b0c3e592 100644 --- a/src/modules/passwordManagement/controllers/recovery_password.controller.js +++ b/src/modules/passwordManagement/controllers/recovery_password.controller.js @@ -104,7 +104,7 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { * @openapi * /api/v1/update-password: * post: - * tags: [Update Password] + * tags: [Reset Password] * summary: Update user password * description: Update a user's password. The user must provide the old password, a new password, and confirm the new password. * operationId: updateUserPassword From 4b03f880abe87da517fc517a64afe59dc7b82600 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 23:20:04 -0400 Subject: [PATCH 187/229] update refresh token path --- src/modules/authentication/LdapAuth.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index b0f461db..79302d5c 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -70,6 +70,7 @@ var init = function ( _insertFunc = insertFunc _loginUrl = loginUrl || '/api/v1/login' _logoutUrl = logoutUrl || '/api/v1/logout' + _refreshUrl = '/api/v1/refresh' _usernameAttributeName = '' passport.use( @@ -267,7 +268,7 @@ var init = function ( * description: User not found or refresh token not found. */ - router.post('/refresh', refresh) + router.post(_refreshUrl, refresh) } /** From 4f9ac25e45a4c04fde93ff527f6e38f8182cde01 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 23:20:18 -0400 Subject: [PATCH 188/229] add callback function to refresh token --- src/modules/authentication/functions/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/authentication/functions/index.js b/src/modules/authentication/functions/index.js index 7e0171b1..e741cdae 100644 --- a/src/modules/authentication/functions/index.js +++ b/src/modules/authentication/functions/index.js @@ -43,7 +43,11 @@ const logout = (req, res) => { const refresh = async (req, res) => { const { username } = req.body - const refreshToken = await getRefreshToken(username) + const refreshToken = await getRefreshToken(username, (err) => { + if (err) { + console.error('Error getting refresh token:', err) + } + }) if (!refreshToken) { return res.status(401).json({ message: 'Refresh token not found' }) From 07db12a9db92644d0928201977a106e15ada1312 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 22 Oct 2023 23:30:29 -0400 Subject: [PATCH 189/229] fix reset password endpoint --- src/api/v1/routes/routes.js | 4 +-- .../recovery_password.controller.js | 34 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/api/v1/routes/routes.js b/src/api/v1/routes/routes.js index bbce3c05..9e446626 100644 --- a/src/api/v1/routes/routes.js +++ b/src/api/v1/routes/routes.js @@ -10,8 +10,8 @@ const addRoutes = (app) => { app.use('/api/v1/users', userController) app.use('/api/v1/groups', groupController) app.use('/api/v1/profile', profileController) - app.use('/api/v1/recovery-password', recoveryPasswordController) - app.use('/api/v1/update-password', updatePasswordController) + app.use('/api/v1/', recoveryPasswordController) + app.use('/api/v1/', updatePasswordController) app.use('/api/v1/dn', dnController) app.use('/api/v1/logs', logsController) } diff --git a/src/modules/passwordManagement/controllers/recovery_password.controller.js b/src/modules/passwordManagement/controllers/recovery_password.controller.js index b0c3e592..29719213 100644 --- a/src/modules/passwordManagement/controllers/recovery_password.controller.js +++ b/src/modules/passwordManagement/controllers/recovery_password.controller.js @@ -102,12 +102,11 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { /** * @openapi - * /api/v1/update-password: + * /reset-password: * post: * tags: [Reset Password] - * summary: Update user password - * description: Update a user's password. The user must provide the old password, a new password, and confirm the new password. - * operationId: updateUserPassword + * summary: Restablecer la contraseña del usuario. + * description: Restablece la contraseña del usuario usando un código de recuperación. * security: * - BearerAuth: [] * requestBody: @@ -117,38 +116,37 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { * schema: * type: object * properties: - * oldPassword: - * type: string - * description: The user's old password. * newPassword: * type: string - * description: The new password. + * description: La nueva contraseña. * confirmPassword: * type: string - * description: Confirmation of the new password. + * description: Confirmación de la nueva contraseña. + * recoveryCode: + * type: string + * description: Código de recuperación. * required: - * - oldPassword * - newPassword * - confirmPassword + * - recoveryCode * responses: * 200: - * description: Password updated successfully. + * description: Contraseña restablecida exitosamente. * 400: - * description: Bad Request. The request is missing required fields or the passwords do not match. + * description: Código de recuperación inválido o caducado. * 401: - * description: Unauthorized. The user is not authenticated. - * 404: - * description: Not Found. User not found. + * description: No autorizado. El token no es válido. * 500: - * description: Internal Server Error. + * description: Error interno del servidor. */ + router.post( '/reset-password', checkAuth, passwordValidationMiddleware, async (req, res) => { try { - const { newPassword, confirmPassword, oldPassword } = req.body + const { newPassword, confirmPassword, recoveryCode } = req.body if (!newPassword || !confirmPassword) { throw boom.unauthorized( @@ -188,7 +186,7 @@ router.post( } } catch (error) { console.error('Error resetting password:', error) - res.status(500).json({ message: 'Error updating password' }) + res.status(500).json({ message: error.message }) } } ) From 0d2e7565e809a264c9a1497ac9627950bda1efaf Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 27 Oct 2023 13:07:05 -0400 Subject: [PATCH 190/229] update swagger server --- src/api/v1/swagger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/v1/swagger.js b/src/api/v1/swagger.js index 0100fc8d..c6b954b2 100644 --- a/src/api/v1/swagger.js +++ b/src/api/v1/swagger.js @@ -20,7 +20,7 @@ const options = { }, servers: [ { - url: `http://localhost:${config.server.port}/api/v1`, + url: `http://localhost:${config.server.port}`, }, ], tags: [ From f3c49857a94a0cdf8624b755c6eb787e866dc9d2 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 27 Oct 2023 13:07:43 -0400 Subject: [PATCH 191/229] user middlewares in users controller again --- src/controllers/user.controller.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index a2e2995b..127732d0 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -11,6 +11,8 @@ const validateQuery = require('@src/middlewares/queryValidator') const ldap = require('ldapjs') const ldapClient = require('@src/connections/LDAP_client') +router.use(checkAuth, checkRoles('admin')) + /** * @openapi * /api/v1/users: @@ -214,6 +216,7 @@ router.get('/', async (req, res) => { const isValid = validateQuery(req.query) const queryFilter = createLdapFilterFromQuery(req.query) const ldapFilter = `(&(objectClass=person)${queryFilter})` + console.log('User', req.user) // Define the LDAP attributes you want to retrieve const attributes = null From 0d089c87faaf8d7a7f0d3d3a4e10e89d5607e517 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 2 Nov 2023 18:41:20 -0400 Subject: [PATCH 192/229] translate some responses --- src/middlewares/auth.handler.js | 10 +++++----- src/modules/authentication/LdapAuth.js | 2 +- .../controllers/recovery_password.controller.js | 2 +- src/services/auth.services.js | 2 ++ src/spec/test-spec.js | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/middlewares/auth.handler.js b/src/middlewares/auth.handler.js index cc2ff473..7d7c2596 100644 --- a/src/middlewares/auth.handler.js +++ b/src/middlewares/auth.handler.js @@ -23,9 +23,9 @@ const checkRoles = (...roles) => { } else { responseError( res, - `You don't have permission to access`, + `No tiene permiso de acceso`, boom.unauthorized( - 'Invalid token, you are not authorized to perform this action' + 'Operación inválida, usted no está autorizado a realizar esta acción.' ) ) } @@ -37,7 +37,7 @@ const checkBlacklist = async (req, res, next) => { const token = req.headers.authorization.split(' ')[1] // Assuming the token is in the Authorization header if (!token) { - return res.status(401).json({ message: 'Unauthorized' }) + return res.status(401).json({ message: 'No autorizado' }) } try { @@ -47,8 +47,8 @@ const checkBlacklist = async (req, res, next) => { if (isBlacklisted) { return res.status(401).json({ success: false, - error: 'Token is expired', - message: 'Try to Log In again', + error: 'Token expirado', + message: 'Intente autenticarse nuevamente', }) } diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 79302d5c..90ba44e0 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -308,7 +308,7 @@ const login = function (req, res, next) { if (!user) { res .status(401) - .json({ success: false, message: 'User cannot be found' }) + .json({ success: false, message: 'Usario no encontrado' }) } else { const last_time_logged = await profileService.getLastLoginByUsername( user.uid diff --git a/src/modules/passwordManagement/controllers/recovery_password.controller.js b/src/modules/passwordManagement/controllers/recovery_password.controller.js index 29719213..e2b13522 100644 --- a/src/modules/passwordManagement/controllers/recovery_password.controller.js +++ b/src/modules/passwordManagement/controllers/recovery_password.controller.js @@ -73,7 +73,7 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { : await userService.getByUsername(emailOrUsername) if (user === undefined) { - return res.status(404).json({ message: 'User not found.' }) + return res.status(404).json({ message: 'Usuario no encontrado' }) } const payload = { diff --git a/src/services/auth.services.js b/src/services/auth.services.js index 7c951266..6f9a1d26 100644 --- a/src/services/auth.services.js +++ b/src/services/auth.services.js @@ -118,6 +118,8 @@ const isAdmin = async (uid, baseDN = config.ldap.base) => { } } + + module.exports = { addToBlackList, getRefreshToken, diff --git a/src/spec/test-spec.js b/src/spec/test-spec.js index 9729b1c5..365e1c39 100644 --- a/src/spec/test-spec.js +++ b/src/spec/test-spec.js @@ -67,7 +67,7 @@ describe('Test ldap authenticate', () => { .send({ username: 'gauss', password: 'bbb' }) expect(response.statusCode).toBe(401) expect(response.body.success).toBeFalsy() - expect(response.body.message).toEqual('Invalid Credentials') + expect(response.body.message).toEqual('Credenciales inválidas') expect(response.body.user).toBeUndefined() let user = User.find((u) => { return u.username === 'gauss' From 8e9d5645366b7e154d788beeb51df1f3985309c3 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 3 Nov 2023 02:43:32 -0400 Subject: [PATCH 193/229] add new user registry function to login --- src/modules/authentication/LdapAuth.js | 36 +++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 90ba44e0..dbdd6fac 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -292,7 +292,38 @@ const initialize = function ( return init(opt, '', router, findFunc, insertFunc, loginUrl, logoutUrl) } -const login = function (req, res, next) { +const addUserRegistry = async (user) => { + const { uid, cn, sn, dn, mail } = user + + try { + // Busca al usuario por su nombre de usuario + let existingUser = await User.findOne({ username: uid }) + + if (existingUser) { + // El usuario ya existe, agrega un nuevo registro a su matriz "registry" + existingUser.registry.push({ timestamp: new Date() }) + await existingUser.save() + } else { + // El usuario no existe, crea un nuevo usuario con el registro inicial + const newUser = new User({ + username: uid, + cn, + sn, + dn, + mail, + registry: [{ timestamp: new Date() }], + }) + await newUser.save() + } + + console.log('Registro de usuario exitoso') + } catch (error) { + console.error('Error al agregar registro de usuario:', error) + throw error // Puedes manejar el error según tus necesidades + } +} + +const login = async function (req, res, next) { passport.authenticate( 'ldap', { @@ -310,6 +341,9 @@ const login = function (req, res, next) { .status(401) .json({ success: false, message: 'Usario no encontrado' }) } else { + // Agrega un nuevo registro al usuario + await addUserRegistry(user) + const last_time_logged = await profileService.getLastLoginByUsername( user.uid ) From 3478ce90860e2996f21444228e2fe30b14b3e83f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 3 Nov 2023 12:26:05 -0400 Subject: [PATCH 194/229] update add user reject message --- src/services/user.services.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/user.services.js b/src/services/user.services.js index 550db3d0..39435781 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -95,7 +95,7 @@ const UserServices = () => { ) if (!!alreadyExistingUser && alreadyExistingUser.length !== 0) { - throw new Error(`User already exists with the given DN.`) + throw new Error(`User already exists, check the uid, CI and email again`) } /* Add necessary atts */ From bf07ebbe5f9472facbfca695bee3fad115dcc840 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 3 Nov 2023 12:53:31 -0400 Subject: [PATCH 195/229] add docker compose file --- docker-compose.yml | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..6ef096c4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,77 @@ +version: '3' + +services: + nodejs: + image: node:18.12.1 + container_name: nodejs + restart: always + volumes: + - ./app:/app + environment: + - SERVER_HOST=127.0.0.1 + - SERVER_PORT=4000 + - MONGODB_URL=mongodb://127.0.0.1/ldapDB + - SESSION_SECRET=ZXaffeqe123!$2 + - API_KEY=sK23#!skb$$d3*03&83#b3nd9#$%#%c83943nqls993nLP#pU%exJk8uSsk + - LDAP_URL=ldap://10.8.1.104 + - LDAP_PORT=389 + - LDAP_BASE=dc=cujae,dc=edu,dc=cu + - LDAP_DN=dc=cujae,dc=edu,dc=cu + - LDAP_PASS=00092068426 + - LDAP_USER_BIND=cn=admin,dc=cu + - LDAP_USER_BIND=cn=admin,dc=cu + - LDAP_SIZE_LIMIT=5000 + - LDAP_TIME_LIMIT=20000 + - ADMIN_USER=cn=admin,dc=cu + - ADMIN_PASS=h015060810 + - REDIS_URL=redis://localhost:6379 + - NODE_ENV=development + - EMAIL_PASSWORD=zolnfnkfhfzrilfr + - EMAIL_HOST=smtp.gmail.com + - EMAIL_USER=ahmedglez.dev@gmail.com + ports: + - '4000:4000' + networks: + - mynetwork + + mongo: + image: mongo + container_name: mongo + restart: always + volumes: + - ./data/db:/data/db + environment: + - MONGO_INITDB_ROOT_USERNAME=root + - MONGO_INITDB_ROOT_PASSWORD=admin134 + ports: + - '27017:27017' + networks: + - mynetwork + + redis: + image: redis + container_name: redis + restart: always + volumes: + - ./data/redis:/data + ports: + - '6379:6379' + networks: + - mynetwork + + nextjs: + image: node:16.0.0 + container_name: nextjs + restart: always + volumes: + - ./client:/app + environment: + - NODE_ENV=development + - API_URL=http://nodejs:4000 + ports: + - '3000:3000' + networks: + - mynetwork + +networks: + mynetwork: From 526adf6b6e974761d596ba2b758a79497ee0d81b Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 10 Nov 2023 15:16:42 -0500 Subject: [PATCH 196/229] config cors --- index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/index.js b/index.js index 8f9791c0..5728076b 100644 --- a/index.js +++ b/index.js @@ -35,6 +35,13 @@ app.use(compression()) //security middlewares app.use(cors()) +app.options('*', cors()) +var allowCrossDomain = function (req, res, next) { + res.header('Access-Control-Allow-Origin', '*') + res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE') + res.header('Access-Control-Allow-Headers', 'Content-Type') + next() +} app.use(helmet()) // Middleware para limitar el numero de solicitudes app.use(limiter) From 72817c9ddf539957ea4efc5ca38c09c667edfa36 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 10 Nov 2023 15:32:00 -0500 Subject: [PATCH 197/229] allow cors --- index.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 5728076b..2cb9b25f 100644 --- a/index.js +++ b/index.js @@ -34,14 +34,25 @@ app.use(express.json()) app.use(compression()) //security middlewares -app.use(cors()) -app.options('*', cors()) -var allowCrossDomain = function (req, res, next) { - res.header('Access-Control-Allow-Origin', '*') - res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE') - res.header('Access-Control-Allow-Headers', 'Content-Type') - next() +app.use( + cors({ + origin: '*', + }) +) + +/* +const whitelist = ['http://developer1.com', 'http://developer2.com'] +const corsOptions = { + origin: (origin, callback) => { + if (whitelist.indexOf(origin) !== -1) { + callback(null, true) + } else { + callback(new Error()) + } + } } +*/ + app.use(helmet()) // Middleware para limitar el numero de solicitudes app.use(limiter) From 21a81dfc35e405d5dede9aad5f8645359eb83f9e Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 10 Nov 2023 15:38:38 -0500 Subject: [PATCH 198/229] allow cors 2 --- index.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 2cb9b25f..1a16346d 100644 --- a/index.js +++ b/index.js @@ -34,11 +34,21 @@ app.use(express.json()) app.use(compression()) //security middlewares -app.use( - cors({ - origin: '*', - }) -) +app.use(cors()) + +app.use(function (req, res, next) { + res.header('Access-Control-Allow-Origin', '*') + res.header('Access-Control-Allow-Methods', 'DELETE, PUT') + res.header( + 'Access-Control-Allow-Headers', + 'Origin, X-Requested-With, Content-Type, Accept' + ) + if ('OPTIONS' == req.method) { + res.sendStatus(200) + } else { + next() + } +}) /* const whitelist = ['http://developer1.com', 'http://developer2.com'] From 1ec55b8011b67aa6f414b16c83584c0620d4ac5d Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 10 Nov 2023 15:50:30 -0500 Subject: [PATCH 199/229] allow cors 3 --- index.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/index.js b/index.js index 1a16346d..e56f842b 100644 --- a/index.js +++ b/index.js @@ -22,6 +22,7 @@ const { swaggerDocs: v1SwaggerDocs } = require('@src/api/v1/swagger.js') //app initialization const app = express() +app.use(cors()) app.use(require('express-status-monitor')()) // load app middlewares @@ -34,21 +35,6 @@ app.use(express.json()) app.use(compression()) //security middlewares -app.use(cors()) - -app.use(function (req, res, next) { - res.header('Access-Control-Allow-Origin', '*') - res.header('Access-Control-Allow-Methods', 'DELETE, PUT') - res.header( - 'Access-Control-Allow-Headers', - 'Origin, X-Requested-With, Content-Type, Accept' - ) - if ('OPTIONS' == req.method) { - res.sendStatus(200) - } else { - next() - } -}) /* const whitelist = ['http://developer1.com', 'http://developer2.com'] From f52761a271345987676409788cf401a49d27529c Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 18 Nov 2023 16:17:31 -0500 Subject: [PATCH 200/229] update swagger desc --- src/api/v1/swagger.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api/v1/swagger.js b/src/api/v1/swagger.js index c6b954b2..1821be49 100644 --- a/src/api/v1/swagger.js +++ b/src/api/v1/swagger.js @@ -12,7 +12,8 @@ const options = { info: { title: 'CUJAE LDAP API', version: '1.0.0', - description: 'API para la administración de usuarios LDAP de la CUJAE', + description: + 'API para la administración de usuarios LDAP de la CUJAE. Esta API facilita la gestión de la autenticación de usuarios, la administración de usuarios y grupos LDAP, perfiles de usuario, recuperación de contraseña, actualización y restablecimiento de contraseña, logs, administración de DNs y configuración de LDAP. Sirve como una solución integral para manejar varios aspectos de la administración de usuarios y directorios en el sistema LDAP de la CUJAE. Desarrollada por Ahmed González, contactable en ahmediglez@gmail.com.', contact: { name: 'Ahmed González', email: 'ahmediglez@gmail.com', @@ -61,6 +62,10 @@ const options = { name: 'DN', description: 'API para la administración de DNs', }, + { + name: 'LDAP', + description: 'API para la administración de la configuración del LDAP', + }, ], components: { From 59d43438657657bbab4e38bd8e20447dd27faa9d Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 18 Nov 2023 16:22:45 -0500 Subject: [PATCH 201/229] increase query params to handle user mistakes --- src/helpers/convertQueryToFilter.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 91c44603..1bda5ef6 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -5,35 +5,62 @@ const attributeFilters = { cn: (value) => `cn=${value}`, username: (value) => `uid=${value}`, CI: (value) => `CI=${value}`, + ci: (value) => `CI=${value}`, email: (value) => `maildrop=${value}`, lastName: (value) => `lastName=${value}`, + lastname: (value) => `lastName=${value}`, sex: (value) => `sex=${value}`, area: (value) => `area=${value}`, userCondition: (value) => `userCondition=${value}`, + usercondition: (value) => `userCondition=${value}`, userStatus: (value) => `userStatus=${value}`, + userstatus: (value) => `userStatus=${value}`, sedeMunicipio: (value) => `sedeMunicipio=${value}`, + sedemunicipio: (value) => `sedeMunicipio=${value}`, userInformation: (value) => `userInformation=${value}`, + userinformation: (value) => `userInformation=${value}`, career: (value) => `career=${value}`, studentClassGroup: (value) => `studentClassGroup=${value}`, + studentclassGroup: (value) => `studentClassGroup=${value}`, + studentclassgroup: (value) => `studentClassGroup=${value}`, studentYear: (value) => `studentYear=${value}`, + studentyear: (value) => `studentYear=${value}`, country: (value) => `country=${value}`, UJC: (value) => `UJC=${value}`, + ujc: (value) => `UJC=${value}`, skinColor: (value) => `skinColor=${value}`, + skincolor: (value) => `skinColor=${value}`, sn: (value) => `sn=${value}`, + SN: (value) => `sn=${value}`, + Sn: (value) => `sn=${value}`, displayName: (value) => `displayName=${value}`, + displayname: (value) => `displayName=${value}`, mail: (value) => `mail=${value}`, maildrop: (value) => `maildrop=${value}`, objectName: (value) => `objectName=${value}`, + objectname: (value) => `objectName=${value}`, dn: (value) => `dn=${value}`, workerID: (value) => `workerID=${value}`, + workerId: (value) => `workerID=${value}`, + workerid: (value) => `workerID=${value}`, workArea: (value) => `workArea=${value}`, + workarea: (value) => `workArea=${value}`, nameInstitution: (value) => `nameInstitution=${value}`, + nameinstitution: (value) => `nameInstitution=${value}`, + workerContract: (value) => `workercontract=${value}`, workercontract: (value) => `workercontract=${value}`, userYears: (value) => `userYears=${value}`, + useryears: (value) => `userYears=${value}`, schoolLevel: (value) => `schoolLevel=${value}`, + schoollevel: (value) => `schoolLevel=${value}`, orgRole: (value) => `orgRole=${value}`, + orgrole: (value) => `orgRole=${value}`, educationalCategory: (value) => `educationalCategory=${value}`, + educationalcategory: (value) => `educationalCategory=${value}`, scientificCategory: (value) => `scientificCategory=${value}`, + scientificcategory: (value) => `scientificCategory=${value}`, + OU: (value) => `ou=${value}`, + Ou: (value) => `ou=${value}`, ou: (value) => `ou=${value}`, } @@ -45,7 +72,6 @@ const userTypeFilters = { const createLdapFilterFromQuery = (query) => { const filters = [] - console.log('query', query) for (const key in query) { if (attributeFilters[key] && query[key]) { From 5ccaa9c723e61e429cc3c6eaf93f93110e11c225 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 18 Nov 2023 16:24:37 -0500 Subject: [PATCH 202/229] delete logs on user controller --- src/controllers/user.controller.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 127732d0..872484bd 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -216,7 +216,6 @@ router.get('/', async (req, res) => { const isValid = validateQuery(req.query) const queryFilter = createLdapFilterFromQuery(req.query) const ldapFilter = `(&(objectClass=person)${queryFilter})` - console.log('User', req.user) // Define the LDAP attributes you want to retrieve const attributes = null From 86f98b18d5df4027f5207a421814b96b43995230 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 18 Nov 2023 16:24:53 -0500 Subject: [PATCH 203/229] add api version to config file --- src/config/config.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/config/config.js b/src/config/config.js index 70396bf7..7acfe12c 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -2,6 +2,9 @@ require('dotenv').config({ path: __dirname + '/../../.env' }) const iesObjectClasses = require('../schemas/ies.schema') module.exports = { + api: { + version: 'v1', + }, server: { port: process.env.SERVER_PORT, host: process.env.HOST, @@ -28,7 +31,6 @@ module.exports = { timeLimit: parseInt(process.env.LDAP_TIME_LIMIT), }, redis: { - host: process.env.REDIS_HOST, - port: process.env.REDIS_PORT, + url: process.env.REDIS_URL, }, } From 289905ff41afce6fa38de7fe206de2791c261756 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 18 Nov 2023 16:25:40 -0500 Subject: [PATCH 204/229] add ldap version --- src/api/v1/routes/routes.js | 18 ++-- src/constants/query_options.js | 44 ++++++++ src/controllers/ldap.controller.js | 162 +++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 7 deletions(-) create mode 100644 src/constants/query_options.js create mode 100644 src/controllers/ldap.controller.js diff --git a/src/api/v1/routes/routes.js b/src/api/v1/routes/routes.js index 9e446626..34d861c3 100644 --- a/src/api/v1/routes/routes.js +++ b/src/api/v1/routes/routes.js @@ -1,3 +1,5 @@ +const config = require('@src/config/config') +const { version } = config.api const userController = require('@src/controllers/user.controller') const groupController = require('@src/controllers/group.controller') const profileController = require('@src/controllers/profile.controller') @@ -5,15 +7,17 @@ const recoveryPasswordController = require('@src/modules/passwordManagement/cont const updatePasswordController = require('@src/modules/passwordManagement/controllers/updated_password.controller') const dnController = require('@src/controllers/dn.controller') const logsController = require('@src/modules/logsManagement/controllers/logs.controller') +const ldapController = require('@src/controllers/ldap.controller') const addRoutes = (app) => { - app.use('/api/v1/users', userController) - app.use('/api/v1/groups', groupController) - app.use('/api/v1/profile', profileController) - app.use('/api/v1/', recoveryPasswordController) - app.use('/api/v1/', updatePasswordController) - app.use('/api/v1/dn', dnController) - app.use('/api/v1/logs', logsController) + app.use(`/api/${version}/users`, userController) + app.use(`/api/${version}/groups`, groupController) + app.use(`/api/${version}/profile`, profileController) + app.use(`/api/${version}/ldap`, ldapController) + app.use(`/api/${version}/dn`, dnController) + app.use(`/api/${version}/`, recoveryPasswordController) + app.use(`/api/${version}/`, updatePasswordController) + app.use(`/api/${version}`, logsController) } module.exports = addRoutes diff --git a/src/constants/query_options.js b/src/constants/query_options.js new file mode 100644 index 00000000..c989a012 --- /dev/null +++ b/src/constants/query_options.js @@ -0,0 +1,44 @@ +const attributeFilters = { + uid: 'ID de Usuario', + cn: 'Nombre Común', + username: 'Nombre de Usuario', + CI: 'Carnet de Identidad', + email: 'Dirección de Correo Electrónico', + lastName: 'Primer Apellido', + sex: 'Género', + area: 'Área', + userCondition: 'Condición del Usuario: (Interno - Semi-Interno)', + userStatus: 'Estado del Usuario (Activo - Inactivo)', + sedeMunicipio: 'Municipio', + userInformation: 'Información del Usuario', + career: 'Carrera', + studentClassGroup: 'Grupo al que Pertenece el Estudiante', + studentYear: 'Año Estudiantil', + country: 'País', + UJC: 'UJC (si, no, true, false)', + skinColor: 'Color de Piel', + sn: 'Apellidos', + displayName: 'Nombre a Mostrar', + mail: 'Correo Electrónico', + maildrop: 'Buzón de Correo', + objectName: 'Nombre del Objeto', + dn: 'LDAP DN', + workerID: 'Identificación del Trabajador', + workArea: 'Área de Trabajo', + nameInstitution: 'Nombre de la Institución', + workercontract: 'Contrato de Trabajo', + userYears: 'Años del Usuario', + schoolLevel: 'Nivel Escolar', + orgRole: 'Rol en la Organización', + educationalCategory: 'Categoría Educativa', + scientificCategory: 'Categoría Científica', + ou: 'Unidad Organizativa', +} + +const userTypeFilters = { + student: 'Estudiante', + docent_employee: 'Trabajador', + employee: 'Trabajador Docente', +} + +module.exports = { userTypeFilters, attributeFilters } diff --git a/src/controllers/ldap.controller.js b/src/controllers/ldap.controller.js new file mode 100644 index 00000000..eacc1d31 --- /dev/null +++ b/src/controllers/ldap.controller.js @@ -0,0 +1,162 @@ +const express = require('express') +const router = express.Router() +const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') +const config = require('@src/config/config') +const ldap = require('ldapjs') +const ldapClient = require('@src/connections/LDAP_client') +const { + userTypeFilters, + attributeFilters, +} = require('@src/constants/query_options') + +router.use(checkAuth, checkRoles('admin')) + +// Función para filtrar información sensible +const sanitizeConfig = (config) => { + // Clona la configuración para no modificar el objeto original + const sanitizedConfig = { ...config } + + // Elimina atributos sensibles + delete sanitizedConfig.apiKey + delete sanitizedConfig.sessionSecret + + // Elimina atributos sensibles dentro del objeto LDAP + delete sanitizedConfig.ldap.password + delete sanitizedConfig.ldap.password_bind + delete sanitizedConfig.ldap.username_bind + delete sanitizedConfig.ldap.admin + + return sanitizedConfig +} +/* +openapi: 3.0.0 +info: + title: LDAP Query Information API + version: 1.0.0 +paths: + /api/v1/ldap/query-info: + get: + tags: + - LDAP + summary: Get information about LDAP query filters. + description: | + Retrieve information about attribute filters and user type filters used + in LDAP queries. This endpoint provides details about available filters + that can be used in queries. + operationId: getLdapQueryInfo + responses: + '200': + description: Successful response with LDAP query information. + content: + application/json: + example: + attributeFilters: + uid: 'Identificación de Usuario' + cn: 'Nombre Común' + username: 'Nombre de Usuario' + CI: 'Número de Identificación' + email: 'Dirección de Correo Electrónico' + lastName: 'Apellido' + sex: 'Género' + area: 'Área' + userCondition: 'Condición del Usuario' + userStatus: 'Estado del Usuario' + sedeMunicipio: 'Sede/Municipio' + userInformation: 'Información del Usuario' + career: 'Carrera' + studentClassGroup: 'Grupo Clase Estudiantil' + studentYear: 'Año Estudiantil' + country: 'País' + UJC: 'UJC' + skinColor: 'Color de Piel' + sn: 'Apellido' + displayName: 'Nombre a Mostrar' + mail: 'Correo Electrónico' + maildrop: 'Mail Drop' + objectName: 'Nombre del Objeto' + dn: 'Nombre Distinguido' + workerID: 'ID del Trabajador' + workArea: 'Área de Trabajo' + nameInstitution: 'Nombre de la Institución' + workercontract: 'Contrato del Trabajador' + userYears: 'Años del Usuario' + schoolLevel: 'Nivel Escolar' + orgRole: 'Rol en la Organización' + educationalCategory: 'Categoría Educativa' + scientificCategory: 'Categoría Científica' + ou: 'Unidad Organizativa' + userTypeFilters: + student: 'Estudiante' + docent_employee: 'Docente/Empleado' + employee: 'Empleado' + +*/ +router.get('/query-info', async (req, res) => { + try { + const queryInfo = { + attributeFilters: attributeFilters, + userTypeFilters: userTypeFilters, + } + res.json(queryInfo) + } catch (error) { + res.status(500).json({ + success: false, + message: error.message, + error: true, + }) + } +}) + +/** + * @openapi + * /api/v1/config: + * get: + * tags: [LDAP] + * summary: Get LDAP server configuration. + * description: Retrieves the configuration details of the LDAP server. This endpoint is accessible only to users with the 'superadmin' role. + * operationId: getLdapConfig + * security: + * - BearerAuth: [] + * responses: + * 200: + * description: LDAP server configuration retrieved successfully. + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * description: Indicates whether the request was successful. + * example: true + * config: + * type: object + * description: LDAP server configuration with sensitive information removed. + * $ref: '#/components/schemas/LdapConfig' + * 401: + * $ref: '#/components/responses/Unauthorized' + * 403: + * $ref: '#/components/responses/Forbidden' + * 500: + * $ref: '#/components/responses/InternalError' + */ + +router.get('/config', checkRoles('superadmin'), async (req, res) => { + try { + // Filtra la información sensible antes de enviarla al cliente + const sanitizedConfig = sanitizeConfig(config) + + res.json({ + success: true, + config: sanitizedConfig, + }) + } catch (error) { + res.status(500).json({ + success: false, + message: error.message, + error: true, + }) + } +}) + +module.exports = router From b15f0636e528972299c8e61553b1c21807b74cbc Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 18 Nov 2023 18:08:20 -0500 Subject: [PATCH 205/229] refactor logs controller --- src/api/v1/routes/routes.js | 2 +- .../controllers/logs.controller.js | 112 ++++++------------ src/schemas/logs.schema.js | 2 +- 3 files changed, 40 insertions(+), 76 deletions(-) diff --git a/src/api/v1/routes/routes.js b/src/api/v1/routes/routes.js index 34d861c3..9d64da6f 100644 --- a/src/api/v1/routes/routes.js +++ b/src/api/v1/routes/routes.js @@ -17,7 +17,7 @@ const addRoutes = (app) => { app.use(`/api/${version}/dn`, dnController) app.use(`/api/${version}/`, recoveryPasswordController) app.use(`/api/${version}/`, updatePasswordController) - app.use(`/api/${version}`, logsController) + app.use(`/api/${version}/logs`, logsController) } module.exports = addRoutes diff --git a/src/modules/logsManagement/controllers/logs.controller.js b/src/modules/logsManagement/controllers/logs.controller.js index a44a1c19..44e3e5c7 100644 --- a/src/modules/logsManagement/controllers/logs.controller.js +++ b/src/modules/logsManagement/controllers/logs.controller.js @@ -2,10 +2,13 @@ const fs = require('fs') const express = require('express') const router = express.Router() const { checkAuth, checkRoles } = require('@src/middlewares/auth.handler') -const { decodeJWT } = require('@src/utils/authentication/tokens/jwtUtils') const { WebSocketServer, OPEN } = require('ws') const Tail = require('tail').Tail const path = require('path') +const Log = require('@src/schemas/logs.schema') + +router.use(checkAuth, checkRoles('superadmin')) + // Initialize the tail instance to monitor the log file const tail = new Tail('logs/all.log', { @@ -142,82 +145,43 @@ router.server = (server) => { * description: Unauthorized or insufficient permissions. */ -router.get('/logs', checkAuth, checkRoles('admin'), (req, res) => { - const payload = decodeJWT(req.headers.authorization.split(' ')[1]) - const isSuperAdmin = payload.roles.includes('superadmin') - const queryParams = req.query - const logs = parseLogFileToJson() - - const filteredLogs = logs.filter((log) => { - return Object.entries(queryParams).every(([key, value]) => { - if (key === 'level' && !log.level.startsWith(value)) { - return false - } - - if (!isSuperAdmin) { - return log.message.branch === payload.localBase - } - - if (key in log.message && log.message[key] !== value) { - return false - } - - return true +router.get('/', async (req, res) => { + try { + const { + method, + url, + user, + status, + page = 1, + limit = 10, + order = 'desc', + } = req.query + + const query = {} + if (method) query['message'] = { $regex: `method: '${method}'` } + if (url) query['message'] = { ...query['message'], $regex: `url: '${url}'` } + if (user) + query['message'] = { ...query['message'], $regex: `user: '${user}'` } + if (status) + query['message'] = { ...query['message'], $regex: `status: '${status}'` } + + const sortOrder = order === 'asc' ? 1 : -1 + + const logs = await Log.find(query) + .sort({ timestamp: sortOrder }) // Always sort by timestamp + .skip((page - 1) * limit) + .limit(parseInt(limit)) + + res.json({ + success: true, + logs, }) - }) - - // Function to filter logs by date range - const filterLogsByDateRange = (logs, startDate, endDate) => { - return logs.filter((log) => { - const logDate = new Date(log.date) - return logDate >= startDate && logDate <= endDate + } catch (error) { + res.status(500).json({ + success: false, + message: error.message, }) } - - const period = req.query.period - if (period === 'daily') { - const currentDate = new Date() - const startDate = new Date( - currentDate.getFullYear(), - currentDate.getMonth(), - currentDate.getDate() - ) - const endDate = new Date( - startDate.getFullYear(), - startDate.getMonth(), - startDate.getDate() + 1 - ) - const dailyLogs = filterLogsByDateRange(filteredLogs, startDate, endDate) - res.json(dailyLogs) - } else if (period === 'weekly') { - const currentDate = new Date() - const startDate = new Date( - currentDate.getFullYear(), - currentDate.getMonth(), - currentDate.getDate() - 7 - ) - const weeklyLogs = filterLogsByDateRange( - filteredLogs, - startDate, - currentDate - ) - res.json(weeklyLogs) - } else if (period === 'monthly') { - const currentDate = new Date() - const startDate = new Date( - currentDate.getFullYear(), - currentDate.getMonth(), - currentDate.getDate() - 30 - ) - const monthlyLogs = filterLogsByDateRange( - filteredLogs, - startDate, - currentDate - ) - res.json(monthlyLogs) - } else { - res.json(filteredLogs) // Return filtered logs without date filtering if no period is specified - } }) /** diff --git a/src/schemas/logs.schema.js b/src/schemas/logs.schema.js index a14cf9a2..4fea6bdc 100644 --- a/src/schemas/logs.schema.js +++ b/src/schemas/logs.schema.js @@ -23,6 +23,6 @@ const logSchema = new mongoose.Schema( } ) -const Log = mongoose.model('Log', logSchema) +const Log = mongoose.model('Logs', logSchema) module.exports = Log From 57d466e166799d51961c9d4f70f79ac4b8c0517f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 18 Nov 2023 18:52:27 -0500 Subject: [PATCH 206/229] fix status query filter --- .../controllers/logs.controller.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/logsManagement/controllers/logs.controller.js b/src/modules/logsManagement/controllers/logs.controller.js index 44e3e5c7..c9b62aa6 100644 --- a/src/modules/logsManagement/controllers/logs.controller.js +++ b/src/modules/logsManagement/controllers/logs.controller.js @@ -9,7 +9,6 @@ const Log = require('@src/schemas/logs.schema') router.use(checkAuth, checkRoles('superadmin')) - // Initialize the tail instance to monitor the log file const tail = new Tail('logs/all.log', { fromBeginning: true, // Start reading from the beginning of the file @@ -157,13 +156,14 @@ router.get('/', async (req, res) => { order = 'desc', } = req.query - const query = {} - if (method) query['message'] = { $regex: `method: '${method}'` } - if (url) query['message'] = { ...query['message'], $regex: `url: '${url}'` } - if (user) - query['message'] = { ...query['message'], $regex: `user: '${user}'` } - if (status) - query['message'] = { ...query['message'], $regex: `status: '${status}'` } + const conditions = [] + + if (method) conditions.push({ message: { $regex: `method: '${method}'` } }) + if (url) conditions.push({ message: { $regex: `url: '${url}'` } }) + if (user) conditions.push({ message: { $regex: `user: '${user}'` } }) + if (status) conditions.push({ message: { $regex: `status: ${status}` } }) + + const query = conditions.length > 0 ? { $and: conditions } : {} const sortOrder = order === 'asc' ? 1 : -1 From e84fe0f32aae3f197f8de603972b39ad99a835c5 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 18 Nov 2023 18:57:45 -0500 Subject: [PATCH 207/229] add filtered logs by date --- .../controllers/logs.controller.js | 35 +++++++----------- .../logsManagement/utils/datesFilters.js | 36 +++++++++++++++++++ 2 files changed, 48 insertions(+), 23 deletions(-) create mode 100644 src/modules/logsManagement/utils/datesFilters.js diff --git a/src/modules/logsManagement/controllers/logs.controller.js b/src/modules/logsManagement/controllers/logs.controller.js index c9b62aa6..80e93257 100644 --- a/src/modules/logsManagement/controllers/logs.controller.js +++ b/src/modules/logsManagement/controllers/logs.controller.js @@ -6,6 +6,9 @@ const { WebSocketServer, OPEN } = require('ws') const Tail = require('tail').Tail const path = require('path') const Log = require('@src/schemas/logs.schema') +const { + filterLogsByTimeframe, +} = require('@src/modules/logsManagement/utils/datesFilters') router.use(checkAuth, checkRoles('superadmin')) @@ -59,28 +62,6 @@ const wss = new WebSocketServer({ }, }) -const parseLogFileToJson = () => { - const logData = fs.readFileSync('logs/all.log', 'utf-8') - const logs = logData - .split('\n') - .filter(Boolean) - .map((line) => { - const splittedLine = line.split(' ') - const message = splittedLine[3].replace(/^"(.*)"$/, '$1') - const parsedMessage = JSON.parse(message) - const log = { - date: splittedLine[0], - time: splittedLine[1], - level: splittedLine[2], - message: parsedMessage, - } - - return log - }) - - return logs -} - tail.on('line', (data) => { sendLogsToClients(data) }) @@ -154,6 +135,7 @@ router.get('/', async (req, res) => { page = 1, limit = 10, order = 'desc', + timeframe = 'all', } = req.query const conditions = [] @@ -172,9 +154,16 @@ router.get('/', async (req, res) => { .skip((page - 1) * limit) .limit(parseInt(limit)) + let filteredLogs = logs + + if (timeframe) { + filteredLogs = filterLogsByTimeframe(filteredLogs, timeframe) + } + res.json({ success: true, - logs, + length: filteredLogs.length, + logs: filteredLogs, }) } catch (error) { res.status(500).json({ diff --git a/src/modules/logsManagement/utils/datesFilters.js b/src/modules/logsManagement/utils/datesFilters.js new file mode 100644 index 00000000..2b592ce2 --- /dev/null +++ b/src/modules/logsManagement/utils/datesFilters.js @@ -0,0 +1,36 @@ +const filterLogsByTimeframe = (logs, timeframe) => { + const now = new Date() + + switch (timeframe) { + case 'today': + return logs.filter((log) => isSameDay(new Date(log.timestamp), now)) + case '3days': + return logs.filter((log) => + isWithinNDays(new Date(log.timestamp), now, 3) + ) + case '5days': + return logs.filter((log) => + isWithinNDays(new Date(log.timestamp), now, 5) + ) + case 'week': + return logs.filter((log) => isWithinAWeek(new Date(log.timestamp), now)) + case 'month': + return logs.filter((log) => isWithinAMonth(new Date(log.timestamp), now)) + default: + return logs + } +} + +const isSameDay = (date1, date2) => + date1.getDate() === date2.getDate() && + date1.getMonth() === date2.getMonth() && + date1.getFullYear() === date2.getFullYear() + +const isWithinNDays = (date, now, n) => + Math.abs(date - now) / (1000 * 60 * 60 * 24) <= n + +const isWithinAWeek = (date, now) => isWithinNDays(date, now, 7) + +const isWithinAMonth = (date, now) => isWithinNDays(date, now, 30) + +module.exports = { filterLogsByTimeframe } From 5bb671b2026be5c82011504f96d50f8073b5b974 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 19 Nov 2023 18:01:12 -0500 Subject: [PATCH 208/229] add error handleling on ldap operations --- src/config/config.js | 1 + src/controllers/group.controller.js | 46 +++++++++++++---------------- src/utils/ldapUtils.js | 17 +++++++++-- 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/config/config.js b/src/config/config.js index 7acfe12c..84a314af 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -4,6 +4,7 @@ const iesObjectClasses = require('../schemas/ies.schema') module.exports = { api: { version: 'v1', + url: process.env.API_URL, }, server: { port: process.env.SERVER_PORT, diff --git a/src/controllers/group.controller.js b/src/controllers/group.controller.js index c0681d4e..ec5d54b9 100644 --- a/src/controllers/group.controller.js +++ b/src/controllers/group.controller.js @@ -157,33 +157,29 @@ router.post('/', checkAuth, validateResponse, async (req, res) => { * description: An error occurred while fetching child groups. */ -router.post( - '/getChilds', - checkAuth, - checkRoles('admin'), - validateResponse, - async (req, res) => { - try { - const { baseDN } = req.body - - if (!baseDN) { - throw new Error(`Base DN required.`) - } - - const response = await service.getChildrensBaseDN(baseDN) - res.json({ - success: true, - data: response, - }) - } catch (error) { - res.status(500).json({ - success: false, - message: 'Error fetching group', - error: `It seems that the group does not exist.`, - }) +router.post('/getChilds', checkAuth, checkRoles('admin'), async (req, res) => { + try { + const { baseDN } = req.body + + if (!baseDN) { + throw new Error(`Base DN requerido.`) } + console.log('entro') + const response = await service.getChildrensBaseDN(baseDN) + console.log('response', response) + res.json({ + success: true, + data: response, + }) + } catch (error) { + console.log(error) + res.status(500).json({ + success: false, + message: 'Parece que el grupo seleccionado no existe o está vacío', + error: error, + }) } -) +}) /** * @openapi diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index bca6d749..675e97e7 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -6,6 +6,8 @@ var assert = require('assert') // Bind to the LDAP server using appropriate credentials const bindLdapClient = () => { return new Promise((resolve, reject) => { + console.log('username', config.ldap.admin.username) + console.log('password', config.ldap.admin.password) ldapClient.bind( config.ldap.admin.username, config.ldap.admin.password, @@ -75,6 +77,9 @@ const performLdapSearch = async (baseDn, filter, attributes) => { searchResponse.on('end', () => { resolve(searchResults) }) + searchResponse.on('error', (error) => { + reject(error) + }) }) } catch (err) { reject(err) @@ -83,6 +88,7 @@ const performLdapSearch = async (baseDn, filter, attributes) => { } const performScopedLdapSearch = async (baseDn, filter, attributes) => { + console.log('baseDN', baseDn) return new Promise((resolve, reject) => { try { bindLdapClient() // Bind before search @@ -109,9 +115,13 @@ const performScopedLdapSearch = async (baseDn, filter, attributes) => { searchResponse.on('end', () => { resolve(searchResults) }) + + searchResponse.on('error', (error) => { + reject(error) + }) }) - } catch (err) { - reject(err) + } catch (error) { + reject(error) } }) } @@ -143,6 +153,9 @@ const performBaseLdapSearch = async (baseDn, filter, attributes) => { searchResponse.on('end', () => { resolve(searchResults) }) + searchResponse.on('error', (error) => { + reject(error) + }) }) } catch (err) { reject(err) From 001aa05434221b7414260886cfbc1fe8ceb264b2 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 19 Nov 2023 18:07:51 -0500 Subject: [PATCH 209/229] update error message --- src/controllers/group.controller.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/controllers/group.controller.js b/src/controllers/group.controller.js index ec5d54b9..a352717a 100644 --- a/src/controllers/group.controller.js +++ b/src/controllers/group.controller.js @@ -164,9 +164,7 @@ router.post('/getChilds', checkAuth, checkRoles('admin'), async (req, res) => { if (!baseDN) { throw new Error(`Base DN requerido.`) } - console.log('entro') const response = await service.getChildrensBaseDN(baseDN) - console.log('response', response) res.json({ success: true, data: response, @@ -175,7 +173,7 @@ router.post('/getChilds', checkAuth, checkRoles('admin'), async (req, res) => { console.log(error) res.status(500).json({ success: false, - message: 'Parece que el grupo seleccionado no existe o está vacío', + message: 'Parece que el grupo seleccionado no existe o se encuentra vacío', error: error, }) } From 1758bdfbacff0643e3879320994c5139e49d8fc1 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 19 Nov 2023 20:53:59 -0500 Subject: [PATCH 210/229] update logs controller --- .../logsManagement/controllers/logs.controller.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/modules/logsManagement/controllers/logs.controller.js b/src/modules/logsManagement/controllers/logs.controller.js index 80e93257..ee0077d3 100644 --- a/src/modules/logsManagement/controllers/logs.controller.js +++ b/src/modules/logsManagement/controllers/logs.controller.js @@ -133,17 +133,21 @@ router.get('/', async (req, res) => { user, status, page = 1, - limit = 10, + limit = 9000, order = 'desc', timeframe = 'all', } = req.query const conditions = [] - if (method) conditions.push({ message: { $regex: `method: '${method}'` } }) - if (url) conditions.push({ message: { $regex: `url: '${url}'` } }) - if (user) conditions.push({ message: { $regex: `user: '${user}'` } }) - if (status) conditions.push({ message: { $regex: `status: ${status}` } }) + if (method && method !== 'all' && method !== 'ALL' && method !== 'All') + conditions.push({ message: { $regex: `method: '${method}'` } }) + if (url && url !== 'all' && url !== 'ALL' && url !== 'All') + conditions.push({ message: { $regex: `url: '${url}'` } }) + if (user && user !== 'all' && user !== 'ALL' && user !== 'All') + conditions.push({ message: { $regex: `user: '${user}'` } }) + if (status && status !== 'all' && status !== 'ALL' && status !== 'All') + conditions.push({ message: { $regex: `status: ${status}` } }) const query = conditions.length > 0 ? { $and: conditions } : {} From 4c26b692b8c106dc871a0b47f817e1c310334c9c Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 30 Nov 2023 12:19:05 -0500 Subject: [PATCH 211/229] update .env.example --- .env.example | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 4b1fde40..85e18b61 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,28 @@ SERVER_HOST='127.0.0.1' SERVER_PORT='4000' - MONGODB_URL='mongodb://127.0.0.1/yourDatabase' - SESSION_SECRET='superSecret' - LDAP_URL='ldap://example.com' LDAP_PORT='389' -LDAP_DN='dc=example,dc=com' \ No newline at end of file +LDAP_DN='dc=example,dc=com'SERVER_HOST='your_server_ip' +API_URL='http://your_server_ip:your_server_port' +LDAP_URL='ldap://your_ldap_host' +LDAP_PORT='your_ldap_port' +LDAP_BASE='your_ldap_base' +LDAP_DN='your_ldap_dn' +LDAP_PASS='your_ldap_password' +LDAP_USER_BIND='your_ldap_user_bind' +LDAP_SIZE_LIMIT='your_ldap_size_limit' +LDAP_TIME_LIMIT='your_ldap_time_limit' + +ADMIN_USER='your_admin_user' +ADMIN_PASS='your_admin_password' + +REDIS_URL='redis://your_redis_host:your_redis_port' + +NODE_ENV='your_node_environment' + +EMAIL_PASSWORD='your_email_password' +EMAIL_HOST='your_email_host' +EMAIL_USER='your_email_user' +EMAIL_CONTACT='your_email_contact' \ No newline at end of file From 69fb8231533e4f91a5ef5de0c8661cdbcc382998 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 30 Nov 2023 12:19:36 -0500 Subject: [PATCH 212/229] update tokens expiration time --- src/modules/authentication/LdapAuth.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index dbdd6fac..a928eb00 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -339,7 +339,7 @@ const login = async function (req, res, next) { if (!user) { res .status(401) - .json({ success: false, message: 'Usario no encontrado' }) + .json({ success: false, message: 'Usuario no encontrado' }) } else { // Agrega un nuevo registro al usuario await addUserRegistry(user) @@ -381,8 +381,8 @@ const login = async function (req, res, next) { } const userObj = { ...user } - const token = signToken(payload, { expiresIn: '45 minutes' }) - const refreshToken = signToken(payload, { expiresIn: '1 day' }) + const token = signToken(payload, { expiresIn: '15 minutes' }) + const refreshToken = signToken(payload, { expiresIn: '1 hour' }) /* await storeRefreshToken(user.uid, refreshToken) */ From f28beff5bb80bbadb22000fe06c70b7f71d597ec Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 30 Nov 2023 12:20:00 -0500 Subject: [PATCH 213/229] update user shcema --- src/schemas/user.schema.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/schemas/user.schema.js b/src/schemas/user.schema.js index 2639182b..056b4d5c 100644 --- a/src/schemas/user.schema.js +++ b/src/schemas/user.schema.js @@ -1,14 +1,5 @@ const mongoose = require('mongoose') -const loginRecordSchema = new mongoose.Schema( - { - timestamp: { type: Date, required: true }, - }, - { - _id: false, // Disable automatic generation of _id for subdocuments - } -) - const userSchema = new mongoose.Schema( { // Fields from LDAP From 8c9515d30d934eec145e17875b982a9843443cce Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 30 Nov 2023 12:20:25 -0500 Subject: [PATCH 214/229] update ldap binding funcion --- src/utils/ldapUtils.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index 675e97e7..fd5a6e5c 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -3,22 +3,19 @@ const ldapClient = require('@src/connections/LDAP_client') const config = require('@src/config/config') var assert = require('assert') +const ADMIN_USER = process.env.ADMIN_USER || 'cn=admin,dc=cu' +const ADMIN_PASS = process.env.ADMIN_PASS || '' + // Bind to the LDAP server using appropriate credentials const bindLdapClient = () => { return new Promise((resolve, reject) => { - console.log('username', config.ldap.admin.username) - console.log('password', config.ldap.admin.password) - ldapClient.bind( - config.ldap.admin.username, - config.ldap.admin.password, - (err) => { - if (err) { - reject(err) - } else { - resolve() - } + ldapClient.bind(ADMIN_USER, ADMIN_PASS, (err) => { + if (err) { + reject(err) + } else { + resolve() } - ) + }) }) } @@ -198,7 +195,6 @@ const performLdapAddition = async (dn, entry) => { entry.lastTimeChange = new Date().toISOString() entry.sambaSID = 'S-1-5-21-1255719363-1350762778-3568053751-513' - console.log('entry', entry) return new Promise((resolve, reject) => { try { @@ -211,8 +207,7 @@ const performLdapAddition = async (dn, entry) => { success: true, message: err, }) - } else { - console.log('created user') + } else { resolve('created User') } }) From 46a42abcaaf870064ad5af8e963e6c0cf9bc6f16 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 30 Nov 2023 12:20:35 -0500 Subject: [PATCH 215/229] add swaggerdocs.json --- swaggerDocs.json | 1602 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1602 insertions(+) create mode 100644 swaggerDocs.json diff --git a/swaggerDocs.json b/swaggerDocs.json new file mode 100644 index 00000000..f36b559d --- /dev/null +++ b/swaggerDocs.json @@ -0,0 +1,1602 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "CUJAE LDAP API", + "version": "1.0.0", + "description": "API para la administración de usuarios LDAP de la CUJAE", + "contact": { + "name": "Ahmed González", + "email": "ahmediglez@gmail.com" + } + }, + "servers": [ + { + "url": "http://localhost:4005" + } + ], + "tags": [ + { + "name": "Authentication", + "description": "API para la autenticación de usuarios" + }, + { + "name": "Users", + "description": "API para la administración de usuarios LDAP" + }, + { + "name": "Groups", + "description": "API para la administración de grupos LDAP" + }, + { + "name": "Profile", + "description": "API para la administración del perfil de usuario" + }, + { + "name": "Recovery Password", + "description": "API para la recuperación de contraseña" + }, + { + "name": "Update Password", + "description": "API para la actualización de contraseña" + }, + { + "name": "Reset Password", + "description": "API para el reseteo de contraseña" + }, + { + "name": "Logs", + "description": "API para la administración de logs" + }, + { + "name": "DN", + "description": "API para la administración de DNs" + } + ], + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "CI": { + "type": "string", + "description": "Carnet de identidad del usuario" + }, + "middleName": { + "type": "string", + "description": "Segundo nombre del usuario" + }, + "lastName": { + "type": "string", + "description": "Apellido del usuario" + }, + "name": { + "type": "string", + "description": "Nombre del usuario" + }, + "homeAddress": { + "type": "string", + "description": "Dirección del usuario" + }, + "telephoneNumber": { + "type": "string", + "description": "Número de teléfono del usuario" + }, + "dayRegister": { + "type": "string", + "format": "date-time", + "description": "Fecha de registro del usuario" + }, + "sex": { + "type": "string", + "enum": [ + "M", + "F" + ], + "description": "Sexo del usuario" + }, + "area": { + "type": "string", + "nullable": true, + "description": "Área del usuario" + }, + "userCondition": { + "type": "string", + "description": "Condición del usuario" + }, + "userStatus": { + "type": "string", + "description": "Estado del usuario", + "enum": [ + "Activo", + "Inactivo" + ] + }, + "sedeMunicipio": { + "type": "string", + "description": "Municipio de la sede del usuario" + }, + "userType": { + "type": "string", + "description": "Tipo de usuario", + "enum": [ + "Estudiante", + "Trabajador", + "Trabajador Docente" + ] + }, + "userInformation": { + "type": "string", + "description": "Información del usuario acerca del curso" + }, + "career": { + "type": "string", + "description": "Carrera del usuario" + }, + "studentClassGroup": { + "type": "string", + "description": "Grupo del usuario" + }, + "studentYear": { + "type": "string", + "description": "Año del usuario" + }, + "country": { + "type": "string", + "description": "País del usuario", + "default": "Cuba" + }, + "UJC": { + "type": "string", + "description": "Pertenece a la UJC" + }, + "skinColor": { + "type": "string", + "description": "Color de piel del usuario" + }, + "nameInstitution": { + "type": "string", + "description": "Nombre de la institución del usuario", + "default": "CUJAE" + }, + "right": { + "type": "string", + "description": "Derecho del usuario", + "default": "Todos" + }, + "hash": { + "type": "string", + "description": "Hash de la contraseña del usuario" + }, + "lastTimeChange": { + "type": "string", + "format": "date-time", + "description": "Fecha de la última actualización de la contraseña" + }, + "uid": { + "type": "string", + "description": "UID del usuario. Formato: nombre.apellido", + "required": true + }, + "homeDirectory": { + "type": "string", + "description": "Directorio del usuario. Formato: /home/uid" + }, + "givenName": { + "type": "string", + "description": "Nombre del usuario" + }, + "cn": { + "type": "string", + "description": "Nombre completo del usuario" + }, + "sn": { + "type": "string", + "description": "Apellido del usuario" + }, + "displayName": { + "type": "string", + "description": "Nombre de visualización del usuario" + }, + "uidNumber": { + "type": "string", + "description": "Número de UID del usuario. Formato: 1000" + }, + "userPassword": { + "type": "string", + "description": "Contraseña del usuario. Formato: {SSHA}hash" + }, + "mail": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Correo electrónico del usuario" + }, + "maildrop": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Correo electrónico del usuario" + }, + "gidNumber": { + "type": "string", + "description": "Número de GID del usuario. Formato: 1000" + }, + "sambaSID": { + "type": "string", + "description": "SID del usuario. Formato: S-1-5-21-1004336348-1177238915-682003330-1000" + }, + "objectClass": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Clases del usuario. Formato: top, person, organizationalPerson, inetOrgPerson, posixAccount, shadowAccount, sambaSamAccount" + } + } + }, + "Student": { + "type": "object", + "properties": { + "career": { + "type": "string" + }, + "studentYear": { + "type": "string" + }, + "studentClassGroup": { + "type": "string" + }, + "userInformation": { + "type": "string" + }, + "userCondition": { + "type": "string" + }, + "userStatus": { + "type": "string" + } + } + }, + "Employee": { + "type": "object", + "properties": { + "dateContract": { + "type": "string", + "format": "date-time" + }, + "dateEndContract": { + "type": "string", + "format": "date-time" + }, + "educationalCategory": { + "type": "string" + }, + "orgRole": { + "type": "string" + }, + "schoolLevel": { + "type": "string" + }, + "scientificCategory": { + "type": "string" + }, + "userYears": { + "type": "string" + }, + "workerContract": { + "type": "string" + }, + "workArea": { + "type": "string" + }, + "workerID": { + "type": "string" + } + } + }, + "LdapEntry": { + "oneOf": [ + { + "$ref": "#/components/schemas/Student" + }, + { + "$ref": "#/components/schemas/Employee" + } + ] + }, + "JWTPayload": { + "type": "object", + "properties": { + "sub": { + "type": "string", + "description": "Subject (sub) del usuario." + }, + "dn": { + "type": "string", + "description": "Distinguished Name (DN) del usuario." + }, + "uid": { + "type": "string", + "description": "Identificador único del usuario." + }, + "groups": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Grupos a los que pertenece el usuario." + }, + "base": { + "type": "string", + "description": "Base DN principal." + }, + "localBase": { + "type": "string", + "description": "Base DN local." + }, + "firstname": { + "type": "string", + "description": "Nombre del usuario." + }, + "lastname": { + "type": "string", + "description": "Apellido del usuario." + }, + "fullname": { + "type": "string", + "description": "Nombre completo del usuario." + }, + "email": { + "type": "string", + "format": "email", + "description": "Dirección de correo electrónico del usuario." + }, + "ci": { + "type": "string", + "description": "Número de identidad del usuario." + }, + "roles": { + "type": "array", + "items": { + "type": "string" + }, + "enum": [ + "user", + "admin", + "superadmin" + ], + "description": "Roles del usuario." + }, + "last_time_logged": { + "type": "string", + "format": "date-time", + "description": "Fecha y hora de la última sesión iniciada." + }, + "loginInfo": { + "type": "string", + "description": "Información de inicio de sesión." + }, + "iat": { + "type": "integer", + "format": "int64", + "description": "Tiempo de emisión del token JWT." + }, + "exp": { + "type": "integer", + "format": "int64", + "description": "Tiempo de expiración del token JWT." + } + } + }, + "UserResponse": { + "type": "object", + "properties": { + "status": { + "type": "string", + "description": "El estado de la respuesta", + "example": "OK" + }, + "data": { + "type": "object", + "description": "El usuario", + "$ref": "#/components/schemas/User" + } + } + } + } + }, + "paths": { + "/api/v1/searchByDN": { + "get": { + "tags": [ + "DN" + ], + "summary": "Search by Distinguished Name (DN).", + "description": "Retrieve information by providing a Distinguished Name (DN).", + "operationId": "searchByDN", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "baseDN", + "required": true, + "schema": { + "type": "string" + }, + "description": "The Distinguished Name (DN) to search for." + } + ], + "responses": { + "200": { + "description": "Successfully retrieved information.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LDAPResponse" + } + } + } + }, + "400": { + "description": "Bad Request. Base DN is missing." + }, + "401": { + "description": "Unauthorized. Requires admin role." + }, + "500": { + "description": "Internal Server Error. Failed to fetch information." + } + } + } + }, + "/api/v1/groups/byName/{group}": { + "get": { + "tags": [ + "Groups" + ], + "summary": "Get a LDAP group by its name.", + "description": "Retrieve information about an LDAP group by specifying its name in the URL path. This endpoint requires authentication to access group information.", + "operationId": "getGroupByName", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "group", + "required": true, + "schema": { + "type": "string" + }, + "description": "The name of the LDAP group to retrieve." + } + ], + "responses": { + "200": { + "description": "LDAP group information retrieved successfully." + }, + "401": { + "description": "Unauthorized. The user is not authenticated." + }, + "500": { + "description": "An error occurred while fetching group information." + } + } + } + }, + "/api/v1/groups/getChilds": { + "post": { + "tags": [ + "Groups" + ], + "summary": "Get child groups of a specified base DN.", + "description": "Retrieve child groups of a specified base DN. This endpoint is restricted to administrators and requires authentication.", + "operationId": "getChildGroups", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "body", + "name": "baseDN", + "required": true, + "description": "The base DN (Distinguished Name) for which to retrieve child groups.", + "schema": { + "type": "object", + "properties": { + "baseDN": { + "type": "string" + } + } + } + } + ], + "responses": { + "200": { + "description": "Child groups retrieved successfully." + }, + "401": { + "description": "Unauthorized. The user is not authenticated." + }, + "403": { + "description": "Forbidden. The user does not have sufficient privileges to access this endpoint." + }, + "500": { + "description": "An error occurred while fetching child groups." + } + } + } + }, + "/api/v1/groups/byType/{type}": { + "post": { + "tags": [ + "Groups" + ], + "summary": "Get a group by type.", + "description": "Retrieve a group by its type using the specified DN. This endpoint requires authentication.", + "operationId": "getGroupByType", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "type", + "required": true, + "description": "The type of the group to retrieve.", + "schema": { + "type": "string" + } + }, + { + "in": "body", + "name": "dn", + "description": "The DN (Distinguished Name) to search for the group.", + "schema": { + "type": "object", + "properties": { + "dn": { + "type": "string" + } + } + } + } + ], + "responses": { + "200": { + "description": "Group retrieved successfully by type." + }, + "401": { + "description": "Unauthorized. The user is not authenticated." + }, + "500": { + "description": "An error occurred while fetching the group by type." + } + } + } + }, + "/api/v1/profile": { + "get": { + "tags": [ + "Profile" + ], + "summary": "Get the user profile.", + "description": "Retrieve the user profile based on the UID (User ID) contained in the JSON Web Token (JWT) passed in the request headers. The endpoint requires authentication and user role permissions to access the profile information.", + "operationId": "getUserProfile", + "security": [ + { + "BearerAuth": [] + } + ], + "responses": { + "200": { + "description": "User profile retrieved successfully." + }, + "401": { + "description": "Unauthorized. The user is not authenticated." + }, + "403": { + "description": "Forbidden. The user does not have the required permissions to access the profile." + }, + "500": { + "description": "An error occurred while retrieving the user profile." + } + } + }, + "put": { + "tags": [ + "Profile" + ], + "summary": "Update the user profile.", + "description": "Update the user profile based on the provided information in the request body. This endpoint requires authentication and user role permissions to access and modify the profile information.", + "operationId": "updateUserProfile", + "security": [ + { + "BearerAuth": [] + } + ], + "requestBody": { + "description": "Profile update information.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "New email address for the user." + }, + "password": { + "type": "string", + "description": "New password for the user." + }, + "confirmPassword": { + "type": "string", + "description": "Confirm the new password (required if password is provided)." + } + }, + "required": [ + "email" + ] + }, + "examples": { + "UpdateProfileExample": { + "value": { + "email": "new.email@example.com", + "password": "newPassword123", + "confirmPassword": "newPassword123" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "User profile updated successfully." + }, + "400": { + "description": "Bad request. Invalid or missing parameters in the request body." + }, + "401": { + "description": "Unauthorized. The user is not authenticated." + }, + "403": { + "description": "Forbidden. The user does not have the required permissions to update the profile." + }, + "500": { + "description": "An error occurred while updating the user profile." + } + } + } + }, + "/api/v1/users": { + "get": { + "tags": [ + "Users" + ], + "summary": "Get a list of users.", + "description": "Retrieve a list of users based on query parameters. You can filter the results by providing one or more of the following query parameters.", + "operationId": "getUsers", + "parameters": [ + { + "in": "query", + "name": "page", + "schema": { + "type": "integer", + "default": 1 + }, + "description": "The page number." + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "default": 10 + }, + "description": "The number of items per page." + }, + { + "in": "query", + "name": "uid", + "schema": { + "type": "string" + }, + "description": "Filter users by UID." + }, + { + "in": "query", + "name": "cn", + "schema": { + "type": "string" + }, + "description": "Filter users by CN." + }, + { + "in": "query", + "name": "username", + "schema": { + "type": "string" + }, + "description": "Filter users by username." + }, + { + "in": "query", + "name": "CI", + "schema": { + "type": "string" + }, + "description": "Filter users by CI." + }, + { + "in": "query", + "name": "email", + "schema": { + "type": "string" + }, + "description": "Filter users by email." + }, + { + "in": "query", + "name": "lastName", + "schema": { + "type": "string" + }, + "description": "Filter users by last name." + }, + { + "in": "query", + "name": "sex", + "schema": { + "type": "string" + }, + "description": "Filter users by sex." + }, + { + "in": "query", + "name": "area", + "schema": { + "type": "string" + }, + "description": "Filter users by area." + }, + { + "in": "query", + "name": "userCondition", + "schema": { + "type": "string" + }, + "description": "Filter users by condition." + }, + { + "in": "query", + "name": "userStatus", + "schema": { + "type": "string" + }, + "description": "Filter users by status." + }, + { + "in": "query", + "name": "sedeMunicipio", + "schema": { + "type": "string" + }, + "description": "Filter users by municipality." + }, + { + "in": "query", + "name": "userInformation", + "schema": { + "type": "string" + }, + "description": "Filter users by information." + }, + { + "in": "query", + "name": "career", + "schema": { + "type": "string" + }, + "description": "Filter users by career." + }, + { + "in": "query", + "name": "studentClassGroup", + "schema": { + "type": "string" + }, + "description": "Filter users by class group." + }, + { + "in": "query", + "name": "studentYear", + "schema": { + "type": "string" + }, + "description": "Filter users by student year." + }, + { + "in": "query", + "name": "country", + "schema": { + "type": "string" + }, + "description": "Filter users by country." + }, + { + "in": "query", + "name": "UJC", + "schema": { + "type": "string" + }, + "description": "Filter users by UJC." + }, + { + "in": "query", + "name": "skinColor", + "schema": { + "type": "string" + }, + "description": "Filter users by skin color." + }, + { + "in": "query", + "name": "sn", + "schema": { + "type": "string" + }, + "description": "Filter users by SN." + }, + { + "in": "query", + "name": "displayName", + "schema": { + "type": "string" + }, + "description": "Filter users by display name." + }, + { + "in": "query", + "name": "mail", + "schema": { + "type": "string" + }, + "description": "Filter users by mail." + }, + { + "in": "query", + "name": "maildrop", + "schema": { + "type": "string" + }, + "description": "Filter users by maildrop." + }, + { + "in": "query", + "name": "objectName", + "schema": { + "type": "string" + }, + "description": "Filter users by object name." + }, + { + "in": "query", + "name": "dn", + "schema": { + "type": "string" + }, + "description": "Filter users by DN." + }, + { + "in": "query", + "name": "workerID", + "schema": { + "type": "string" + }, + "description": "Filter users by worker ID." + }, + { + "in": "query", + "name": "workArea", + "schema": { + "type": "string" + }, + "description": "Filter users by work area." + }, + { + "in": "query", + "name": "nameInstitution", + "schema": { + "type": "string" + }, + "description": "Filter users by institution name." + }, + { + "in": "query", + "name": "workercontract", + "schema": { + "type": "string" + }, + "description": "Filter users by worker contract." + }, + { + "in": "query", + "name": "userYears", + "schema": { + "type": "string" + }, + "description": "Filter users by years of service." + }, + { + "in": "query", + "name": "schoolLevel", + "schema": { + "type": "string" + }, + "description": "Filter users by school level." + }, + { + "in": "query", + "name": "orgRole", + "schema": { + "type": "string" + }, + "description": "Filter users by organizational role." + }, + { + "in": "query", + "name": "educationalCategory", + "schema": { + "type": "string" + }, + "description": "Filter users by educational category." + }, + { + "in": "query", + "name": "scientificCategory", + "schema": { + "type": "string" + }, + "description": "Filter users by scientific category." + }, + { + "in": "query", + "name": "ou", + "schema": { + "type": "string" + }, + "description": "Filter users by organizational unit." + } + ], + "responses": { + "200": { + "description": "A list of users." + }, + "500": { + "description": "An error occurred." + } + } + } + }, + "/api/v1/group/{group}": { + "get": { + "tags": [ + "Users" + ], + "summary": "Get a list of users in a specific group.", + "description": "Retrieve a list of users in the specified group based on query parameters.", + "parameters": [ + { + "in": "path", + "name": "group", + "required": true, + "schema": { + "type": "string" + }, + "description": "The group name for which to retrieve users." + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer", + "default": 1 + }, + "description": "The page number." + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "default": 10 + }, + "description": "The number of items per page." + } + ], + "responses": { + "200": { + "description": "A list of users in the group." + }, + "500": { + "description": "An error occurred." + } + } + } + }, + "/api/v1/users/baseDN": { + "post": { + "tags": [ + "Users" + ], + "summary": "Get a list of users based on a custom baseDN.", + "description": "Retrieve a list of users based on a custom baseDN. You can specify the baseDN in the request body to filter the results. Optionally, you can provide additional query parameters to further refine the search.", + "operationId": "getUsersByBaseDN", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "baseDN": { + "type": "string", + "description": "The custom baseDN to search for users.", + "example": "ou=example,dc=example,dc=com" + } + } + } + } + } + }, + "parameters": [ + { + "in": "query", + "name": "page", + "schema": { + "type": "integer", + "default": 1 + }, + "description": "The page number." + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "default": 10 + }, + "description": "The number of items per page." + } + ], + "responses": { + "200": { + "description": "A list of users based on the custom baseDN." + }, + "400": { + "description": "Bad request. The `baseDN` parameter is missing in the request body." + }, + "500": { + "description": "An error occurred." + } + } + } + }, + "/api/v1/users/{username}": { + "put": { + "tags": [ + "Users" + ], + "summary": "Update a user's attributes.", + "description": "Update a user's attributes by specifying the username in the URL path. You can provide the attribute name (`att`) and the new value (`value`) in the request body to perform the update.", + "operationId": "updateUserAttributes", + "parameters": [ + { + "in": "path", + "name": "username", + "required": true, + "schema": { + "type": "string" + }, + "description": "The username of the user to update.", + "example": "johndoe" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "att": { + "type": "string", + "description": "The attribute name to update.", + "example": "email" + }, + "value": { + "type": "string", + "description": "The new value for the attribute.", + "example": "newemail@example.com" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "User attributes updated successfully." + }, + "400": { + "description": "Bad request. The `username` parameter is missing or invalid." + }, + "500": { + "description": "An error occurred while updating user attributes." + } + } + } + }, + "/api/v1/users/modify-ldap": { + "post": { + "tags": [ + "Users" + ], + "summary": "Modify LDAP user attributes.", + "description": "Modify LDAP user attributes by specifying the DN (Distinguished Name) and the new attribute values. You can provide a DN in the request body and an object containing attributes to modify. The attributes are provided as key-value pairs, where the key is the attribute name and the value is the new value to set. All specified attributes will be replaced with the new values.", + "operationId": "modifyLdapUserAttributes", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "dn": { + "type": "string", + "description": "The Distinguished Name (DN) of the user whose attributes you want to modify.", + "example": "cn=johndoe,ou=people,dc=example,dc=com" + }, + "attributes": { + "type": "object", + "description": "A key-value object containing attributes to modify.", + "example": { + "email": "newemail@example.com", + "telephoneNumber": "+1 123-456-7890" + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "LDAP user attributes modified successfully." + }, + "400": { + "description": "Bad request. The `dn` or `attributes` parameter is missing or invalid." + }, + "500": { + "description": "An error occurred while modifying LDAP user attributes." + } + } + } + }, + "/api/v1/users/newUser": { + "post": { + "tags": [ + "Users" + ], + "summary": "Add a new user to LDAP.", + "description": "Add a new user to the LDAP directory. Specify the DN (Distinguished Name) where the new user will be created and provide user attributes as a JSON object. The request body should contain the `newUser` object with user attributes and the `userDN` string that defines the DN of the new user.", + "operationId": "addNewUser", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "newUser": { + "type": "object", + "description": "User attributes for the new LDAP user.", + "example": { + "cn": "johndoe", + "sn": "Doe", + "givenName": "John", + "mail": "johndoe@example.com" + } + }, + "userDN": { + "type": "string", + "description": "The Distinguished Name (DN) of the new user.", + "example": "cn=johndoe,ou=people,dc=example,dc=com" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "New user added to LDAP successfully." + }, + "400": { + "description": "Bad request. The `newUser` or `userDN` parameter is missing or invalid." + }, + "500": { + "description": "An error occurred while adding the new user to LDAP." + } + } + } + }, + "/api/v1/logs": { + "get": { + "tags": [ + "Logs" + ], + "summary": "Get log entries.", + "description": "Retrieve log entries based on query parameters.", + "operationId": "getLogEntries", + "security": [ + { + "BearerAuth": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "level", + "schema": { + "type": "string" + }, + "description": "Filter log entries by level." + }, + { + "in": "query", + "name": "period", + "schema": { + "type": "string" + }, + "description": "Filter log entries by period (daily, weekly, monthly)." + } + ], + "responses": { + "200": { + "description": "A list of log entries." + }, + "401": { + "description": "Unauthorized or insufficient permissions." + } + } + } + }, + "/api/v1/log-file": { + "get": { + "tags": [ + "Logs" + ], + "summary": "Download log file.", + "description": "Download the log file containing all log entries.", + "operationId": "downloadLogFile", + "security": [ + { + "BearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Log file download." + }, + "401": { + "description": "Unauthorized or insufficient permissions." + } + } + } + }, + "/api/v1/forgot-password": { + "post": { + "tags": [ + "Recovery Password" + ], + "summary": "Request a password reset.", + "description": "Request a password reset for a user.", + "operationId": "requestPasswordReset", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "emailOrUsername": { + "type": "string" + } + } + }, + "required": [ + "emailOrUsername" + ] + } + } + }, + "responses": { + "200": { + "description": "Password reset email sent successfully." + }, + "400": { + "description": "Bad request. Invalid input data." + }, + "404": { + "description": "User not found." + }, + "500": { + "description": "Internal Server Error." + } + } + } + }, + "/reset-password": { + "post": { + "tags": [ + "Reset Password" + ], + "summary": "Restablecer la contraseña del usuario.", + "description": "Restablece la contraseña del usuario usando un código de recuperación.", + "security": [ + { + "BearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "newPassword": { + "type": "string", + "description": "La nueva contraseña." + }, + "confirmPassword": { + "type": "string", + "description": "Confirmación de la nueva contraseña." + }, + "recoveryCode": { + "type": "string", + "description": "Código de recuperación." + } + } + }, + "required": [ + "newPassword", + "confirmPassword", + "recoveryCode" + ] + } + } + }, + "responses": { + "200": { + "description": "Contraseña restablecida exitosamente." + }, + "400": { + "description": "Código de recuperación inválido o caducado." + }, + "401": { + "description": "No autorizado. El token no es válido." + }, + "500": { + "description": "Error interno del servidor." + } + } + } + }, + "/api/v1/update-password": { + "post": { + "tags": [ + "Update Password" + ], + "summary": "Update user password", + "description": "Update a user's password. The user must provide the old password, a new password, and confirm the new password.", + "operationId": "updateUserPassword", + "security": [ + { + "BearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "oldPassword": { + "type": "string", + "description": "The user's old password." + }, + "newPassword": { + "type": "string", + "description": "The new password." + }, + "confirmPassword": { + "type": "string", + "description": "Confirmation of the new password." + } + } + }, + "required": [ + "oldPassword", + "newPassword", + "confirmPassword" + ] + } + } + }, + "responses": { + "200": { + "description": "Password updated successfully." + }, + "400": { + "description": "Bad Request. The request is missing required fields or the passwords do not match." + }, + "401": { + "description": "Unauthorized. The user is not authenticated." + }, + "404": { + "description": "Not Found. User not found." + }, + "500": { + "description": "Internal Server Error." + } + } + } + }, + "/api/v1/login": { + "post": { + "tags": [ + "Authentication" + ], + "summary": "User login.", + "description": "Authenticate a user using LDAP credentials.", + "operationId": "loginUser", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "required": [ + "username", + "password" + ] + } + } + }, + "responses": { + "200": { + "description": "User authenticated successfully." + }, + "401": { + "description": "Authentication failed. Invalid credentials or user not found." + }, + "500": { + "description": "An error occurred during authentication." + } + } + } + }, + "/api/v1/logout": { + "post": { + "tags": [ + "Authentication" + ], + "summary": "User logout.", + "description": "Log out a user and invalidate their access token.", + "operationId": "logoutUser", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accessToken": { + "type": "string" + } + } + }, + "required": [ + "accessToken" + ] + } + } + }, + "responses": { + "200": { + "description": "User logged out successfully." + }, + "400": { + "description": "Bad request. Access token is required." + } + } + } + }, + "/api/v1/refresh": { + "post": { + "tags": [ + "Authentication" + ], + "summary": "Refresh access token.", + "description": "Refresh the user's access token using a refresh token.", + "operationId": "refreshAccessToken", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "username": { + "type": "string" + } + } + }, + "required": [ + "username" + ] + } + } + }, + "responses": { + "200": { + "description": "Access token successfully refreshed.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "newToken": { + "type": "string", + "description": "The new access token." + }, + "newRefreshToken": { + "type": "string", + "description": "The new refresh token." + } + } + } + } + } + }, + "401": { + "description": "User not found or refresh token not found." + } + } + } + } + } +} \ No newline at end of file From a0954b16c7aef4cec0d84d3303a57dada746c9f5 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 30 Nov 2023 12:21:31 -0500 Subject: [PATCH 216/229] add swageer pdf documentation --- api-documentation.pdf | Bin 0 -> 542807 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 api-documentation.pdf diff --git a/api-documentation.pdf b/api-documentation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..302cfac80819cfa1bee52613882a2abbe97e8ae0 GIT binary patch literal 542807 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 X&7EDpo?A&o91etVi;Ajft1|x!UT?N6 literal 0 HcmV?d00001 From a4204574d1e4e8fac64e5b4580faea72df502b45 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 30 Nov 2023 20:55:56 -0500 Subject: [PATCH 217/229] refactor authentication module --- src/modules/authentication/LdapAuth.js | 135 +------------ .../controllers/auth.controller.js | 189 ++++++++++++++++++ src/modules/authentication/functions/index.js | 104 ---------- .../authentication/services/auth.services.js | 34 ++++ 4 files changed, 230 insertions(+), 232 deletions(-) create mode 100644 src/modules/authentication/controllers/auth.controller.js delete mode 100644 src/modules/authentication/functions/index.js create mode 100644 src/modules/authentication/services/auth.services.js diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index a928eb00..7cd934d6 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -4,27 +4,16 @@ const passport = require('passport') const CustomStrategy = require('passport-custom').Strategy -const JwtStrategy = require('../../utils/authentication/strategies/jwtStrategy') +const JwtStrategy = require('@src/utils/authentication/strategies/jwtStrategy') const { authenticate } = require('ldap-authentication') -const userService = require('../../services/user.services')() -const { isSuperAdmin, isAdmin } = require('@src/services/auth.services') -const User = require('../../schemas/user.schema').User -const profileService = require('../../services/profile.services')() -const { checkAuth } = require('@src/middlewares/auth.handler') -const { logout, refresh } = require('./functions/index.js') -const { - storeRefreshToken, - getRefreshToken, -} = require('@src/services/auth.services') +const userService = require('@src/services/user.services')() +const User = require('@src/schemas/user.schema').User -/* helpers */ const { - extractBaseFromDn, - extractGroupsFromDn, -} = require('../../helpers/dnHelper') - -const { signToken } = require('../../utils/authentication/tokens/token_sign') -const { responseSuccess } = require('../../schemas/response.schema') + login, + logout, + refresh, +} = require('@src/modules/authentication/controllers/auth.controller.js') var _backwardCompatible = false var _dn @@ -292,117 +281,7 @@ const initialize = function ( return init(opt, '', router, findFunc, insertFunc, loginUrl, logoutUrl) } -const addUserRegistry = async (user) => { - const { uid, cn, sn, dn, mail } = user - - try { - // Busca al usuario por su nombre de usuario - let existingUser = await User.findOne({ username: uid }) - - if (existingUser) { - // El usuario ya existe, agrega un nuevo registro a su matriz "registry" - existingUser.registry.push({ timestamp: new Date() }) - await existingUser.save() - } else { - // El usuario no existe, crea un nuevo usuario con el registro inicial - const newUser = new User({ - username: uid, - cn, - sn, - dn, - mail, - registry: [{ timestamp: new Date() }], - }) - await newUser.save() - } - - console.log('Registro de usuario exitoso') - } catch (error) { - console.error('Error al agregar registro de usuario:', error) - throw error // Puedes manejar el error según tus necesidades - } -} - -const login = async function (req, res, next) { - passport.authenticate( - 'ldap', - { - successRedirect: '/dashboard', - failureRedirect: '/login', - failureFlash: true, - }, - async (err, user) => { - if (err) { - res.status(401).json({ success: false, message: err.message }) - return - } - if (!user) { - res - .status(401) - .json({ success: false, message: 'Usuario no encontrado' }) - } else { - // Agrega un nuevo registro al usuario - await addUserRegistry(user) - const last_time_logged = await profileService.getLastLoginByUsername( - user.uid - ) - const loginInfo = await profileService.updateLastTimeLogged(user.uid) - // Example usage - const ldapDn = user.dn - const groups = extractGroupsFromDn(ldapDn) - const rootBaseDN = extractBaseFromDn(ldapDn) - const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') - - let roles = ['user'] - const isSpAdmin = await isSuperAdmin(user.uid) - if (isSpAdmin) { - roles = [...roles, 'admin', 'superadmin'] - } else { - const isAdm = await isAdmin(user.uid, localBaseDN) - isAdm ? (roles = [...roles, 'admin']) : [...roles] - } - - const payload = { - sub: user.uid, - dn: user.dn, - uid: user.uid, - groups: groups, - base: rootBaseDN, - localBase: localBaseDN, - firstname: user.givenName, - lastname: user.sn, - fullname: user.cn, - email: user.mail, - ci: user.CI, - roles: roles, - last_time_logged, - loginInfo, - } - - const userObj = { ...user } - const token = signToken(payload, { expiresIn: '15 minutes' }) - const refreshToken = signToken(payload, { expiresIn: '1 hour' }) - - /* await storeRefreshToken(user.uid, refreshToken) */ - - req.login(user, (loginErr) => { - if (loginErr) { - return next(loginErr) - } - if (!!user) { - const data = { - token: token, - refreshToken: refreshToken, - user: userObj, - } - return responseSuccess(res, 'authentication succeeded', data) - } - }) - } - } - )(req, res, next) -} module.exports.init = init module.exports.initialize = initialize diff --git a/src/modules/authentication/controllers/auth.controller.js b/src/modules/authentication/controllers/auth.controller.js new file mode 100644 index 00000000..b75f56e7 --- /dev/null +++ b/src/modules/authentication/controllers/auth.controller.js @@ -0,0 +1,189 @@ +const { isSuperAdmin, isAdmin } = require('@src/services/auth.services') +const profileService = require('@src/services/profile.services')() + +/* helpers */ +const { + extractBaseFromDn, + extractGroupsFromDn, +} = require('@src/helpers/dnHelper') + +const { signToken } = require('@src/utils/authentication/tokens/token_sign') +const { responseSuccess } = require('@src/schemas/response.schema') +const passport = require('passport') + +const { + addToBlackList, + deleteRefreshToken, + getRefreshToken, + storeRefreshToken, +} = require('@src/services/auth.services') + +const { + addUserRegistry, +} = require('@src/modules/authentication/services/auth.services') + +const login = async function (req, res, next) { + passport.authenticate( + 'ldap', + { + successRedirect: '/dashboard', + failureRedirect: '/login', + failureFlash: true, + }, + async (err, user) => { + if (err) { + res.status(401).json({ success: false, message: err.message }) + return + } + if (!user) { + res + .status(401) + .json({ success: false, message: 'Usuario no encontrado' }) + } else { + // Agrega un nuevo registro al usuario + await addUserRegistry(user) + + const last_time_logged = await profileService.getLastLoginByUsername( + user.uid + ) + const loginInfo = await profileService.updateLastTimeLogged(user.uid) + // Example usage + const ldapDn = user.dn + const groups = extractGroupsFromDn(ldapDn) + const rootBaseDN = extractBaseFromDn(ldapDn) + const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') + + let roles = ['user'] + const isSpAdmin = await isSuperAdmin(user.uid) + if (isSpAdmin) { + roles = [...roles, 'admin', 'superadmin'] + } else { + const isAdm = await isAdmin(user.uid, localBaseDN) + isAdm ? (roles = [...roles, 'admin']) : [...roles] + } + + const payload = { + sub: user.uid, + dn: user.dn, + uid: user.uid, + groups: groups, + base: rootBaseDN, + localBase: localBaseDN, + firstname: user.givenName, + lastname: user.sn, + fullname: user.cn, + email: user.mail, + ci: user.CI, + roles: roles, + last_time_logged, + loginInfo, + } + + const userObj = { ...user } + const token = signToken(payload, { expiresIn: '15 minutes' }) + const refreshToken = signToken(payload, { expiresIn: '1 hour' }) + + /* await storeRefreshToken(user.uid, refreshToken) */ + + req.login(user, (loginErr) => { + if (loginErr) { + return next(loginErr) + } + if (!!user) { + const data = { + token: token, + refreshToken: refreshToken, + user: userObj, + } + return responseSuccess(res, 'authentication succeeded', data) + } + }) + } + } + )(req, res, next) +} + +const logout = (req, res) => { + const accessToken = req.body.accessToken // You can adjust this based on how you send the token in the request + clearSession(req) + if (!accessToken) { + return res.status(400).json({ message: 'Access token is required.' }) + } + addToBlackList(req.user.uid, accessToken) + return res.status(200).json({ message: 'User logged out successfully.' }) +} + +const refresh = async (req, res) => { + const { username } = req.body + + const refreshToken = await getRefreshToken(username, (err) => { + if (err) { + console.error('Error getting refresh token:', err) + } + }) + + if (!refreshToken) { + return res.status(401).json({ message: 'Refresh token not found' }) + } + + const response = await userService.getByUsername(username) + const user = response[0] + + if (!user) { + return res.status(401).json({ message: 'User not found' }) + } + + const ldapDn = user.dn + const groups = extractGroupsFromDn(ldapDn) + const rootBaseDN = extractBaseFromDn(ldapDn) + const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') + + const last_time_logged = await profileService.getLastLoginByUsername(user.uid) + const loginInfo = await profileService.updateLastTimeLogged(user.uid) + + const isAdmin = true + + const payload = { + sub: user.uid, + dn: user.dn, + uid: user.uid, + groups: groups, + base: rootBaseDN, + localBase: localBaseDN, + firstname: user.givenName, + lastname: user.sn, + fullname: user.cn, + email: user.mail, + ci: user.CI, + roles: isAdmin ? ['admin', 'user'] : ['user'], + last_time_logged, + loginInfo, + } + + const newToken = signToken(payload, { expiresIn: '15 minutes' }) + const newRefreshToken = signToken(payload, { expiresIn: '1 day' }) + + await deleteRefreshToken(username) + setTimeout(async () => { + storeRefreshToken(user.uid, newRefreshToken) + }, 100) + + res.status(200).json({ + newToken, + newRefreshToken, + }) +} + +const clearSession = (req) => { + return new Promise((resolve, reject) => { + req.session.destroy((err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) +} + +module.exports = { login, logout, refresh, clearSession } diff --git a/src/modules/authentication/functions/index.js b/src/modules/authentication/functions/index.js deleted file mode 100644 index e741cdae..00000000 --- a/src/modules/authentication/functions/index.js +++ /dev/null @@ -1,104 +0,0 @@ -const { - addToBlackList, - deleteRefreshToken, - getRefreshToken, - storeRefreshToken, -} = require('@src/services/auth.services') - -const UserServices = require('@src/services/user.services') -const ProfileServices = require('@src/services/profile.services') - -const { - extractBaseFromDn, - extractGroupsFromDn, -} = require('@src/helpers/dnHelper') - -const { signToken } = require('@src/utils/authentication/tokens/token_sign') - -const userService = UserServices() -const profileService = ProfileServices() - -const clearSession = (req) => { - return new Promise((resolve, reject) => { - req.session.destroy((err) => { - if (err) { - reject(err) - } else { - resolve() - } - }) - }) -} - -const logout = (req, res) => { - const accessToken = req.body.accessToken // You can adjust this based on how you send the token in the request - clearSession(req) - if (!accessToken) { - return res.status(400).json({ message: 'Access token is required.' }) - } - addToBlackList(req.user.uid, accessToken) - return res.status(200).json({ message: 'User logged out successfully.' }) -} - -const refresh = async (req, res) => { - const { username } = req.body - - const refreshToken = await getRefreshToken(username, (err) => { - if (err) { - console.error('Error getting refresh token:', err) - } - }) - - if (!refreshToken) { - return res.status(401).json({ message: 'Refresh token not found' }) - } - - const response = await userService.getByUsername(username) - const user = response[0] - - if (!user) { - return res.status(401).json({ message: 'User not found' }) - } - - const ldapDn = user.dn - const groups = extractGroupsFromDn(ldapDn) - const rootBaseDN = extractBaseFromDn(ldapDn) - const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') - - const last_time_logged = await profileService.getLastLoginByUsername(user.uid) - const loginInfo = await profileService.updateLastTimeLogged(user.uid) - - const isAdmin = true - - const payload = { - sub: user.uid, - dn: user.dn, - uid: user.uid, - groups: groups, - base: rootBaseDN, - localBase: localBaseDN, - firstname: user.givenName, - lastname: user.sn, - fullname: user.cn, - email: user.mail, - ci: user.CI, - roles: isAdmin ? ['admin', 'user'] : ['user'], - last_time_logged, - loginInfo, - } - - const newToken = signToken(payload, { expiresIn: '15 minutes' }) - const newRefreshToken = signToken(payload, { expiresIn: '1 day' }) - - await deleteRefreshToken(username) - setTimeout(async () => { - storeRefreshToken(user.uid, newRefreshToken) - }, 100) - - res.status(200).json({ - newToken, - newRefreshToken, - }) -} - -module.exports = { logout, refresh } diff --git a/src/modules/authentication/services/auth.services.js b/src/modules/authentication/services/auth.services.js new file mode 100644 index 00000000..45807654 --- /dev/null +++ b/src/modules/authentication/services/auth.services.js @@ -0,0 +1,34 @@ +const User = require('@src/schemas/user.schema').User + +const addUserRegistry = async (user) => { + const { uid, cn, sn, dn, mail } = user + + try { + // Busca al usuario por su nombre de usuario + let existingUser = await User.findOne({ username: uid }) + + if (existingUser) { + // El usuario ya existe, agrega un nuevo registro a su matriz "registry" + existingUser.registry.push({ timestamp: new Date() }) + await existingUser.save() + } else { + // El usuario no existe, crea un nuevo usuario con el registro inicial + const newUser = new User({ + username: uid, + cn, + sn, + dn, + mail, + registry: [{ timestamp: new Date() }], + }) + await newUser.save() + } + + console.log('Registro de usuario exitoso') + } catch (error) { + console.error('Error al agregar registro de usuario:', error) + throw error // Puedes manejar el error según tus necesidades + } +} + +module.exports = { addUserRegistry } From e480ff057732e3b2bfa38fc3a7bf029b5c930c96 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 30 Nov 2023 21:29:19 -0500 Subject: [PATCH 218/229] add sigenu integration module --- src/api/v1/swagger.js | 4 + src/modules/authentication/LdapAuth.js | 46 +++++++- .../controllers/auth.controller.js | 102 ++++++++++++++++++ .../helpers/sigenu.helper.js | 36 +++++++ 4 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 src/modules/sigenu_integration/controllers/auth.controller.js create mode 100644 src/modules/sigenu_integration/helpers/sigenu.helper.js diff --git a/src/api/v1/swagger.js b/src/api/v1/swagger.js index 1821be49..c129e070 100644 --- a/src/api/v1/swagger.js +++ b/src/api/v1/swagger.js @@ -66,6 +66,10 @@ const options = { name: 'LDAP', description: 'API para la administración de la configuración del LDAP', }, + { + name: 'SIGENU', + description: 'API para la integracion con los modulos de SIGENU', + }, ], components: { diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 7cd934d6..59fbe1ed 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -8,6 +8,11 @@ const JwtStrategy = require('@src/utils/authentication/strategies/jwtStrategy') const { authenticate } = require('ldap-authentication') const userService = require('@src/services/user.services')() const User = require('@src/schemas/user.schema').User +const config = require('@src/config/config') +const { + login_sigenu, +} = require('@src/modules/sigenu_integration/controllers/auth.controller') +const { version } = config.api const { login, @@ -36,6 +41,7 @@ var _usernameAttributeName * @param {function} insertFunc - function(user) to upsert user into local db * @param {string} [loginUrl] - path to login page. Default: /login * @param {string} [logoutUrl] - path to logout page. Default: /logout + * @param {string} [sigenuLoginUrl] */ var init = function ( @@ -57,9 +63,10 @@ var init = function ( } _findFunc = findFunc _insertFunc = insertFunc - _loginUrl = loginUrl || '/api/v1/login' - _logoutUrl = logoutUrl || '/api/v1/logout' - _refreshUrl = '/api/v1/refresh' + _loginUrl = loginUrl || `/api/${version}/login` + _logoutUrl = logoutUrl || `/api/${version}/logout` + _sigenuLoginUrl = `/api/${version}/sigenu/login` + _refreshUrl = '/api/${version}/refresh' _usernameAttributeName = '' passport.use( @@ -194,6 +201,37 @@ var init = function ( router.post(_loginUrl, login) + /** + * @openapi + * /api/v1/sigenu/login: + * post: + * tags: [Authentication, SIGENU] + * summary: User login. + * description: Authenticate a user using LDAP credentials. + * operationId: loginUser + * requestBody: + * content: + * application/json: + * schema: + * type: object + * properties: + * username: + * type: string + * password: + * type: string + * required: + * - username + * - password + * responses: + * 200: + * description: User authenticated successfully. + * 401: + * description: Authentication failed. Invalid credentials or user not found. + * 500: + * description: An error occurred during authentication. + */ + router.post(_sigenuLoginUrl, login_sigenu) + /** * @openapi * /api/v1/logout: @@ -281,7 +319,5 @@ const initialize = function ( return init(opt, '', router, findFunc, insertFunc, loginUrl, logoutUrl) } - - module.exports.init = init module.exports.initialize = initialize diff --git a/src/modules/sigenu_integration/controllers/auth.controller.js b/src/modules/sigenu_integration/controllers/auth.controller.js new file mode 100644 index 00000000..41274067 --- /dev/null +++ b/src/modules/sigenu_integration/controllers/auth.controller.js @@ -0,0 +1,102 @@ +const { isSuperAdmin, isAdmin } = require('@src/services/auth.services') +const profileService = require('@src/services/profile.services')() + +/* helpers */ +const { + extractBaseFromDn, + extractGroupsFromDn, +} = require('@src/helpers/dnHelper') + +const { signToken } = require('@src/utils/authentication/tokens/token_sign') +const { responseSuccess } = require('@src/schemas/response.schema') +const passport = require('passport') + +const { + addUserRegistry, +} = require('@src/modules/authentication/services/auth.services') + +const { + parseLoginPayload, +} = require('@src/modules/sigenu_integration/helpers/sigenu.helper') + +const login_sigenu = async function (req, res, next) { + passport.authenticate( + 'ldap', + { + successRedirect: '/dashboard', + failureRedirect: '/login', + failureFlash: true, + }, + async (err, user) => { + if (err) { + res.status(401).json({ success: false, message: err.message }) + return + } + if (!user) { + res + .status(401) + .json({ success: false, message: 'Usuario no encontrado' }) + } else { + // Agrega un nuevo registro al usuario + await addUserRegistry(user) + + const last_time_logged = await profileService.getLastLoginByUsername( + user.uid + ) + const loginInfo = await profileService.updateLastTimeLogged(user.uid) + // Example usage + const ldapDn = user.dn + const groups = extractGroupsFromDn(ldapDn) + const rootBaseDN = extractBaseFromDn(ldapDn) + const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') + + let roles = ['user'] + const isSpAdmin = await isSuperAdmin(user.uid) + if (isSpAdmin) { + roles = [...roles, 'admin', 'superadmin'] + } else { + const isAdm = await isAdmin(user.uid, localBaseDN) + isAdm ? (roles = [...roles, 'admin']) : [...roles] + } + + const parsedPayload = parseLoginPayload(user) + + const payload = { + sub: user.uid, + dn: user.dn, + uid: user.uid, + groups: groups, + base: rootBaseDN, + localBase: localBaseDN, + firstname: user.givenName, + lastname: user.sn, + fullname: user.cn, + email: user.mail, + ci: user.CI, + roles: roles, + last_time_logged, + loginInfo, + } + + const userObj = { ...user } + const token = signToken(payload, { expiresIn: '15 minutes' }) + const refreshToken = signToken(payload, { expiresIn: '1 hour' }) + + /* await storeRefreshToken(user.uid, refreshToken) */ + + req.login(user, (loginErr) => { + if (loginErr) { + return next(loginErr) + } + if (!!user) { + res.status(200).json({ + ...parsedPayload, + }) + } + }) + } + } + )(req, res, next) +} + +module.exports = { login_sigenu } diff --git a/src/modules/sigenu_integration/helpers/sigenu.helper.js b/src/modules/sigenu_integration/helpers/sigenu.helper.js new file mode 100644 index 00000000..30808e4b --- /dev/null +++ b/src/modules/sigenu_integration/helpers/sigenu.helper.js @@ -0,0 +1,36 @@ +/* { + "email": "MiCorreo", + "facultyId": "Id de facultad en SIGENU |null", + "townUniversityId": "Id de la filial o sede universitaria en SIGENU |null", + "identification": "MiCI", + "lastname": "SegundoApellido", + "name": "Nombre", + "role": "PROFESSOR|STUDENT", + "status": "ACTIVE|BLOCKED", + "surname": "PrimerApellido", + "username": "MiUsuario" +} + */ + +const parseLoginPayload = (ldapUser) => { + + const payload = { + email: ldapUser?.mail || ldapUser?.maildrop, + facultyId: ldapUser?.area || ldapUser?.workArea || null, + townUniversityId: ldapUser?.nameInstitution || null, + identification: ldapUser?.CI, + lastname: ldapUser?.lastName, + name: ldapUser?.cn || ldapUser?.name, + role: ldapUser?.userType === 'Estudiante' ? 'STUDENT' : 'PROFESSOR', + status: + ldapUser?.userStatus === 'Activo' || ldapUser?.userStatus === true + ? 'ACTIVE' + : 'BLOCKED', + surname: ldapUser?.middleName, + username: ldapUser?.uid, + } + + return payload +} + +module.exports = { parseLoginPayload } From 10ce0139ed15202d07322d640c10c508a43e2a59 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 30 Nov 2023 23:40:27 -0500 Subject: [PATCH 219/229] update morgan middleware to receive basic auth req --- src/middlewares/morganMiddleware.js | 28 +++++++++++++++++---- src/utils/authentication/tokens/jwtUtils.js | 17 +++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/middlewares/morganMiddleware.js b/src/middlewares/morganMiddleware.js index 5a0b6f2d..3628f0f3 100644 --- a/src/middlewares/morganMiddleware.js +++ b/src/middlewares/morganMiddleware.js @@ -3,7 +3,10 @@ const winston = require('winston') const mongoose = require('mongoose') // Import mongoose const { MongoDB } = require('winston-mongodb') // Import MongoDB transport const config = require('@src/config/config') -const { decodeJWT } = require('@src/utils/authentication/tokens/jwtUtils') +const { + decodeJWT, + getAuthTokenType, +} = require('@src/utils/authentication/tokens/jwtUtils') const logger = require('@src/utils/logger') const { validate } = require('@src/schemas/logs.schema') @@ -14,10 +17,25 @@ morgan.token('user', (req) => { const logFormat = (tokens, req, res) => { const status = tokens.status(req, res) - const payload = - req.headers.authorization !== undefined - ? decodeJWT(req.headers.authorization.split(' ')[1]) - : undefined + const authorizationHeader = req.headers.authorization + const authType = getAuthTokenType(authorizationHeader) + + let payload + + if (authType?.type === 'Bearer') { + payload = decodeJWT(authorizationHeader.split(' ')[1]) + } else if (authType?.type === 'Basic') { + const decodedToken = Buffer.from( + authorizationHeader.split(' ')[1], + 'base64' + ).toString('utf-8') + const [username, password] = decodedToken.split(':') + payload = { + uid: username, + dn: undefined, + localBase: undefined, + } + } const log = { method: tokens.method(req, res), diff --git a/src/utils/authentication/tokens/jwtUtils.js b/src/utils/authentication/tokens/jwtUtils.js index 45e948b5..bedfd2cd 100644 --- a/src/utils/authentication/tokens/jwtUtils.js +++ b/src/utils/authentication/tokens/jwtUtils.js @@ -5,6 +5,23 @@ const decodeJWT = (jwt) => { return decodedToken } +const getAuthTokenType = (authorizationHeader) => { + if (!authorizationHeader) { + return undefined + } + + const [authType, token] = authorizationHeader.split(' ') + + if (authType.toLowerCase() === 'bearer') { + return { type: 'Bearer', token } + } else if (authType.toLowerCase() === 'basic') { + return { type: 'Basic', token } + } + + return undefined +} + module.exports = { decodeJWT, + getAuthTokenType } From e96491f17f3dbe5514fec4f68d406e9bc895d572 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 1 Dec 2023 01:41:55 -0500 Subject: [PATCH 220/229] add professor endpoint to sigenu_modules --- src/api/v1/routes/routes.js | 3 + src/controllers/user.controller.js | 2 + src/helpers/convertQueryToFilter.js | 3 + src/middlewares/basicAuth.handler.js | 102 ++++++++++++++++++ .../controllers/proffesors.controller.js | 67 ++++++++++++ .../helpers/sigenu.helper.js | 63 ++++++++++- .../helpers/validation.helper.js | 14 +++ .../sigenu_integration/routes/routes.js | 11 ++ 8 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 src/middlewares/basicAuth.handler.js create mode 100644 src/modules/sigenu_integration/controllers/proffesors.controller.js create mode 100644 src/modules/sigenu_integration/helpers/validation.helper.js create mode 100644 src/modules/sigenu_integration/routes/routes.js diff --git a/src/api/v1/routes/routes.js b/src/api/v1/routes/routes.js index 9d64da6f..4fd3e01e 100644 --- a/src/api/v1/routes/routes.js +++ b/src/api/v1/routes/routes.js @@ -9,6 +9,8 @@ const dnController = require('@src/controllers/dn.controller') const logsController = require('@src/modules/logsManagement/controllers/logs.controller') const ldapController = require('@src/controllers/ldap.controller') +const addSigenuRoutes = require('@src/modules/sigenu_integration/routes/routes') + const addRoutes = (app) => { app.use(`/api/${version}/users`, userController) app.use(`/api/${version}/groups`, groupController) @@ -18,6 +20,7 @@ const addRoutes = (app) => { app.use(`/api/${version}/`, recoveryPasswordController) app.use(`/api/${version}/`, updatePasswordController) app.use(`/api/${version}/logs`, logsController) + addSigenuRoutes(app) } module.exports = addRoutes diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 872484bd..fe695501 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -217,6 +217,8 @@ router.get('/', async (req, res) => { const queryFilter = createLdapFilterFromQuery(req.query) const ldapFilter = `(&(objectClass=person)${queryFilter})` + console.log('ldapFilter', ldapFilter) + // Define the LDAP attributes you want to retrieve const attributes = null diff --git a/src/helpers/convertQueryToFilter.js b/src/helpers/convertQueryToFilter.js index 1bda5ef6..ed47a62b 100644 --- a/src/helpers/convertQueryToFilter.js +++ b/src/helpers/convertQueryToFilter.js @@ -9,6 +9,9 @@ const attributeFilters = { email: (value) => `maildrop=${value}`, lastName: (value) => `lastName=${value}`, lastname: (value) => `lastName=${value}`, + name: (value) => `name=${value}`, + middleName: (value) => `middleName=${value}`, + middlename: (value) => `middleName=${value}`, sex: (value) => `sex=${value}`, area: (value) => `area=${value}`, userCondition: (value) => `userCondition=${value}`, diff --git a/src/middlewares/basicAuth.handler.js b/src/middlewares/basicAuth.handler.js new file mode 100644 index 00000000..9cfb4b0d --- /dev/null +++ b/src/middlewares/basicAuth.handler.js @@ -0,0 +1,102 @@ +const { getByUsername } = require('@src/services/user.services')() +const { authenticate } = require('ldap-authentication') +const config = require('@src/config/config') +const { isSuperAdmin, isAdmin } = require('@src/services/auth.services') +const profileService = require('@src/services/profile.services')() + +/* helpers */ +const { + extractBaseFromDn, + extractGroupsFromDn, +} = require('@src/helpers/dnHelper') + +const { + addUserRegistry, +} = require('@src/modules/authentication/services/auth.services') + +const LDAP_URL = config.ldap.url || 'ldap://10.8.1.104' + +const generatePayloadAuth = async (user) => { + await addUserRegistry(user) + + const last_time_logged = await profileService.getLastLoginByUsername(user.uid) + const loginInfo = await profileService.updateLastTimeLogged(user.uid) + // Example usage + const ldapDn = user.dn + const groups = extractGroupsFromDn(ldapDn) + const rootBaseDN = extractBaseFromDn(ldapDn) + const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') + + let roles = ['user'] + const isSpAdmin = await isSuperAdmin(user.uid) + if (isSpAdmin) { + roles = [...roles, 'admin', 'superadmin'] + } else { + const isAdm = await isAdmin(user.uid, localBaseDN) + isAdm ? (roles = [...roles, 'admin']) : [...roles] + } + + const payload = { + dn: user.dn, + uid: user.uid, + groups: groups, + base: rootBaseDN, + localBase: localBaseDN, + firstname: user.givenName, + lastname: user.sn, + fullname: user.cn, + email: user.mail, + ci: user.CI, + roles, + last_time_logged, + loginInfo, + } + + return payload +} + +// Middleware for basic authentication +const basicAuth = async (req, res, next) => { + try { + const authHeader = req.headers.authorization + + if (!authHeader || !authHeader.startsWith('Basic ')) { + res.status(401).send('Unauthorized') + return + } + + const base64Credentials = authHeader.split(' ')[1] + const credentials = Buffer.from(base64Credentials, 'base64').toString( + 'ascii' + ) + const [username, password] = credentials.split(':') + + if (!username || !password) throw new Error('Invalid username or password') + + const user = await getByUsername(username) + + if (!user) { + res.status(401).send('No user found') + return + } + + const authenticated = await authenticate({ + ldapOpts: { url: LDAP_URL }, + userDn: user.dn, + userPassword: password, + }) + + if (authenticated) { + const payload = await generatePayloadAuth(user) + req.user = payload + next() // Authentication successful, proceed to the next middleware or route handler + } else { + res.status(401).send('Unauthorized') + } + } catch (error) { + console.error('Error in basicAuth middleware:', error) + res.status(500).send(error.message) + } +} + +module.exports = { basicAuth } diff --git a/src/modules/sigenu_integration/controllers/proffesors.controller.js b/src/modules/sigenu_integration/controllers/proffesors.controller.js new file mode 100644 index 00000000..c4cb15ee --- /dev/null +++ b/src/modules/sigenu_integration/controllers/proffesors.controller.js @@ -0,0 +1,67 @@ +const express = require('express') +const router = express.Router() +const UserServices = require('@src/services/user.services.js') +const { basicAuth } = require('@src/middlewares/basicAuth.handler') +const service = UserServices() +const config = require('@src/config/config') + +const { + validateAttributes, +} = require('@src/modules/sigenu_integration/helpers/validation.helper') +const { + createProfessorsFilter, + parseLdapEntryToProfessorDto, +} = require('@src/modules/sigenu_integration/helpers/sigenu.helper') + +router.use(basicAuth) + +const BASE_ROUTE = '/professors' +const USER_TYPE = 'Trabajador Docente' +const LIMIT = 100000 +const PAGE = 1 +router.post(`${BASE_ROUTE}/`, async (req, res) => { + try { + const { roles, localBase } = req.user + const queryJson = req.body + const requiredAttributes = [ + 'identification', + 'name', + 'lastname', + 'surname', + 'email', + ] + // Validate the attributes + validateAttributes(queryJson, requiredAttributes) + const queryFilter = createProfessorsFilter(queryJson) + const ldapFilter = `(&(objectClass=person)(userType=${USER_TYPE})${queryFilter})` + + const attributes = null + console.log('roles', roles) + console.log('ldapFilter', ldapFilter) + + // Call the performLdapSearch function to retrieve users matching the group filters + const searchResults = await service.handleFilteredSearch( + roles.includes('superadmin') ? config.ldap.base : localBase, + ldapFilter, + attributes, + PAGE, + LIMIT + ) + + console.log('searchResults', searchResults) + + const parsedResults = parseLdapEntryToProfessorDto(searchResults) + + console.log('parsedResults', parsedResults) + + res.status(200).json(parsedResults) + } catch (error) { + res.status(400).json({ + success: false, + message: 'Invalid request body', + error: error.message, + }) + } +}) + +module.exports = router diff --git a/src/modules/sigenu_integration/helpers/sigenu.helper.js b/src/modules/sigenu_integration/helpers/sigenu.helper.js index 30808e4b..518c4db4 100644 --- a/src/modules/sigenu_integration/helpers/sigenu.helper.js +++ b/src/modules/sigenu_integration/helpers/sigenu.helper.js @@ -12,8 +12,22 @@ } */ -const parseLoginPayload = (ldapUser) => { +/* { + "address": "Dirección Particular", + "email": "Correo del profesor", + "identification": "CI del profesor", + "lastname": "Segundo apellido", + "name": "Nombre", + "phone": "Teléfono", + "scientificCategory": "Categoría Científica|null", + "surname": "Segundo apellido", + "teachingCategory": "Categoría Docente|null ", + "area": "Nombre del area a la que pertece|null", + "user": "Nombre de usuario" + } + */ +const parseLoginPayload = (ldapUser) => { const payload = { email: ldapUser?.mail || ldapUser?.maildrop, facultyId: ldapUser?.area || ldapUser?.workArea || null, @@ -33,4 +47,49 @@ const parseLoginPayload = (ldapUser) => { return payload } -module.exports = { parseLoginPayload } +const createProfessorsFilter = (queryJson) => { + const { identification, name, lastname, surname, email } = queryJson + const filters = [] + + if (!!identification && identification !== '') + filters.push(`(CI=${identification})`) + if (!!name && name !== '') filters.push(`(name=${name})`) + if (!!lastname && lastname !== '') filters.push(`(lastName=${lastname})`) + if (!!surname && surname !== '') filters.push(`(middleName=${surname})`) + if (!!email && email !== '') filters.push(`(mail=${email})`) + + if (filters.length === 0) { + return '' + } + + const ldapFilter = filters.join('') + + return ldapFilter +} + +// Parsea un array de entradas del LDAP a un array de Json customizados +const parseLdapEntryToProfessorDto = (entries) => { + const parsedEntries = entries.map((entry) => { + return { + address: entry.homeAddress, + email: entry.mail, + identification: entry.CI, + lastname: entry.lastName, + name: entry.name, + phone: entry.telephoneNumber, + scientificCategory: entry.scientificCategory || null, + surname: entry.middleName, + teachingCategory: entry.schoolLevel || null, + area: entry.area || null, + user: entry.user, + } + }) + + return parsedEntries +} + +module.exports = { + parseLoginPayload, + createProfessorsFilter, + parseLdapEntryToProfessorDto, +} diff --git a/src/modules/sigenu_integration/helpers/validation.helper.js b/src/modules/sigenu_integration/helpers/validation.helper.js new file mode 100644 index 00000000..aceef1a0 --- /dev/null +++ b/src/modules/sigenu_integration/helpers/validation.helper.js @@ -0,0 +1,14 @@ +const validateAttributes = (attributes, requiredAttributes) => { + const missingAttributes = requiredAttributes.filter( + (attr) => !attributes.hasOwnProperty(attr) + ) + + if (missingAttributes.length > 0) { + const errorMessage = `Missing required attribute(s): ${missingAttributes.join( + ', ' + )}` + throw new Error(errorMessage) + } +} + +module.exports = { validateAttributes } diff --git a/src/modules/sigenu_integration/routes/routes.js b/src/modules/sigenu_integration/routes/routes.js new file mode 100644 index 00000000..77dd76a3 --- /dev/null +++ b/src/modules/sigenu_integration/routes/routes.js @@ -0,0 +1,11 @@ +const config = require('@src/config/config') +const { version } = config.api +const professorsController = require('@src/modules/sigenu_integration/controllers/proffesors.controller') + +const BASE_ROUTE = '/sigenu' + +const addRoutes = (app) => { + app.use(`/api/${version}${BASE_ROUTE}`, professorsController) +} + +module.exports = addRoutes From 2cac6334f3a346f6fe00a1bfaf2df0508c3e3f3f Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 1 Dec 2023 01:44:25 -0500 Subject: [PATCH 221/229] update postman collection --- LDAP_INF.postman_collection.json | 271 ++++++++++++++++++++++++++++--- 1 file changed, 245 insertions(+), 26 deletions(-) diff --git a/LDAP_INF.postman_collection.json b/LDAP_INF.postman_collection.json index 456371f3..02b2c71f 100644 --- a/LDAP_INF.postman_collection.json +++ b/LDAP_INF.postman_collection.json @@ -22,7 +22,7 @@ "header": [], "body": { "mode": "raw", - "raw": " {\r\n \"username\": \"javier.lorenzo\",\r\n \"password\": \"85100804886\"\r\n} \r\n\r\n\r\n\r\n/* {\r\n \"username\": \"ahmedivan.gonzalez\",\r\n \"password\": \"Ba12345678#\"\r\n} */", + "raw": " {\r\n \"username\": \"javier.lorenzo\",\r\n \"password\": \"85100804886\"\r\n} \r\n\r\n\r\n\r\n /* {\r\n \"username\": \"ahmedivan.gonzalez\",\r\n \"password\": \"Ba12345678#\"\r\n} */\r\n\r\n\r\n/* {\r\n \"username\":\"fernando.picayo\",\r\n \"password\":\"84110906021\"\r\n} */\r\n\r\n/* {\r\n \"username\": \"ssigenu\",\r\n \"password\": \"RSEfxVSL90rGP#LMs3v%\"\r\n} */ ", "options": { "raw": { "language": "json" @@ -58,7 +58,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYXZpZXIubG9yZW56byIsImRuIjoidWlkPWphdmllci5sb3JlbnpvLG91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsInVpZCI6Imphdmllci5sb3JlbnpvIiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiYXJlYUNlbnRyYWwiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6Imphdmllci5sb3JlbnpvIiwibGFzdG5hbWUiOiJMb3JlbnpvIE5leXJhIiwiZnVsbG5hbWUiOiJKYXZpZXIiLCJlbWFpbCI6Imphdmllci5sb3JlbnpvQGN1amFlLmVkdS5jdSIsImNpIjoiODUxMDA4MDQ4ODYiLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMDktMDhUMjI6MzM6NTAuOTQ3WiIsImxvZ2luSW5mbyI6IjkvOC8yMDIzLCA2OjQ0OjIxIFBNIiwiaWF0IjoxNjk0MjEzMDYxLCJleHAiOjE2OTQyMTM5NjF9.WDLIWhLc_Sa_fWOa5uPwJeSodfo7bDKMtU2m-GWr4H8\"\r\n}", + "raw": "{\r\n \"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbInVzZXIiLCJhZG1pbiJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOS0yMlQxNTozMDo1Ni4wMDFaIiwibG9naW5JbmZvIjoiOS8yMi8yMDIzLCAxMjoxNTo1MyBQTSIsImlhdCI6MTY5NTM5OTM1MywiZXhwIjoxNjk1NDAwMjUzfQ.B7l3Ol0aC_u4572k93ifHfsfXgeNpzrs77kCi2cY8Wc\"\r\n}", "options": { "raw": { "language": "json" @@ -85,7 +85,7 @@ "bearer": [ { "key": "token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYXZpZXIubG9yZW56byIsImRuIjoidWlkPWphdmllci5sb3JlbnpvLG91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsInVpZCI6Imphdmllci5sb3JlbnpvIiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiYXJlYUNlbnRyYWwiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6Imphdmllci5sb3JlbnpvIiwibGFzdG5hbWUiOiJMb3JlbnpvIE5leXJhIiwiZnVsbG5hbWUiOiJKYXZpZXIiLCJlbWFpbCI6Imphdmllci5sb3JlbnpvQGN1amFlLmVkdS5jdSIsImNpIjoiODUxMDA4MDQ4ODYiLCJyb2xlcyI6WyJhZG1pbiIsInVzZXIiXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMDktMDhUMjM6MTc6NDMuMDU1WiIsImxvZ2luSW5mbyI6IjkvOC8yMDIzLCA3OjMyOjA5IFBNIiwiaWF0IjoxNjk0MjE1OTI5LCJleHAiOjE2OTQyMTY4Mjl9.E2gw4olFGmZYWHZDVegbxG_NvEIO7M5I-dJ0GKC3iW8", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJHb256YWxleiBCZXRhbmNvdXJ0IiwiZnVsbG5hbWUiOiJBaG1lZCIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMTAtMjJUMjI6MjI6NTcuMjc5WiIsImxvZ2luSW5mbyI6IjEwLzIyLzIwMjMsIDExOjE2OjA2IFBNIiwiaWF0IjoxNjk4MDMwOTY3LCJleHAiOjE2OTgxMTczNjd9.VEStEVLdOTuOOEzbKWpe_pfdwShG7raZ956jNQbKFts", "type": "string" } ] @@ -121,7 +121,7 @@ "bearer": [ { "key": "token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImVtYWlsIjoiYWhtZWRpZ2xlekBnbWFpbC5jb20iLCJpYXQiOjE2OTQ0NzgyMjksImV4cCI6MTY5NDQ3OTEyOX0.GvWGo7QdMh1QnSmih5v0FR3QeBYm3rbhWeYEwLcEuWw", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsIm5hbWUiOiJBaG1lZCBJdsOhbiIsImVtYWlsIjoiYWhtZWRpZ2xlekBnbWFpbC5jb20iLCJpYXQiOjE2OTgwMzE1NTUsImV4cCI6MTY5ODAzMjQ1NX0.r9MD3WdBIUgIDrZTWUjEhql9v-o6lzDKizUCu4mDI-k", "type": "string" } ] @@ -130,7 +130,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"recoveryCode\": '385268,\r\n \"newPassword\": \"A1222234a#\"\r\n}", + "raw": "{\r\n \"recoveryCode\": \"892945\",\r\n \"newPassword\": \"A1222234a#\",\r\n \"confirmPassword\": \"A1222234a#\"\r\n}", "options": { "raw": { "language": "json" @@ -154,6 +154,15 @@ "request": { "method": "POST", "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"emailOrUsername\":\"ahmediglez@gmail.com\"\r\n\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { "raw": "{{server}}/forgot-password", "host": [ @@ -295,7 +304,7 @@ "bearer": [ { "key": "token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJHb256YWxleiBCZXQiLCJmdWxsbmFtZSI6IkFobWVkY2l0byIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMTAtMDRUMDI6NTc6MjMuNTAwWiIsImxvZ2luSW5mbyI6IjEwLzQvMjAyMywgNzoyNzo0MSBQTSIsImlhdCI6MTY5NjQ2MjA2MiwiZXhwIjoxNjk2NDYyOTYyfQ.sVwLQr0KuvXQ4LQn-FGkkJ8K2BLNbJe1nJ9udzdNe8k", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJHb256YWxleiBCZXQiLCJmdWxsbmFtZSI6IkFobWVkY2l0byIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMTAtMDRUMjM6Mjc6NDEuNjk4WiIsImxvZ2luSW5mbyI6IjEwLzQvMjAyMywgMTA6NTY6NDMgUE0iLCJpYXQiOjE2OTY0NzQ2MDYsImV4cCI6MTY5NjQ3NzMwNn0.G7ACYk4AZj1EK1Zw8rIXVX6yYYDVSNqm4jO56wAZRv0", "type": "string" } ] @@ -304,7 +313,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"baseDN\": \"ou=grupos,ou=informatica,dc=cujae,dc=edu,dc=cu\"\r\n}", + "raw": "{\r\n \"baseDN\": \"ou=roles,ou=informatica,dc=cujae,dc=edu,dc=cu\"\r\n}", "options": { "raw": { "language": "json" @@ -331,21 +340,33 @@ "item": [ { "name": "get all", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, "request": { "auth": { "type": "bearer", "bearer": [ { "key": "token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbImFkbWluIiwidXNlciJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOC0zMFQwMjo0MDoyMC4wNzdaIiwibG9naW5JbmZvIjoiOC8yOS8yMDIzLCAxMToyMToxOCBQTSIsImlhdCI6MTY5MzM2NTY3OCwiZXhwIjoxNjkzMzY4Mzc4fQ.IRKbK1YSm43BFTXvCXGoGJUBBtafDnfPIeiM0ACGvYc", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYXZpZXIubG9yZW56byIsImRuIjoidWlkPWphdmllci5sb3JlbnpvLG91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsInVpZCI6Imphdmllci5sb3JlbnpvIiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiYXJlYUNlbnRyYWwiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6Imphdmllci5sb3JlbnpvIiwibGFzdG5hbWUiOiJMb3JlbnpvIE5leXJhIiwiZnVsbG5hbWUiOiJKYXZpZXIiLCJlbWFpbCI6Imphdmllci5sb3JlbnpvQGN1amFlLmVkdS5jdSIsImNpIjoiODUxMDA4MDQ4ODYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iLCJzdXBlcmFkbWluIl0sImxhc3RfdGltZV9sb2dnZWQiOiIyMDIzLTExLTI3VDA0OjMzOjEyLjQwMloiLCJsb2dpbkluZm8iOiIxMS8yNy8yMDIzLCA0OjMzOjEyIEFNIiwiaWF0IjoxNzAxMDU5NTkzLCJleHAiOjE3MDEwNjIyOTN9.Rg6xm0AdLCVMBbQ2Zh9sTO-54On003Wnv0lLPLzp83o", "type": "string" } ] }, "method": "GET", "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { - "raw": "{{server}}/users?all=true", + "raw": "{{server}}/users?lastName=Gonzalez&studentYear=4&userCondition=Seminterno&userStatus=Activo", "host": [ "{{server}}" ], @@ -354,8 +375,20 @@ ], "query": [ { - "key": "all", - "value": "true" + "key": "lastName", + "value": "Gonzalez" + }, + { + "key": "studentYear", + "value": "4" + }, + { + "key": "userCondition", + "value": "Seminterno" + }, + { + "key": "userStatus", + "value": "Activo" } ] } @@ -419,7 +452,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"baseDN\": \"ou=informatica,cn=cujae,cn=edu,cn=cu\"\r\n}", + "raw": "{\r\n \"baseDN\": \"ou=informatica,dc=cujae,dc=edu,dc=cu\"\r\n}", "options": { "raw": { "language": "json" @@ -427,13 +460,19 @@ } }, "url": { - "raw": "{{server}}/users/baseDN", + "raw": "{{server}}/users/baseDN?limit=50000", "host": [ "{{server}}" ], "path": [ "users", "baseDN" + ], + "query": [ + { + "key": "limit", + "value": "50000" + } ] } }, @@ -447,7 +486,7 @@ "bearer": [ { "key": "token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJHb256YWxleiBCZXRhbmNvdXJ0IiwiZnVsbG5hbWUiOiJBaG1lZCIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMTAtMjJUMTk6MTI6NTEuNDExWiIsImxvZ2luSW5mbyI6IjEwLzIyLzIwMjMsIDM6MTM6MzggUE0iLCJpYXQiOjE2OTgwMDIwMTgsImV4cCI6MTY5ODAwNDcxOH0.jmFuZ0u8SeW0nAAHjiIVPyUHGkuLIIhT1_dXUqCFjuM", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJHb256YWxleiBCZXRhbmNvdXJ0IiwiZnVsbG5hbWUiOiJBaG1lZCIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMTEtMDNUMTg6MTI6NDEuNTI5WiIsImxvZ2luSW5mbyI6IjExLzMvMjAyMywgMjoxMjo0MSBQTSIsImlhdCI6MTY5OTAzNTE2MSwiZXhwIjoxNjk5MDM3ODYxfQ.R3MEA2IfQ78HMHEOWV0nKP1FyFOQzuo0nqwvXRGNslY", "type": "string" } ] @@ -456,7 +495,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"newUser\": {\r\n \"middleName\": \"González\",\r\n \"lastName\": \"Betancourt\",\r\n \"name\": \"Ahmed Iván\",\r\n \"homeAddress\": \"Calle 15 # 272 APTO A E/Calle J y Calle I\",\r\n \"telephoneNumber\": \"(7) 879-32-68\",\r\n \"sex\": \"M\",\r\n \"area\": \"INGENIERIA INFORMATICA\",\r\n \"userStatus\": \"Activo\",\r\n \"sedeMunicipio\": \"PLAZA DE LA REVOLUCION\",\r\n \"userType\": \"Estudiante\",\r\n \"country\": \"Cuba\",\r\n \"UJC\": \"no\",\r\n \"skinColor\": \"Blanco\",\r\n \"nameInstitution\": \"null\", \r\n \"uid\": \"ahmedtest6.gonzalez\", \r\n \"mail\": [\"ahmedtest6@cujae.edu.cu\"],\r\n \"objectClass\": [\r\n \"top\",\r\n \"person\",\r\n \"posixAccount\",\r\n \"shadowAccount\",\r\n \"inetOrgPerson\",\r\n \"iesServices\",\r\n \"sambaSamAccount\",\r\n \"radiusprofile\",\r\n \"CourierMailAlias\",\r\n \"iesMember\"\r\n ],\r\n \"userPassword\": \"{SSHA}xyot2WiSiFVKmM2q0xkOMREVN3CyN3F+uKrIqrAqWiZsniG3KN3JzB8eVhvU/Gm+MSKuOg==\",\r\n \"displayName\": \"Ahmed Ivan\",\r\n \"givenName\": \"Ahmed\",\r\n \"maildrop\": [\"ahmedtest6@gmail.com\"],\r\n \"CI\": \"00092068439\",\r\n \"cn\": \"Ahmed\",\r\n \"sn\": \"Gonzalez Betancourt\"\r\n },\r\n \"userDN\":\"uid=ahmedtest6.gonzalez,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu\"\r\n}", + "raw": "{\r\n \"newUser\": { \r\n \"CI\": \"00082068486\",\r\n \"middleName\": \"Ahmedtestnueve\",\r\n \"lastName\": \"Glez\",\r\n \"name\": \"Ahmedtestnueve\",\r\n \"homeAddress\": \"SM 027 M 11 L 16 AV. TANKAH E1\",\r\n \"telephoneNumber\": \"83445888\",\r\n \"sex\": \"M\",\r\n \"area\": \"INGENIERIA Informatica\",\r\n \"sedeMunicipio\": \"Diez de Octubre\",\r\n \"userType\": \"Estudiante\",\r\n \"country\": \"México\",\r\n \"UJC\": \"Si\",\r\n \"skinColor\": \"Blanco\",\r\n \"nameInstitution\": \"null\",\r\n \"userStatus\": \"Activo\",\r\n \"uid\": \"ahmedtestnueve.glez\",\r\n \"givenName\": \"Ahmedtestnueve\",\r\n \"cn\": \"Ahmedtestnueve\",\r\n \"sn\": \"Glez\",\r\n \"displayName\": \"Ahmedtestnueve\",\r\n \"userPassword\": \"00082068410\",\r\n \"mail\": [\r\n \"ahmedtestnueve.glez@cujae.edu.cu\"\r\n ],\r\n \"maildrop\": [\r\n \"ahmedtestnueve.glez@cujae.edu.cu\"\r\n ]\r\n \r\n},\r\n \"userDN\":\"uid=ahmedtestnueve.gonzalez,ou=usuarios,ou=informatica,dc=cujae,dc=edu,dc=cu\"\r\n}", "options": { "raw": { "language": "json" @@ -502,12 +541,12 @@ "bearer": [ { "key": "token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJBaG1lZCBJdmFuIiwiZnVsbG5hbWUiOiJBaG1lZCBJdsOhbiIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMDktMjlUMTY6NDc6NDYuNTYzWiIsImxvZ2luSW5mbyI6IjkvMjkvMjAyMywgMTowMzo1NiBQTSIsImlhdCI6MTY5NjAwNzAzNiwiZXhwIjoxNjk2MDA3OTM2fQ.RKHzZp9mvqotxPb3oGoDtmDnYeI1vzn3aWrYn0TDEI8", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJBaG1lZCBJdmFuIiwiZnVsbG5hbWUiOiJBaG1lZCBJdsOhbiIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMDktMjlUMTc6MDM6NTYuNDU1WiIsImxvZ2luSW5mbyI6IjkvMjkvMjAyMywgNjo0ODowOCBQTSIsImlhdCI6MTY5NjAyNzY4OCwiZXhwIjoxNjk2MDI4NTg4fQ.hNHpjDSSD17xH_UUWxsynPriuBKDb1AkNocIEHfD28U", "type": "string" } ] }, - "method": "PUT", + "method": "POST", "header": [], "body": { "mode": "raw", @@ -539,6 +578,16 @@ { "name": "me", "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6ImFobWVkaXZhbi5nb256YWxleiIsImxhc3RuYW1lIjoiR29uesOhbGV6IEJldGFuY291cnQiLCJmdWxsbmFtZSI6IkFobWVkIEl2w6FuIiwiZW1haWwiOiJhaG1lZGl2YW4uZ29uemFsZXpAY3VqYWUuZWR1LmN1IiwiY2kiOiIwMDA5MjA2ODQyNiIsInJvbGVzIjpbInVzZXIiLCJhZG1pbiJdLCJsYXN0X3RpbWVfbG9nZ2VkIjoiMjAyMy0wOS0yOVQwMDowMToyMS4xMzJaIiwibG9naW5JbmZvIjoiOS8yOS8yMDIzLCAxMjo0Nzo0NiBQTSIsImlhdCI6MTY5NjAwNjA2NywiZXhwIjoxNjk2MDA2OTY3fQ.lUyms3OUqMgK93foh6HhPeJKqeASYkhz-1qMiHcTGC4", + "type": "string" + } + ] + }, "method": "GET", "header": [], "url": { @@ -595,42 +644,127 @@ "name": "logs", "item": [ { - "name": "test", + "name": "get all", "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYXZpZXIubG9yZW56byIsImRuIjoidWlkPWphdmllci5sb3JlbnpvLG91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsInVpZCI6Imphdmllci5sb3JlbnpvIiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiYXJlYUNlbnRyYWwiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6Imphdmllci5sb3JlbnpvIiwibGFzdG5hbWUiOiJMb3JlbnpvIE5leXJhIiwiZnVsbG5hbWUiOiJKYXZpZXIiLCJlbWFpbCI6Imphdmllci5sb3JlbnpvQGN1amFlLmVkdS5jdSIsImNpIjoiODUxMDA4MDQ4ODYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iLCJzdXBlcmFkbWluIl0sImxhc3RfdGltZV9sb2dnZWQiOiIyMDIzLTExLTIwVDAxOjQ0OjU0LjQwOVoiLCJsb2dpbkluZm8iOiIxMS8xOS8yMDIzLCA4OjQ0OjU0IFBNIiwiaWF0IjoxNzAwNDQ0Njk0LCJleHAiOjE3MDA0NDczOTR9._nKn1y29UoTR61Ez8KzF-waA77wwyVmAYJiFtoNxq7E", + "type": "string" + } + ] + }, "method": "GET", - "header": [] + "header": [], + "url": { + "raw": "{{server}}/logs?method=all&user=all&url=all&timeframe=all", + "host": [ + "{{server}}" + ], + "path": [ + "logs" + ], + "query": [ + { + "key": "method", + "value": "all" + }, + { + "key": "user", + "value": "all" + }, + { + "key": "url", + "value": "all" + }, + { + "key": "timeframe", + "value": "all" + } + ] + } }, "response": [] }, { "name": "get log file", "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYXZpZXIubG9yZW56byIsImRuIjoidWlkPWphdmllci5sb3JlbnpvLG91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsInVpZCI6Imphdmllci5sb3JlbnpvIiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiYXJlYUNlbnRyYWwiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWFyZWFDZW50cmFsLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6Imphdmllci5sb3JlbnpvIiwibGFzdG5hbWUiOiJMb3JlbnpvIE5leXJhIiwiZnVsbG5hbWUiOiJKYXZpZXIiLCJlbWFpbCI6Imphdmllci5sb3JlbnpvQGN1amFlLmVkdS5jdSIsImNpIjoiODUxMDA4MDQ4ODYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iLCJzdXBlcmFkbWluIl0sImxhc3RfdGltZV9sb2dnZWQiOiIyMDIzLTExLTE4VDIxOjMxOjU2LjI5NloiLCJsb2dpbkluZm8iOiIxMS8xOC8yMDIzLCA0OjMxOjU2IFBNIiwiaWF0IjoxNzAwMzQzMTE2LCJleHAiOjE3MDAzNDU4MTZ9.KNZPR4UBHCE9-vZGw3xIY4tCBc8gc4i3zgPIxmpQVIY", + "type": "string" + } + ] + }, "method": "GET", - "header": [] + "header": [], + "url": { + "raw": "{{server}}/logs/log-file", + "host": [ + "{{server}}" + ], + "path": [ + "logs", + "log-file" + ] + } }, "response": [] } ] }, { - "name": "email", - "item": [] + "name": "docs", + "item": [ + { + "name": "getSwaggerDoc", + "request": { + "method": "GET", + "header": [] + }, + "response": [] + } + ] }, { - "name": "searchByDN", + "name": "ldap", "item": [ { - "name": "searchByDN", + "name": "Get Query Info", "request": { + "method": "GET", + "header": [] + }, + "response": [] + }, + { + "name": "Get Config", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaG1lZGl2YW4uZ29uemFsZXoiLCJkbiI6InVpZD1haG1lZGl2YW4uZ29uemFsZXosb3U9dXN1YXJpb3Msb3U9aW5mb3JtYXRpY2EsZGM9Y3VqYWUsZGM9ZWR1LGRjPWN1IiwidWlkIjoiYWhtZWRpdmFuLmdvbnphbGV6IiwiZ3JvdXBzIjpbInVzdWFyaW9zIiwiaW5mb3JtYXRpY2EiXSwiYmFzZSI6ImRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImxvY2FsQmFzZSI6Im91PXVzdWFyaW9zLG91PWluZm9ybWF0aWNhLGRjPWN1amFlLGRjPWVkdSxkYz1jdSIsImZpcnN0bmFtZSI6IkFobWVkIiwibGFzdG5hbWUiOiJHb256YWxleiBCZXRhbmNvdXJ0IiwiZnVsbG5hbWUiOiJBaG1lZCIsImVtYWlsIjoiYWhtZWRpdmFuLmdvbnphbGV6QGN1amFlLmVkdS5jdSIsImNpIjoiMDAwOTIwNjg0MjYiLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwibGFzdF90aW1lX2xvZ2dlZCI6IjIwMjMtMTEtMThUMjE6MDk6NTMuMDYxWiIsImxvZ2luSW5mbyI6IjExLzE4LzIwMjMsIDQ6MDk6NTMgUE0iLCJpYXQiOjE3MDAzNDE3OTQsImV4cCI6MTcwMDM0NDQ5NH0.490kaI11eawQX8G_7zmOssMgWmI29-njOB2jAGyHlIE", + "type": "string" + } + ] + }, "method": "GET", "header": [], "url": { - "raw": "{{server}}/searchByDN", + "raw": "{{server}}/ldap/config", "host": [ "{{server}}" ], "path": [ - "searchByDN" + "ldap", + "config" ] } }, @@ -638,6 +772,86 @@ } ] }, + { + "name": "sigenu", + "item": [ + { + "name": "auth", + "item": [ + { + "name": "Log In", + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": " {\r\n \"username\": \"javier.lorenzo\",\r\n \"password\": \"85100804886\"\r\n} \r\n\r\n\r\n\r\n /* {\r\n \"username\": \"ahmedivan.gonzalez\",\r\n \"password\": \"Ba12345678#\"\r\n} */\r\n\r\n\r\n/* {\r\n \"username\":\"fernando.picayo\",\r\n \"password\":\"84110906021\"\r\n} */\r\n\r\n/* {\r\n \"username\": \"ssigenu\",\r\n \"password\": \"RSEfxVSL90rGP#LMs3v%\"\r\n} */ ", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{server}}/login", + "host": [ + "{{server}}" + ], + "path": [ + "login" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "professors", + "item": [ + { + "name": "Filter Professors", + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "groupldap2021*+", + "type": "string" + }, + { + "key": "username", + "value": "Administrator", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "url": { + "raw": "{{sigenu}}/professors/", + "host": [ + "{{sigenu}}" + ], + "path": [ + "professors", + "" + ] + } + }, + "response": [] + } + ] + } + ] + }, { "name": "New Request", "request": { @@ -691,6 +905,11 @@ "key": "server", "value": "http://127.0.0.1:4000", "type": "string" + }, + { + "key": "sigenu", + "value": "http://127.0.0.1:4000/api/sigenu", + "type": "string" } ] } \ No newline at end of file From 1c5aa60125b52e57b0129c0aa33fe1142c4609c2 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sat, 2 Dec 2023 15:37:06 -0500 Subject: [PATCH 222/229] update details --- src/controllers/profile.controller.js | 2 +- src/middlewares/auth.handler.js | 46 +++++++++++++------ .../recovery_password.controller.js | 6 +-- .../services/restore-password.service.js | 2 +- .../templates/reset-password-email.html | 3 +- .../templates/successful-password-change.html | 8 +--- src/services/profile.services.js | 13 ++++-- 7 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/controllers/profile.controller.js b/src/controllers/profile.controller.js index 5f1483bc..90bd1cf3 100644 --- a/src/controllers/profile.controller.js +++ b/src/controllers/profile.controller.js @@ -34,7 +34,7 @@ const service = ProfileServices() router.get( '/', checkAuth, - checkBlacklist, +/* checkBlacklist, */ checkRoles('user'), validateResponse, (req, res) => { diff --git a/src/middlewares/auth.handler.js b/src/middlewares/auth.handler.js index 7d7c2596..289ac1b9 100644 --- a/src/middlewares/auth.handler.js +++ b/src/middlewares/auth.handler.js @@ -5,25 +5,45 @@ const { responseError } = require('../schemas/response.schema') const { isBlackListed } = require('@src/services/auth.services') const checkAuth = (req, res, next) => { - const auth = passport.authenticate('jwt', { session: false }) - auth(req, res, next) + try { + const auth = passport.authenticate('jwt', { session: false }) + auth(req, res, next) + } catch (error) { + responseError( + res, + `Token Invalido`, + boom.unauthorized( + 'Operación inválida, usted no está autorizado a realizar esta acción.' + ) + ) + } } const checkRoles = (...roles) => { return (req, res, next) => { - const payload = verifyToken(req.headers.authorization.split(' ')[1]) - if (!payload) { - const error = boom.unauthorized('Token is not valid') - next(error) - } - const { roles: userRoles } = payload - const hasRole = roles.some((role) => userRoles.includes(role)) - if (hasRole) { - next() - } else { + try { + const payload = verifyToken(req.headers.authorization.split(' ')[1]) + if (!payload) { + const error = boom.unauthorized('Token is not valid') + throw error + } + const { roles: userRoles } = payload + const hasRole = roles.some((role) => userRoles.includes(role)) + if (hasRole) { + next() + } else { + responseError( + res, + `No tiene permiso de acceso`, + boom.unauthorized( + 'Operación inválida, usted no está autorizado a realizar esta acción.' + ) + ) + } + } catch (error) { responseError( res, - `No tiene permiso de acceso`, + `Token Invalido`, boom.unauthorized( 'Operación inválida, usted no está autorizado a realizar esta acción.' ) diff --git a/src/modules/passwordManagement/controllers/recovery_password.controller.js b/src/modules/passwordManagement/controllers/recovery_password.controller.js index e2b13522..adc1dcc3 100644 --- a/src/modules/passwordManagement/controllers/recovery_password.controller.js +++ b/src/modules/passwordManagement/controllers/recovery_password.controller.js @@ -96,7 +96,7 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { }) } catch (error) { console.error('Error in /forgot-password:', error) - res.status(500).json({ message: 'Internal Server Error' }) + res.status(500).json({ message: error.message }) } }) @@ -171,7 +171,7 @@ router.post( ) if (!checkedCode.isValid) { const error = boom.unauthorized(checkedCode.isValid.message) - throw error + throw new Error("Invalid code") } const encriptedPassword = hashPassword(newPassword) @@ -182,7 +182,7 @@ router.post( ) if (!!updatedUser) { await sendSuccessPasswordEmailTo(payload.name, payload.email) - res.status(200).json({ success: true, message: 'updated user' }) + res.status(200).json({ success: true, message: 'updated password' }) } } catch (error) { console.error('Error resetting password:', error) diff --git a/src/modules/passwordManagement/services/restore-password.service.js b/src/modules/passwordManagement/services/restore-password.service.js index 896c05cc..b4f7399b 100644 --- a/src/modules/passwordManagement/services/restore-password.service.js +++ b/src/modules/passwordManagement/services/restore-password.service.js @@ -79,7 +79,7 @@ const sendRecoveryPasswordEmailTo = async (user, code) => { const mailOptions = { from: process.env.EMAIL_USER, to: email, - subject: 'Reestablecimiento de Contrasena', + subject: 'Reestablecimiento de Contraseña', html: htmlToSend, } diff --git a/src/modules/passwordManagement/templates/reset-password-email.html b/src/modules/passwordManagement/templates/reset-password-email.html index 5065dec0..28367cfa 100644 --- a/src/modules/passwordManagement/templates/reset-password-email.html +++ b/src/modules/passwordManagement/templates/reset-password-email.html @@ -47,8 +47,7 @@

-
- Company Logo +

Restablecimiento de Contraseña

diff --git a/src/modules/passwordManagement/templates/successful-password-change.html b/src/modules/passwordManagement/templates/successful-password-change.html index 30972fe6..fcffd99f 100644 --- a/src/modules/passwordManagement/templates/successful-password-change.html +++ b/src/modules/passwordManagement/templates/successful-password-change.html @@ -29,13 +29,7 @@ text-align: center; padding: 20px; " - > - Cujae Logo + >

Cambio de Contraseña Exitoso

diff --git a/src/services/profile.services.js b/src/services/profile.services.js index bc1a7351..9cd10ecb 100644 --- a/src/services/profile.services.js +++ b/src/services/profile.services.js @@ -9,10 +9,15 @@ const { User } = require('../schemas/user.schema') const ProfileServices = () => { const getProfile = (req) => { - const token = req.headers.authorization.split(' ')[1] - const payload = verifyToken(token) - const { sub } = payload - return service.getByUsername(sub) + try { + const token = req.headers.authorization.split(' ')[1] + const payload = verifyToken(token) + const { sub } = payload + return service.getByUsername(sub) + } catch (error) { + throw new Error('Invalid token') + return null + } } const updateProfile = async (email, password, req) => { From f220dab14d63b9dc85bbeb9319d75a9767d598e1 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Sun, 3 Dec 2023 01:39:05 -0500 Subject: [PATCH 223/229] add logs integration test --- src/config/config.js | 4 + src/controllers/user.controller.js | 3 +- .../__tests__/integration.test.js | 84 ++++++++----------- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/src/config/config.js b/src/config/config.js index 84a314af..caf0c0a2 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -6,6 +6,10 @@ module.exports = { version: 'v1', url: process.env.API_URL, }, + tests: { + username: process.env.USERNAME_TEST, + password: process.env.PASSWORD_TEST, + }, server: { port: process.env.SERVER_PORT, host: process.env.HOST, diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index fe695501..18308371 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -433,7 +433,6 @@ router.post('/baseDN', async (req, res) => { * 500: * description: An error occurred while updating user attributes. */ - router.put('/:username', async (req, res) => { try { const { att, value } = req.body @@ -568,7 +567,6 @@ router.post('/modify-ldap', async (req, res) => { * 500: * description: An error occurred while adding the new user to LDAP. */ - router.post('/newUser', async (req, res) => { try { const { newUser, userDN } = req.body @@ -599,4 +597,5 @@ router.post('/newUser', async (req, res) => { res.status(500).json({ error: error.message }) } }) + module.exports = router diff --git a/src/modules/logsManagement/__tests__/integration.test.js b/src/modules/logsManagement/__tests__/integration.test.js index a16e3376..35028f4d 100644 --- a/src/modules/logsManagement/__tests__/integration.test.js +++ b/src/modules/logsManagement/__tests__/integration.test.js @@ -1,57 +1,47 @@ -const axios = require('axios') -const axiosMockAdapter = require('axios-mock-adapter') -const WebSocket = require('ws') - -// Import your REST API server module here. -// Import your WebSocket server module here. - -describe('Integration Test', () => { - let axiosMock - let ws - let wsMessages - - beforeAll(() => { - // Start your REST API server (if it's not already running) - // Start your WebSocket server (if it's not already running) - - // Create a WebSocket connection - ws = new WebSocket('ws://localhost:8080') - wsMessages = [] - - // Handle messages from the WebSocket server - ws.on('message', (message) => { - wsMessages.push(message) +const request = require('supertest') +const apiUrl = 'http://127.0.0.1:4000/api/v1' +const config = require('@src/config/config') + +const username = config.tests.username +const password = config.tests.password + +const randomNumber = Math.floor(Math.random() * 100000000) + .toString() + .padStart(8, '0') +const TEST_URL_ID = `/${randomNumber}` + +describe('API Tests', () => { + it('should handle authentication with incorrect credentials', async () => { + const response = await request(apiUrl).post('/login').send({ + username: 'incorrectUsername', + password: 'incorrectPassword', }) - // Create an instance of the axios mock adapter - axiosMock = new axiosMockAdapter(axios) - - // Mock the HTTP request to your REST API - axiosMock - .onGet('http://localhost:3000/logs') - .reply(200, { data: 'Mocked data from REST API' }) - }) - - afterAll(() => { - // Close the WebSocket connection and perform any necessary cleanup - ws.close() - axiosMock.restore() + expect(response.statusCode).toBe(401) }) - it('should perform HTTP and WebSocket communication', async () => { - // Make an HTTP request to your REST API - const response = await axios.get('http://localhost:3000/logs') + it('should get logs with a valid JWT token', async () => { + const response = await request(apiUrl).post('/login').send({ + username: username, + password: password, + }) - expect(response.status).toBe(200) - expect(response.data).toEqual({ data: 'Mocked data from REST API' }) + expect(response.statusCode).toBe(200) + + const authToken = response.body.data.token - // Send a message to the WebSocket server - ws.send('Hello, WebSocket server!') + const testRequest = await request(apiUrl) + .get(TEST_URL_ID) + .set('Authorization', `Bearer ${authToken}`) - // Wait for a moment to allow the WebSocket server to process the message - await new Promise((resolve) => setTimeout(resolve, 100)) + const responselogs = await request(apiUrl) + .get( + `/logs?method=GET&user=${username}&url=/api/v1${TEST_URL_ID}&timeframe=today` + ) + .set('Authorization', `Bearer ${authToken}`) - // Assertions for WebSocket messages - expect(wsMessages).toContain('Hello, WebSocket server!') + expect(responselogs.statusCode).toBe(200) + expect(responselogs._body.logs.length).toBeGreaterThan(0) + }) }) From 9986644915b508144846205873b5092a57ff9768 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Mon, 4 Dec 2023 11:02:34 -0500 Subject: [PATCH 224/229] update auth controller --- .../controllers/auth.controller.js | 57 +------------------ .../controllers/proffesors.controller.js | 7 +-- 2 files changed, 5 insertions(+), 59 deletions(-) diff --git a/src/modules/sigenu_integration/controllers/auth.controller.js b/src/modules/sigenu_integration/controllers/auth.controller.js index 41274067..925f75b3 100644 --- a/src/modules/sigenu_integration/controllers/auth.controller.js +++ b/src/modules/sigenu_integration/controllers/auth.controller.js @@ -1,20 +1,8 @@ -const { isSuperAdmin, isAdmin } = require('@src/services/auth.services') const profileService = require('@src/services/profile.services')() - -/* helpers */ -const { - extractBaseFromDn, - extractGroupsFromDn, -} = require('@src/helpers/dnHelper') - -const { signToken } = require('@src/utils/authentication/tokens/token_sign') -const { responseSuccess } = require('@src/schemas/response.schema') const passport = require('passport') - const { addUserRegistry, } = require('@src/modules/authentication/services/auth.services') - const { parseLoginPayload, } = require('@src/modules/sigenu_integration/helpers/sigenu.helper') @@ -39,51 +27,10 @@ const login_sigenu = async function (req, res, next) { } else { // Agrega un nuevo registro al usuario await addUserRegistry(user) - - const last_time_logged = await profileService.getLastLoginByUsername( - user.uid - ) - const loginInfo = await profileService.updateLastTimeLogged(user.uid) - // Example usage - const ldapDn = user.dn - const groups = extractGroupsFromDn(ldapDn) - const rootBaseDN = extractBaseFromDn(ldapDn) - const localBaseDN = user.dn.replace(`uid=${user.uid},`, '') - - let roles = ['user'] - const isSpAdmin = await isSuperAdmin(user.uid) - if (isSpAdmin) { - roles = [...roles, 'admin', 'superadmin'] - } else { - const isAdm = await isAdmin(user.uid, localBaseDN) - isAdm ? (roles = [...roles, 'admin']) : [...roles] - } + await profileService.getLastLoginByUsername(user.uid) + await profileService.updateLastTimeLogged(user.uid) const parsedPayload = parseLoginPayload(user) - - const payload = { - sub: user.uid, - dn: user.dn, - uid: user.uid, - groups: groups, - base: rootBaseDN, - localBase: localBaseDN, - firstname: user.givenName, - lastname: user.sn, - fullname: user.cn, - email: user.mail, - ci: user.CI, - roles: roles, - last_time_logged, - loginInfo, - } - - const userObj = { ...user } - const token = signToken(payload, { expiresIn: '15 minutes' }) - const refreshToken = signToken(payload, { expiresIn: '1 hour' }) - - /* await storeRefreshToken(user.uid, refreshToken) */ - req.login(user, (loginErr) => { if (loginErr) { return next(loginErr) diff --git a/src/modules/sigenu_integration/controllers/proffesors.controller.js b/src/modules/sigenu_integration/controllers/proffesors.controller.js index c4cb15ee..a1c1cb86 100644 --- a/src/modules/sigenu_integration/controllers/proffesors.controller.js +++ b/src/modules/sigenu_integration/controllers/proffesors.controller.js @@ -36,8 +36,7 @@ router.post(`${BASE_ROUTE}/`, async (req, res) => { const ldapFilter = `(&(objectClass=person)(userType=${USER_TYPE})${queryFilter})` const attributes = null - console.log('roles', roles) - console.log('ldapFilter', ldapFilter) + // Call the performLdapSearch function to retrieve users matching the group filters const searchResults = await service.handleFilteredSearch( @@ -48,11 +47,11 @@ router.post(`${BASE_ROUTE}/`, async (req, res) => { LIMIT ) - console.log('searchResults', searchResults) + const parsedResults = parseLdapEntryToProfessorDto(searchResults) - console.log('parsedResults', parsedResults) + res.status(200).json(parsedResults) } catch (error) { From ffffbc5ea45e1dabd030e3030f059e5a46624ce4 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Thu, 7 Dec 2023 11:49:22 -0500 Subject: [PATCH 225/229] comment blacklist --- src/controllers/profile.controller.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/profile.controller.js b/src/controllers/profile.controller.js index 90bd1cf3..3ea85551 100644 --- a/src/controllers/profile.controller.js +++ b/src/controllers/profile.controller.js @@ -6,7 +6,7 @@ const validateResponse = require('../middlewares/validateResponse') const { checkAuth, checkRoles, - checkBlacklist, + /* checkBlacklist, */ } = require('../middlewares/auth.handler') const service = ProfileServices() @@ -95,12 +95,12 @@ router.get( router.put( '/', checkAuth, - checkBlacklist, + /* checkBlacklist, */ checkRoles('user'), validateResponse, (req, res) => { const { email, password, confirmPassword } = req.body - if (!email && !password) { + if (!email || !password) { responseError(res, 'fields cannot be empty', null) } else if (password) { if (password !== confirmPassword) { From bfc35b2f9b0f69990088244979b4f0472597035d Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 8 Dec 2023 14:58:23 -0500 Subject: [PATCH 226/229] add try catch to the update user controller --- src/controllers/user.controller.js | 66 +++++++++++++++--------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 18308371..a7fbbbdc 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -489,49 +489,51 @@ router.put('/:username', async (req, res) => { * description: An error occurred while modifying LDAP user attributes. */ router.post('/modify-ldap', async (req, res) => { - const dn = req.body.dn - const attributes = req.body.attributes + try { + const dn = req.body.dn + const attributes = req.body.attributes - const modifications = [] + const modifications = [] - // Loop through the updated attributes and create modification objects - for (const attributeName in attributes) { - if (attributes.hasOwnProperty(attributeName)) { - const attributeValue = attributes[attributeName] + // Loop through the updated attributes and create modification objects + for (const attributeName in attributes) { + if (attributes.hasOwnProperty(attributeName)) { + const attributeValue = attributes[attributeName] - // Create a modification object to replace the attribute value - const modification = new ldap.Change({ - operation: 'replace', // Use 'replace' to replace the attribute value - modification: { - type: attributeName, - values: [attributeValue], - }, - }) + // Create a modification object to replace the attribute value + const modification = new ldap.Change({ + operation: 'replace', // Use 'replace' to replace the attribute value + modification: { + type: attributeName, + values: [...attributeValue], + }, + }) - modifications.push(modification) + modifications.push(modification) + } } - } - let errorOccurred = false // Track if any modification failed - - // Perform the LDAP modify operation with all modifications - for (const modification of modifications) { - ldapClient.modify(dn, modification, (err) => { - if (err) { - console.error('Error modifying attributes:', err) - errorOccurred = true - } - }) - } + // Perform the LDAP modify operation with all modifications + for (const modification of modifications) { + await new Promise((resolve, reject) => { + ldapClient.modify(dn, modification, (err) => { + if (err) { + console.error('Error modifying attributes:', err) + reject(err) + } else { + resolve() + } + }) + }) + } - if (errorOccurred) { - res.status(500).json({ error: 'Error modifying attributes' }) - } else { console.log('Attributes modified successfully') res.json({ message: 'Attributes modified successfully' }) + } catch (error) { + console.error('Error in modify-ldap endpoint:', error) + res.status(500).json({ error: error.message }) } }) - /** * @openapi * /api/v1/users/newUser: From fed3222c4872a15157eae46dd369bb5eccbaf318 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 8 Dec 2023 14:59:15 -0500 Subject: [PATCH 227/229] update getByEmail service to user both mail and maildrop filter --- src/services/user.services.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/user.services.js b/src/services/user.services.js index 39435781..ce5754ac 100644 --- a/src/services/user.services.js +++ b/src/services/user.services.js @@ -52,7 +52,7 @@ const UserServices = () => { const getByEmail = async (email) => { const results = await performLdapSearch( config.ldap.base, - `(maildrop=${email})`, + `(|(maildrop=${email})(mail=${email}))`, null ) return results[0] From 361331f0d007fe12ee5101808762074d21f50dd6 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 8 Dec 2023 14:59:55 -0500 Subject: [PATCH 228/229] update mail services to send to all the mails array --- .../controllers/recovery_password.controller.js | 14 +++++++++++--- .../services/restore-password.service.js | 5 ++--- src/utils/ldapUtils.js | 9 ++++++--- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/modules/passwordManagement/controllers/recovery_password.controller.js b/src/modules/passwordManagement/controllers/recovery_password.controller.js index adc1dcc3..222ff9be 100644 --- a/src/modules/passwordManagement/controllers/recovery_password.controller.js +++ b/src/modules/passwordManagement/controllers/recovery_password.controller.js @@ -87,8 +87,16 @@ router.post('/forgot-password', validateEmailOrUsername, async (req, res) => { const currentTime = new Date() const expiration = new Date(currentTime.getTime() + 15 * 60 * 1000) // 15 minutes in milliseconds - const recoveryCode = await generateRecoveryCode(user, expiration) - await sendRecoveryPasswordEmailTo(user, recoveryCode) + if (user.mail.length === 0) { + throw new Error( + 'Lo sentimos, este usuario no tiene asignado ningun correo de recuperación' + ) + } else { + user.mail.map(async (email) => { + const recoveryCode = await generateRecoveryCode(user, expiration) + await sendRecoveryPasswordEmailTo(user, recoveryCode, email) + }) + } res.json({ message: 'Password reset email sent successfully.', @@ -171,7 +179,7 @@ router.post( ) if (!checkedCode.isValid) { const error = boom.unauthorized(checkedCode.isValid.message) - throw new Error("Invalid code") + throw new Error('Invalid code') } const encriptedPassword = hashPassword(newPassword) diff --git a/src/modules/passwordManagement/services/restore-password.service.js b/src/modules/passwordManagement/services/restore-password.service.js index b4f7399b..af2ad2f1 100644 --- a/src/modules/passwordManagement/services/restore-password.service.js +++ b/src/modules/passwordManagement/services/restore-password.service.js @@ -25,7 +25,7 @@ const generateRecoveryCode = async (user, expiration) => { username: user.uid, // Adjust the field name as needed // Set other user fields as needed (e.g., name, email, password) // ... - email: user.maildrop, + email: user.mail.length !== 0 ? user.mail[0] : user.maildrop.length !==0 ? user.maildrop[0] : user.uid, recoveryCode: { code: code, expiresAt: expiration, @@ -44,10 +44,9 @@ const generateRecoveryCode = async (user, expiration) => { } } -const sendRecoveryPasswordEmailTo = async (user, code) => { +const sendRecoveryPasswordEmailTo = async (user, code, email) => { try { const name = user.name - const email = user.maildrop const transporter = nodemailer.createTransport( smtpTransport({ diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index fd5a6e5c..5f016254 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -27,7 +27,11 @@ const unbindLdapClient = () => { const getObject = (arr) => { const newObj = {} arr.forEach((obj) => { - newObj[obj.type] = obj.values.length === 1 ? obj.values[0] : obj.values + if (obj.type === 'mail' || obj.type === 'maildrop') { + newObj[obj.type] = obj.values + } else { + newObj[obj.type] = obj.values.length === 1 ? obj.values[0] : obj.values + } }) return newObj } @@ -195,7 +199,6 @@ const performLdapAddition = async (dn, entry) => { entry.lastTimeChange = new Date().toISOString() entry.sambaSID = 'S-1-5-21-1255719363-1350762778-3568053751-513' - return new Promise((resolve, reject) => { try { bindLdapClient() // Bind before search @@ -207,7 +210,7 @@ const performLdapAddition = async (dn, entry) => { success: true, message: err, }) - } else { + } else { resolve('created User') } }) From c31a2a37df03017b468f0b686517a044df7b8f75 Mon Sep 17 00:00:00 2001 From: Ahmedglez Date: Fri, 8 Dec 2023 16:46:59 -0500 Subject: [PATCH 229/229] update user and email --- src/controllers/user.controller.js | 2 ++ src/modules/authentication/LdapAuth.js | 7 +++++-- src/modules/authentication/controllers/auth.controller.js | 2 ++ src/utils/ldapUtils.js | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index a7fbbbdc..e42b04e8 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -499,6 +499,8 @@ router.post('/modify-ldap', async (req, res) => { for (const attributeName in attributes) { if (attributes.hasOwnProperty(attributeName)) { const attributeValue = attributes[attributeName] + console.log('attributeName', attributeName) + console.log('attributeValue', attributeValue) // Create a modification object to replace the attribute value const modification = new ldap.Change({ diff --git a/src/modules/authentication/LdapAuth.js b/src/modules/authentication/LdapAuth.js index 59fbe1ed..af7584ee 100644 --- a/src/modules/authentication/LdapAuth.js +++ b/src/modules/authentication/LdapAuth.js @@ -81,8 +81,8 @@ var init = function ( const localSession = { user: username } - if (localSession.user === req.session?.passport?.user) - throw new Error('log out before logging back in') + /* if (localSession.user === req.session?.passport?.user) + throw new Error('log out before logging back in') */ const response = await userService.getByUsername(username) @@ -128,8 +128,11 @@ var init = function ( options.adminPassword = opt.adminPassword } } + + console.log('options', options) // ldap authenticate the user const user = await authenticate(options) + console.log('user authenticated', user) // success done(null, user) } catch (error) { diff --git a/src/modules/authentication/controllers/auth.controller.js b/src/modules/authentication/controllers/auth.controller.js index b75f56e7..9f383205 100644 --- a/src/modules/authentication/controllers/auth.controller.js +++ b/src/modules/authentication/controllers/auth.controller.js @@ -79,6 +79,8 @@ const login = async function (req, res, next) { loginInfo, } + console.log('user ', user) + const userObj = { ...user } const token = signToken(payload, { expiresIn: '15 minutes' }) const refreshToken = signToken(payload, { expiresIn: '1 hour' }) diff --git a/src/utils/ldapUtils.js b/src/utils/ldapUtils.js index 5f016254..4529905c 100644 --- a/src/utils/ldapUtils.js +++ b/src/utils/ldapUtils.js @@ -28,7 +28,7 @@ const getObject = (arr) => { const newObj = {} arr.forEach((obj) => { if (obj.type === 'mail' || obj.type === 'maildrop') { - newObj[obj.type] = obj.values + newObj[obj.type] = [...obj.values] } else { newObj[obj.type] = obj.values.length === 1 ? obj.values[0] : obj.values }