From 43daff29dc807bf35d1c7dd5869f060f6f5dc6a3 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 14 Dec 2022 18:04:51 +0000 Subject: [PATCH 001/122] fix: upgrade @sentry/tracing from 7.19.0 to 7.21.1 Snyk has created this PR to upgrade @sentry/tracing from 7.19.0 to 7.21.1. See this package in npm: https://www.npmjs.com/package/@sentry/tracing See this project in Snyk: https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr --- backend/package-lock.json | 120 +++++++++++++++++++------------------- backend/package.json | 2 +- 2 files changed, 60 insertions(+), 62 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index c13f138d4..4adb217b9 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@godaddy/terminus": "^4.11.2", "@sentry/node": "^7.14.0", - "@sentry/tracing": "^7.19.0", + "@sentry/tracing": "^7.21.1", "@types/crypto-js": "^4.1.1", "axios": "^1.1.3", "bigint-conversion": "^2.2.2", @@ -2602,6 +2602,19 @@ "@maxmind/geoip2-node": "^3.4.0" } }, + "node_modules/@sentry/core": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz", + "integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==", + "dependencies": { + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@sentry/node": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz", @@ -2653,46 +2666,33 @@ } }, "node_modules/@sentry/tracing": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.19.0.tgz", - "integrity": "sha512-SWY17M3TsgBePaGowUcSqBwaT0TJQzuNexVnLojuU0k6F57L9hubvP9zaoosoCfARXQ/3NypAFWnlJyf570rFQ==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.21.1.tgz", + "integrity": "sha512-b1BTPsRaNQpohzegoz59KGuBl+To651vEq0vMS4tCzSyIdxkYso3JCrjDdEqW/2MliQYANNVrUai2bmwmU9h1g==", "dependencies": { - "@sentry/core": "7.19.0", - "@sentry/types": "7.19.0", - "@sentry/utils": "7.19.0", + "@sentry/core": "7.21.1", + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", "tslib": "^1.9.3" }, "engines": { "node": ">=8" } }, - "node_modules/@sentry/tracing/node_modules/@sentry/core": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz", - "integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==", - "dependencies": { - "@sentry/types": "7.19.0", - "@sentry/utils": "7.19.0", - "tslib": "^1.9.3" - }, + "node_modules/@sentry/types": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz", + "integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==", "engines": { "node": ">=8" } }, - "node_modules/@sentry/tracing/node_modules/@sentry/types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz", - "integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/tracing/node_modules/@sentry/utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz", - "integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==", + "node_modules/@sentry/utils": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz", + "integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==", "dependencies": { - "@sentry/types": "7.19.0", + "@sentry/types": "7.21.1", "tslib": "^1.9.3" }, "engines": { @@ -13212,6 +13212,16 @@ "@maxmind/geoip2-node": "^3.4.0" } }, + "@sentry/core": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz", + "integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==", + "requires": { + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", + "tslib": "^1.9.3" + } + }, "@sentry/node": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz", @@ -13253,40 +13263,28 @@ } }, "@sentry/tracing": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.19.0.tgz", - "integrity": "sha512-SWY17M3TsgBePaGowUcSqBwaT0TJQzuNexVnLojuU0k6F57L9hubvP9zaoosoCfARXQ/3NypAFWnlJyf570rFQ==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.21.1.tgz", + "integrity": "sha512-b1BTPsRaNQpohzegoz59KGuBl+To651vEq0vMS4tCzSyIdxkYso3JCrjDdEqW/2MliQYANNVrUai2bmwmU9h1g==", "requires": { - "@sentry/core": "7.19.0", - "@sentry/types": "7.19.0", - "@sentry/utils": "7.19.0", + "@sentry/core": "7.21.1", + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", + "tslib": "^1.9.3" + } + }, + "@sentry/types": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz", + "integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==" + }, + "@sentry/utils": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz", + "integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==", + "requires": { + "@sentry/types": "7.21.1", "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/core": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz", - "integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==", - "requires": { - "@sentry/types": "7.19.0", - "@sentry/utils": "7.19.0", - "tslib": "^1.9.3" - } - }, - "@sentry/types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz", - "integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==" - }, - "@sentry/utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz", - "integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==", - "requires": { - "@sentry/types": "7.19.0", - "tslib": "^1.9.3" - } - } } }, "@sinclair/typebox": { diff --git a/backend/package.json b/backend/package.json index 32ca68b19..99ca1926c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,7 +2,7 @@ "dependencies": { "@godaddy/terminus": "^4.11.2", "@sentry/node": "^7.14.0", - "@sentry/tracing": "^7.19.0", + "@sentry/tracing": "^7.21.1", "@types/crypto-js": "^4.1.1", "axios": "^1.1.3", "bigint-conversion": "^2.2.2", From 7efdbeb78784a2c338c689bd7e4040d55fb3bb2f Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 14 Dec 2022 18:04:55 +0000 Subject: [PATCH 002/122] fix: upgrade @sentry/node from 7.19.0 to 7.21.1 Snyk has created this PR to upgrade @sentry/node from 7.19.0 to 7.21.1. See this package in npm: https://www.npmjs.com/package/@sentry/node See this project in Snyk: https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr --- backend/package-lock.json | 146 +++++++++++++++++++------------------- backend/package.json | 2 +- 2 files changed, 73 insertions(+), 75 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index c13f138d4..c85f9155d 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -10,7 +10,7 @@ "license": "ISC", "dependencies": { "@godaddy/terminus": "^4.11.2", - "@sentry/node": "^7.14.0", + "@sentry/node": "^7.21.1", "@sentry/tracing": "^7.19.0", "@types/crypto-js": "^4.1.1", "axios": "^1.1.3", @@ -2602,14 +2602,27 @@ "@maxmind/geoip2-node": "^3.4.0" } }, - "node_modules/@sentry/node": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz", - "integrity": "sha512-yG7Tx32WqOkEHVotFLrumCcT9qlaSDTkFNZ+yLSvZXx74ifsE781DzBA9W7K7bBdYO3op+p2YdsOKzf3nPpAyQ==", + "node_modules/@sentry/core": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz", + "integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==", "dependencies": { - "@sentry/core": "7.19.0", - "@sentry/types": "7.19.0", - "@sentry/utils": "7.19.0", + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/node": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.21.1.tgz", + "integrity": "sha512-B+p1nQHaFWdCCRVmvqlr/+vdQCI3mGLObucNfK2YC22IQZg7+3u6tEbxJ7umITIjeSSKgf7ZoZwCxL9VfkrNXg==", + "dependencies": { + "@sentry/core": "7.21.1", + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", @@ -2619,39 +2632,6 @@ "node": ">=8" } }, - "node_modules/@sentry/node/node_modules/@sentry/core": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz", - "integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==", - "dependencies": { - "@sentry/types": "7.19.0", - "@sentry/utils": "7.19.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/node/node_modules/@sentry/types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz", - "integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/node/node_modules/@sentry/utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz", - "integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==", - "dependencies": { - "@sentry/types": "7.19.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@sentry/tracing": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.19.0.tgz", @@ -2699,6 +2679,26 @@ "node": ">=8" } }, + "node_modules/@sentry/types": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz", + "integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz", + "integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==", + "dependencies": { + "@sentry/types": "7.21.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", @@ -13212,44 +13212,28 @@ "@maxmind/geoip2-node": "^3.4.0" } }, - "@sentry/node": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz", - "integrity": "sha512-yG7Tx32WqOkEHVotFLrumCcT9qlaSDTkFNZ+yLSvZXx74ifsE781DzBA9W7K7bBdYO3op+p2YdsOKzf3nPpAyQ==", + "@sentry/core": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz", + "integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==", "requires": { - "@sentry/core": "7.19.0", - "@sentry/types": "7.19.0", - "@sentry/utils": "7.19.0", + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", + "tslib": "^1.9.3" + } + }, + "@sentry/node": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.21.1.tgz", + "integrity": "sha512-B+p1nQHaFWdCCRVmvqlr/+vdQCI3mGLObucNfK2YC22IQZg7+3u6tEbxJ7umITIjeSSKgf7ZoZwCxL9VfkrNXg==", + "requires": { + "@sentry/core": "7.21.1", + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/core": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz", - "integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==", - "requires": { - "@sentry/types": "7.19.0", - "@sentry/utils": "7.19.0", - "tslib": "^1.9.3" - } - }, - "@sentry/types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz", - "integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==" - }, - "@sentry/utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz", - "integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==", - "requires": { - "@sentry/types": "7.19.0", - "tslib": "^1.9.3" - } - } } }, "@sentry/tracing": { @@ -13289,6 +13273,20 @@ } } }, + "@sentry/types": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz", + "integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==" + }, + "@sentry/utils": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz", + "integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==", + "requires": { + "@sentry/types": "7.21.1", + "tslib": "^1.9.3" + } + }, "@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", diff --git a/backend/package.json b/backend/package.json index 32ca68b19..86c8b2b80 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,7 +1,7 @@ { "dependencies": { "@godaddy/terminus": "^4.11.2", - "@sentry/node": "^7.14.0", + "@sentry/node": "^7.21.1", "@sentry/tracing": "^7.19.0", "@types/crypto-js": "^4.1.1", "axios": "^1.1.3", From d4bc92bd5bac93a82ede8793af5e6ce4cf5a07ed Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 14 Dec 2022 18:04:59 +0000 Subject: [PATCH 003/122] fix: upgrade axios from 1.1.3 to 1.2.0 Snyk has created this PR to upgrade axios from 1.1.3 to 1.2.0. See this package in npm: https://www.npmjs.com/package/axios See this project in Snyk: https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr --- backend/package-lock.json | 14 +++++++------- backend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index c13f138d4..49711ba4e 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,7 +13,7 @@ "@sentry/node": "^7.14.0", "@sentry/tracing": "^7.19.0", "@types/crypto-js": "^4.1.1", - "axios": "^1.1.3", + "axios": "^1.2.0", "bigint-conversion": "^2.2.2", "cookie-parser": "^1.4.6", "cors": "^2.8.5", @@ -3370,9 +3370,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz", - "integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.0.tgz", + "integrity": "sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -13822,9 +13822,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "axios": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz", - "integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.0.tgz", + "integrity": "sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", diff --git a/backend/package.json b/backend/package.json index 32ca68b19..9fd462b64 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,7 +4,7 @@ "@sentry/node": "^7.14.0", "@sentry/tracing": "^7.19.0", "@types/crypto-js": "^4.1.1", - "axios": "^1.1.3", + "axios": "^1.2.0", "bigint-conversion": "^2.2.2", "cookie-parser": "^1.4.6", "cors": "^2.8.5", From 59ab4bf7f902fed21f27d79c6ec7da62c75f58bc Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 14 Dec 2022 18:05:06 +0000 Subject: [PATCH 004/122] fix: upgrade mongoose from 6.7.2 to 6.7.3 Snyk has created this PR to upgrade mongoose from 6.7.2 to 6.7.3. See this package in npm: https://www.npmjs.com/package/mongoose See this project in Snyk: https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr --- backend/package-lock.json | 14 +++++++------- backend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index c13f138d4..5d6943ef3 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -26,7 +26,7 @@ "helmet": "^5.1.1", "jsonwebtoken": "^8.5.1", "jsrp": "^0.2.4", - "mongoose": "^6.7.2", + "mongoose": "^6.7.3", "nodemailer": "^6.8.0", "posthog-node": "^2.1.0", "query-string": "^7.1.3", @@ -6478,9 +6478,9 @@ } }, "node_modules/mongoose": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.2.tgz", - "integrity": "sha512-lrP2V5U1qhaf+z33fiIn7aYAZZ1fVDly+TkFRjTujNBF/FIHESATj2RbgAOSlWqv32fsZXkXejXzeVfjbv35Ow==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.3.tgz", + "integrity": "sha512-bLC2Pt6Vpoov+1kBYvQgJXG/2DWXbfIvfK4Gh68kCdYGh6CVO31YxYuIGz70hyGwX2g4DmSzbs5IA8Px2neMCQ==", "dependencies": { "bson": "^4.7.0", "kareem": "2.4.1", @@ -16151,9 +16151,9 @@ } }, "mongoose": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.2.tgz", - "integrity": "sha512-lrP2V5U1qhaf+z33fiIn7aYAZZ1fVDly+TkFRjTujNBF/FIHESATj2RbgAOSlWqv32fsZXkXejXzeVfjbv35Ow==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.3.tgz", + "integrity": "sha512-bLC2Pt6Vpoov+1kBYvQgJXG/2DWXbfIvfK4Gh68kCdYGh6CVO31YxYuIGz70hyGwX2g4DmSzbs5IA8Px2neMCQ==", "requires": { "bson": "^4.7.0", "kareem": "2.4.1", diff --git a/backend/package.json b/backend/package.json index 32ca68b19..0b7b24791 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,7 +17,7 @@ "helmet": "^5.1.1", "jsonwebtoken": "^8.5.1", "jsrp": "^0.2.4", - "mongoose": "^6.7.2", + "mongoose": "^6.7.3", "nodemailer": "^6.8.0", "posthog-node": "^2.1.0", "query-string": "^7.1.3", From a6387e75521502a308c9ad7331ae9349aa157f29 Mon Sep 17 00:00:00 2001 From: akhilmhdh Date: Thu, 26 Jan 2023 00:14:01 +0530 Subject: [PATCH 005/122] feat(ui): added new auth guard with react-query and axios --- docker-compose.dev.yml | 6 +-- frontend/package-lock.json | 50 +++++++++++++++++++ frontend/package.json | 1 + .../components/utilities/SecurityClient.ts | 21 +++----- frontend/src/config/request.ts | 19 +++++++ .../src/context/AuthContext/AuthContext.tsx | 49 ++++++++++++++++++ frontend/src/context/AuthContext/index.tsx | 1 + frontend/src/hooks/api/auth/index.tsx | 1 + frontend/src/hooks/api/auth/queries.tsx | 26 ++++++++++ frontend/src/hooks/api/auth/types.ts | 3 ++ frontend/src/hooks/api/index.tsx | 1 + frontend/src/hooks/index.ts | 1 + frontend/src/hooks/useToggle.tsx | 30 +++++++++++ frontend/src/pages/_app.tsx | 28 +++++++---- frontend/src/pages/login.tsx | 6 ++- frontend/src/reactQuery.ts | 21 ++++++++ 16 files changed, 234 insertions(+), 30 deletions(-) create mode 100644 frontend/src/config/request.ts create mode 100644 frontend/src/context/AuthContext/AuthContext.tsx create mode 100644 frontend/src/context/AuthContext/index.tsx create mode 100644 frontend/src/hooks/api/auth/index.tsx create mode 100644 frontend/src/hooks/api/auth/queries.tsx create mode 100644 frontend/src/hooks/api/auth/types.ts create mode 100644 frontend/src/hooks/api/index.tsx create mode 100644 frontend/src/hooks/useToggle.tsx create mode 100644 frontend/src/reactQuery.ts diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 564fdb0c4..e7bef8824 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -46,11 +46,7 @@ services: context: ./frontend dockerfile: Dockerfile.dev volumes: - - ./frontend/src/pages:/app/src/pages - - ./frontend/src/components:/app/src/components - - ./frontend/src/ee:/app/src/ee - - ./frontend/src/locales:/app/src/locales - - ./frontend/src/styles:/app/src/styles + - ./frontend/src:/app/src/ # mounted whole src to avoid missing reload on new files - ./frontend/public:/app/public - ./frontend/next-i18next.config.js:/app/next-i18next.config.js env_file: .env diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1348fbdc3..84e34b8ff 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,6 +28,7 @@ "@reduxjs/toolkit": "^1.8.3", "@stripe/react-stripe-js": "^1.10.0", "@stripe/stripe-js": "^1.46.0", + "@tanstack/react-query": "^4.23.0", "add": "^2.0.6", "axios": "^0.27.2", "axios-auth-refresh": "^3.3.3", @@ -6630,6 +6631,41 @@ "tailwindcss": ">=3.0.0 || insiders" } }, + "node_modules/@tanstack/query-core": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.22.4.tgz", + "integrity": "sha512-t79CMwlbBnj+yL82tEcmRN93bL4U3pae2ota4t5NN2z3cIeWw74pzdWrKRwOfTvLcd+b30tC+ciDlfYOKFPGUw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.23.0.tgz", + "integrity": "sha512-cfQsrecZQjYYueiow4WcK8ItokXJnv+b2OrK8Lf5kF7lM9uCo1ilyygFB8wo4MfxchUBVM6Cs8wq4Ed7fouwkA==", + "dependencies": { + "@tanstack/query-core": "4.22.4", + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/@testing-library/dom": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", @@ -26887,6 +26923,20 @@ "postcss-selector-parser": "6.0.10" } }, + "@tanstack/query-core": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.22.4.tgz", + "integrity": "sha512-t79CMwlbBnj+yL82tEcmRN93bL4U3pae2ota4t5NN2z3cIeWw74pzdWrKRwOfTvLcd+b30tC+ciDlfYOKFPGUw==" + }, + "@tanstack/react-query": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.23.0.tgz", + "integrity": "sha512-cfQsrecZQjYYueiow4WcK8ItokXJnv+b2OrK8Lf5kF7lM9uCo1ilyygFB8wo4MfxchUBVM6Cs8wq4Ed7fouwkA==", + "requires": { + "@tanstack/query-core": "4.22.4", + "use-sync-external-store": "^1.2.0" + } + }, "@testing-library/dom": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 70e2f2358..3f3711c63 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,6 +35,7 @@ "@reduxjs/toolkit": "^1.8.3", "@stripe/react-stripe-js": "^1.10.0", "@stripe/stripe-js": "^1.46.0", + "@tanstack/react-query": "^4.23.0", "add": "^2.0.6", "axios": "^0.27.2", "axios-auth-refresh": "^3.3.3", diff --git a/frontend/src/components/utilities/SecurityClient.ts b/frontend/src/components/utilities/SecurityClient.ts index 57e35d6b2..fa1ec0bb0 100644 --- a/frontend/src/components/utilities/SecurityClient.ts +++ b/frontend/src/components/utilities/SecurityClient.ts @@ -1,27 +1,18 @@ -import token from '@app/pages/api/auth/Token'; +import { getAuthToken, setAuthToken } from '@app/reactQuery'; +// depreciated: go for apiRequest module in config/api export default class SecurityClient { - static #token = ''; - static setToken(tokenStr: string) { - this.#token = tokenStr; + setAuthToken(tokenStr); } static async fetchCall(resource: RequestInfo, options?: RequestInit | undefined) { const req = new Request(resource, options); - if (this.#token === '') { - try { - // TODO: This should be moved to a context to do it only once when app loads - // this try catch saves route guard from a stuck state - this.setToken(await token()); - } catch (error) { - console.error('Unauthorized access'); - } - } + const token = getAuthToken(); - if (this.#token) { - req.headers.set('Authorization', `Bearer ${this.#token}`); + if (token) { + req.headers.set('Authorization', `Bearer ${token}`); } return fetch(req); diff --git a/frontend/src/config/request.ts b/frontend/src/config/request.ts new file mode 100644 index 000000000..0e04789b2 --- /dev/null +++ b/frontend/src/config/request.ts @@ -0,0 +1,19 @@ +import axios from 'axios'; + +import { getAuthToken } from '@app/reactQuery'; + +export const apiRequest = axios.create({ + baseURL: '/', + headers: { + 'Content-Type': 'application/json' + } +}); + +apiRequest.interceptors.request.use((config) => { + const token = getAuthToken(); + if (token && config.headers) { + // eslint-disable-next-line no-param-reassign + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); diff --git a/frontend/src/context/AuthContext/AuthContext.tsx b/frontend/src/context/AuthContext/AuthContext.tsx new file mode 100644 index 000000000..c064852bd --- /dev/null +++ b/frontend/src/context/AuthContext/AuthContext.tsx @@ -0,0 +1,49 @@ +import { ReactNode, useEffect } from 'react'; +import { useRouter } from 'next/router'; + +import { publicPaths } from '@app/const'; +import { useToggle } from '@app/hooks'; +import { useGetAuthToken } from '@app/hooks/api'; +import { isLoggedIn } from '@app/reactQuery'; + +type Props = { + children: ReactNode; +}; + +// TODO(akhilmhdh): Using react-simple-animate from hard dom offloading +// smoother dom offloading needs to be done + +// Authentication controller +// Does route checking +// Provide a context for whole app to notify user is authorized or not +export const AuthProvider = ({ children }: Props): JSX.Element => { + const { isLoading } = useGetAuthToken(); + const { pathname, push } = useRouter(); + const [isReady, setIsReady] = useToggle(false); + + useEffect(() => { + // check if loading of auth is done + if (!isLoading) { + // not a public path and not authenticated kick to login page + if (!publicPaths.includes(pathname) && !isLoggedIn()) { + push('/login').then(() => { + setIsReady.on(); + }); + } else { + // else good to go + setIsReady.on(); + } + } + }, [pathname, isLoading]); + + // wait for app to load the auth state + if (isLoading || !isReady) { + return ( +
+ loading animation +
+ ); + } + + return children as JSX.Element; +}; diff --git a/frontend/src/context/AuthContext/index.tsx b/frontend/src/context/AuthContext/index.tsx new file mode 100644 index 000000000..e385cc124 --- /dev/null +++ b/frontend/src/context/AuthContext/index.tsx @@ -0,0 +1 @@ +export { AuthProvider } from './AuthContext'; diff --git a/frontend/src/hooks/api/auth/index.tsx b/frontend/src/hooks/api/auth/index.tsx new file mode 100644 index 000000000..5b7837303 --- /dev/null +++ b/frontend/src/hooks/api/auth/index.tsx @@ -0,0 +1 @@ +export { useGetAuthToken } from './queries'; diff --git a/frontend/src/hooks/api/auth/queries.tsx b/frontend/src/hooks/api/auth/queries.tsx new file mode 100644 index 000000000..ceae44ed8 --- /dev/null +++ b/frontend/src/hooks/api/auth/queries.tsx @@ -0,0 +1,26 @@ +import { useQuery } from '@tanstack/react-query'; + +import { apiRequest } from '@app/config/request'; +import { setAuthToken } from '@app/reactQuery'; + +import { GetAuthTokenAPI } from './types'; + +const authKeys = { + getAuthToken: ['token'] as const +}; + +// Refresh token is set as cookie when logged in +// Using that we fetch the auth bearer token needed for auth calls +const fetchAuthToken = async () => { + const { data } = await apiRequest.post('/api/v1/auth/token', undefined, { + withCredentials: true + }); + + return data; +}; + +export const useGetAuthToken = () => + useQuery(authKeys.getAuthToken, fetchAuthToken, { + onSuccess: (data) => setAuthToken(data.token), + retry: 0 + }); diff --git a/frontend/src/hooks/api/auth/types.ts b/frontend/src/hooks/api/auth/types.ts new file mode 100644 index 000000000..998df8a5d --- /dev/null +++ b/frontend/src/hooks/api/auth/types.ts @@ -0,0 +1,3 @@ +export type GetAuthTokenAPI = { + token: string; +}; diff --git a/frontend/src/hooks/api/index.tsx b/frontend/src/hooks/api/index.tsx new file mode 100644 index 000000000..9df70dcf0 --- /dev/null +++ b/frontend/src/hooks/api/index.tsx @@ -0,0 +1 @@ +export { useGetAuthToken } from './auth'; diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts index dcea2eb7c..c34dc4e41 100644 --- a/frontend/src/hooks/index.ts +++ b/frontend/src/hooks/index.ts @@ -1 +1,2 @@ export { usePopUp } from './usePopUp'; +export { useToggle } from './useToggle'; diff --git a/frontend/src/hooks/useToggle.tsx b/frontend/src/hooks/useToggle.tsx new file mode 100644 index 000000000..a9321ebdf --- /dev/null +++ b/frontend/src/hooks/useToggle.tsx @@ -0,0 +1,30 @@ +import { useCallback, useState } from 'react'; + +type VoidFn = () => void; + +type UseToggleReturn = [ + boolean, + { + on: VoidFn; + off: VoidFn; + toggle: VoidFn; + } +]; + +export const useToggle = (initialState = false): UseToggleReturn => { + const [value, setValue] = useState(initialState); + + const on = useCallback(() => { + setValue(true); + }, []); + + const off = useCallback(() => { + setValue(false); + }, []); + + const toggle = useCallback((isOpen?: boolean) => { + setValue((prev) => (typeof isOpen === 'boolean' ? isOpen : !prev)); + }, []); + + return [value, { on, off, toggle }]; +}; diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index 4e22c943f..5bfaaa1e3 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -4,12 +4,14 @@ import { AppProps } from 'next/app'; import { useRouter } from 'next/router'; import { appWithTranslation } from 'next-i18next'; import { config } from '@fortawesome/fontawesome-svg-core'; +import { QueryClientProvider } from '@tanstack/react-query'; import Layout from '@app/components/basic/Layout'; import NotificationProvider from '@app/components/context/Notifications/NotificationProvider'; -import RouteGuard from '@app/components/RouteGuard'; import Telemetry from '@app/components/utilities/telemetry/Telemetry'; import { publicPaths } from '@app/const'; +import { AuthProvider } from '@app/context/AuthContext'; +import { queryClient } from '@app/reactQuery'; import '@fortawesome/fontawesome-svg-core/styles.css'; import '../styles/globals.css'; @@ -54,17 +56,25 @@ const App = ({ Component, pageProps, ...appProps }: NextAppProp): JSX.Element => publicPaths.includes(`/${appProps.router.pathname.split('/')[1]}`) || !Component.requireAuth ) { - return ; + return ( + + + + + + ); } return ( - - - - - - - + + + + + + + + + ); }; diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx index 571a636f7..c32c4bd4f 100644 --- a/frontend/src/pages/login.tsx +++ b/frontend/src/pages/login.tsx @@ -13,6 +13,7 @@ import InputField from '@app/components/basic/InputField'; import ListBox from '@app/components/basic/Listbox'; import attemptLogin from '@app/components/utilities/attemptLogin'; import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps'; +import { isLoggedIn } from '@app/reactQuery'; import getWorkspaces from './api/workspace/getWorkspaces'; @@ -31,6 +32,7 @@ export default function Login() { }; useEffect(() => { + // TODO(akhilmhdh): workspace will be controlled by a workspace context const redirectToDashboard = async () => { let userWorkspace; try { @@ -41,7 +43,9 @@ export default function Login() { console.log('Error - Not logged in yet'); } }; - redirectToDashboard(); + if (isLoggedIn()) { + redirectToDashboard(); + } }, []); /** diff --git a/frontend/src/reactQuery.ts b/frontend/src/reactQuery.ts new file mode 100644 index 000000000..c28dc39b1 --- /dev/null +++ b/frontend/src/reactQuery.ts @@ -0,0 +1,21 @@ +import { QueryClient } from '@tanstack/react-query'; + +// this is saved in react-query cache +export const AUTH_TOKEN_CACHE_KEY = ['infisical__auth-token']; + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: 1 + } + } +}); + +// set token in memory cache +export const setAuthToken = (token: string) => + queryClient.setQueryData(AUTH_TOKEN_CACHE_KEY, token); + +export const getAuthToken = () => queryClient.getQueryData(AUTH_TOKEN_CACHE_KEY) as string; + +export const isLoggedIn = () => Boolean(getAuthToken()); From bc70bedb781de416364785cb3bfc573f92584cd3 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Sat, 28 Jan 2023 20:41:54 -0800 Subject: [PATCH 006/122] Fixed the bug with empty variables --- frontend/src/components/dashboard/SideBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/dashboard/SideBar.tsx b/frontend/src/components/dashboard/SideBar.tsx index 5f67808f1..1a3f99a29 100644 --- a/frontend/src/components/dashboard/SideBar.tsx +++ b/frontend/src/components/dashboard/SideBar.tsx @@ -106,7 +106,7 @@ const SideBar = ({ blurred={false} /> - {data[0]?.value ? ( + {(data[0]?.value || data[0]?.value === "") ? (
)}
- {data[0]?.value && ( + {(data[0]?.value || data[0]?.value === "") && (

{t('dashboard:sidebar.override')}

Date: Sat, 28 Jan 2023 22:32:13 -0800 Subject: [PATCH 007/122] increase cli version --- cli/packages/util/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/packages/util/constants.go b/cli/packages/util/constants.go index 609cfe818..1243fb325 100644 --- a/cli/packages/util/constants.go +++ b/cli/packages/util/constants.go @@ -11,5 +11,5 @@ const ( KEYRING_SERVICE_NAME = "infisical" PERSONAL_SECRET_TYPE_NAME = "personal" SHARED_SECRET_TYPE_NAME = "shared" - CLI_VERSION = "v0.2.7" + CLI_VERSION = "v0.2.8" ) From 3c05a4cebd088672d92d3d5713f16f14e27447cb Mon Sep 17 00:00:00 2001 From: Neeraj138 Date: Sun, 29 Jan 2023 14:01:40 +0530 Subject: [PATCH 008/122] Add check for subscriptions call before setting the current plan --- frontend/src/components/basic/table/EnvironmentsTable.tsx | 4 +++- frontend/src/pages/settings/billing/[id].tsx | 5 +++-- frontend/src/pages/settings/org/[id].tsx | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/basic/table/EnvironmentsTable.tsx b/frontend/src/components/basic/table/EnvironmentsTable.tsx index 8ff7d9f0a..df0d06ef5 100644 --- a/frontend/src/components/basic/table/EnvironmentsTable.tsx +++ b/frontend/src/components/basic/table/EnvironmentsTable.tsx @@ -34,7 +34,9 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }: const subscriptions = await getOrganizationSubscriptions({ orgId }); - setPlan(subscriptions.data[0].plan.product); + if (subscriptions) { + setPlan(subscriptions.data[0].plan.product) + } })(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/frontend/src/pages/settings/billing/[id].tsx b/frontend/src/pages/settings/billing/[id].tsx index 1f90afcd4..626dbf69d 100644 --- a/frontend/src/pages/settings/billing/[id].tsx +++ b/frontend/src/pages/settings/billing/[id].tsx @@ -66,8 +66,9 @@ export default function SettingsBilling() { const subscriptions = await getOrganizationSubscriptions({ orgId }); - - setCurrentPlan(subscriptions.data[0].plan.product); + if (subscriptions) { + setCurrentPlan(subscriptions.data[0].plan.product); + } const orgUsers = await getOrganizationUsers({ orgId }); diff --git a/frontend/src/pages/settings/org/[id].tsx b/frontend/src/pages/settings/org/[id].tsx index bb25d0a5c..1fd67c52c 100644 --- a/frontend/src/pages/settings/org/[id].tsx +++ b/frontend/src/pages/settings/org/[id].tsx @@ -87,7 +87,9 @@ export default function SettingsOrg() { const subscriptions = await getOrganizationSubscriptions({ orgId }); - setCurrentPlan(subscriptions.data[0].plan.product); + if (subscriptions) { + setCurrentPlan(subscriptions.data[0].plan.product); + } })(); }, []); From 9f8ad95a59c0a994d70b0a070be0c2f90e8c936a Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Sun, 29 Jan 2023 09:17:22 -0800 Subject: [PATCH 009/122] Revert "correct tags in docker image workflow" This reverts commit 3ef2ac8a77b50c1fbac1fa2173acccbf1736a011. --- .github/workflows/docker-image.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 15142984d..b4f9546ae 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -45,9 +45,8 @@ jobs: token: ${{ secrets.DEPOT_PROJECT_TOKEN }} push: true context: backend - tags: | - infisical/backend:${{ steps.commit.outputs.short }} - infisical/backend:latest + tags: infisical/backend:${{ steps.commit.outputs.short }}, + infisical/backend:latest platforms: linux/amd64,linux/arm64 frontend-image: @@ -95,9 +94,8 @@ jobs: push: true token: ${{ secrets.DEPOT_PROJECT_TOKEN }} context: frontend - tags: | - infisical/frontend:${{ steps.commit.outputs.short }} - infisical/frontend:latest + tags: infisical/frontend:${{ steps.commit.outputs.short }}, + infisical/frontend:latest platforms: linux/amd64,linux/arm64 build-args: | POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} @@ -137,4 +135,4 @@ jobs: exit 1 else echo "Helm upgrade was successful" - fi + fi \ No newline at end of file From 3e177539d573677819aca4c82a670ab9ebfbadde Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Sun, 29 Jan 2023 15:48:25 -0800 Subject: [PATCH 010/122] Remove state from password controllers --- .../src/controllers/v1/passwordController.ts | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/backend/src/controllers/v1/passwordController.ts b/backend/src/controllers/v1/passwordController.ts index 27d712a6b..d429cbf41 100644 --- a/backend/src/controllers/v1/passwordController.ts +++ b/backend/src/controllers/v1/passwordController.ts @@ -9,8 +9,8 @@ import { checkEmailVerification } from '../../helpers/signup'; import { createToken } from '../../helpers/auth'; import { sendMail } from '../../helpers/nodemailer'; import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../../config'; - -const clientPublicKeys: any = {}; +import LoginSRPDetail from '../../models/LoginSRPDetail'; +import { BadRequestError } from '../../utils/errors'; /** * Password reset step 1: Send email verification link to email [email] @@ -32,7 +32,7 @@ export const emailPasswordReset = async (req: Request, res: Response) => { error: 'Failed to send email verification for password reset' }); } - + const token = crypto.randomBytes(16).toString('hex'); await Token.findOneAndUpdate( @@ -44,7 +44,7 @@ export const emailPasswordReset = async (req: Request, res: Response) => { }, { upsert: true, new: true } ); - + await sendMail({ template: 'passwordReset.handlebars', subjectLine: 'Infisical password reset', @@ -55,15 +55,15 @@ export const emailPasswordReset = async (req: Request, res: Response) => { callback_url: SITE_URL + '/password-reset' } }); - + } catch (err) { Sentry.setUser(null); Sentry.captureException(err); return res.status(400).send({ message: 'Failed to send email for account recovery' - }); + }); } - + return res.status(200).send({ message: `Sent an email for account recovery to ${email}` }); @@ -79,7 +79,7 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => { let user, token; try { const { email, code } = req.body; - + user = await User.findOne({ email }).select('+publicKey'); if (!user || !user?.publicKey) { // case: user doesn't exist with email [email] or @@ -93,7 +93,7 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => { email, code }); - + // generate temporary password-reset token token = createToken({ payload: { @@ -107,7 +107,7 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => { Sentry.captureException(err); return res.status(400).send({ message: 'Failed email verification for password reset' - }); + }); } return res.status(200).send({ @@ -130,7 +130,7 @@ export const srp1 = async (req: Request, res: Response) => { const user = await User.findOne({ email: req.user.email }).select('+salt +verifier'); - + if (!user) throw new Error('Failed to find user'); const server = new jsrp.server(); @@ -139,13 +139,15 @@ export const srp1 = async (req: Request, res: Response) => { salt: user.salt, verifier: user.verifier }, - () => { + async () => { // generate server-side public key const serverPublicKey = server.getPublicKey(); - clientPublicKeys[req.user.email] = { - clientPublicKey, - serverBInt: bigintConversion.bigintToBuf(server.bInt) - }; + + await LoginSRPDetail.findOneAndReplace({ email: req.user.email }, { + email: req.user.email, + clientPublicKey: clientPublicKey, + serverBInt: bigintConversion.bigintToBuf(server.bInt), + }, { upsert: true, returnNewDocument: false }) return res.status(200).send({ serverPublicKey, @@ -180,17 +182,21 @@ export const changePassword = async (req: Request, res: Response) => { if (!user) throw new Error('Failed to find user'); + const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email }) + + if (!loginSRPDetailFromDB) { + return BadRequestError(Error("It looks like some details from the first login are not found. Please try login one again")) + } + const server = new jsrp.server(); server.init( { salt: user.salt, verifier: user.verifier, - b: clientPublicKeys[req.user.email].serverBInt + b: loginSRPDetailFromDB.serverBInt }, async () => { - server.setClientPublicKey( - clientPublicKeys[req.user.email].clientPublicKey - ); + server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey); // compare server and client shared keys if (server.checkClientProof(clientProof)) { @@ -249,16 +255,22 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => { if (!user) throw new Error('Failed to find user'); + const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email }) + + if (!loginSRPDetailFromDB) { + return BadRequestError(Error("It looks like some details from the first login are not found. Please try login one again")) + } + const server = new jsrp.server(); server.init( { salt: user.salt, verifier: user.verifier, - b: clientPublicKeys[req.user.email].serverBInt + b: loginSRPDetailFromDB.serverBInt }, async () => { server.setClientPublicKey( - clientPublicKeys[req.user.email].clientPublicKey + loginSRPDetailFromDB.clientPublicKey ); // compare server and client shared keys @@ -311,16 +323,16 @@ export const getBackupPrivateKey = async (req: Request, res: Response) => { backupPrivateKey = await BackupPrivateKey.findOne({ user: req.user._id }).select('+encryptedPrivateKey +iv +tag'); - + if (!backupPrivateKey) throw new Error('Failed to find backup private key'); } catch (err) { - Sentry.setUser({ email: req.user.email}); + Sentry.setUser({ email: req.user.email }); Sentry.captureException(err); return res.status(400).send({ message: 'Failed to get backup private key' }); } - + return res.status(200).send({ backupPrivateKey }); @@ -348,15 +360,15 @@ export const resetPassword = async (req: Request, res: Response) => { { new: true } - ); + ); } catch (err) { - Sentry.setUser({ email: req.user.email}); + Sentry.setUser({ email: req.user.email }); Sentry.captureException(err); return res.status(400).send({ message: 'Failed to get backup private key' - }); + }); } - + return res.status(200).send({ message: 'Successfully reset password' }); From bf743f5f729769beac4bb935a11311ba96c879e8 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Sun, 29 Jan 2023 15:55:01 -0800 Subject: [PATCH 011/122] Make the loading animation smaller --- frontend/src/context/AuthContext/AuthContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/context/AuthContext/AuthContext.tsx b/frontend/src/context/AuthContext/AuthContext.tsx index c064852bd..93ef52d76 100644 --- a/frontend/src/context/AuthContext/AuthContext.tsx +++ b/frontend/src/context/AuthContext/AuthContext.tsx @@ -40,7 +40,7 @@ export const AuthProvider = ({ children }: Props): JSX.Element => { if (isLoading || !isReady) { return (
- loading animation + infisical loading indicator
); } From 5162ba9b91041f929033b4739de5dacbe9b1302f Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Tue, 24 Jan 2023 23:16:08 -0800 Subject: [PATCH 012/122] add basic auth model for Organization --- backend/src/rbac/main.polar | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 backend/src/rbac/main.polar diff --git a/backend/src/rbac/main.polar b/backend/src/rbac/main.polar new file mode 100644 index 000000000..c88143e51 --- /dev/null +++ b/backend/src/rbac/main.polar @@ -0,0 +1,15 @@ +# Actoers +actor User {} + +# Resource block +resource Organization { + permissions = ["view", "delete", "edit_billing", "edit_members"]; + roles = ["owner", "member"]; + + "view" if "member"; + "delete" if "owner"; + "edit_billing" if "owner"; + "edit_members" if "owner"; + + "member" if "owner"; +} \ No newline at end of file From a940fa210a7b34a8c26b9c47e1dee4bfcdafa220 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Sun, 29 Jan 2023 21:04:47 -0800 Subject: [PATCH 013/122] Add deny api/get envs api --- backend/package-lock.json | 25 ++++ backend/package.json | 3 + .../controllers/v1/membershipController.ts | 4 +- .../controllers/v2/environmentController.ts | 53 +++++++++ .../src/controllers/v2/secretsController.ts | 22 +++- backend/src/ee/controllers/v1/index.ts | 4 +- .../ee/controllers/v1/membershipController.ts | 63 +++++++++++ .../ee/helpers/checkMembershipPermissions.ts | 18 +++ backend/src/helpers/secret.ts | 107 ++++++++++-------- backend/src/models/membership.ts | 20 +++- backend/src/rbac/main.polar | 15 --- backend/src/routes/v1/membership.ts | 24 +++- backend/src/routes/v2/environment.ts | 13 +++ backend/src/variables/organization.ts | 8 +- 14 files changed, 303 insertions(+), 76 deletions(-) create mode 100644 backend/src/ee/controllers/v1/membershipController.ts create mode 100644 backend/src/ee/helpers/checkMembershipPermissions.ts delete mode 100644 backend/src/rbac/main.polar diff --git a/backend/package-lock.json b/backend/package-lock.json index 9caf23dd1..2c7abb793 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -19,6 +19,7 @@ "axios": "^1.1.3", "bcrypt": "^5.1.0", "bigint-conversion": "^2.2.2", + "builder-pattern": "^2.2.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "crypto-js": "^4.1.1", @@ -32,6 +33,7 @@ "jsonwebtoken": "^9.0.0", "jsrp": "^0.2.4", "libsodium-wrappers": "^0.7.10", + "lodash": "^4.17.21", "mongoose": "^6.7.2", "nodemailer": "^6.8.0", "posthog-node": "^2.2.2", @@ -58,6 +60,7 @@ "@types/express": "^4.17.14", "@types/jest": "^29.2.4", "@types/jsonwebtoken": "^8.5.9", + "@types/lodash": "^4.14.191", "@types/node": "^18.11.3", "@types/nodemailer": "^6.4.6", "@types/supertest": "^2.0.12", @@ -3221,6 +3224,12 @@ "resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", "integrity": "sha512-BqI9B92u+cM3ccp8mpHf+HzJ8fBlRwdmyd6+fz3p99m3V6ifT5O3zmOMi612PGkpeFeG/G6loxUnzlDNhfjPSA==" }, + "node_modules/@types/lodash": { + "version": "4.14.191", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", + "dev": true + }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", @@ -4085,6 +4094,11 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/builder-pattern": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/builder-pattern/-/builder-pattern-2.2.0.tgz", + "integrity": "sha512-cES3qdeBzA4QyJi7rV/l/kAhIFX6AKo3vK66ZPXLNpjcQWCS8sjLKscly8imlfW2YPTo/hquMRMnaWpZ80Kj+g==" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -14641,6 +14655,12 @@ "resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", "integrity": "sha512-BqI9B92u+cM3ccp8mpHf+HzJ8fBlRwdmyd6+fz3p99m3V6ifT5O3zmOMi612PGkpeFeG/G6loxUnzlDNhfjPSA==" }, + "@types/lodash": { + "version": "4.14.191", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", + "dev": true + }, "@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", @@ -15269,6 +15289,11 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "builder-pattern": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/builder-pattern/-/builder-pattern-2.2.0.tgz", + "integrity": "sha512-cES3qdeBzA4QyJi7rV/l/kAhIFX6AKo3vK66ZPXLNpjcQWCS8sjLKscly8imlfW2YPTo/hquMRMnaWpZ80Kj+g==" + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", diff --git a/backend/package.json b/backend/package.json index 7a1552dd3..e64d9d446 100644 --- a/backend/package.json +++ b/backend/package.json @@ -36,6 +36,7 @@ "@types/express": "^4.17.14", "@types/jest": "^29.2.4", "@types/jsonwebtoken": "^8.5.9", + "@types/lodash": "^4.14.191", "@types/node": "^18.11.3", "@types/nodemailer": "^6.4.6", "@types/supertest": "^2.0.12", @@ -85,6 +86,7 @@ "axios": "^1.1.3", "bcrypt": "^5.1.0", "bigint-conversion": "^2.2.2", + "builder-pattern": "^2.2.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "crypto-js": "^4.1.1", @@ -98,6 +100,7 @@ "jsonwebtoken": "^9.0.0", "jsrp": "^0.2.4", "libsodium-wrappers": "^0.7.10", + "lodash": "^4.17.21", "mongoose": "^6.7.2", "nodemailer": "^6.8.0", "posthog-node": "^2.2.2", diff --git a/backend/src/controllers/v1/membershipController.ts b/backend/src/controllers/v1/membershipController.ts index 42cc29d9c..3627ce3fb 100644 --- a/backend/src/controllers/v1/membershipController.ts +++ b/backend/src/controllers/v1/membershipController.ts @@ -1,6 +1,6 @@ import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; -import { Membership, MembershipOrg, User, Key } from '../../models'; +import { Membership, MembershipOrg, User, Key, IMembership, Workspace } from '../../models'; import { findMembership, deleteMembership as deleteMember @@ -230,4 +230,4 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => { invitee, latestKey }); -}; +}; \ No newline at end of file diff --git a/backend/src/controllers/v2/environmentController.ts b/backend/src/controllers/v2/environmentController.ts index 47832f641..d92ec8264 100644 --- a/backend/src/controllers/v2/environmentController.ts +++ b/backend/src/controllers/v2/environmentController.ts @@ -6,8 +6,12 @@ import { Workspace, Integration, ServiceTokenData, + Membership, } from '../../models'; import { SecretVersion } from '../../ee/models'; +import { BadRequestError } from '../../utils/errors'; +import _ from 'lodash'; +import { ABILITY_READ } from '../../variables/organization'; /** * Create new workspace environment named [environmentName] under workspace with id @@ -120,6 +124,15 @@ export const renameWorkspaceEnvironment = async ( { workspace: workspaceId, environment: oldEnvironmentSlug }, { environment: environmentSlug } ); + await Membership.updateMany( + { + workspace: workspaceId, + "deniedPermissions.environmentSlug": oldEnvironmentSlug + }, + { $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } }, + { arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] } + ) + } catch (err) { Sentry.setUser({ email: req.user.email }); Sentry.captureException(err); @@ -188,6 +201,11 @@ export const deleteWorkspaceEnvironment = async ( workspace: workspaceId, environment: environmentSlug, }); + await Membership.updateMany( + { workspace: workspaceId }, + { $pull: { deniedPermissions: { environmentSlug: environmentSlug } } } + ) + } catch (err) { Sentry.setUser({ email: req.user.email }); Sentry.captureException(err); @@ -202,3 +220,38 @@ export const deleteWorkspaceEnvironment = async ( environment: environmentSlug, }); }; + + +export const getAllAccessibleEnvironmentsOfWorkspace = async ( + req: Request, + res: Response +) => { + const { workspaceId } = req.params; + const workspacesUserIsMemberOf = await Membership.findOne({ + workspace: workspaceId, + user: req.user + }) + + if (!workspacesUserIsMemberOf) { + throw BadRequestError() + } + + const accessibleEnvironments: { name: string; slug: string; }[] = [] + const deniedPermission = workspacesUserIsMemberOf.deniedPermissions + + const relatedWorkspace = await Workspace.findById(workspaceId) + if (!relatedWorkspace) { + throw BadRequestError() + } + relatedWorkspace.environments.forEach(environment => { + const isReadBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_READ }) + // const isWriteBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_WRITE }) + if (isReadBlocked) { + return + } else { + accessibleEnvironments.push(environment) + } + }) + + res.json({ accessibleEnvironments }) +}; diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index 4a5a919fd..90eb8e48d 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -1,7 +1,7 @@ import to from 'await-to-js'; import { Types } from 'mongoose'; import { Request, Response } from 'express'; -import { ISecret, Secret } from '../../models'; +import { ISecret, Membership, Secret, Workspace } from '../../models'; import { SECRET_PERSONAL, SECRET_SHARED, @@ -10,13 +10,14 @@ import { ACTION_UPDATE_SECRETS, ACTION_DELETE_SECRETS } from '../../variables'; -import { ValidationError } from '../../utils/errors'; +import { UnauthorizedRequestError, ValidationError } from '../../utils/errors'; import { EventService } from '../../services'; import { eventPushSecrets } from '../../events'; import { EESecretService, EELogService } from '../../ee/services'; import { postHogClient } from '../../services'; -import { BadRequestError } from '../../utils/errors'; import { getChannelFromUserAgent } from '../../utils/posthog'; +import { ABILITY_READ, ABILITY_WRITE } from '../../variables/organization'; +import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions'; /** * Create secret(s) for workspace with id [workspaceId] and environment [environment] @@ -76,9 +77,15 @@ export const createSecrets = async (req: Request, res: Response) => { } } */ + const channel = getChannelFromUserAgent(req.headers['user-agent']) const { workspaceId, environment } = req.body; + const hasAccess = await userHasWorkspaceAccess(req.user, workspaceId, environment, ABILITY_WRITE) + if (!hasAccess) { + throw UnauthorizedRequestError({ message: "You do not have the necessary permission(s) perform this action" }) + } + let toAdd; if (Array.isArray(req.body.secrets)) { // case: create multiple secrets @@ -269,6 +276,14 @@ export const getSecrets = async (req: Request, res: Response) => { userEmail = req.serviceTokenData.user.email; } + // none service token case as service tokens are already scoped + if (!req.serviceTokenData) { + const hasAccess = await userHasWorkspaceAccess(userId, workspaceId, environment, ABILITY_READ) + if (!hasAccess) { + throw UnauthorizedRequestError({ message: "You do not have the necessary permission(s) perform this action" }) + } + } + const [err, secrets] = await to(Secret.find( { workspace: workspaceId, @@ -371,7 +386,6 @@ export const updateSecrets = async (req: Request, res: Response) => { */ const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; - // TODO: move type interface PatchSecret { id: string; diff --git a/backend/src/ee/controllers/v1/index.ts b/backend/src/ee/controllers/v1/index.ts index dd88f1178..aaa697286 100644 --- a/backend/src/ee/controllers/v1/index.ts +++ b/backend/src/ee/controllers/v1/index.ts @@ -3,11 +3,13 @@ import * as secretController from './secretController'; import * as secretSnapshotController from './secretSnapshotController'; import * as workspaceController from './workspaceController'; import * as actionController from './actionController'; +import * as membershipController from './membershipController'; export { stripeController, secretController, secretSnapshotController, workspaceController, - actionController + actionController, + membershipController } \ No newline at end of file diff --git a/backend/src/ee/controllers/v1/membershipController.ts b/backend/src/ee/controllers/v1/membershipController.ts new file mode 100644 index 000000000..fcf158ba8 --- /dev/null +++ b/backend/src/ee/controllers/v1/membershipController.ts @@ -0,0 +1,63 @@ +import { Request, Response } from "express"; +import { Membership, Workspace } from "../../../models"; +import { IMembershipPermission } from "../../../models/membership"; +import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors"; +import { ABILITY_READ, ABILITY_WRITE, ADMIN, MEMBER } from "../../../variables/organization"; +import { Builder } from "builder-pattern" +import _ from "lodash"; + +export const denyMembershipPermissions = async (req: Request, res: Response) => { + const { membershipId } = req.params; + const { permissions } = req.body; + const sanitizedMembershipPermissions: IMembershipPermission[] = permissions.map((permission: IMembershipPermission) => { + if (!permission.ability || !permission.environmentSlug || ![ABILITY_READ, ABILITY_WRITE].includes(permission.ability)) { + throw BadRequestError({ message: "One or more required fields are missing from the request or have incorrect type" }) + } + + return Builder() + .environmentSlug(permission.environmentSlug) + .ability(permission.ability) + .build(); + }) + + const sanitizedMembershipPermissionsUnique = _.uniqWith(sanitizedMembershipPermissions, _.isEqual) + + const membershipToModify = await Membership.findById(membershipId) + if (!membershipToModify) { + throw BadRequestError({ message: "Unable to locate resource" }) + } + + // check if the user making the request is a admin of this project + if (![ADMIN, MEMBER].includes(membershipToModify.role)) { + throw UnauthorizedRequestError() + } + + // check if the requested slugs are indeed a part of this related workspace + const relatedWorkspace = await Workspace.findById(membershipToModify.workspace) + if (!relatedWorkspace) { + throw BadRequestError({ message: "Something went wrong when locating the related workspace" }) + } + + const uniqueEnvironmentSlugs = new Set(_.uniq(_.map(relatedWorkspace.environments, 'slug'))); + + sanitizedMembershipPermissionsUnique.forEach(permission => { + if (!uniqueEnvironmentSlugs.has(permission.environmentSlug)) { + throw BadRequestError({ message: "Unknown environment slug reference" }) + } + }) + + // update the permissions + const updatedMembershipWithPermissions = await Membership.findByIdAndUpdate( + { _id: membershipToModify._id }, + { $set: { deniedPermissions: sanitizedMembershipPermissionsUnique } }, + { new: true } + ) + + if (!updatedMembershipWithPermissions) { + throw BadRequestError({ message: "The resource has been removed before it can be modified" }) + } + + res.send({ + permissionsDenied: updatedMembershipWithPermissions.deniedPermissions + }) +} diff --git a/backend/src/ee/helpers/checkMembershipPermissions.ts b/backend/src/ee/helpers/checkMembershipPermissions.ts new file mode 100644 index 000000000..55155e885 --- /dev/null +++ b/backend/src/ee/helpers/checkMembershipPermissions.ts @@ -0,0 +1,18 @@ +import _ from "lodash"; +import { Membership } from "../../models"; + +export const userHasWorkspaceAccess = async (userId: any, workspaceId: any, environment: any, action: any) => { + const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId }) + if (!membershipForWorkspace) { + return false + } + + const deniedMembershipPermissions = membershipForWorkspace.deniedPermissions; + const isDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: action }); + + if (isDisallowed) { + return false + } + + return true +} \ No newline at end of file diff --git a/backend/src/helpers/secret.ts b/backend/src/helpers/secret.ts index 74ba062bd..a16d114c6 100644 --- a/backend/src/helpers/secret.ts +++ b/backend/src/helpers/secret.ts @@ -12,14 +12,17 @@ import { import { IAction } from '../ee/models'; -import { - SECRET_SHARED, +import { + SECRET_SHARED, SECRET_PERSONAL, ACTION_ADD_SECRETS, ACTION_UPDATE_SECRETS, ACTION_DELETE_SECRETS, ACTION_READ_SECRETS } from '../variables'; +import _ from 'lodash'; +import { ABILITY_WRITE } from '../variables/organization'; +import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; /** * Validate that user with id [userId] can modify secrets with ids [secretIds] @@ -34,7 +37,7 @@ const validateSecrets = async ({ }: { userId: string; secretIds: string[]; -}) =>{ +}) => { let secrets; try { secrets = await Secret.find({ @@ -42,20 +45,31 @@ const validateSecrets = async ({ $in: secretIds.map((secretId: string) => new Types.ObjectId(secretId)) } }); - - const workspaceIdsSet = new Set((await Membership.find({ - user: userId - }, 'workspace')) - .map((m) => m.workspace.toString())); - + + if (secrets.length != secretIds.length) { + throw BadRequestError({ message: 'Unable to validate some secrets' }) + } + + const userMemberships = await Membership.find({ user: userId }) + const userMembershipById = _.keyBy(userMemberships, 'workspace'); + const workspaceIdsSet = new Set(userMemberships.map((m) => m.workspace.toString())); + + // for each secret check if the secret belongs to a workspace the user is a member of secrets.forEach((secret: ISecret) => { - if (!workspaceIdsSet.has(secret.workspace.toString())) { - throw new Error('Failed to validate secret'); + if (workspaceIdsSet.has(secret.workspace.toString())) { + const deniedMembershipPermissions = userMembershipById[secret.workspace.toString()].deniedPermissions; + const isDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: secret.environment, ability: ABILITY_WRITE }); + + if (isDisallowed) { + throw UnauthorizedRequestError({ message: 'You do not have the required permissions to perform this action' }); + } + } else { + throw BadRequestError({ message: 'You cannot edit secrets of a workspace you are not a member of' }); } }); - + } catch (err) { - throw new Error('Failed to validate secrets'); + throw BadRequestError({ message: 'Unable to validate secrets' }) } return secrets; @@ -127,13 +141,13 @@ const v1PushSecrets = async ({ workspaceId, environment }); - - const oldSecretsObj: any = oldSecrets.reduce((accumulator, s: any) => + + const oldSecretsObj: any = oldSecrets.reduce((accumulator, s: any) => ({ ...accumulator, [`${s.type}-${s.secretKeyHash}`]: s }) - , {}); - const newSecretsObj: any = secrets.reduce((accumulator, s) => + , {}); + const newSecretsObj: any = secrets.reduce((accumulator, s) => ({ ...accumulator, [`${s.type}-${s.hashKey}`]: s }) - , {}); + , {}); // handle deleting secrets const toDelete = oldSecrets @@ -150,12 +164,12 @@ const v1PushSecrets = async ({ secretIds: toDelete }); } - + const toUpdate = oldSecrets .filter((s) => { if (`${s.type}-${s.secretKeyHash}` in newSecretsObj) { - if (s.secretValueHash !== newSecretsObj[`${s.type}-${s.secretKeyHash}`].hashValue - || s.secretCommentHash !== newSecretsObj[`${s.type}-${s.secretKeyHash}`].hashComment) { + if (s.secretValueHash !== newSecretsObj[`${s.type}-${s.secretKeyHash}`].hashValue + || s.secretCommentHash !== newSecretsObj[`${s.type}-${s.secretKeyHash}`].hashComment) { // case: filter secrets where value or comment changed return true; } @@ -165,7 +179,7 @@ const v1PushSecrets = async ({ return true; } } - + return false; }); @@ -217,7 +231,7 @@ const v1PushSecrets = async ({ }; }); await Secret.bulkWrite(operations as any); - + // (EE) add secret versions for updated secrets await EESecretService.addSecretVersions({ secretVersions: toUpdate.map(({ @@ -245,7 +259,7 @@ const v1PushSecrets = async ({ secretValueTag: newSecret.tagValue, secretValueHash: newSecret.hashValue }) - }) + }) }); // handle adding new secrets @@ -319,7 +333,7 @@ const v1PushSecrets = async ({ })) }); } - + // (EE) take a secret snapshot await EESecretService.takeSecretSnapshot({ workspaceId @@ -344,7 +358,7 @@ const v1PushSecrets = async ({ * @param {String} obj.channel - channel (web/cli/auto) * @param {String} obj.ipAddress - ip address of request to push secrets */ - const v2PushSecrets = async ({ +const v2PushSecrets = async ({ userId, workspaceId, environment, @@ -362,20 +376,20 @@ const v1PushSecrets = async ({ // TODO: clean up function and fix up types try { const actions: IAction[] = []; - + // construct useful data structures const oldSecrets = await getSecrets({ userId, workspaceId, environment }); - - const oldSecretsObj: any = oldSecrets.reduce((accumulator, s: any) => + + const oldSecretsObj: any = oldSecrets.reduce((accumulator, s: any) => ({ ...accumulator, [`${s.type}-${s.secretKeyHash}`]: s }) - , {}); - const newSecretsObj: any = secrets.reduce((accumulator, s) => + , {}); + const newSecretsObj: any = secrets.reduce((accumulator, s) => ({ ...accumulator, [`${s.type}-${s.secretKeyHash}`]: s }) - , {}); + , {}); // handle deleting secrets const toDelete = oldSecrets @@ -391,7 +405,7 @@ const v1PushSecrets = async ({ await EESecretService.markDeletedSecretVersions({ secretIds: toDelete }); - + const deleteAction = await EELogService.createActionSecret({ name: ACTION_DELETE_SECRETS, userId, @@ -401,12 +415,12 @@ const v1PushSecrets = async ({ deleteAction && actions.push(deleteAction); } - + const toUpdate = oldSecrets .filter((s) => { if (`${s.type}-${s.secretKeyHash}` in newSecretsObj) { - if (s.secretValueHash !== newSecretsObj[`${s.type}-${s.secretKeyHash}`].secretValueHash - || s.secretCommentHash !== newSecretsObj[`${s.type}-${s.secretKeyHash}`].secretCommentHash) { + if (s.secretValueHash !== newSecretsObj[`${s.type}-${s.secretKeyHash}`].secretValueHash + || s.secretCommentHash !== newSecretsObj[`${s.type}-${s.secretKeyHash}`].secretCommentHash) { // case: filter secrets where value or comment changed return true; } @@ -416,7 +430,7 @@ const v1PushSecrets = async ({ return true; } } - + return false; }); @@ -469,7 +483,7 @@ const v1PushSecrets = async ({ }; }); await Secret.bulkWrite(operations as any); - + // (EE) add secret versions for updated secrets await EESecretService.addSecretVersions({ secretVersions: toUpdate.map((s) => { @@ -482,7 +496,7 @@ const v1PushSecrets = async ({ environment: s.environment, isDeleted: false }) - }) + }) }); const updateAction = await EELogService.createActionSecret({ @@ -507,18 +521,19 @@ const v1PushSecrets = async ({ workspace: workspaceId, type: toAdd[idx].type, environment, - ...( toAdd[idx].type === 'personal' ? { user: userId } : {}) + ...(toAdd[idx].type === 'personal' ? { user: userId } : {}) })) ); // (EE) add secret versions for new secrets EESecretService.addSecretVersions({ - secretVersions: newSecrets.map((secretDocument) => { + secretVersions: newSecrets.map((secretDocument) => { return { ...secretDocument.toObject(), secret: secretDocument._id, isDeleted: false - }}) + } + }) }); const addAction = await EELogService.createActionSecret({ @@ -529,7 +544,7 @@ const v1PushSecrets = async ({ }); addAction && actions.push(addAction); } - + // (EE) take a secret snapshot await EESecretService.takeSecretSnapshot({ workspaceId @@ -560,7 +575,7 @@ const v1PushSecrets = async ({ * @param {String} obj.workspaceId - id of workspace to pull from * @param {String} obj.environment - environment for secrets */ - const getSecrets = async ({ +const getSecrets = async ({ userId, workspaceId, environment @@ -570,7 +585,7 @@ const v1PushSecrets = async ({ environment: string; }): Promise => { let secrets: any; // TODO: FIX any - + try { // get shared workspace secrets const sharedSecrets = await Secret.find({ @@ -622,7 +637,7 @@ const pullSecrets = async ({ ipAddress: string; }): Promise => { let secrets: any; - + try { secrets = await getSecrets({ userId, diff --git a/backend/src/models/membership.ts b/backend/src/models/membership.ts index abbce82c1..55b8f2048 100644 --- a/backend/src/models/membership.ts +++ b/backend/src/models/membership.ts @@ -1,15 +1,21 @@ import { Schema, model, Types } from 'mongoose'; import { ADMIN, MEMBER } from '../variables'; +export interface IMembershipPermission { + environmentSlug: string, + ability: string +} + export interface IMembership { _id: Types.ObjectId; user: Types.ObjectId; inviteEmail?: string; workspace: Types.ObjectId; role: 'admin' | 'member'; + deniedPermissions: IMembershipPermission[] } -const membershipSchema = new Schema( +const membershipSchema = new Schema( { user: { type: Schema.Types.ObjectId, @@ -23,6 +29,18 @@ const membershipSchema = new Schema( ref: 'Workspace', required: true }, + deniedPermissions: { + type: [ + { + environmentSlug: String, + ability: { + type: String, + enum: ['read', 'write'] + }, + }, + ], + default: [] + }, role: { type: String, enum: [ADMIN, MEMBER], diff --git a/backend/src/rbac/main.polar b/backend/src/rbac/main.polar deleted file mode 100644 index c88143e51..000000000 --- a/backend/src/rbac/main.polar +++ /dev/null @@ -1,15 +0,0 @@ -# Actoers -actor User {} - -# Resource block -resource Organization { - permissions = ["view", "delete", "edit_billing", "edit_members"]; - roles = ["owner", "member"]; - - "view" if "member"; - "delete" if "owner"; - "edit_billing" if "owner"; - "edit_members" if "owner"; - - "member" if "owner"; -} \ No newline at end of file diff --git a/backend/src/routes/v1/membership.ts b/backend/src/routes/v1/membership.ts index 4481b8843..aaacada74 100644 --- a/backend/src/routes/v1/membership.ts +++ b/backend/src/routes/v1/membership.ts @@ -3,14 +3,15 @@ const router = express.Router(); import { body, param } from 'express-validator'; import { requireAuth, validateRequest } from '../../middleware'; import { membershipController } from '../../controllers/v1'; +import { membershipController as EEMembershipControllers } from '../../ee/controllers/v1'; // note: ALL DEPRECIATED (moved to api/v2/workspace/:workspaceId/memberships/:membershipId) router.get( // used for old CLI (deprecate) '/:workspaceId/connect', requireAuth({ - acceptedAuthModes: ['jwt'] - }), + acceptedAuthModes: ['jwt'] + }), param('workspaceId').exists().trim(), validateRequest, membershipController.validateMembership @@ -19,8 +20,8 @@ router.get( // used for old CLI (deprecate) router.delete( '/:membershipId', requireAuth({ - acceptedAuthModes: ['jwt'] - }), + acceptedAuthModes: ['jwt'] + }), param('membershipId').exists().trim(), validateRequest, membershipController.deleteMembership @@ -29,11 +30,22 @@ router.delete( router.post( '/:membershipId/change-role', requireAuth({ - acceptedAuthModes: ['jwt'] - }), + acceptedAuthModes: ['jwt'] + }), body('role').exists().trim(), validateRequest, membershipController.changeMembershipRole ); +router.post( + '/:membershipId/deny-permissions', + requireAuth({ + acceptedAuthModes: ['jwt'] + }), + param('membershipId').isMongoId().exists().trim(), + body('permissions').isArray().exists(), + validateRequest, + EEMembershipControllers.denyMembershipPermissions +); + export default router; diff --git a/backend/src/routes/v2/environment.ts b/backend/src/routes/v2/environment.ts index 924db18fc..11afa4f2e 100644 --- a/backend/src/routes/v2/environment.ts +++ b/backend/src/routes/v2/environment.ts @@ -54,4 +54,17 @@ router.delete( environmentController.deleteWorkspaceEnvironment ); +router.get( + '/:workspaceId/environments', + requireAuth({ + acceptedAuthModes: ['jwt'], + }), + requireWorkspaceAuth({ + acceptedRoles: [MEMBER, ADMIN], + }), + param('workspaceId').exists().trim(), + validateRequest, + environmentController.getAllAccessibleEnvironmentsOfWorkspace +); + export default router; diff --git a/backend/src/variables/organization.ts b/backend/src/variables/organization.ts index 1b74bd3ee..806460997 100644 --- a/backend/src/variables/organization.ts +++ b/backend/src/variables/organization.ts @@ -6,6 +6,10 @@ const MEMBER = 'member'; // membership statuses const INVITED = 'invited'; +// membership permissions ability +const ABILITY_READ = 'read'; +const ABILITY_WRITE = 'write'; + // -- organization const ACCEPTED = 'accepted'; @@ -14,5 +18,7 @@ export { ADMIN, MEMBER, INVITED, - ACCEPTED + ACCEPTED, + ABILITY_READ, + ABILITY_WRITE } \ No newline at end of file From 55f0a491cb0b6ee8aa61650b3ffa850b988b5c13 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Sun, 29 Jan 2023 22:38:20 -0800 Subject: [PATCH 014/122] Release fly.io integrartion --- backend/src/variables/integration.ts | 2 +- .../basic/dialog/IntegrationAccessTokenDialog.tsx | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index 872b3e096..7cecb54c2 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -90,7 +90,7 @@ const INTEGRATION_OPTIONS = [ name: 'Fly.io', slug: 'flyio', image: 'Flyio.svg', - isAvailable: false, + isAvailable: true, type: 'pat', clientId: '', docsLink: '' diff --git a/frontend/src/components/basic/dialog/IntegrationAccessTokenDialog.tsx b/frontend/src/components/basic/dialog/IntegrationAccessTokenDialog.tsx index 89b98dd39..0a09f21e9 100644 --- a/frontend/src/components/basic/dialog/IntegrationAccessTokenDialog.tsx +++ b/frontend/src/components/basic/dialog/IntegrationAccessTokenDialog.tsx @@ -85,10 +85,11 @@ const IntegrationAccessTokenDialog = ({

