diff --git a/docsfera.json b/docsfera.json index 65c1901..bb1c9b8 100644 --- a/docsfera.json +++ b/docsfera.json @@ -5993,6 +5993,207 @@ ] } }, + "/identity-providers": { + "post": { + "operationId": "IdentityProviderController_addIdentityProvider", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateIdentityProvider" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IdentityProviderResponse" + } + } + } + } + }, + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + }, + { + "access-token": [] + } + ] + }, + "get": { + "operationId": "IdentityProviderController_getProviders", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IdentityProviderListResponse" + } + } + } + } + }, + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + }, + { + "access-token": [] + } + ] + } + }, + "/identity-providers/{id}": { + "delete": { + "operationId": "IdentityProviderController_deleteIdentityProvider", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } + ] + }, + "put": { + "operationId": "IdentityProviderController_updateIdentityProviders", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateIdentityProvider" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + }, + { + "access-token": [] + } + ] + }, + "get": { + "operationId": "IdentityProviderController_loginIdp", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + } + } + } + }, + "/identity-providers/callback": { + "post": { + "operationId": "IdentityProviderController_callbackIdp", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, + "/identity-providers/links": { + "get": { + "operationId": "IdentityProviderController_providerLinks", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + } + } + }, "/network-policy": { "get": { "operationId": "NetworkPolicyController_getNetworks", @@ -6301,13 +6502,13 @@ "refreshToken": { "type": "string" }, - "customerName": { + "userId": { "type": "string" } }, "required": [ "refreshToken", - "customerName" + "userId" ] }, "AuthRefreshAccessTokenRes": { @@ -8949,6 +9150,80 @@ "theme" ] }, + "CreateIdentityProvider": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "clientSecret": { + "type": "string" + }, + "issuerUrl": { + "type": "string" + }, + "permissions": { + "type": "array", + "items": { + "type": "number" + } + } + }, + "required": [ + "name", + "clientId", + "clientSecret", + "issuerUrl", + "permissions" + ] + }, + "IdentityProviderResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "issueUrl": { + "type": "string" + }, + "permissions": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "name", + "clientId", + "issueUrl", + "permissions" + ] + }, + "IdentityProviderListResponse": { + "type": "object", + "properties": { + "providers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IdentityProviderResponse" + } + } + }, + "required": [ + "providers" + ] + }, "NetworkPoliciesDTO": { "type": "object", "properties": { diff --git a/maestro/templates/deployment.yaml b/maestro/templates/deployment.yaml index ac9eaae..12c70d9 100644 --- a/maestro/templates/deployment.yaml +++ b/maestro/templates/deployment.yaml @@ -102,6 +102,12 @@ spec: value: {{ .Values.maestro.open_group_id }} - name: DEDICATED_PROXY value: {{ .Values.maestro.dedicated_proxy }} + - name: REDIS_DATABASE + value: "{{ .Values.maestro.redis_database }}" + - name: REDIS_HOST + value: {{ .Values.maestro.redis_host }} + - name: REDIS_PORT + value: "{{ .Values.maestro.redis_port }}" - name: JWT_PRIVATE_KEY valueFrom: secretKeyRef: diff --git a/maestro/values.yaml b/maestro/values.yaml index f568ed9..2e9526d 100644 --- a/maestro/values.yaml +++ b/maestro/values.yaml @@ -45,6 +45,9 @@ maestro: open_group_id: 401573bb-334f-44b2-b30e-88d4cea31ae9 dedicated_proxy: "" restricted_ip: "" + redis_host: "product-redis-prd.z4xvqj.0001.use1.cache.amazonaws.com" + redis_port: "6379" + redis_database: "0" autoscaling: enabled: false minReplicas: 1 diff --git a/package-lock.json b/package-lock.json index c9d852f..4092f52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@aws-sdk/client-secrets-manager": "^3.414.0", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4", "@dadosfera/protospack": "2.5.3", - "@dadosfera/protospack-v2": "3.37.0-beta.23", + "@dadosfera/protospack-v2": "3.38.0-beta.10", "@grpc/grpc-js": "^1.9.3", "@grpc/proto-loader": "^0.7.9", "@nestjs/cli": "^9.5.0", @@ -27,6 +27,8 @@ "@nestjs/swagger": "^6.3.0", "@nestjs/testing": "^9.4.3", "axios": "^0.27.2", + "cache-manager": "^5.1.4", + "cache-manager-ioredis-yet": "^1.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cron-parser": "^4.9.0", @@ -39,6 +41,7 @@ "jwk-to-pem": "^2.0.5", "mixpanel": "^0.17.0", "ms": "^3.0.0-canary.1", + "openid-client": "^5.7.1", "passport": "^0.6.0", "passport-facebook": "^3.0.0", "passport-forcedotcom": "^0.2.0", @@ -46,13 +49,16 @@ "passport-hubspot-oauth2": "^1.0.3", "passport-mailchimp": "^1.1.0", "puppeteer": "^24.7.2", + "redis": "^4.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.5.5", "swagger-ui-express": "^4.6.3" }, "devDependencies": { + "@types/cache-manager": "^4.0.6", "@types/express": "^4.17.17", + "@types/express-session": "^1.18.1", "@types/jest": "27.0.2", "@types/jsonwebtoken": "^8.5.9", "@types/jwk-to-pem": "^2.0.1", @@ -1400,9 +1406,9 @@ } }, "node_modules/@dadosfera/protospack-v2": { - "version": "3.37.0-beta.23", - "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.37.0-beta.23.tgz", - "integrity": "sha512-Dv3rODwbiubHB4u8sI5uJOSm1ETOXzlpaxHNEmzTNG2fLTIJxsaF1GlOBST3x9HTkhJC6hUJ8r77/qQu1FKPpQ==", + "version": "3.38.0-beta.10", + "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.38.0-beta.10.tgz", + "integrity": "sha512-4miwovVFHtBY1VTHdW8BVmSB8m+LXfIA8uigHbeo8IwreMtTHuLpvfenT4MBSPkdVmlo9+0XBKKf+PaSqtdMAg==", "license": "ISC", "dependencies": { "@grpc/grpc-js": "^1.9.3", @@ -1600,6 +1606,12 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "license": "MIT" + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2969,6 +2981,65 @@ "node": ">=18" } }, + "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==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", + "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -3654,6 +3725,13 @@ "@types/node": "*" } }, + "node_modules/@types/cache-manager": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/cache-manager/-/cache-manager-4.0.6.tgz", + "integrity": "sha512-8qL93MF05/xrzFm/LSPtzNEOE1eQF3VwGHAcQEylgp5hDSTe41jtFwbSYAPfyYcVa28y1vYSjIt0c1fLLUiC/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3723,6 +3801,16 @@ "@types/send": "*" } }, + "node_modules/@types/express-session": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.1.tgz", + "integrity": "sha512-S6TkD/lljxDlQ2u/4A70luD8/ZxZcrU5pQwI1rVXCiaVIywoFgbA+PIUNDjPhQpPdK0dGleLtYc/y7XWBfclBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -5237,6 +5325,41 @@ "node": ">= 0.8" } }, + "node_modules/cache-manager": { + "version": "5.7.6", + "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-5.7.6.tgz", + "integrity": "sha512-wBxnBHjDxF1RXpHCBD6HGvKER003Ts7IIm0CHpggliHzN1RZditb7rXoduE1rplc2DEFYKxhLKgFuchXMJje9w==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "lodash.clonedeep": "^4.5.0", + "lru-cache": "^10.2.2", + "promise-coalesce": "^1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/cache-manager-ioredis-yet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cache-manager-ioredis-yet/-/cache-manager-ioredis-yet-1.2.2.tgz", + "integrity": "sha512-o03N/tQxfFONZ1XLGgIxOFHuQQpjpRdnSAL1THG1YWZIVp1JMUfjU3ElSAjFN1LjbJXa55IpC8waG+VEoLUCUw==", + "deprecated": "With cache-manager v6 we now are using Keyv", + "license": "MIT", + "dependencies": { + "cache-manager": "^5.2.3", + "ioredis": "^5.3.2" + }, + "engines": { + "node": ">= 16.17.0" + } + }, + "node_modules/cache-manager/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -5514,6 +5637,15 @@ "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==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6027,6 +6159,15 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6802,6 +6943,12 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -7470,6 +7617,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "license": "MIT", + "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", @@ -8045,6 +8201,30 @@ "node": ">= 0.10" } }, + "node_modules/ioredis": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", + "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", + "license": "MIT", + "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/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", @@ -9044,6 +9224,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9366,12 +9555,30 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT" }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -10023,6 +10230,15 @@ "integrity": "sha512-CsubGNxhIEChNY4cXYuA6KXafztzHqzLLZ/y3Kasf3A+sa3lL9thq3z+7o0pZqzEinjXT6lXDPAfVWI59dUyzQ==", "license": "MIT" }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-identity-map": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/object-identity-map/-/object-identity-map-1.0.2.tgz", @@ -10068,6 +10284,15 @@ "node": ">= 0.4" } }, + "node_modules/oidc-token-hash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", + "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -10113,6 +10338,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openid-client": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "license": "MIT", + "dependencies": { + "jose": "^4.15.9", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/optional-js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/optional-js/-/optional-js-2.3.0.tgz", @@ -10815,6 +11055,15 @@ "node": ">=0.4.0" } }, + "node_modules/promise-coalesce": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/promise-coalesce/-/promise-coalesce-1.1.2.tgz", + "integrity": "sha512-zLaJ9b8hnC564fnJH6NFSOGZYYdzrAJn2JUUIwzoQb32fG2QAakpDNM+CZo1km6keXkRXRM+hml1BFAPVnPkxg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -11194,6 +11443,44 @@ "node": ">= 0.10" } }, + "node_modules/redis": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", + "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.1", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect-metadata": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", @@ -11881,6 +12168,12 @@ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", "license": "MIT" }, + "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==", + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", diff --git a/package.json b/package.json index fd3eb0b..ac37a76 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@aws-sdk/client-secrets-manager": "^3.414.0", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4", "@dadosfera/protospack": "2.5.3", - "@dadosfera/protospack-v2": "3.37.0-beta.23", + "@dadosfera/protospack-v2": "3.38.0-beta.10", "@grpc/grpc-js": "^1.9.3", "@grpc/proto-loader": "^0.7.9", "@nestjs/cli": "^9.5.0", @@ -45,6 +45,8 @@ "@nestjs/swagger": "^6.3.0", "@nestjs/testing": "^9.4.3", "axios": "^0.27.2", + "cache-manager": "^5.1.4", + "cache-manager-ioredis-yet": "^1.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cron-parser": "^4.9.0", @@ -57,6 +59,7 @@ "jwk-to-pem": "^2.0.5", "mixpanel": "^0.17.0", "ms": "^3.0.0-canary.1", + "openid-client": "^5.7.1", "passport": "^0.6.0", "passport-facebook": "^3.0.0", "passport-forcedotcom": "^0.2.0", @@ -64,6 +67,7 @@ "passport-hubspot-oauth2": "^1.0.3", "passport-mailchimp": "^1.1.0", "puppeteer": "^24.7.2", + "redis": "^4.5.1", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.5.5", @@ -73,7 +77,9 @@ "multer": "1.4.5-lts.1" }, "devDependencies": { + "@types/cache-manager": "^4.0.6", "@types/express": "^4.17.17", + "@types/express-session": "^1.18.1", "@types/jest": "27.0.2", "@types/jsonwebtoken": "^8.5.9", "@types/jwk-to-pem": "^2.0.1", diff --git a/src/app.module.ts b/src/app.module.ts index fedb388..1cb7032 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -28,6 +28,7 @@ import { MixpanelModule } from './modules/mixpanel/mixpanel.module'; import { CustomersModule } from './modules/customers/customers.module'; import { OpenDataModule } from './modules/open-data/open-data.module'; import { ThemeModule } from './modules/theme/theme.module'; +import { IdentityProviderModule } from './modules/identity-provider/identity-provider.module'; import { NetworkPolicyModule } from './modules/network-policy/network-policy.module'; @Module({ @@ -64,6 +65,7 @@ import { NetworkPolicyModule } from './modules/network-policy/network-policy.mod ThemeModule, //Always leave HealthModule last, so it is on the bottom of swagger HealthModule, + IdentityProviderModule, NetworkPolicyModule ], }) diff --git a/src/main.ts b/src/main.ts index 7511269..4065312 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import { AppModule } from './app.module'; import { writeFileSync } from 'fs'; import { execSync } from 'child_process'; import { INestApplication } from '@nestjs/common'; + async function bootstrap() { DadosferaLogger.setupLogger({ serviceName: 'maestro', @@ -26,8 +27,12 @@ async function bootstrap() { }, }); app.use(helmet()); - app.use('/catalog/register-dataset', json({ limit: '10mb' })); - app.use('/catalog/register-dataset', urlencoded({ extended: true, limit: '10mb' })); + + if (process.env.ENV === 'prd') { + app.use('/catalog/register-dataset', json({ limit: '10mb' })); + app.use('/catalog/register-dataset', urlencoded({ extended: true, limit: '10mb' })); + } + configureSwagger(app); await app.listen(3333); if (process.env.KILL_AFTER_START) await app.close(); diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 66870d5..0be40fa 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -26,7 +26,7 @@ import { AuthConfirmResetPasswordRequest, AuthEnableTotpMfaRequest, AuthDisableTotpMfaRequest, - AuthVerifyTotpMfaRequest, + AuthVerifyTotpMfaRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages'; import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum'; @@ -102,15 +102,14 @@ export class AuthController { @Headers('origin') origin: string, ) { this.logger.info('/auth - RefreshAccessToken'); - const { refreshToken, customerName: customer_name } = body; + const { refreshToken, userId } = body; const frontHost = origin.replace(/^https?:\/\//, ''); const metadata = PackTheMetadata({ language, - customer_name, custom_host: frontHost, }); - return this.authClient.refreshAccessToken({ refreshToken }, metadata); + return this.authClient.refreshAccessToken({ refreshToken, userId }, metadata); } @ApiInternalOnlyEndpoint() @@ -418,4 +417,5 @@ export class AuthController { return this.authClient.resetUsers(body.users, metadata); } + } diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index a8ceedd..c94a8d0 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -4,7 +4,7 @@ import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; import { lastValueFrom } from 'rxjs'; import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc'; -import { AuthProtoService as AuthServiceInterface } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service'; +import { AuthProtoService as AuthServiceInterface, IdentityProviderProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service'; import { AuthSnowflakeSignInRequest, AuthSignInRequest, @@ -23,11 +23,16 @@ import { DucClient } from '../duc/client.config'; import { Metadata } from '@grpc/grpc-js'; import { BulkEditResponse } from './dtos/login'; + @Injectable() export class AuthClientService implements OnModuleInit { + + logger: DadosferaLogger; + private authService: AuthServiceInterface; + private identityProviderService: IdentityProviderProtoService; constructor( @Inject(DadosferaLogger) dadosferaLogger: DadosferaLogger, @@ -40,6 +45,10 @@ export class AuthClientService implements OnModuleInit { this.authService = this.grpcClient.getService( ProtoServices.AuthProtoService, ); + + this.identityProviderService = this.grpcClient.getService( + ProtoServices.IdentityProviderProtoService, + ); } async getPublicKeys() { @@ -100,13 +109,13 @@ export class AuthClientService implements OnModuleInit { } async refreshAccessToken( - { refreshToken }: AuthRefreshAccessTokenRequest, + { refreshToken, userId }: AuthRefreshAccessTokenRequest, metadata: Metadata, ) { this.logger.info('RefreshAccessToken'); return lastValueFrom( - this.authService.AuthRefreshAccessToken({ refreshToken }, metadata), + this.authService.AuthRefreshAccessToken({ refreshToken, userId }, metadata), ); } diff --git a/src/modules/auth/dtos/login.ts b/src/modules/auth/dtos/login.ts index 0cd90ae..dc57d54 100644 --- a/src/modules/auth/dtos/login.ts +++ b/src/modules/auth/dtos/login.ts @@ -122,7 +122,7 @@ export class AuthRefreshAccessTokenReq { @ApiProperty() refreshToken: string; @ApiProperty() - customerName: string; + userId: string; } export class AuthRefreshAccessTokenRes { @ApiProperty() diff --git a/src/modules/identity-provider/dto/identity-provider.dto.ts b/src/modules/identity-provider/dto/identity-provider.dto.ts new file mode 100644 index 0000000..8eff00c --- /dev/null +++ b/src/modules/identity-provider/dto/identity-provider.dto.ts @@ -0,0 +1,47 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class CreateIdentityProvider { + + @ApiProperty() + name: string; + + @ApiProperty() + clientId: string; + + @ApiProperty() + clientSecret: string; + + @ApiProperty() + issuerUrl: string; + + @ApiProperty() + permissions: number[]; +} + + +export class IdentityProviderResponse { + + @ApiProperty() + id: string; + + @ApiProperty() + name: string; + + @ApiProperty() + clientId: string; + + @ApiProperty() + issueUrl: string; + + @ApiProperty() + permissions: { + id: number; + name: string; + }[]; +} + +export class IdentityProviderListResponse { + + @ApiProperty() + providers: IdentityProviderResponse[] +} \ No newline at end of file diff --git a/src/modules/identity-provider/dto/sso-signin.dto.ts b/src/modules/identity-provider/dto/sso-signin.dto.ts new file mode 100644 index 0000000..4bf26e2 --- /dev/null +++ b/src/modules/identity-provider/dto/sso-signin.dto.ts @@ -0,0 +1,10 @@ +export class SsoSignInDto { + readonly nonce: string; + readonly codeVerifier: string; + readonly state: string; + readonly id: string; + readonly clientId: string; + readonly clientSecret: string; + readonly issuerUrl: string; + readonly redirectUrls: string[]; +} \ No newline at end of file diff --git a/src/modules/identity-provider/identity-provider.controller.ts b/src/modules/identity-provider/identity-provider.controller.ts new file mode 100644 index 0000000..c25b2b4 --- /dev/null +++ b/src/modules/identity-provider/identity-provider.controller.ts @@ -0,0 +1,206 @@ +import DadosferaLogger from '@dadosfera/dadosfera-logs'; +import { + Body, + Controller, + Delete, + Get, + HttpCode, + HttpStatus, + Inject, + Param, + Post, + Put, + Redirect, + Req, +} from '@nestjs/common'; +import { IdentityProviderService } from './identity-provider.service'; +import { RequestUser, User } from 'src/decorators/user.decorator'; +import { Language } from 'src/decorators/language.decorator'; +import { LanguageEnum } from 'src/utils/languages.enum'; +import { PackTheMetadata } from 'src/utils/ PackTheMetadata'; +import { ApiOkResponse } from '@nestjs/swagger'; +import { + CreateIdentityProvider, + IdentityProviderListResponse, + IdentityProviderResponse, +} from './dto/identity-provider.dto'; +import { Request } from 'express'; +import ErrorCodes from 'src/utils/errorCodes'; +import { + Authenticated, + RequireModule, + RequireSomePermission, +} from 'src/decorators/authentication.decorator'; +import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum'; + +@Controller('identity-providers') +export class IdentityProviderController { + logger: DadosferaLogger; + + constructor( + @Inject(DadosferaLogger) + dadosferaLogger: DadosferaLogger, + private identityProviderService: IdentityProviderService, + ) { + this.logger = dadosferaLogger.logger; + } + + @Post() + @HttpCode(HttpStatus.OK) + @ApiOkResponse({ type: IdentityProviderResponse }) + @Authenticated() + @RequireModule('sso') + @RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN) + async addIdentityProvider( + @User() user: RequestUser, + @Body() body: CreateIdentityProvider, + @Language() language: LanguageEnum, + ) { + this.logger.info('POST /identity-providers'); + + const metadata = PackTheMetadata({ + ...user, + language, + }); + + return await this.identityProviderService.create(body, metadata); + } + + @Get() + @HttpCode(HttpStatus.OK) + @ApiOkResponse({ type: IdentityProviderListResponse }) + @Authenticated() + @RequireModule('sso') + @RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN) + async getProviders( + @User() user: RequestUser, + @Language() language: LanguageEnum, + ) { + this.logger.info('GET identity-providers'); + const metadata = PackTheMetadata({ + ...user, + language, + }); + + const result = await this.identityProviderService.getList(metadata); + return result; + } + + @Delete(':id') + @HttpCode(HttpStatus.NO_CONTENT) + @RequireModule('sso') + @RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN) + async deleteIdentityProvider( + @Param('id') id: string, + @User() user: RequestUser, + ) { + this.logger.info('DELETE /identity-providers'); + + const metadata = PackTheMetadata({ + ...user, + }); + + return await this.identityProviderService.deleteIdentityProvider( + id, + metadata, + ); + } + + @Put(':id') + @HttpCode(HttpStatus.OK) + @Authenticated() + @RequireModule('sso') + @RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN) + async updateIdentityProviders( + @Param('id') id: string, + @Body() body: CreateIdentityProvider, + @User() user: RequestUser, + ) { + this.logger.info('PUT /identity-providers'); + + const metadata = PackTheMetadata({ + ...user, + }); + + return await this.identityProviderService.updateIdentityProviders( + id, + body, + metadata, + ); + } + + @Post('/callback') + @HttpCode(HttpStatus.OK) + async callbackIdp( + @Req() req: Request, + @Language() language: LanguageEnum, + @Body() + body: { + state: string; + code: string; + }, + ) { + this.logger.info('GET /identity-providers/callback'); + const { code, state } = body; + + if (!code) { + this.logger.error('No code received from IDP'); + throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_RESPONSE); + } + + if (!state) { + this.logger.error('No state received from IDP'); + throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_RESPONSE); + } + + const origin = req.headers['origin'] as string; + + const lang = language.substring(0, 2) + language.substring(2).toUpperCase(); + const callbackUrl = process.env.ENV !== "prd" ? `${origin}/auth/callback` : `${origin}/${lang}/auth/callback`; + + return await this.identityProviderService.getTokenByIdp(code, state, callbackUrl); + } + + @Get('/links') + @HttpCode(HttpStatus.OK) + async providerLinks(@Req() req: Request) { + this.logger.info('GET /identity-providers/links'); + + const frontDomain = req.headers['origin'] as string; + + if (!frontDomain) { + throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_HEADER); + } + + const result = + await this.identityProviderService.identityProvidersLinksPerDomain( + frontDomain, + ); + return result; + } + + @Get(':id') + @HttpCode(HttpStatus.OK) + @Redirect() + async loginIdp(@Param('id') id: string, @Req() req: Request, @Language() language: LanguageEnum) { + this.logger.info('GET /identity-providers/:id'); + + + const frontDomain = req.headers['origin'] as string || req.headers['referer'] as string; + this.logger.info(`Front domain: ${frontDomain}`); + const host = frontDomain.lastIndexOf('/') !== -1 + ? frontDomain.substring(0, frontDomain.lastIndexOf('/')) + : frontDomain; + + const lang = language.substring(0, 2) + language.substring(2).toUpperCase(); + const callbackUrl = process.env.ENV !== "prd" ? `${host}/auth/callback` : `${host}/${lang}/auth/callback`; + + const redirectUrl = + await this.identityProviderService.loginIdentityProvider(id, callbackUrl); + + this.logger.info(`Redirecting to: ${redirectUrl}`); + return { + url: redirectUrl, + }; + } +} diff --git a/src/modules/identity-provider/identity-provider.module.ts b/src/modules/identity-provider/identity-provider.module.ts new file mode 100644 index 0000000..97d6299 --- /dev/null +++ b/src/modules/identity-provider/identity-provider.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { IdentityProviderController } from './identity-provider.controller'; +import { IdentityProviderService } from './identity-provider.service'; +import { ClientsModule } from '@nestjs/microservices'; +import { DucClient } from '../duc/client.config'; +import DadosferaLogger from '@dadosfera/dadosfera-logs'; +import { ServicesModule } from 'src/services/service.module'; + +const client = new DucClient(); + +@Module({ + imports: [ClientsModule.register([client.providerOptions]), ServicesModule], + controllers: [IdentityProviderController], + providers: [IdentityProviderService, DadosferaLogger], +}) +export class IdentityProviderModule {} diff --git a/src/modules/identity-provider/identity-provider.service.ts b/src/modules/identity-provider/identity-provider.service.ts new file mode 100644 index 0000000..4cca0fe --- /dev/null +++ b/src/modules/identity-provider/identity-provider.service.ts @@ -0,0 +1,163 @@ +import { + BadRequestException, + Inject, + Injectable, + OnModuleInit, +} from '@nestjs/common'; +import { DucClient } from '../duc/client.config'; +import { ClientGrpc } from '@nestjs/microservices'; +import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc'; +import { IdentityProviderProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service'; +import { lastValueFrom } from 'rxjs'; +import { IdentityProviderRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages'; +import { Metadata } from '@grpc/grpc-js'; +import { Issuer, generators } from 'openid-client'; +import { SsoSignInDto } from './dto/sso-signin.dto'; +import { CacheService } from 'src/services/cache.service'; +import { Request } from 'express'; +import { CreateIdentityProvider } from './dto/identity-provider.dto'; + +@Injectable() +export class IdentityProviderService implements OnModuleInit { + private identityProviderService: IdentityProviderProtoService; + constructor( + @Inject(DucClient.name) private readonly grpcClient: ClientGrpc, + private readonly cacheService: CacheService, + ) {} + + onModuleInit() { + this.identityProviderService = + this.grpcClient.getService( + ProtoServices.IdentityProviderProtoService, + ); + } + + async create(body: IdentityProviderRequest, metadata: Metadata) { + return await lastValueFrom( + this.identityProviderService.Create(body, metadata), + ); + } + + async getList(metadata: Metadata) { + return await lastValueFrom( + this.identityProviderService.GetList({}, metadata), + ); + } + + async loginIdentityProvider(id: string, callbackUrl: string) { + const idp = await lastValueFrom( + this.identityProviderService.FindIdentityProvider({ id }), + ); + + const issuer = await Issuer.discover(idp.issuerUrl); + const client = new issuer.Client({ + client_id: idp.clientId, + client_secret: idp.clientSecret, + redirect_uris: idp.redirectUrls, + response_types: ['code'], + }); + + const code_verifier: string = generators.codeVerifier(); + const code_challenge: string = generators.codeChallenge(code_verifier); + + const state = generators.state(); + const nonce = generators.nonce(); + + // Using state because it is returned in the callback + // and we can use it to retrieve the code_verifier and nonce + await this.cacheService.set(state, { + codeVerifier: code_verifier, + nonce, + id: idp.id, + state, + clientId: idp.clientId, + clientSecret: idp.clientSecret, + issuerUrl: idp.issuerUrl, + redirectUrls: idp.redirectUrls, + }); + + const url = client.authorizationUrl({ + scope: 'openid email', + response_type: 'code', + code_challenge, + code_challenge_method: 'S256', + state, + nonce, + redirect_uri: callbackUrl, + }); + const idpUrl = url + '&identity_provider=' + idp.name; + return idpUrl; + } + + async getTokenByIdp(code: string, state: string, callbackUrl: string) { + const ssoSign = await this.cacheService.get(state); + + if (!ssoSign) { + throw new BadRequestException('SSO sign-in is expired or not found'); + } + + const issuer = await Issuer.discover(ssoSign.issuerUrl); + const client = new issuer.Client({ + client_id: ssoSign.clientId, + client_secret: ssoSign.clientSecret, + redirect_uris: ssoSign.redirectUrls, + }); + + if (ssoSign === null) { + throw new BadRequestException('SSO sign-in is expired or not found'); + } + + const params = client.callbackParams( + `${callbackUrl}?code=${code}&state=${state}`, + ); + try { + const tokenSet = await client.callback(callbackUrl, params, { + nonce: ssoSign.nonce, + code_verifier: ssoSign.codeVerifier, + state: ssoSign.state + }); + + await this.cacheService.delete(ssoSign.state); + + return await lastValueFrom( + this.identityProviderService.SignInUser({ + accessToken: tokenSet.access_token, + idToken: tokenSet.id_token, + refreshToken: tokenSet.refresh_token, + id: ssoSign.id, + }), + ); + } catch (error) { + console.log(error); + throw error; + } + } + + async deleteIdentityProvider(id: string, metadata: Metadata) { + return await lastValueFrom( + this.identityProviderService.DeleteIdentityProvider({ id }, metadata), + ); + } + + async updateIdentityProviders( + id: string, + body: CreateIdentityProvider, + metadata: Metadata, + ) { + return await lastValueFrom( + this.identityProviderService.UpdateIdentityProvider( + { + id, + ...body, + }, + metadata, + ), + ); + } + + async identityProvidersLinksPerDomain(frontDomain: string) { + return await lastValueFrom( + this.identityProviderService.GetProviderLinksFromDomain({ frontDomain }), + ); + } +} diff --git a/src/services/cache.service.ts b/src/services/cache.service.ts new file mode 100644 index 0000000..25de25b --- /dev/null +++ b/src/services/cache.service.ts @@ -0,0 +1,23 @@ +import { CACHE_MANAGER, Inject, Injectable } from '@nestjs/common'; +import { Cache } from 'cache-manager'; + +@Injectable() +export class CacheService { + + constructor( + @Inject(CACHE_MANAGER) + private readonly cacheManager: Cache, + ) {} + + async get(key: string): Promise { + return await this.cacheManager.get(key); + } + + async set(key: string, value: T): Promise { + await this.cacheManager.set(key, value); + } + + async delete(key: string) { + await this.cacheManager.del(key); + } +} diff --git a/src/services/service.module.ts b/src/services/service.module.ts new file mode 100644 index 0000000..d129eaf --- /dev/null +++ b/src/services/service.module.ts @@ -0,0 +1,22 @@ +import { CacheModule, Module } from '@nestjs/common'; +import { CacheService } from './cache.service'; +import { redisStore } from 'cache-manager-ioredis-yet'; + +@Module({ + imports: [ + CacheModule.registerAsync({ + useFactory: async () => ({ + store: await redisStore({ + ttl: 1000 * 60, //1 minute + host: process.env.REDIS_HOST, + port: process.env.REDIS_PORT && Number(process.env.REDIS_PORT), + db: process.env.REDIS_DATABASE && Number(process.env.REDIS_DATABASE), + keyPrefix: 'maestro:sso', + }), + }), + }), + ], + providers: [CacheService], + exports: [CacheService], +}) +export class ServicesModule {} diff --git a/src/utils/errorCodes.ts b/src/utils/errorCodes.ts index 1ba83e6..a5856bd 100644 --- a/src/utils/errorCodes.ts +++ b/src/utils/errorCodes.ts @@ -75,6 +75,10 @@ const CATALOG = { PREVIEW_TOO_BIG: 'CATALOG.PREVIEW_TOO_BIG', METADATA_TOO_BIG: 'CATALOG.METADATA_TOO_BIG', }; +const IDENTITY_PROVIDER = { + INVALID_RESPONSE: 'IDENTITY_PROVIDER.INVALID_RESPONSE', + INVALID_HEADER: 'IDENTITY_PROVIDER.INVALID_HEADER', +} const ErrorCodes = { UNKNOWN: 'UNKNOWN', RATE_LIMIT: 'RATE_LIMIT', @@ -91,6 +95,7 @@ const ErrorCodes = { TRANSFORMATION, CONNECTION, CATALOG, + IDENTITY_PROVIDER }; export default ErrorCodes;