diff --git a/.env.dist b/.env.dist index 183d6b3..6239b34 100644 --- a/.env.dist +++ b/.env.dist @@ -11,6 +11,11 @@ MESSAGE_BROKER_URI=amqp://v3-broker:5672 MESSAGE_BROKER_EXCHANGE=mobicoop MESSAGE_BROKER_EXCHANGE_DURABILITY=true +# REDIS +REDIS_HOST=v3-redis +REDIS_PASSWORD=redis +REDIS_PORT=6379 + # OPA -OPA_IMAGE=openpolicyagent/opa:0.57.0 +OPA_IMAGE=openpolicyagent/opa:0.58.0 OPA_URL=http://v3-auth-opa:8181/v1/data/ diff --git a/Dockerfile b/Dockerfile index f59b37f..db77849 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,10 @@ +ARG NODE_VERSION=20.9.0 + ################### # BUILD FOR LOCAL DEVELOPMENT ################### -FROM node:18-alpine3.16 As development +FROM node:${NODE_VERSION} As development # Create app directory WORKDIR /usr/src/app @@ -29,7 +31,7 @@ USER node # BUILD FOR PRODUCTION ################### -FROM node:18-alpine3.16 As build +FROM node:${NODE_VERSION} As build WORKDIR /usr/src/app @@ -66,7 +68,7 @@ USER node # PRODUCTION ################### -FROM node:18-alpine3.16 As production +FROM node:${NODE_VERSION} As production # Copy package.json to be able to execute migration command COPY --chown=node:node package*.json ./ diff --git a/package-lock.json b/package-lock.json index 36dffa6..76778bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@golevelup/nestjs-rabbitmq": "^4.0.0", "@grpc/grpc-js": "^1.9.9", "@grpc/proto-loader": "^0.7.10", + "@mobicoop/configuration-module": "^7.2.1", "@mobicoop/ddd-library": "^2.1.1", "@mobicoop/health-module": "^2.3.1", "@mobicoop/message-broker-module": "^2.1.1", @@ -25,6 +26,7 @@ "@nestjs/platform-express": "^10.2.8", "@nestjs/terminus": "^10.1.1", "@prisma/client": "^5.5.2", + "argon2": "^0.31.2", "axios": "^1.6.0", "bcrypt": "5.1.0", "class-transformer": "^0.5.1", @@ -280,30 +282,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", + "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", + "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/generator": "^7.23.3", "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", + "@babel/parser": "^7.23.3", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", + "@babel/traverse": "^7.23.3", + "@babel/types": "^7.23.3", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -328,12 +330,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -414,9 +416,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -592,9 +594,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -664,9 +666,9 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "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==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -766,9 +768,9 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "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==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -795,19 +797,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/generator": "^7.23.3", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -825,9 +827,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", @@ -901,9 +903,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -946,9 +948,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", - "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1010,9 +1012,9 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.9.9", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.9.tgz", - "integrity": "sha512-vQ1qwi/Kiyprt+uhb1+rHMpyk4CVRMTGNUGGPRGS7pLNfWkdCHrGEnT6T3/JyC2VZgoOX/X1KwdoU0WYQAeYcQ==", + "version": "1.9.10", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.10.tgz", + "integrity": "sha512-6W1NMW40R0fs4Tlv+Tx2+4J23IA/kV+ifnNaOcDAqDOBfbk3BTPuFmHVOxVQX0DEK9oD6KzKPyu2OiHW87tFpA==", "dependencies": { "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" @@ -1071,6 +1073,11 @@ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1693,13 +1700,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@mobicoop/configuration-module": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@mobicoop/configuration-module/-/configuration-module-7.3.0.tgz", + "integrity": "sha512-11Pezs1O+NcOL9aTmMmwsj9BQRqygdk44ubS2SXnZmi9sZUAFwet/lFe77YgKcOAhTeRc5GT9KkebA7r7EmpzQ==", + "dependencies": { + "@songkeys/nestjs-redis": "^10.0.0", + "ioredis": "^5.3.2" + }, + "peerDependencies": { + "@nestjs/common": "^10.2.7" + } + }, "node_modules/@mobicoop/ddd-library": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@mobicoop/ddd-library/-/ddd-library-2.1.1.tgz", - "integrity": "sha512-Up9d0Ad6aM8Eoc7u3cTMMhQ6uP16thvXLy7h8OL3pfM4QPUNdSygxFjJhwg+wUyjGRH1bsb3xuWp8+GsOVI7+g==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mobicoop/ddd-library/-/ddd-library-2.3.0.tgz", + "integrity": "sha512-Hgdno1b/kqcfodUaCcriicsfznVnd1MIugyCNXxFt2zN3oc5wTNQHNxx/E7RzAJDJr6xvuCleJEf7oExAroo5w==", "dependencies": { "@nestjs/event-emitter": "^2.0.2", "@nestjs/microservices": "^10.2.7", + "@nestjs/swagger": "^7.1.14", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "uuid": "^9.0.1" @@ -1994,9 +2014,9 @@ } }, "node_modules/@nestjs/event-emitter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-2.0.2.tgz", - "integrity": "sha512-qxJE+6yKSW/ReBzT1jKES2m3zZh6gmgunDtIvCl66G8i9zZ4TQciwoq01MigqnruTgXjH/AzNPqtr6ZUt207mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-2.0.3.tgz", + "integrity": "sha512-Pt7KAERrgK0OjvarSI3wfVhwZ8X1iLq1lXuodyRe+Zx3aLLP7fraFUHirASbFkB6KIQ1Zj+gZ1g8a9eu4GfFhw==", "dependencies": { "eventemitter2": "6.4.9" }, @@ -2007,9 +2027,9 @@ } }, "node_modules/@nestjs/mapped-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz", - "integrity": "sha512-V0izw6tWs6fTp9+KiiPUbGHWALy563Frn8X6Bm87ANLRuE46iuBMD5acKBDP5lKL/75QFvrzSJT7HkCbB0jTpg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.3.tgz", + "integrity": "sha512-40Zdqg98lqoF0+7ThWIZFStxgzisK6GG22+1ABO4kZiGF/Tu2FE+DYLw+Q9D94vcFWizJ+MSjNN4ns9r6hIGxw==", "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", "class-transformer": "^0.4.0 || ^0.5.0", @@ -2119,15 +2139,15 @@ } }, "node_modules/@nestjs/swagger": { - "version": "7.1.14", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.14.tgz", - "integrity": "sha512-2Ol4S6qHeYVVmkshkWBM8E/qkmEqEOUj2QIewr0jLSyo30H7f3v81pJyks6pTLy4PK0LGUXojMvIfFIE3mmGQQ==", + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.16.tgz", + "integrity": "sha512-f9KBk/BX9MUKPTj7tQNYJ124wV/jP5W2lwWHLGwe/4qQXixuDOo39zP55HIJ44LE7S04B7BOeUOo9GBJD/vRcw==", "dependencies": { - "@nestjs/mapped-types": "2.0.2", + "@nestjs/mapped-types": "2.0.3", "js-yaml": "4.1.0", "lodash": "4.17.21", "path-to-regexp": "3.2.0", - "swagger-ui-dist": "5.9.0" + "swagger-ui-dist": "5.9.1" }, "peerDependencies": { "@fastify/static": "^6.0.0", @@ -2297,6 +2317,14 @@ "npm": ">=5.0.0" } }, + "node_modules/@phc/format": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", + "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2327,12 +2355,12 @@ } }, "node_modules/@prisma/client": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.5.2.tgz", - "integrity": "sha512-54XkqR8M+fxbzYqe+bIXimYnkkcGqgOh0dn0yWtIk6CQT4IUCAvNFNcQZwk2KqaLU+/1PHTSWrcHtx4XjluR5w==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.6.0.tgz", + "integrity": "sha512-mUDefQFa1wWqk4+JhKPYq8BdVoFk9NFMBXUI8jAkBfQTtgx8WPx02U2HB/XbAz3GSUJpeJOKJQtNvaAIDs6sug==", "hasInstallScript": true, "dependencies": { - "@prisma/engines-version": "5.5.1-1.aebc046ce8b88ebbcb45efe31cbe7d06fd6abc0a" + "@prisma/engines-version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee" }, "engines": { "node": ">=16.13" @@ -2347,16 +2375,16 @@ } }, "node_modules/@prisma/engines": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.5.2.tgz", - "integrity": "sha512-Be5hoNF8k+lkB3uEMiCHbhbfF6aj1GnrTBnn5iYFT7GEr3TsOEp1soviEcBR0tYCgHbxjcIxJMhdbvxALJhAqg==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.6.0.tgz", + "integrity": "sha512-Mt2q+GNJpU2vFn6kif24oRSBQv1KOkYaterQsi0k2/lA+dLvhRX6Lm26gon6PYHwUM8/h8KRgXIUMU0PCLB6bw==", "devOptional": true, "hasInstallScript": true }, "node_modules/@prisma/engines-version": { - "version": "5.5.1-1.aebc046ce8b88ebbcb45efe31cbe7d06fd6abc0a", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.5.1-1.aebc046ce8b88ebbcb45efe31cbe7d06fd6abc0a.tgz", - "integrity": "sha512-O+qHFnZvAyOFk1tUco2/VdiqS0ym42a3+6CYLScllmnpbyiTplgyLt2rK/B9BTjYkSHjrgMhkG47S0oqzdIckA==" + "version": "5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.6.0-32.e95e739751f42d8ca026f6b910f5a2dc5adeaeee.tgz", + "integrity": "sha512-UoFgbV1awGL/3wXuUK3GDaX2SolqczeeJ5b4FVec9tzeGbSWJboPSbT0psSrmgYAKiKnkOPFSLlH6+b+IyOwAw==" }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", @@ -2436,6 +2464,27 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@songkeys/nestjs-redis": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@songkeys/nestjs-redis/-/nestjs-redis-10.0.0.tgz", + "integrity": "sha512-s56+NECuJXzcaPLYzpvA2xjL0e/1Zy55UE0q6b1UqpbQSKI06TFPFCWCMUadJigiuB26O1hxi+lmDbzahKvcLg==", + "dependencies": { + "tslib": "2.6.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "ioredis": "^5.0.0" + } + }, + "node_modules/@songkeys/nestjs-redis/node_modules/tslib": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", + "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -2461,17 +2510,17 @@ "dev": true }, "node_modules/@types/amqplib": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.3.tgz", - "integrity": "sha512-ZVkD5RqoW6+n84WanWf0a74mT0CVWdXOEEEC9nZvseD//j36XiYGR2phjpHQM6JpYR4vcWnjgUbqFCFG7MB+lw==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.4.tgz", + "integrity": "sha512-Y5Sqquh/LqDxSgxYaAAFNM0M7GyONtSDCcFMJk+DQwYEjibPyW6y+Yu9H9omdkKc3epyXULmFN3GTaeBHhn2Hg==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/babel__core": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.3.tgz", - "integrity": "sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==", + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz", + "integrity": "sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==", "dev": true, "dependencies": { "@babel/parser": "^7.20.7", @@ -2482,18 +2531,18 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.6.tgz", - "integrity": "sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==", + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", + "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.3.tgz", - "integrity": "sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -2501,27 +2550,27 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.3.tgz", - "integrity": "sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==", + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", + "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" } }, "node_modules/@types/bcrypt": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.1.tgz", - "integrity": "sha512-dIIrEsLV1/v0AUNI8oHMaRRTSeVjoy5ID8oclJavtPj8CwPJoD1eFoNXEypuu6k091brEzBeOo3LlxeAH9zRZg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/body-parser": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.4.tgz", - "integrity": "sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, "dependencies": { "@types/connect": "*", @@ -2529,24 +2578,24 @@ } }, "node_modules/@types/connect": { - "version": "3.4.37", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.37.tgz", - "integrity": "sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-LZ8SD3LpNmLMDLkG2oCBjZg+ETnx6XdCjydUE0HwojDmnDfDUnhMKKbtth1TZh+hzcqb03azrYWoXLS8sMXdqg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-b698BLJ6kPVd6uhHsY7wlebZdrWPXYied883PDSzpJZYOP97EOn/oGdLCH3jJf157srkFReIZY5v0H1s8Dozrg==", "dev": true }, "node_modules/@types/eslint": { - "version": "8.44.6", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.6.tgz", - "integrity": "sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw==", + "version": "8.44.7", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz", + "integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==", "dev": true, "dependencies": { "@types/estree": "*", @@ -2554,9 +2603,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.6", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.6.tgz", - "integrity": "sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -2564,15 +2613,15 @@ } }, "node_modules/@types/estree": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.4.tgz", - "integrity": "sha512-2JwWnHK9H+wUZNorf2Zr6ves96WHoWDJIftkcxPKsS7Djta6Zu519LarhRNljPXkpsZR2ZMwNCPeW7omW07BJw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, "node_modules/@types/express": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.20.tgz", - "integrity": "sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, "dependencies": { "@types/body-parser": "*", @@ -2582,9 +2631,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.39", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz", - "integrity": "sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ==", + "version": "4.17.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", "dev": true, "dependencies": { "@types/node": "*", @@ -2594,39 +2643,39 @@ } }, "node_modules/@types/graceful-fs": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.8.tgz", - "integrity": "sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/http-errors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.3.tgz", - "integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "dev": true }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.2.tgz", - "integrity": "sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.3.tgz", - "integrity": "sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" @@ -2643,47 +2692,47 @@ } }, "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==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/mime": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz", - "integrity": "sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, "node_modules/@types/node": { - "version": "20.8.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz", - "integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==", + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/@types/qs": { - "version": "6.9.9", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", - "integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==", + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", "dev": true }, "node_modules/@types/range-parser": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.6.tgz", - "integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, "node_modules/@types/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", + "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", "dev": true }, "node_modules/@types/send": { - "version": "0.17.3", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.3.tgz", - "integrity": "sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, "dependencies": { "@types/mime": "^1", @@ -2691,9 +2740,9 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.4.tgz", - "integrity": "sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", "dev": true, "dependencies": { "@types/http-errors": "*", @@ -2702,15 +2751,15 @@ } }, "node_modules/@types/stack-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.2.tgz", - "integrity": "sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "node_modules/@types/superagent": { - "version": "4.1.20", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.20.tgz", - "integrity": "sha512-GfpwJgYSr3yO+nArFkmyqv3i0vZavyEG5xPd/o95RwpKYpsOKJYI5XLdxLpdRbZI3YiGKKdIOFIf/jlP7A0Jxg==", + "version": "4.1.21", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.21.tgz", + "integrity": "sha512-yrbAccEEY9+BSa1wji3ry8R3/BdW9kyWnjkRKctrtw5ebn/k2a2CsMeaQ7dD4iLfomgHkomBVIVgOFRMV4XYHA==", "dev": true, "dependencies": { "@types/cookiejar": "*", @@ -2718,51 +2767,51 @@ } }, "node_modules/@types/supertest": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.15.tgz", - "integrity": "sha512-jUCZZ/TMcpGzoSaed9Gjr8HCf3HehExdibyw3OHHEL1als1KmyzcOZZH4MjbObI8TkWsEr7bc7gsW0WTDni+qQ==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.16.tgz", + "integrity": "sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==", "dev": true, "dependencies": { "@types/superagent": "*" } }, "node_modules/@types/uuid": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz", - "integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", "dev": true }, "node_modules/@types/validator": { - "version": "13.11.5", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.5.tgz", - "integrity": "sha512-xW4qsT4UIYILu+7ZrBnfQdBYniZrMLYYK3wN9M/NdeIHgBN5pZI2/8Q7UfdWIcr5RLJv/OGENsx91JIpUUoC7Q==" + "version": "13.11.6", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.6.tgz", + "integrity": "sha512-HUgHujPhKuNzgNXBRZKYexwoG+gHKU+tnfPqjWXFghZAnn73JElicMkuSKJyLGr9JgyA8IgK7fj88IyA9rwYeQ==" }, "node_modules/@types/yargs": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.29.tgz", - "integrity": "sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==", + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.31.tgz", + "integrity": "sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.2", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz", - "integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz", - "integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz", + "integrity": "sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.9.1", - "@typescript-eslint/type-utils": "6.9.1", - "@typescript-eslint/utils": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", + "@typescript-eslint/scope-manager": "6.11.0", + "@typescript-eslint/type-utils": "6.11.0", + "@typescript-eslint/utils": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -2788,15 +2837,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz", - "integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.11.0.tgz", + "integrity": "sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.9.1", - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/typescript-estree": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", + "@typescript-eslint/scope-manager": "6.11.0", + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/typescript-estree": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0", "debug": "^4.3.4" }, "engines": { @@ -2816,13 +2865,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", - "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz", + "integrity": "sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1" + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2833,13 +2882,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz", - "integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.11.0.tgz", + "integrity": "sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.9.1", - "@typescript-eslint/utils": "6.9.1", + "@typescript-eslint/typescript-estree": "6.11.0", + "@typescript-eslint/utils": "6.11.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -2860,9 +2909,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", - "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.11.0.tgz", + "integrity": "sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2873,13 +2922,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", - "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.11.0.tgz", + "integrity": "sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2900,17 +2949,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz", - "integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.11.0.tgz", + "integrity": "sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.9.1", - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/scope-manager": "6.11.0", + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/typescript-estree": "6.11.0", "semver": "^7.5.4" }, "engines": { @@ -2925,12 +2974,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", - "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.11.0.tgz", + "integrity": "sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/types": "6.11.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -3364,6 +3413,20 @@ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, + "node_modules/argon2": { + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.31.2.tgz", + "integrity": "sha512-QSnJ8By5Mth60IEte45w9Y7v6bWcQw3YhRtJKKN8oNCxnTLDiv/AXXkDPf2srTMfxFVn3QJdVv2nhXESsUa+Yg==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "@phc/format": "^1.0.0", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3401,9 +3464,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -3564,6 +3627,11 @@ "node": ">= 10.0.0" } }, + "node_modules/bcrypt/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -3897,9 +3965,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001559", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz", - "integrity": "sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==", + "version": "1.0.30001562", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz", + "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==", "dev": true, "funding": [ { @@ -4115,6 +4183,14 @@ "node": ">=0.8" } }, + "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", @@ -4630,6 +4706,14 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4761,9 +4845,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.574", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.574.tgz", - "integrity": "sha512-bg1m8L0n02xRzx4LsTTMbBPiUd9yIR+74iPtS/Ao65CuXvhVZHP0ym1kSdDG3yHFDXqHQQBKujlN1AQ8qZnyFg==", + "version": "1.4.585", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.585.tgz", + "integrity": "sha512-B4yBlX0azdA3rVMxpYwLQfDpdwOgcnLCkpvSOd68iFmeedo+WYjaBJS3/W58LVD8CB2nf+o7C4K9xz1l09RkWg==", "dev": true }, "node_modules/emittery": { @@ -4823,9 +4907,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true }, "node_modules/escalade": { @@ -4854,15 +4938,15 @@ } }, "node_modules/eslint": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", - "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.52.0", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -5282,9 +5366,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -5427,9 +5511,9 @@ } }, "node_modules/flat-cache": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", - "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { "flatted": "^3.2.9", @@ -5437,7 +5521,7 @@ "rimraf": "^3.0.2" }, "engines": { - "node": ">=12.0.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flat-cache/node_modules/glob": { @@ -6120,6 +6204,29 @@ "node": ">= 0.10" } }, + "node_modules/ioredis": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -6318,9 +6425,9 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" @@ -7202,6 +7309,16 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -7500,9 +7617,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "funding": [ { "type": "github", @@ -7543,9 +7660,9 @@ "dev": true }, "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", + "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==" }, "node_modules/node-emoji": { "version": "1.11.0", @@ -7890,9 +8007,12 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.2.tgz", + "integrity": "sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==", + "dependencies": { + "semver": "^7.3.5" + }, "engines": { "node": "14 || >=16.14" } @@ -8021,9 +8141,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -8074,13 +8194,13 @@ } }, "node_modules/prisma": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.5.2.tgz", - "integrity": "sha512-WQtG6fevOL053yoPl6dbHV+IWgKo25IRN4/pwAGqcWmg7CrtoCzvbDbN9fXUc7QS2KK0LimHIqLsaCOX/vHl8w==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.6.0.tgz", + "integrity": "sha512-EEaccku4ZGshdr2cthYHhf7iyvCcXqwJDvnoQRAJg5ge2Tzpv0e2BaMCp+CbbDUwoVTzwgOap9Zp+d4jFa2O9A==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.5.2" + "@prisma/engines": "5.6.0" }, "bin": { "prisma": "build/index.js" @@ -8298,6 +8418,25 @@ "node": ">= 0.10" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -8832,6 +8971,11 @@ "node": ">=8" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -9016,9 +9160,9 @@ } }, "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==" + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.1.tgz", + "integrity": "sha512-5zAx+hUwJb9T3EAntc7TqYkV716CMqG6sZpNlAAMOMWkNXRYxGkN8ADIvD55dQZ10LxN90ZM/TQmN7y1gpICnw==" }, "node_modules/symbol-observable": { "version": "4.0.0", @@ -9356,9 +9500,9 @@ } }, "node_modules/ts-loader": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.0.tgz", - "integrity": "sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, "dependencies": { "chalk": "^4.1.0", diff --git a/package.json b/package.json index d6ef7df..4642766 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@golevelup/nestjs-rabbitmq": "^4.0.0", "@grpc/grpc-js": "^1.9.9", "@grpc/proto-loader": "^0.7.10", + "@mobicoop/configuration-module": "^7.2.1", "@mobicoop/ddd-library": "^2.1.1", "@mobicoop/health-module": "^2.3.1", "@mobicoop/message-broker-module": "^2.1.1", @@ -46,6 +47,7 @@ "@nestjs/platform-express": "^10.2.8", "@nestjs/terminus": "^10.1.1", "@prisma/client": "^5.5.2", + "argon2": "^0.31.2", "axios": "^1.6.0", "bcrypt": "5.1.0", "class-transformer": "^0.5.1", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e438c35..facbdee 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -3,7 +3,7 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["linux-musl", "debian-openssl-3.0.x"] + binaryTargets = ["linux-musl", "debian-openssl-3.0.x", "linux-musl-openssl-3.0.x"] } datasource db { diff --git a/src/app.module.ts b/src/app.module.ts index 2eeb576..9de1c35 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; +import { ConfigModule, ConfigService } from '@nestjs/config'; import { AuthenticationModule } from '@modules/authentication/authentication.module'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { @@ -21,6 +21,10 @@ import { HEALTH_USERNAME_REPOSITORY, SERVICE_NAME, } from './app.constants'; +import { + ConfigurationModule, + ConfigurationModuleOptions, +} from '@mobicoop/configuration-module'; @Module({ imports: [ @@ -53,6 +57,19 @@ import { messagePublisher, }), }), + ConfigurationModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async ( + configService: ConfigService, + ): Promise => { + return { + host: configService.get('REDIS_HOST') as string, + port: configService.get('REDIS_PORT') as number, + password: configService.get('REDIS_PASSWORD'), + }; + }, + }), AuthenticationModule, AuthorizationModule, MessagerModule, diff --git a/src/modules/authentication/authentication.constants.ts b/src/modules/authentication/authentication.constants.ts index d9bdd50..5e4596d 100644 --- a/src/modules/authentication/authentication.constants.ts +++ b/src/modules/authentication/authentication.constants.ts @@ -1,3 +1,7 @@ +import { + ConfigurationDomainGet, + ConfigurationType, +} from '@mobicoop/configuration-module'; import { IsStrongPasswordOptions } from 'class-validator'; export const STRONG_PASSWORD_OPTIONS: IsStrongPasswordOptions = { @@ -7,3 +11,11 @@ export const STRONG_PASSWORD_OPTIONS: IsStrongPasswordOptions = { minSymbols: 1, minUppercase: 1, }; + +export const AUTH_CONFIG_ENCRYPTION_ALGORITHM = 'encryptionAlgorithm'; +export const AuthConfig: ConfigurationDomainGet[] = [ + { + key: AUTH_CONFIG_ENCRYPTION_ALGORITHM, + type: ConfigurationType.STRING, + }, +]; diff --git a/src/modules/authentication/authentication.di-tokens.ts b/src/modules/authentication/authentication.di-tokens.ts index a92983a..2c40bfd 100644 --- a/src/modules/authentication/authentication.di-tokens.ts +++ b/src/modules/authentication/authentication.di-tokens.ts @@ -1,3 +1,7 @@ export const AUTH_MESSAGE_PUBLISHER = Symbol('AUTH_MESSAGE_PUBLISHER'); export const AUTHENTICATION_REPOSITORY = Symbol('AUTHENTICATION_REPOSITORY'); export const USERNAME_REPOSITORY = Symbol('USERNAME_REPOSITORY'); +export const AUTHENTICATION_CONFIGURATION_REPOSITORY = Symbol( + 'AUTHENTICATION_CONFIGURATION_REPOSITORY', +); +export const PASSWORD_VERIFIER = Symbol('PASSWORD_VERIFIER'); diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index 3bc9a10..496cdcd 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -4,7 +4,9 @@ import { CreateAuthenticationService } from './core/application/commands/create- import { AuthenticationMapper } from './authentication.mapper'; import { AUTH_MESSAGE_PUBLISHER, + AUTHENTICATION_CONFIGURATION_REPOSITORY, AUTHENTICATION_REPOSITORY, + PASSWORD_VERIFIER, USERNAME_REPOSITORY, } from './authentication.di-tokens'; import { AuthenticationRepository } from './infrastructure/authentication.repository'; @@ -27,6 +29,8 @@ import { ValidateAuthenticationGrpcController } from './interface/grpc-controlle import { ValidateAuthenticationQueryHandler } from './core/application/queries/validate-authentication/validate-authentication.query-handler'; import { UserUpdatedMessageHandler } from './interface/message-handlers/user-updated.message-handler'; import { UserDeletedMessageHandler } from './interface/message-handlers/user-deleted.message-handler'; +import { ConfigurationRepository } from '@mobicoop/configuration-module'; +import { PasswordVerifier } from './infrastructure/password-verifier'; const grpcControllers = [ CreateAuthenticationGrpcController, @@ -62,6 +66,10 @@ const repositories: Provider[] = [ provide: USERNAME_REPOSITORY, useClass: UsernameRepository, }, + { + provide: AUTHENTICATION_CONFIGURATION_REPOSITORY, + useClass: ConfigurationRepository, + }, ]; const messagePublishers: Provider[] = [ @@ -71,7 +79,13 @@ const messagePublishers: Provider[] = [ }, ]; -const orms: Provider[] = [PrismaService]; +const orms: Provider[] = [ + PrismaService, + { + provide: PASSWORD_VERIFIER, + useClass: PasswordVerifier, + }, +]; @Module({ imports: [CqrsModule], diff --git a/src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts index 3ff15f1..674c46b 100644 --- a/src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts +++ b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts @@ -6,26 +6,53 @@ import { UniqueConstraintException, } from '@mobicoop/ddd-library'; import { CreateAuthenticationCommand } from './create-authentication.command'; -import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { + AUTHENTICATION_CONFIGURATION_REPOSITORY, + AUTHENTICATION_REPOSITORY, +} from '@modules/authentication/authentication.di-tokens'; import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; import { AuthenticationAlreadyExistsException, UsernameAlreadyExistsException, } from '@modules/authentication/core/domain/authentication.errors'; +import { + ConfigurationDomain, + Configurator, + GetConfigurationRepositoryPort, +} from '@mobicoop/configuration-module'; +import { + AUTH_CONFIG_ENCRYPTION_ALGORITHM, + AuthConfig, +} from '@modules/authentication/authentication.constants'; +import { EncryptionAlgorithm } from '@modules/authentication/core/domain/username.types'; +import { PasswordEncrypter } from '@modules/authentication/infrastructure/password-encrypter'; +import { PasswordEncrypterPort } from '../../ports/password-encrypter.port'; @CommandHandler(CreateAuthenticationCommand) export class CreateAuthenticationService implements ICommandHandler { constructor( @Inject(AUTHENTICATION_REPOSITORY) private readonly authenticationRepository: AuthenticationRepositoryPort, + @Inject(AUTHENTICATION_CONFIGURATION_REPOSITORY) + private readonly configurationRepository: GetConfigurationRepositoryPort, ) {} async execute(command: CreateAuthenticationCommand): Promise { + const authConfigurator: Configurator = + await this.configurationRepository.mget( + ConfigurationDomain.AUTH, + AuthConfig, + ); + const passwordEncrypter: PasswordEncrypterPort = new PasswordEncrypter( + authConfigurator.get( + AUTH_CONFIG_ENCRYPTION_ALGORITHM, + ), + ); const authentication: AuthenticationEntity = await AuthenticationEntity.create({ userId: command.userId, - password: command.password, + password: await passwordEncrypter.encrypt(command.password), usernames: command.usernames, }); try { diff --git a/src/modules/authentication/core/application/commands/update-password/update-password.service.ts b/src/modules/authentication/core/application/commands/update-password/update-password.service.ts index 61ef660..1df5566 100644 --- a/src/modules/authentication/core/application/commands/update-password/update-password.service.ts +++ b/src/modules/authentication/core/application/commands/update-password/update-password.service.ts @@ -1,22 +1,54 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { Inject } from '@nestjs/common'; import { AggregateID } from '@mobicoop/ddd-library'; -import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { + AUTHENTICATION_CONFIGURATION_REPOSITORY, + AUTHENTICATION_REPOSITORY, +} from '@modules/authentication/authentication.di-tokens'; import { UpdatePasswordCommand } from './update-password.command'; import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; +import { + ConfigurationDomain, + Configurator, + GetConfigurationRepositoryPort, +} from '@mobicoop/configuration-module'; +import { + AUTH_CONFIG_ENCRYPTION_ALGORITHM, + AuthConfig, +} from '@modules/authentication/authentication.constants'; +import { EncryptionAlgorithm } from '@modules/authentication/core/domain/username.types'; +import { PasswordEncrypterPort } from '../../ports/password-encrypter.port'; +import { PasswordEncrypter } from '@modules/authentication/infrastructure/password-encrypter'; @CommandHandler(UpdatePasswordCommand) export class UpdatePasswordService implements ICommandHandler { constructor( @Inject(AUTHENTICATION_REPOSITORY) private readonly authenticationRepository: AuthenticationRepositoryPort, + @Inject(AUTHENTICATION_CONFIGURATION_REPOSITORY) + private readonly configurationRepository: GetConfigurationRepositoryPort, ) {} async execute(command: UpdatePasswordCommand): Promise { + const authConfigurator: Configurator = + await this.configurationRepository.mget( + ConfigurationDomain.AUTH, + AuthConfig, + ); + const encryptionAlgorithm: EncryptionAlgorithm = + authConfigurator.get( + AUTH_CONFIG_ENCRYPTION_ALGORITHM, + ); + const passwordEncrypter: PasswordEncrypterPort = new PasswordEncrypter( + encryptionAlgorithm, + ); const authentication: AuthenticationEntity = await this.authenticationRepository.findOneById(command.userId); - await authentication.updatePassword(command.password); + const encryptedPassword: string = await passwordEncrypter.encrypt( + command.password, + ); + await authentication.updatePassword(encryptedPassword); await this.authenticationRepository.update(command.userId, authentication); return authentication.id; } diff --git a/src/modules/authentication/core/application/ports/password-encrypter.port.ts b/src/modules/authentication/core/application/ports/password-encrypter.port.ts new file mode 100644 index 0000000..de0ca97 --- /dev/null +++ b/src/modules/authentication/core/application/ports/password-encrypter.port.ts @@ -0,0 +1,6 @@ +import { EncryptionAlgorithm } from '../../domain/username.types'; + +export abstract class PasswordEncrypterPort { + constructor(protected readonly encryptionAlgorithm: EncryptionAlgorithm) {} + abstract encrypt(plainPassword: string): Promise; +} diff --git a/src/modules/authentication/core/application/ports/password-verifier.port.ts b/src/modules/authentication/core/application/ports/password-verifier.port.ts new file mode 100644 index 0000000..d8037c6 --- /dev/null +++ b/src/modules/authentication/core/application/ports/password-verifier.port.ts @@ -0,0 +1,3 @@ +export interface PasswordVerifierPort { + verify(passwordToVerify: string, encryptedPassword: string): Promise; +} diff --git a/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler.ts b/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler.ts index 88aa703..95ba512 100644 --- a/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler.ts +++ b/src/modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler.ts @@ -3,6 +3,7 @@ import { Inject, UnauthorizedException } from '@nestjs/common'; import { ValidateAuthenticationQuery } from './validate-authentication.query'; import { AUTHENTICATION_REPOSITORY, + PASSWORD_VERIFIER, USERNAME_REPOSITORY, } from '@modules/authentication/authentication.di-tokens'; import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; @@ -10,6 +11,7 @@ import { UsernameRepositoryPort } from '../../ports/username.repository.port'; import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; import { AggregateID, NotFoundException } from '@mobicoop/ddd-library'; +import { PasswordVerifierPort } from '../../ports/password-verifier.port'; @QueryHandler(ValidateAuthenticationQuery) export class ValidateAuthenticationQueryHandler implements IQueryHandler { @@ -18,6 +20,8 @@ export class ValidateAuthenticationQueryHandler implements IQueryHandler { private readonly authenticationRepository: AuthenticationRepositoryPort, @Inject(USERNAME_REPOSITORY) private readonly usernameRepository: UsernameRepositoryPort, + @Inject(PASSWORD_VERIFIER) + private readonly passwordVerifier: PasswordVerifierPort, ) {} execute = async ( @@ -40,6 +44,7 @@ export class ValidateAuthenticationQueryHandler implements IQueryHandler { try { const isAuthenticated = await authenticationEntity.authenticate( query.password, + this.passwordVerifier, ); if (isAuthenticated) return authenticationEntity.id; throw new UnauthorizedException(); diff --git a/src/modules/authentication/core/domain/authentication.entity.ts b/src/modules/authentication/core/domain/authentication.entity.ts index 184a0af..176fcb3 100644 --- a/src/modules/authentication/core/domain/authentication.entity.ts +++ b/src/modules/authentication/core/domain/authentication.entity.ts @@ -1,5 +1,4 @@ import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library'; -import * as bcrypt from 'bcrypt'; import { AuthenticationProps, CreateAuthenticationProps, @@ -7,6 +6,7 @@ import { import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-event'; import { AuthenticationDeletedDomainEvent } from './events/authentication-deleted.domain-event'; import { PasswordUpdatedDomainEvent } from './events/password-updated.domain-event'; +import { PasswordVerifierPort } from '../application/ports/password-verifier.port'; export class AuthenticationEntity extends AggregateRoot { protected readonly _id: AggregateID; @@ -15,12 +15,11 @@ export class AuthenticationEntity extends AggregateRoot { create: CreateAuthenticationProps, ): Promise => { const props: AuthenticationProps = { ...create }; - const hash = await AuthenticationEntity.encryptPassword(props.password); const authentication = new AuthenticationEntity({ id: props.userId, props: { userId: props.userId, - password: hash, + password: props.password, usernames: props.usernames, }, }); @@ -31,7 +30,7 @@ export class AuthenticationEntity extends AggregateRoot { }; updatePassword = async (password: string): Promise => { - this.props.password = await AuthenticationEntity.encryptPassword(password); + this.props.password = password; this.addEvent( new PasswordUpdatedDomainEvent({ aggregateId: this.id, @@ -47,13 +46,13 @@ export class AuthenticationEntity extends AggregateRoot { ); } - authenticate = async (password: string): Promise => - await bcrypt.compare(password, this.props.password); + authenticate = async ( + password: string, + passwordVerifier: PasswordVerifierPort, + ): Promise => + await passwordVerifier.verify(password, this.props.password); validate(): void { // entity business rules validation to protect it's invariant before saving entity to a database } - - private static encryptPassword = async (password: string): Promise => - await bcrypt.hash(password, 10); } diff --git a/src/modules/authentication/core/domain/username.types.ts b/src/modules/authentication/core/domain/username.types.ts index 0d3df9b..32eca66 100644 --- a/src/modules/authentication/core/domain/username.types.ts +++ b/src/modules/authentication/core/domain/username.types.ts @@ -19,3 +19,10 @@ export enum Type { EMAIL = 'EMAIL', PHONE = 'PHONE', } + +export enum EncryptionAlgorithm { + BCRYPT = 'BCRYPT', + ARGON2I = 'ARGON2I', + ARGON2D = 'ARGON2D', + ARGON2ID = 'ARGON2ID', +} diff --git a/src/modules/authentication/infrastructure/password-encrypter.ts b/src/modules/authentication/infrastructure/password-encrypter.ts new file mode 100644 index 0000000..2cecaad --- /dev/null +++ b/src/modules/authentication/infrastructure/password-encrypter.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@nestjs/common'; +import { EncryptionAlgorithm } from '../core/domain/username.types'; +import * as bcrypt from 'bcrypt'; +import * as argon2 from 'argon2'; +import { PasswordEncrypterPort } from '../core/application/ports/password-encrypter.port'; + +@Injectable() +export class PasswordEncrypter extends PasswordEncrypterPort { + encrypt = async (plainPassword: string): Promise => { + switch (this.encryptionAlgorithm) { + case EncryptionAlgorithm.BCRYPT: + return await bcrypt.hash(plainPassword, 10); + case EncryptionAlgorithm.ARGON2D: + case EncryptionAlgorithm.ARGON2I: + case EncryptionAlgorithm.ARGON2ID: + return await argon2.hash(plainPassword, { + type: this.argonType(), + }); + } + }; + + private argonType = (): + | typeof argon2.argon2d + | typeof argon2.argon2i + | typeof argon2.argon2id => { + switch (this.encryptionAlgorithm) { + case EncryptionAlgorithm.ARGON2D: + return argon2.argon2d; + case EncryptionAlgorithm.ARGON2I: + return argon2.argon2i; + default: + return argon2.argon2id; + } + }; +} diff --git a/src/modules/authentication/infrastructure/password-verifier.ts b/src/modules/authentication/infrastructure/password-verifier.ts new file mode 100644 index 0000000..a03ec08 --- /dev/null +++ b/src/modules/authentication/infrastructure/password-verifier.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@nestjs/common'; +import { EncryptionAlgorithm } from '../core/domain/username.types'; +import * as bcrypt from 'bcrypt'; +import * as argon2 from 'argon2'; +import { PasswordVerifierPort } from '../core/application/ports/password-verifier.port'; + +@Injectable() +export class PasswordVerifier implements PasswordVerifierPort { + verify = async ( + passwordToVerify: string, + encryptedPassword: string, + ): Promise => { + const encryptionAlgorithm: EncryptionAlgorithm = + this.guessEncryptionAlgorithm(encryptedPassword); + switch (encryptionAlgorithm) { + case EncryptionAlgorithm.BCRYPT: + return await bcrypt.compare(passwordToVerify, encryptedPassword); + case EncryptionAlgorithm.ARGON2D: + case EncryptionAlgorithm.ARGON2I: + case EncryptionAlgorithm.ARGON2ID: + return await argon2.verify(encryptedPassword, passwordToVerify, { + type: this.argonType(encryptionAlgorithm), + }); + } + }; + + private guessEncryptionAlgorithm = ( + password: string, + ): EncryptionAlgorithm => { + if (password.substring(1, 9) === 'argon2id') + return EncryptionAlgorithm.ARGON2ID; + if (password.substring(1, 8) === 'argon2i') + return EncryptionAlgorithm.ARGON2I; + if (password.substring(1, 8) === 'argon2d') + return EncryptionAlgorithm.ARGON2D; + return EncryptionAlgorithm.BCRYPT; + }; + + private argonType = ( + encryptionAlgorithm: EncryptionAlgorithm, + ): typeof argon2.argon2d | typeof argon2.argon2i | typeof argon2.argon2id => { + switch (encryptionAlgorithm) { + case EncryptionAlgorithm.ARGON2D: + return argon2.argon2d; + case EncryptionAlgorithm.ARGON2I: + return argon2.argon2i; + default: + return argon2.argon2id; + } + }; +} diff --git a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts index 551687a..b8550d1 100644 --- a/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts +++ b/src/modules/authentication/tests/unit/core/authentication.entity.spec.ts @@ -1,3 +1,4 @@ +import { PasswordVerifierPort } from '@modules/authentication/core/application/ports/password-verifier.port'; import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; import { CreateAuthenticationProps } from '@modules/authentication/core/domain/authentication.types'; import { AuthenticationDeletedDomainEvent } from '@modules/authentication/core/domain/events/authentication-deleted.domain-event'; @@ -30,6 +31,10 @@ const createAuthenticationPropsWith2Usernames: CreateAuthenticationProps = { ], }; +const mockPasswordVerifier: PasswordVerifierPort = { + verify: jest.fn().mockResolvedValueOnce(true).mockResolvedValueOnce(false), +}; + describe('Authentication entity create', () => { it('should create a new authentication entity', async () => { const authenticationEntity: AuthenticationEntity = @@ -37,7 +42,6 @@ describe('Authentication entity create', () => { expect(authenticationEntity.id).toBe( '165192d4-398a-4469-a16b-98c02cc6f531', ); - expect(authenticationEntity.getProps().password.length).toBe(60); expect(authenticationEntity.domainEvents.length).toBe(1); }); it('should create a new authentication entity with 2 usernames', async () => { @@ -80,15 +84,19 @@ describe('Authentication password validation', () => { it('should validate a valid password', async () => { const authenticationEntity: AuthenticationEntity = await AuthenticationEntity.create(createAuthenticationProps); - const result: boolean = - await authenticationEntity.authenticate('somePassword'); + const result: boolean = await authenticationEntity.authenticate( + 'somePassword', + mockPasswordVerifier, + ); expect(result).toBeTruthy(); }); it('should not validate an invalid password', async () => { const authenticationEntity: AuthenticationEntity = await AuthenticationEntity.create(createAuthenticationProps); - const result: boolean = - await authenticationEntity.authenticate('someWrongPassword'); + const result: boolean = await authenticationEntity.authenticate( + 'someWrongPassword', + mockPasswordVerifier, + ); expect(result).toBeFalsy(); }); }); diff --git a/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts b/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts index b4df3d5..0d75a5b 100644 --- a/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts +++ b/src/modules/authentication/tests/unit/core/create-authentication.service.spec.ts @@ -1,9 +1,19 @@ +import { + ConfigurationDomain, + ConfigurationDomainGet, + Configurator, + GetConfigurationRepositoryPort, +} from '@mobicoop/configuration-module'; import { AggregateID, ConflictException, UniqueConstraintException, } from '@mobicoop/ddd-library'; -import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { AUTH_CONFIG_ENCRYPTION_ALGORITHM } from '@modules/authentication/authentication.constants'; +import { + AUTHENTICATION_CONFIGURATION_REPOSITORY, + AUTHENTICATION_REPOSITORY, +} from '@modules/authentication/authentication.di-tokens'; import { CreateAuthenticationCommand } from '@modules/authentication/core/application/commands/create-authentication/create-authentication.command'; import { CreateAuthenticationService } from '@modules/authentication/core/application/commands/create-authentication/create-authentication.service'; import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; @@ -44,6 +54,25 @@ const mockAuthenticationRepository = { }), }; +const mockConfigurationRepository: GetConfigurationRepositoryPort = { + get: jest.fn(), + mget: jest.fn().mockImplementation( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (domain: ConfigurationDomain, configs: ConfigurationDomainGet[]) => { + switch (domain) { + case ConfigurationDomain.AUTH: + return new Configurator(ConfigurationDomain.AUTH, [ + { + domain: ConfigurationDomain.AUTH, + key: AUTH_CONFIG_ENCRYPTION_ALGORITHM, + value: 'BCRYPT', + }, + ]); + } + }, + ), +}; + describe('Create Authentication Service', () => { let createAuthenticationService: CreateAuthenticationService; @@ -54,6 +83,10 @@ describe('Create Authentication Service', () => { provide: AUTHENTICATION_REPOSITORY, useValue: mockAuthenticationRepository, }, + { + provide: AUTHENTICATION_CONFIGURATION_REPOSITORY, + useValue: mockConfigurationRepository, + }, CreateAuthenticationService, ], }).compile(); diff --git a/src/modules/authentication/tests/unit/core/update-password.service.spec.ts b/src/modules/authentication/tests/unit/core/update-password.service.spec.ts index 3270f0f..de7c7ab 100644 --- a/src/modules/authentication/tests/unit/core/update-password.service.spec.ts +++ b/src/modules/authentication/tests/unit/core/update-password.service.spec.ts @@ -1,10 +1,20 @@ import { AggregateID } from '@mobicoop/ddd-library'; -import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { + AUTHENTICATION_CONFIGURATION_REPOSITORY, + AUTHENTICATION_REPOSITORY, +} from '@modules/authentication/authentication.di-tokens'; import { UpdatePasswordService } from '@modules/authentication/core/application/commands/update-password/update-password.service'; import { Type } from '@modules/authentication/core/domain/username.types'; import { UpdatePasswordRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/update-password.request.dto'; import { Test, TestingModule } from '@nestjs/testing'; import { UpdatePasswordCommand } from '@modules/authentication/core/application/commands/update-password/update-password.command'; +import { + ConfigurationDomain, + ConfigurationDomainGet, + Configurator, + GetConfigurationRepositoryPort, +} from '@mobicoop/configuration-module'; +import { AUTH_CONFIG_ENCRYPTION_ALGORITHM } from '@modules/authentication/authentication.constants'; const updatePasswordRequest: UpdatePasswordRequestDto = { userId: '165192d4-398a-4469-a16b-98c02cc6f531', @@ -24,6 +34,25 @@ const mockAuthenticationRepository = { update: jest.fn().mockImplementation(), }; +const mockConfigurationRepository: GetConfigurationRepositoryPort = { + get: jest.fn(), + mget: jest.fn().mockImplementation( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + (domain: ConfigurationDomain, configs: ConfigurationDomainGet[]) => { + switch (domain) { + case ConfigurationDomain.AUTH: + return new Configurator(ConfigurationDomain.AUTH, [ + { + domain: ConfigurationDomain.AUTH, + key: AUTH_CONFIG_ENCRYPTION_ALGORITHM, + value: 'BCRYPT', + }, + ]); + } + }, + ), +}; + describe('Update Password Service', () => { let updatePasswordService: UpdatePasswordService; @@ -34,6 +63,10 @@ describe('Update Password Service', () => { provide: AUTHENTICATION_REPOSITORY, useValue: mockAuthenticationRepository, }, + { + provide: AUTHENTICATION_CONFIGURATION_REPOSITORY, + useValue: mockConfigurationRepository, + }, UpdatePasswordService, ], }).compile(); diff --git a/src/modules/authentication/tests/unit/core/validate-authentication.query-handler.spec.ts b/src/modules/authentication/tests/unit/core/validate-authentication.query-handler.spec.ts index dd71fe3..20fac8c 100644 --- a/src/modules/authentication/tests/unit/core/validate-authentication.query-handler.spec.ts +++ b/src/modules/authentication/tests/unit/core/validate-authentication.query-handler.spec.ts @@ -1,8 +1,10 @@ import { AggregateID, NotFoundException } from '@mobicoop/ddd-library'; import { AUTHENTICATION_REPOSITORY, + PASSWORD_VERIFIER, USERNAME_REPOSITORY, } from '@modules/authentication/authentication.di-tokens'; +import { PasswordVerifierPort } from '@modules/authentication/core/application/ports/password-verifier.port'; import { ValidateAuthenticationQuery } from '@modules/authentication/core/application/queries/validate-authentication/validate-authentication.query'; import { ValidateAuthenticationQueryHandler } from '@modules/authentication/core/application/queries/validate-authentication/validate-authentication.query-handler'; import { UnauthorizedException } from '@nestjs/common'; @@ -50,6 +52,10 @@ const mockAuthenticationRepository = { }), }; +const mockPasswordVerifier: PasswordVerifierPort = { + verify: jest.fn().mockResolvedValueOnce(true).mockResolvedValueOnce(false), +}; + describe('Validate Authentication Query Handler', () => { let validateAuthenticationQueryHandler: ValidateAuthenticationQueryHandler; @@ -64,6 +70,10 @@ describe('Validate Authentication Query Handler', () => { provide: USERNAME_REPOSITORY, useValue: mockUsernameRepository, }, + { + provide: PASSWORD_VERIFIER, + useValue: mockPasswordVerifier, + }, ValidateAuthenticationQueryHandler, ], }).compile(); diff --git a/src/modules/authentication/tests/unit/infrastructure/password-encrypter.spec.ts b/src/modules/authentication/tests/unit/infrastructure/password-encrypter.spec.ts new file mode 100644 index 0000000..12753a8 --- /dev/null +++ b/src/modules/authentication/tests/unit/infrastructure/password-encrypter.spec.ts @@ -0,0 +1,41 @@ +import { PasswordEncrypterPort } from '@modules/authentication/core/application/ports/password-encrypter.port'; +import { EncryptionAlgorithm } from '@modules/authentication/core/domain/username.types'; +import { PasswordEncrypter } from '@modules/authentication/infrastructure/password-encrypter'; + +describe('Password encrypter', () => { + it('should encrypt a password in bcrypt', async () => { + const passwordEncrypter: PasswordEncrypterPort = new PasswordEncrypter( + EncryptionAlgorithm.BCRYPT, + ); + const encryptedPassword: string = + await passwordEncrypter.encrypt('somePassword'); + expect(encryptedPassword.substring(0, 2)).toBe('$2'); + }); + + it('should encrypt a password in argon2i', async () => { + const passwordEncrypter: PasswordEncrypterPort = new PasswordEncrypter( + EncryptionAlgorithm.ARGON2I, + ); + const encryptedPassword: string = + await passwordEncrypter.encrypt('somePassword'); + expect(encryptedPassword.substring(0, 9)).toBe('$argon2i$'); + }); + + it('should encrypt a password in argon2d', async () => { + const passwordEncrypter: PasswordEncrypterPort = new PasswordEncrypter( + EncryptionAlgorithm.ARGON2D, + ); + const encryptedPassword: string = + await passwordEncrypter.encrypt('somePassword'); + expect(encryptedPassword.substring(0, 9)).toBe('$argon2d$'); + }); + + it('should encrypt a password in argon2id', async () => { + const passwordEncrypter: PasswordEncrypterPort = new PasswordEncrypter( + EncryptionAlgorithm.ARGON2ID, + ); + const encryptedPassword: string = + await passwordEncrypter.encrypt('somePassword'); + expect(encryptedPassword.substring(0, 10)).toBe('$argon2id$'); + }); +}); diff --git a/src/modules/authentication/tests/unit/infrastructure/password-verifier.spec.ts b/src/modules/authentication/tests/unit/infrastructure/password-verifier.spec.ts new file mode 100644 index 0000000..1019b73 --- /dev/null +++ b/src/modules/authentication/tests/unit/infrastructure/password-verifier.spec.ts @@ -0,0 +1,46 @@ +import { PasswordVerifierPort } from '@modules/authentication/core/application/ports/password-verifier.port'; +import { PasswordVerifier } from '@modules/authentication/infrastructure/password-verifier'; + +describe('Password verifier', () => { + const passwordVerifier: PasswordVerifierPort = new PasswordVerifier(); + + it('should verify a bcrypt password', async () => { + const bcryptEncryptedPassword: string = + '$2b$10$IGMj7/tH66kzO8rqwdogK.tgY2nFOdtMC.dzMAYUVfbSTPwWk7sk.'; + const verified: boolean = await passwordVerifier.verify( + 'somePassword', + bcryptEncryptedPassword, + ); + expect(verified).toBeTruthy(); + }); + + it('should verify an argon2i password', async () => { + const argon2iEncryptedPassword: string = + '$argon2i$v=19$m=16,t=2,p=1$c2lERkU0UmpMYkhSa1h3eQ$hSyr7TZZDzvq9XJgA+D/pw'; + const verified: boolean = await passwordVerifier.verify( + 'somePassword', + argon2iEncryptedPassword, + ); + expect(verified).toBeTruthy(); + }); + + it('should verify an argon2d password', async () => { + const argon2dEncryptedPassword: string = + '$argon2d$v=19$m=16,t=2,p=1$c2lERkU0UmpMYkhSa1h3eQ$YBopyKamOUl7ZPhu+KPASw'; + const verified: boolean = await passwordVerifier.verify( + 'somePassword', + argon2dEncryptedPassword, + ); + expect(verified).toBeTruthy(); + }); + + it('should verify an argon2id password', async () => { + const argon2idEncryptedPassword: string = + '$argon2id$v=19$m=16,t=2,p=1$c2lERkU0UmpMYkhSa1h3eQ$Heokt0Lk6mtBmRE07q94lw'; + const verified: boolean = await passwordVerifier.verify( + 'somePassword', + argon2idEncryptedPassword, + ); + expect(verified).toBeTruthy(); + }); +});