- {`This integration requires you to obtain an API key from ${selectedIntegrationOption?.name ?? ''} and store it with Infisical.`} + {`This integration requires you to obtain an API key from ${selectedIntegrationOption?.name ?? ''} and store it with Infisical. `} + You can learn how to do this here.

-
+
-
+
+ +
+ } + onClose={onClose} + > +
+ + Type {deleteKey} to delete the resource +
+ } + className="mb-4" + > + setInputData(e.target.value)} /> + + + + + ); +}; diff --git a/frontend/src/components/v2/DeleteActionModal/index.tsx b/frontend/src/components/v2/DeleteActionModal/index.tsx new file mode 100644 index 000000000..b69763984 --- /dev/null +++ b/frontend/src/components/v2/DeleteActionModal/index.tsx @@ -0,0 +1 @@ +export { DeleteActionModal } from './DeleteActionModal'; diff --git a/frontend/src/components/v2/FormControl/FormControl.tsx b/frontend/src/components/v2/FormControl/FormControl.tsx index 14e58d5be..5a545acec 100644 --- a/frontend/src/components/v2/FormControl/FormControl.tsx +++ b/frontend/src/components/v2/FormControl/FormControl.tsx @@ -11,7 +11,7 @@ export type FormLabelProps = { }; export const FormLabel = ({ id, label, isRequired }: FormLabelProps) => ( - + {label} {isRequired && *} @@ -25,7 +25,7 @@ export type FormHelperTextProps = { export const FormHelperText = ({ isError, text }: FormHelperTextProps) => (
@@ -46,6 +46,7 @@ export type FormControlProps = { helperText?: ReactNode; errorText?: ReactNode; children: JSX.Element; + className?: string; }; export const FormControl = ({ @@ -55,10 +56,11 @@ export const FormControl = ({ helperText, errorText, id, - isError + isError, + className }: FormControlProps): JSX.Element => { return ( -
+
{typeof label === 'string' ? ( ) : ( diff --git a/frontend/src/components/v2/IconButton/IconButton.tsx b/frontend/src/components/v2/IconButton/IconButton.tsx index b1a1f4813..eb4919617 100644 --- a/frontend/src/components/v2/IconButton/IconButton.tsx +++ b/frontend/src/components/v2/IconButton/IconButton.tsx @@ -14,7 +14,7 @@ const iconButtonVariants = cva( [ 'button', 'transition-all', - 'font-inter font-medium', + 'font-inter font-medium user-select-none', 'cursor-pointer', 'inline-flex items-center justify-center', 'relative' diff --git a/frontend/src/components/v2/Input/Input.tsx b/frontend/src/components/v2/Input/Input.tsx index 4f502e31d..8c3685dac 100644 --- a/frontend/src/components/v2/Input/Input.tsx +++ b/frontend/src/components/v2/Input/Input.tsx @@ -8,6 +8,7 @@ type Props = { isRequired?: boolean; leftIcon?: ReactNode; rightIcon?: ReactNode; + isDisabled?: boolean; }; const inputVariants = cva( @@ -46,7 +47,7 @@ const inputParentContainerVariants = cva('inline-flex font-inter items-center bo }, isError: { true: 'border-red', - false: 'border-mineshaft-400' + false: 'border-mineshaft-500' }, isFullWidth: { true: 'w-full', @@ -70,6 +71,7 @@ export const Input = forwardRef( className, isRounded = true, isFullWidth = true, + isDisabled, isError = false, isRequired, leftIcon, @@ -87,6 +89,7 @@ export const Input = forwardRef( {...props} required={isRequired} ref={ref} + disabled={isDisabled} className={twMerge( leftIcon ? 'pl-9' : 'pl-4', rightIcon ? 'pr-9' : 'pr-4', diff --git a/frontend/src/components/v2/Modal/Modal.tsx b/frontend/src/components/v2/Modal/Modal.tsx index c7d42541d..eea12fbc5 100644 --- a/frontend/src/components/v2/Modal/Modal.tsx +++ b/frontend/src/components/v2/Modal/Modal.tsx @@ -11,31 +11,32 @@ export type ModalContentProps = DialogPrimitive.DialogContentProps & { title?: ReactNode; subTitle?: string; footerContent?: ReactNode; + onClose?: () => void; }; export const ModalContent = forwardRef( - ({ children, title, subTitle, className, footerContent, ...props }, forwardedRef) => ( + ({ children, title, subTitle, className, footerContent, onClose, ...props }, forwardedRef) => ( {title && {title}} {children} {footerContent && {footerContent}} - + @@ -55,3 +56,6 @@ export const Modal = ({ isOpen, ...props }: ModalProps) => ( export const ModalTrigger = DialogPrimitive.Trigger; export type ModalTriggerProps = DialogPrimitive.DialogTriggerProps; + +export const ModalClose = DialogPrimitive.Close; +export type ModalCloseProps = DialogPrimitive.DialogCloseProps; diff --git a/frontend/src/components/v2/Modal/index.tsx b/frontend/src/components/v2/Modal/index.tsx index c97a564fb..7517b3ec1 100644 --- a/frontend/src/components/v2/Modal/index.tsx +++ b/frontend/src/components/v2/Modal/index.tsx @@ -1,2 +1,2 @@ -export type { ModalContentProps, ModalProps, ModalTriggerProps } from './Modal'; -export { Modal, ModalContent, ModalTrigger } from './Modal'; +export type { ModalCloseProps, ModalContentProps, ModalProps, ModalTriggerProps } from './Modal'; +export { Modal, ModalClose, ModalContent, ModalTrigger } from './Modal'; diff --git a/frontend/src/components/v2/Select/Select.tsx b/frontend/src/components/v2/Select/Select.tsx index c093b8b70..e76e90260 100644 --- a/frontend/src/components/v2/Select/Select.tsx +++ b/frontend/src/components/v2/Select/Select.tsx @@ -22,21 +22,23 @@ export const Select = forwardRef( - {!props.disabled && - - } + {!props.disabled && ( + + + + )} @@ -75,11 +77,11 @@ export const SelectItem = forwardRef( (
( @@ -47,7 +47,7 @@ export type THeadProps = { }; export const THead = ({ children, className }: THeadProps): JSX.Element => ( - + {children} ); @@ -56,15 +56,17 @@ export const THead = ({ children, className }: THeadProps): JSX.Element => ( export type TrProps = { children: ReactNode; className?: string; -}; +} & HTMLAttributes; -export const Tr = ({ children, className }: TrProps): JSX.Element => ( - {children} +export const Tr = ({ children, className, ...props }: TrProps): JSX.Element => ( + + {children} + ); // table head columns export type ThProps = { - children: ReactNode; + children?: ReactNode; className?: string; }; @@ -84,10 +86,12 @@ export const TBody = ({ children, className }: TBodyProps): JSX.Element => ( // table body columns export type TdProps = { - children: ReactNode; + children?: ReactNode; className?: string; -}; +} & TdHTMLAttributes; -export const Td = ({ children, className }: TdProps): JSX.Element => ( - +export const Td = ({ children, className, ...props }: TdProps): JSX.Element => ( + ); diff --git a/frontend/src/components/v2/UpgradePlanModal/UpgradePlanModal.tsx b/frontend/src/components/v2/UpgradePlanModal/UpgradePlanModal.tsx new file mode 100644 index 000000000..e991ca3a8 --- /dev/null +++ b/frontend/src/components/v2/UpgradePlanModal/UpgradePlanModal.tsx @@ -0,0 +1,36 @@ +import Link from 'next/link'; + +import { Button } from '../Button'; +import { Modal, ModalClose, ModalContent } from '../Modal'; + +type Props = { + isOpen?: boolean; + onOpenChange?: (isOpen: boolean) => void; + text: string; +}; + +export const UpgradePlanModal = ({ text, isOpen, onOpenChange }: Props): JSX.Element => ( + + + + , + + + + ]} + > +

{text}

+

+ Upgrade and get access to this, as well as to other powerful enhancements. +

+
+
+); diff --git a/frontend/src/components/v2/UpgradePlanModal/index.tsx b/frontend/src/components/v2/UpgradePlanModal/index.tsx new file mode 100644 index 000000000..e4649c2df --- /dev/null +++ b/frontend/src/components/v2/UpgradePlanModal/index.tsx @@ -0,0 +1 @@ +export { UpgradePlanModal } from './UpgradePlanModal'; diff --git a/frontend/src/components/v2/index.tsx b/frontend/src/components/v2/index.tsx index 437114047..0f89a3a2b 100644 --- a/frontend/src/components/v2/index.tsx +++ b/frontend/src/components/v2/index.tsx @@ -1,6 +1,7 @@ export * from './Button'; export * from './Card'; export * from './Checkbox'; +export * from './DeleteActionModal'; export * from './Dropdown'; export * from './FormControl'; export * from './IconButton'; @@ -12,3 +13,4 @@ export * from './Spinner'; export * from './Switch'; export * from './Table'; export * from './TextArea'; +export * from './UpgradePlanModal'; From a4f78437278a593a92513283742b7ac461f3b9b2 Mon Sep 17 00:00:00 2001 From: akhilmhdh Date: Fri, 3 Feb 2023 22:18:30 +0530 Subject: [PATCH 057/122] feat(ui): global workspace and subscription context --- .../SubscriptionContext.tsx | 47 +++++++++++++++++ .../src/context/SubscriptionContext/index.tsx | 1 + .../WorkspaceContext/WorkspaceContext.tsx | 52 +++++++++++++++++++ .../src/context/WorkspaceContext/index.tsx | 1 + frontend/src/context/index.tsx | 2 + 5 files changed, 103 insertions(+) create mode 100644 frontend/src/context/SubscriptionContext/SubscriptionContext.tsx create mode 100644 frontend/src/context/SubscriptionContext/index.tsx create mode 100644 frontend/src/context/WorkspaceContext/WorkspaceContext.tsx create mode 100644 frontend/src/context/WorkspaceContext/index.tsx create mode 100644 frontend/src/context/index.tsx diff --git a/frontend/src/context/SubscriptionContext/SubscriptionContext.tsx b/frontend/src/context/SubscriptionContext/SubscriptionContext.tsx new file mode 100644 index 000000000..ae9910224 --- /dev/null +++ b/frontend/src/context/SubscriptionContext/SubscriptionContext.tsx @@ -0,0 +1,47 @@ +import { createContext, ReactNode, useContext, useMemo } from 'react'; + +import { useGetOrgSubscription } from '@app/hooks/api'; +import { GetSubscriptionPlan } from '@app/hooks/api/types'; + +import { useWorkspace } from '../WorkspaceContext'; +// import { Subscription } from '@app/hooks/api/workspace/types'; + +type TSubscriptionContext = { + subscription?: GetSubscriptionPlan; + subscriptionPlan: string; + isLoading: boolean; +}; + +const SubscriptionContext = createContext(null); + +type Props = { + children: ReactNode; +}; + +export const SubscriptionProvider = ({ children }: Props): JSX.Element => { + const { currentWorkspace } = useWorkspace(); + const { data, isLoading } = useGetOrgSubscription({ + orgID: currentWorkspace?.organization || '' + }); + + // memorize the workspace details for the context + const value = useMemo( + () => ({ + subscription: data, + subscriptionPlan: data?.data?.[0]?.plan?.product || '', + isLoading + }), + [data, isLoading] + ); + + return {children}; +}; + +export const useSubscription = () => { + const ctx = useContext(SubscriptionContext); + if (!ctx) { + throw new Error('useSubscription has to be used within '); + } + + return ctx; +}; diff --git a/frontend/src/context/SubscriptionContext/index.tsx b/frontend/src/context/SubscriptionContext/index.tsx new file mode 100644 index 000000000..9a2b07b15 --- /dev/null +++ b/frontend/src/context/SubscriptionContext/index.tsx @@ -0,0 +1 @@ +export { SubscriptionProvider, useSubscription } from './SubscriptionContext'; diff --git a/frontend/src/context/WorkspaceContext/WorkspaceContext.tsx b/frontend/src/context/WorkspaceContext/WorkspaceContext.tsx new file mode 100644 index 000000000..ea82fbbe7 --- /dev/null +++ b/frontend/src/context/WorkspaceContext/WorkspaceContext.tsx @@ -0,0 +1,52 @@ +import { createContext, ReactNode, useContext, useEffect, useMemo } from 'react'; +import { useRouter } from 'next/router'; + +import { useGetUserWorkspaces } from '@app/hooks/api'; +import { Workspace } from '@app/hooks/api/workspace/types'; + +type TWorkspaceContext = { + workspaces: Workspace[]; + currentWorkspace?: Workspace; + isLoading: boolean; +}; + +const WorkspaceContext = createContext(null); + +type Props = { + children: ReactNode; +}; + +export const WorkspaceProvider = ({ children }: Props): JSX.Element => { + const { data: ws, isLoading } = useGetUserWorkspaces(); + const router = useRouter(); + const workspaceId = router.query.id; + + // memorize the workspace details for the context + const value = useMemo(() => { + return { + workspaces: ws || [], + currentWorkspace: (ws || []).find(({ _id: id }) => id === workspaceId), + isLoading + }; + }, [ws, workspaceId, isLoading]); + + useEffect(() => { + // not loading and current workspace is empty + // ws empty means user has no access to the ws + // push to the first workspace + if (!isLoading && !value?.currentWorkspace?._id) { + router.push(`/dashboard/${value.workspaces?.[0]?._id}`); + } + }, [value?.currentWorkspace?._id, isLoading, value.workspaces?.[0]?._id, router.pathname]); + + return {children}; +}; + +export const useWorkspace = () => { + const ctx = useContext(WorkspaceContext); + if (!ctx) { + throw new Error('useWorkspace has to be used within '); + } + + return ctx; +}; diff --git a/frontend/src/context/WorkspaceContext/index.tsx b/frontend/src/context/WorkspaceContext/index.tsx new file mode 100644 index 000000000..3496038c3 --- /dev/null +++ b/frontend/src/context/WorkspaceContext/index.tsx @@ -0,0 +1 @@ +export { useWorkspace, WorkspaceProvider } from './WorkspaceContext'; diff --git a/frontend/src/context/index.tsx b/frontend/src/context/index.tsx new file mode 100644 index 000000000..1bf59f9ac --- /dev/null +++ b/frontend/src/context/index.tsx @@ -0,0 +1,2 @@ +export { SubscriptionProvider, useSubscription } from './SubscriptionContext'; +export { useWorkspace, WorkspaceProvider } from './WorkspaceContext'; From ed148a542d6671dc2f172e90d265777328e765cb Mon Sep 17 00:00:00 2001 From: akhilmhdh Date: Fri, 3 Feb 2023 22:19:03 +0530 Subject: [PATCH 058/122] feat(ui): implemented the new project settings page --- frontend/public/locales/en/section-token.json | 2 +- frontend/src/components/basic/Layout.tsx | 57 ++- .../basic/table/EnvironmentsTable.tsx | 57 ++- frontend/src/components/dashboard/SideBar.tsx | 4 +- .../components/navigation/NavBarDashboard.tsx | 2 +- .../src/components/navigation/NavHeader.tsx | 23 +- frontend/src/const.ts | 28 +- frontend/src/pages/_app.tsx | 16 +- frontend/src/pages/activity/[id].tsx | 2 +- frontend/src/pages/dashboard/[id].tsx | 4 +- frontend/src/pages/settings/project/[id].tsx | 328 +----------------- .../ProjectSettingsPage.tsx | 287 +++++++++++++++ .../CopyProjectIDSection.tsx | 66 ++++ .../components/CopyProjectIDSection/index.tsx | 1 + .../EnvironmentSection/EnvironmentSection.tsx | 239 +++++++++++++ .../components/EnvironmentSection/index.tsx | 1 + .../ProjectNameChangeSection.tsx | 71 ++++ .../ProjectNameChangeSection/index.tsx | 1 + .../ServiceTokenSection.tsx | 307 ++++++++++++++++ .../components/ServiceTokenSection/index.tsx | 1 + .../ProjectSettingsPage/components/index.tsx | 6 + .../Settings/ProjectSettingsPage/index.tsx | 1 + 22 files changed, 1099 insertions(+), 405 deletions(-) create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/ProjectSettingsPage.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/components/CopyProjectIDSection/CopyProjectIDSection.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/components/CopyProjectIDSection/index.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentSection.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/index.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/components/ProjectNameChangeSection/ProjectNameChangeSection.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/components/ProjectNameChangeSection/index.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/components/ServiceTokenSection/ServiceTokenSection.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/components/ServiceTokenSection/index.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/components/index.tsx create mode 100644 frontend/src/views/Settings/ProjectSettingsPage/index.tsx diff --git a/frontend/public/locales/en/section-token.json b/frontend/public/locales/en/section-token.json index d12bd202c..ce468b1af 100644 --- a/frontend/public/locales/en/section-token.json +++ b/frontend/public/locales/en/section-token.json @@ -4,7 +4,7 @@ "add-new": "Add New Token", "add-dialog": { "title": "Add a service token for {{target}}", - "description": "Specify the name, environment, and expiry period. When a token is generated, you will only be able to see it once before it disappears. Make sure to save it somewhere.", + "description": "When a token is generated, you will only be able to see it once before it disappears. Make sure to save it somewhere.", "name": "Service Token Name", "add": "Add Service Token", "copy-service-token": "Copy your service token", diff --git a/frontend/src/components/basic/Layout.tsx b/frontend/src/components/basic/Layout.tsx index a3d6092a1..ba4cf73b2 100644 --- a/frontend/src/components/basic/Layout.tsx +++ b/frontend/src/components/basic/Layout.tsx @@ -187,10 +187,10 @@ const Layout = ({ children }: LayoutProps) => { }); const userWorkspaces = orgUserProjects; if ( - userWorkspaces.length === 0 && - router.asPath !== '/noprojects' && - !router.asPath.includes('home') && - !router.asPath.includes('settings') || + (userWorkspaces.length === 0 && + router.asPath !== '/noprojects' && + !router.asPath.includes('home') && + !router.asPath.includes('settings')) || router.asPath === '/dashboard/undefined' ) { router.push('/noprojects'); @@ -246,16 +246,15 @@ const Layout = ({ children }: LayoutProps) => { return ( <> -
-
{children} + {children} +