diff --git a/.github/workflows/release_build.yml b/.github/workflows/release_build.yml index df00c5c92..3d11a1157 100644 --- a/.github/workflows/release_build.yml +++ b/.github/workflows/release_build.yml @@ -19,6 +19,7 @@ jobs: with: fetch-depth: 0 - run: git fetch --force --tags + - run: echo "Ref name ${{github.ref_name}}" - uses: actions/setup-go@v3 with: go-version: '>=1.19.3' @@ -33,11 +34,11 @@ jobs: run: | mkdir ../../osxcross git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target - - uses: goreleaser/goreleaser-action@v2 + - uses: goreleaser/goreleaser-action@v4 with: distribution: goreleaser version: latest - args: release --rm-dist + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }} FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 51154a4f3..fc39224aa 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -14,6 +14,9 @@ before: builds: - id: darwin-build binary: infisical + ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }} + flags: + - -trimpath env: - CGO_ENABLED=1 - CC=/home/runner/work/osxcross/target/bin/o64-clang @@ -24,10 +27,14 @@ builds: - goos: darwin goarch: "386" dir: ./cli + - id: all-other-builds env: - CGO_ENABLED=0 binary: infisical + ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }} + flags: + - -trimpath goos: - freebsd - linux @@ -65,8 +72,10 @@ release: checksum: name_template: 'checksums.txt' + snapshot: - name_template: "{{ incpatch .Version }}" + name_template: "{{ incpatch .Version }}-devel" + changelog: sort: asc filters: @@ -80,6 +89,7 @@ changelog: # - infisical # dir: "{{ dir .ArtifactPath }}" # cmd: curl -F package=@{{ .ArtifactName }} https://{{ .Env.FURY_TOKEN }}@push.fury.io/infisical/ + brews: - name: infisical tap: @@ -91,6 +101,13 @@ brews: folder: Formula homepage: "https://infisical.com" description: "The official Infisical CLI" + install: |- + bin.install "infisical" + bash_completion.install "completions/infisical.bash" => "infisical" + zsh_completion.install "completions/infisical.zsh" => "_infisical" + fish_completion.install "completions/infisical.fish" + man1.install "manpages/infisical.1.gz" + nfpms: - id: infisical package_name: infisical @@ -116,6 +133,7 @@ nfpms: dst: /usr/share/zsh/site-functions/_infisical - src: ./manpages/infisical.1.gz dst: /usr/share/man/man1/infisical.1.gz + scoop: bucket: owner: Infisical @@ -126,6 +144,7 @@ scoop: homepage: "https://infisical.com" description: "The official Infisical CLI" license: MIT + aurs: - name: infisical-bin diff --git a/README.md b/README.md index 8db0b919d..1ec8cff50 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ git commit activity - Cloudsmith downloads + Cloudsmith downloads Slack community channel @@ -55,11 +55,11 @@ - **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** to view the change history for any secret - **[Activity Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** to record every action taken in a project - **[Point-in-time Secrets Recovery](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** for rolling back to any snapshot of your secrets +- **Role-based Access Controls** per environment +- πŸ”œ **2FA** (next week) - πŸ”œ **1-Click Deploy** to Digital Ocean and Heroku -- πŸ”œ **Authentication/Authorization** for projects (read/write controls soon) - πŸ”œ **Automatic Secret Rotation** -- πŸ”œ **2FA** -- πŸ”œ **Slack Integration & MS Teams** integrations +- πŸ”œ **Slack & MS Teams** integrations And more. @@ -99,15 +99,13 @@ Not sure where to get started? You can: - [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) (For any bugs and errors you encounter using Infisical) - [Twitter](https://twitter.com/infisical) (Get news fast) -## πŸ₯ Status +## πŸŒ„ Status -- [x] Public Alpha: Anyone can sign up over at [infisical.com](https://infisical.com) but go easy on us, there are kinks and we're just getting started. +- [x] Public Alpha: Anyone can sign up over at [infisical.com](https://infisical.com) but there are some minor kinks. - [ ] Public Beta: Stable enough for most non-enterprise use-cases. - [ ] Public: Production-ready. -We're currently in Public Alpha. - -## πŸ”Œ Integrations +## οΏ½πŸ”Œ Integrations We're currently setting the foundation and building [integrations](https://infisical.com/docs/integrations/overview) so secrets can be synced everywhere. Any help is welcome! :) @@ -157,7 +155,7 @@ We're currently setting the foundation and building [integrations](https://infis - πŸ”œ AWS + πŸ”œ AWS PS (https://github.com/Infisical/infisical/issues/286) @@ -170,7 +168,7 @@ We're currently setting the foundation and building [integrations](https://infis - πŸ”œ GCP + πŸ”œ GCP SM (https://github.com/Infisical/infisical/issues/285) πŸ”œ GitLab CI/CD (https://github.com/Infisical/infisical/issues/134) @@ -216,6 +214,17 @@ We're currently setting the foundation and building [integrations](https://infis + + + πŸ”œ BuddyCI + + + πŸ”œ Serverless + + + πŸ”œ AWS Lambda + + @@ -347,7 +356,7 @@ Infisical officially launched as v.1.0 on November 21st, 2022. There are a lot o - + ## 🌎 Translations diff --git a/backend/package-lock.json b/backend/package-lock.json index 2c7abb793..6703e198f 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -10,13 +10,13 @@ "license": "ISC", "dependencies": { "@godaddy/terminus": "^4.11.2", + "@sentry/node": "^7.21.1", "@octokit/rest": "^19.0.5", - "@sentry/node": "^7.14.0", - "@sentry/tracing": "^7.19.0", + "@sentry/tracing": "^7.21.1", "@types/crypto-js": "^4.1.1", + "axios": "^1.2.0", "@types/libsodium-wrappers": "^0.7.10", "await-to-js": "^3.0.0", - "axios": "^1.1.3", "bcrypt": "^5.1.0", "bigint-conversion": "^2.2.2", "builder-pattern": "^2.2.0", @@ -32,9 +32,9 @@ "js-yaml": "^4.1.0", "jsonwebtoken": "^9.0.0", "jsrp": "^0.2.4", + "mongoose": "^6.7.3", "libsodium-wrappers": "^0.7.10", "lodash": "^4.17.21", - "mongoose": "^6.7.2", "nodemailer": "^6.8.0", "posthog-node": "^2.2.2", "query-string": "^7.1.3", @@ -2838,6 +2838,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/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -2914,28 +2927,25 @@ "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", + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", "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==", + "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/types": "7.19.0", + "@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" }, "engines": { @@ -2943,46 +2953,53 @@ } }, "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==", + "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.19.0", - "@sentry/utils": "7.19.0", + "@sentry/types": "7.21.1", "tslib": "^1.9.3" }, "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==", + "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/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": { @@ -3767,9 +3784,9 @@ } }, "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", @@ -4448,9 +4465,9 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, "node_modules/core-util-is": { @@ -7165,9 +7182,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", @@ -14289,6 +14306,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" + } + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -14344,80 +14371,56 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "@sentry/node": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz", - "integrity": "sha512-yG7Tx32WqOkEHVotFLrumCcT9qlaSDTkFNZ+yLSvZXx74ifsE781DzBA9W7K7bBdYO3op+p2YdsOKzf3nPpAyQ==", + "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.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", "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": { - "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" + } + }, + "@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": { @@ -15051,9 +15054,9 @@ "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==" }, "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", @@ -15561,9 +15564,9 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, "core-util-is": { @@ -17626,9 +17629,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 e64d9d446..f2a2710c9 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,4 +1,32 @@ { + "dependencies": { + "@godaddy/terminus": "^4.11.2", + "@sentry/node": "^7.21.1", + "@sentry/tracing": "^7.21.1", + "@types/crypto-js": "^4.1.1", + "axios": "^1.2.0", + "bigint-conversion": "^2.2.2", + "cookie-parser": "^1.4.6", + "cors": "^2.8.5", + "crypto-js": "^4.1.1", + "dotenv": "^16.0.1", + "express": "^4.18.1", + "express-rate-limit": "^6.7.0", + "express-validator": "^6.14.2", + "handlebars": "^4.7.7", + "helmet": "^5.1.1", + "jsonwebtoken": "^8.5.1", + "jsrp": "^0.2.4", + "mongoose": "^6.7.3", + "nodemailer": "^6.8.0", + "posthog-node": "^2.1.0", + "query-string": "^7.1.3", + "rimraf": "^3.0.2", + "stripe": "^10.7.0", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1", + "typescript": "^4.9.3" + }, "name": "infisical-api", "version": "1.0.0", "main": "src/index.js", diff --git a/backend/src/controllers/v1/authController.ts b/backend/src/controllers/v1/authController.ts index 19850680e..6e2de53b3 100644 --- a/backend/src/controllers/v1/authController.ts +++ b/backend/src/controllers/v1/authController.ts @@ -4,16 +4,21 @@ import jwt from 'jsonwebtoken'; import * as Sentry from '@sentry/node'; import * as bigintConversion from 'bigint-conversion'; const jsrp = require('jsrp'); -import { User } from '../../models'; +import { User, LoginSRPDetail } from '../../models'; import { createToken, issueTokens, clearTokens } from '../../helpers/auth'; +import { + ACTION_LOGIN, + ACTION_LOGOUT +} from '../../variables'; import { NODE_ENV, JWT_AUTH_LIFETIME, JWT_AUTH_SECRET, JWT_REFRESH_SECRET } from '../../config'; -import LoginSRPDetail from '../../models/LoginSRPDetail'; import { BadRequestError } from '../../utils/errors'; +import { EELogService } from '../../ee/services'; +import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this declare module 'jsonwebtoken' { export interface UserIDJwtPayload extends jwt.JwtPayload { @@ -116,6 +121,18 @@ export const login2 = async (req: Request, res: Response) => { secure: NODE_ENV === 'production' ? true : false }); + const loginAction = await EELogService.createAction({ + name: ACTION_LOGIN, + userId: user._id + }); + + loginAction && await EELogService.createLog({ + userId: user._id, + actions: [loginAction], + channel: getChannelFromUserAgent(req.headers['user-agent']), + ipAddress: req.ip + }); + // return (access) token in response return res.status(200).send({ token: tokens.token, @@ -159,6 +176,19 @@ export const logout = async (req: Request, res: Response) => { sameSite: 'strict', secure: NODE_ENV === 'production' ? true : false }); + + const logoutAction = await EELogService.createAction({ + name: ACTION_LOGOUT, + userId: req.user._id + }); + + logoutAction && await EELogService.createLog({ + userId: req.user._id, + actions: [logoutAction], + channel: getChannelFromUserAgent(req.headers['user-agent']), + ipAddress: req.ip + }); + } catch (err) { Sentry.setUser({ email: req.user.email }); Sentry.captureException(err); diff --git a/backend/src/controllers/v1/membershipOrgController.ts b/backend/src/controllers/v1/membershipOrgController.ts index f3703b889..0040b583e 100644 --- a/backend/src/controllers/v1/membershipOrgController.ts +++ b/backend/src/controllers/v1/membershipOrgController.ts @@ -77,8 +77,6 @@ export const changeMembershipOrgRole = async (req: Request, res: Response) => { // change role for (target) organization membership with id // [membershipOrgId] - // TODO - let membershipToChangeRole; // try { // } catch (err) { diff --git a/backend/src/controllers/v1/passwordController.ts b/backend/src/controllers/v1/passwordController.ts index d429cbf41..0c5530f58 100644 --- a/backend/src/controllers/v1/passwordController.ts +++ b/backend/src/controllers/v1/passwordController.ts @@ -4,12 +4,11 @@ import crypto from 'crypto'; // eslint-disable-next-line @typescript-eslint/no-var-requires const jsrp = require('jsrp'); import * as bigintConversion from 'bigint-conversion'; -import { User, Token, BackupPrivateKey } from '../../models'; +import { User, Token, BackupPrivateKey, LoginSRPDetail } from '../../models'; 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'; -import LoginSRPDetail from '../../models/LoginSRPDetail'; import { BadRequestError } from '../../utils/errors'; /** diff --git a/backend/src/controllers/v2/environmentController.ts b/backend/src/controllers/v2/environmentController.ts index d92ec8264..7a0d5e1c5 100644 --- a/backend/src/controllers/v2/environmentController.ts +++ b/backend/src/controllers/v2/environmentController.ts @@ -11,7 +11,7 @@ import { import { SecretVersion } from '../../ee/models'; import { BadRequestError } from '../../utils/errors'; import _ from 'lodash'; -import { ABILITY_READ } from '../../variables/organization'; +import { ABILITY_READ, ABILITY_WRITE } from '../../variables/organization'; /** * Create new workspace environment named [environmentName] under workspace with id @@ -236,7 +236,7 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async ( throw BadRequestError() } - const accessibleEnvironments: { name: string; slug: string; }[] = [] + const accessibleEnvironments: any = [] const deniedPermission = workspacesUserIsMemberOf.deniedPermissions const relatedWorkspace = await Workspace.findById(workspaceId) @@ -245,11 +245,15 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async ( } relatedWorkspace.environments.forEach(environment => { const isReadBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_READ }) - // const isWriteBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_WRITE }) + const isWriteBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_WRITE }) if (isReadBlocked) { return } else { - accessibleEnvironments.push(environment) + accessibleEnvironments.push({ + name: environment.name, + slug: environment.slug, + isWriteDenied: isWriteBlocked + }) } }) diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index 90eb8e48d..5c6f5898e 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -79,7 +79,7 @@ export const createSecrets = async (req: Request, res: Response) => { */ const channel = getChannelFromUserAgent(req.headers['user-agent']) - const { workspaceId, environment } = req.body; + const { workspaceId, environment }: { workspaceId: string, environment: string } = req.body; const hasAccess = await userHasWorkspaceAccess(req.user, workspaceId, environment, ABILITY_WRITE) if (!hasAccess) { @@ -175,17 +175,17 @@ export const createSecrets = async (req: Request, res: Response) => { })) }); - const addAction = await EELogService.createActionSecret({ + const addAction = await EELogService.createAction({ name: ACTION_ADD_SECRETS, - userId: req.user._id.toString(), - workspaceId, + userId: req.user._id, + workspaceId: new Types.ObjectId(workspaceId), secretIds: newSecrets.map((n) => n._id) }); // (EE) create (audit) log addAction && await EELogService.createLog({ userId: req.user._id.toString(), - workspaceId, + workspaceId: new Types.ObjectId(workspaceId), actions: [addAction], channel, ipAddress: req.ip @@ -300,16 +300,16 @@ export const getSecrets = async (req: Request, res: Response) => { const channel = getChannelFromUserAgent(req.headers['user-agent']) - const readAction = await EELogService.createActionSecret({ + const readAction = await EELogService.createAction({ name: ACTION_READ_SECRETS, - userId: userId, - workspaceId: workspaceId as string, + userId: new Types.ObjectId(userId), + workspaceId: new Types.ObjectId(workspaceId as string), secretIds: secrets.map((n: any) => n._id) }); readAction && await EELogService.createLog({ - userId: userId, - workspaceId: workspaceId as string, + userId: new Types.ObjectId(userId), + workspaceId: new Types.ObjectId(workspaceId as string), actions: [readAction], channel, ipAddress: req.ip @@ -505,17 +505,17 @@ export const updateSecrets = async (req: Request, res: Response) => { }); }, 10000); - const updateAction = await EELogService.createActionSecret({ + const updateAction = await EELogService.createAction({ name: ACTION_UPDATE_SECRETS, - userId: req.user._id.toString(), - workspaceId: key, + userId: req.user._id, + workspaceId: new Types.ObjectId(key), secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) }); // (EE) create (audit) log updateAction && await EELogService.createLog({ userId: req.user._id.toString(), - workspaceId: key, + workspaceId: new Types.ObjectId(key), actions: [updateAction], channel, ipAddress: req.ip @@ -631,17 +631,17 @@ export const deleteSecrets = async (req: Request, res: Response) => { workspaceId: key }) }); - const deleteAction = await EELogService.createActionSecret({ + const deleteAction = await EELogService.createAction({ name: ACTION_DELETE_SECRETS, - userId: req.user._id.toString(), - workspaceId: key, + userId: req.user._id, + workspaceId: new Types.ObjectId(key), secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) }); // (EE) create (audit) log deleteAction && await EELogService.createLog({ userId: req.user._id.toString(), - workspaceId: key, + workspaceId: new Types.ObjectId(key), actions: [deleteAction], channel, ipAddress: req.ip diff --git a/backend/src/controllers/v2/serviceTokenDataController.ts b/backend/src/controllers/v2/serviceTokenDataController.ts index 83c8fea94..780bae9a4 100644 --- a/backend/src/controllers/v2/serviceTokenDataController.ts +++ b/backend/src/controllers/v2/serviceTokenDataController.ts @@ -8,6 +8,8 @@ import { import { SALT_ROUNDS } from '../../config'; +import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions'; +import { ABILITY_READ } from '../../variables/organization'; /** * Return service token data associated with service token on request @@ -37,6 +39,11 @@ export const createServiceTokenData = async (req: Request, res: Response) => { expiresIn } = req.body; + const hasAccess = await userHasWorkspaceAccess(req.user, workspaceId, environment, ABILITY_READ) + if (!hasAccess) { + throw UnauthorizedRequestError({ message: "You do not have the necessary permission(s) perform this action" }) + } + const secret = crypto.randomBytes(16).toString('hex'); const secretHash = await bcrypt.hash(secret, SALT_ROUNDS); @@ -100,4 +107,8 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => { return res.status(200).send({ serviceTokenData }); -} \ No newline at end of file +} + +function UnauthorizedRequestError(arg0: { message: string; }) { + throw new Error('Function not implemented.'); +} diff --git a/backend/src/ee/helpers/action.ts b/backend/src/ee/helpers/action.ts index 389cfedbf..eb94810f4 100644 --- a/backend/src/ee/helpers/action.ts +++ b/backend/src/ee/helpers/action.ts @@ -1,39 +1,40 @@ import * as Sentry from '@sentry/node'; import { Types } from 'mongoose'; -import { SecretVersion, Action } from '../models'; +import { Action } from '../models'; import { getLatestSecretVersionIds, getLatestNSecretSecretVersionIds } from '../helpers/secretVersion'; -import { ACTION_UPDATE_SECRETS } from '../../variables'; +import { + ACTION_LOGIN, + ACTION_LOGOUT, + ACTION_ADD_SECRETS, + ACTION_READ_SECRETS, + ACTION_DELETE_SECRETS, + ACTION_UPDATE_SECRETS, +} from '../../variables'; /** - * Create an (audit) action for secrets including - * add, delete, update, and read actions. + * Create an (audit) action for updating secrets * @param {Object} obj * @param {String} obj.name - name of action - * @param {ObjectId[]} obj.secretIds - ids of relevant secrets + * @param {Types.ObjectId} obj.secretIds - ids of relevant secrets * @returns {Action} action - new action */ -const createActionSecretHelper = async ({ +const createActionUpdateSecret = async ({ name, userId, workspaceId, secretIds }: { name: string; - userId: string; - workspaceId: string; + userId: Types.ObjectId; + workspaceId: Types.ObjectId; secretIds: Types.ObjectId[]; }) => { - let action; - let latestSecretVersions; try { - if (name === ACTION_UPDATE_SECRETS) { - // case: action is updating secrets - // -> add old and new secret versions - latestSecretVersions = (await getLatestNSecretSecretVersionIds({ + const latestSecretVersions = (await getLatestNSecretSecretVersionIds({ secretIds, n: 2 })) @@ -41,17 +42,7 @@ const createActionSecretHelper = async ({ oldSecretVersion: s.versions[0]._id, newSecretVersion: s.versions[1]._id })); - } else { - // case: action is adding, deleting, or reading secrets - // -> add new secret versions - latestSecretVersions = (await getLatestSecretVersionIds({ - secretIds - })) - .map((s) => ({ - newSecretVersion: s.versionId - })); - } - + action = await new Action({ name, user: userId, @@ -64,10 +55,148 @@ const createActionSecretHelper = async ({ } catch (err) { Sentry.setUser(null); Sentry.captureException(err); + throw new Error('Failed to create update secret action'); + } + + return action; +} + +/** + * Create an (audit) action for creating, reading, and deleting + * secrets + * @param {Object} obj + * @param {String} obj.name - name of action + * @param {Types.ObjectId} obj.secretIds - ids of relevant secrets + * @returns {Action} action - new action + */ +const createActionSecret = async ({ + name, + userId, + workspaceId, + secretIds +}: { + name: string; + userId: Types.ObjectId; + workspaceId: Types.ObjectId; + secretIds: Types.ObjectId[]; +}) => { + let action; + try { + // case: action is adding, deleting, or reading secrets + // -> add new secret versions + const latestSecretVersions = (await getLatestSecretVersionIds({ + secretIds + })) + .map((s) => ({ + newSecretVersion: s.versionId + })); + + action = await new Action({ + name, + user: userId, + workspace: workspaceId, + payload: { + secretVersions: latestSecretVersions + } + }).save(); + + } catch (err) { + Sentry.setUser(null); + Sentry.captureException(err); + throw new Error('Failed to create action create/read/delete secret action'); + } + + return action; +} + +/** + * Create an (audit) action for user with id [userId] + * @param {Object} obj + * @param {String} obj.name - name of action + * @param {String} obj.userId - id of user associated with action + * @returns + */ +const createActionUser = ({ + name, + userId +}: { + name: string; + userId: Types.ObjectId; +}) => { + let action; + try { + action = new Action({ + name, + user: userId + }).save(); + } catch (err) { + Sentry.setUser(null); + Sentry.captureException(err); + throw new Error('Failed to create user action'); + } + + return action; +} + +/** + * Create an (audit) action. + * @param {Object} obj + * @param {Object} obj.name - name of action + * @param {Types.ObjectId} obj.userId - id of user associated with action + * @param {Types.ObjectId} obj.workspaceId - id of workspace associated with action + * @param {Types.ObjectId[]} obj.secretIds - ids of secrets associated with action +*/ +const createActionHelper = async ({ + name, + userId, + workspaceId, + secretIds, +}: { + name: string; + userId: Types.ObjectId; + workspaceId?: Types.ObjectId; + secretIds?: Types.ObjectId[]; +}) => { + let action; + try { + switch (name) { + case ACTION_LOGIN: + case ACTION_LOGOUT: + action = await createActionUser({ + name, + userId + }); + break; + case ACTION_ADD_SECRETS: + case ACTION_READ_SECRETS: + case ACTION_DELETE_SECRETS: + if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret'); + action = await createActionSecret({ + name, + userId, + workspaceId, + secretIds + }); + break; + case ACTION_UPDATE_SECRETS: + if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret'); + action = await createActionUpdateSecret({ + name, + userId, + workspaceId, + secretIds + }); + break; + } + } catch (err) { + Sentry.setUser(null); + Sentry.captureException(err); throw new Error('Failed to create action'); } return action; } -export { createActionSecretHelper }; \ No newline at end of file +export { + createActionHelper +}; \ No newline at end of file diff --git a/backend/src/ee/helpers/log.ts b/backend/src/ee/helpers/log.ts index c357c9818..bdb0a2380 100644 --- a/backend/src/ee/helpers/log.ts +++ b/backend/src/ee/helpers/log.ts @@ -1,9 +1,19 @@ import * as Sentry from '@sentry/node'; +import { Types } from 'mongoose'; import { Log, IAction } from '../models'; - +/** + * Create an (audit) log + * @param {Object} obj + * @param {Types.ObjectId} obj.userId - id of user associated with the log + * @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the log + * @param {IAction[]} obj.actions - actions to include in log + * @param {String} obj.channel - channel (web/cli/auto) associated with the log + * @param {String} obj.ipAddress - ip address associated with the log + * @returns {Log} log - new audit log + */ const createLogHelper = async ({ userId, workspaceId, @@ -11,8 +21,8 @@ const createLogHelper = async ({ channel, ipAddress }: { - userId: string; - workspaceId: string; + userId: Types.ObjectId; + workspaceId?: Types.ObjectId; actions: IAction[]; channel: string; ipAddress: string; @@ -21,7 +31,7 @@ const createLogHelper = async ({ try { log = await new Log({ user: userId, - workspace: workspaceId, + workspace: workspaceId ?? undefined, actionNames: actions.map((a) => a.name), actions, channel, diff --git a/backend/src/ee/models/action.ts b/backend/src/ee/models/action.ts index 3d48aa04d..7cdd7a355 100644 --- a/backend/src/ee/models/action.ts +++ b/backend/src/ee/models/action.ts @@ -1,10 +1,18 @@ import { Schema, model, Types } from 'mongoose'; +import { + ACTION_LOGIN, + ACTION_LOGOUT, + ACTION_ADD_SECRETS, + ACTION_UPDATE_SECRETS, + ACTION_READ_SECRETS, + ACTION_DELETE_SECRETS +} from '../../variables'; export interface IAction { name: string; user?: Types.ObjectId, workspace?: Types.ObjectId, - payload: { + payload?: { secretVersions?: Types.ObjectId[] } } @@ -13,7 +21,15 @@ const actionSchema = new Schema( { name: { type: String, - required: true + required: true, + enum: [ + ACTION_LOGIN, + ACTION_LOGOUT, + ACTION_ADD_SECRETS, + ACTION_UPDATE_SECRETS, + ACTION_READ_SECRETS, + ACTION_DELETE_SECRETS + ] }, user: { type: Schema.Types.ObjectId, diff --git a/backend/src/ee/models/log.ts b/backend/src/ee/models/log.ts index b9eadc016..47be2e58f 100644 --- a/backend/src/ee/models/log.ts +++ b/backend/src/ee/models/log.ts @@ -1,5 +1,7 @@ import { Schema, model, Types } from 'mongoose'; import { + ACTION_LOGIN, + ACTION_LOGOUT, ACTION_ADD_SECRETS, ACTION_UPDATE_SECRETS, ACTION_READ_SECRETS, @@ -29,6 +31,8 @@ const logSchema = new Schema( actionNames: { type: [String], enum: [ + ACTION_LOGIN, + ACTION_LOGOUT, ACTION_ADD_SECRETS, ACTION_UPDATE_SECRETS, ACTION_READ_SECRETS, diff --git a/backend/src/ee/services/EELogService.ts b/backend/src/ee/services/EELogService.ts index c1b2da6fb..bbe03e09e 100644 --- a/backend/src/ee/services/EELogService.ts +++ b/backend/src/ee/services/EELogService.ts @@ -1,14 +1,12 @@ import { Types } from 'mongoose'; import { - Log, - Action, IAction } from '../models'; import { createLogHelper } from '../helpers/log'; import { - createActionSecretHelper + createActionHelper } from '../helpers/action'; import EELicenseService from './EELicenseService'; @@ -33,8 +31,8 @@ class EELogService { channel, ipAddress }: { - userId: string; - workspaceId: string; + userId: Types.ObjectId; + workspaceId?: Types.ObjectId; actions: IAction[]; channel: string; ipAddress: string; @@ -50,26 +48,26 @@ class EELogService { } /** - * Create an (audit) action for secrets including - * add, delete, update, and read actions. + * Create an (audit) action * @param {Object} obj * @param {String} obj.name - name of action - * @param {ObjectId[]} obj.secretIds - secret ids + * @param {Types.ObjectId} obj.userId - id of user associated with the action + * @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the action + * @param {ObjectId[]} obj.secretIds - ids of secrets associated with the action * @returns {Action} action - new action */ - static async createActionSecret({ + static async createAction({ name, userId, workspaceId, secretIds }: { name: string; - userId: string; - workspaceId: string; - secretIds: Types.ObjectId[]; + userId: Types.ObjectId; + workspaceId?: Types.ObjectId; + secretIds?: Types.ObjectId[]; }) { - if (!EELicenseService.isLicenseValid) return null; - return await createActionSecretHelper({ + return await createActionHelper({ name, userId, workspaceId, diff --git a/backend/src/helpers/secret.ts b/backend/src/helpers/secret.ts index a16d114c6..9466cf7aa 100644 --- a/backend/src/helpers/secret.ts +++ b/backend/src/helpers/secret.ts @@ -406,10 +406,10 @@ const v2PushSecrets = async ({ secretIds: toDelete }); - const deleteAction = await EELogService.createActionSecret({ + const deleteAction = await EELogService.createAction({ name: ACTION_DELETE_SECRETS, - userId, - workspaceId, + userId: new Types.ObjectId(userId), + workspaceId: new Types.ObjectId(userId), secretIds: toDelete }); @@ -499,10 +499,10 @@ const v2PushSecrets = async ({ }) }); - const updateAction = await EELogService.createActionSecret({ + const updateAction = await EELogService.createAction({ name: ACTION_UPDATE_SECRETS, - userId, - workspaceId, + userId: new Types.ObjectId(userId), + workspaceId: new Types.ObjectId(workspaceId), secretIds: toUpdate.map((u) => u._id) }); @@ -536,10 +536,10 @@ const v2PushSecrets = async ({ }) }); - const addAction = await EELogService.createActionSecret({ + const addAction = await EELogService.createAction({ name: ACTION_ADD_SECRETS, - userId, - workspaceId, + userId: new Types.ObjectId(userId), + workspaceId: new Types.ObjectId(workspaceId), secretIds: newSecrets.map((n) => n._id) }); addAction && actions.push(addAction); @@ -553,8 +553,8 @@ const v2PushSecrets = async ({ // (EE) create (audit) log if (actions.length > 0) { await EELogService.createLog({ - userId, - workspaceId, + userId: new Types.ObjectId(userId), + workspaceId: new Types.ObjectId(workspaceId), actions, channel, ipAddress @@ -645,16 +645,16 @@ const pullSecrets = async ({ environment }) - const readAction = await EELogService.createActionSecret({ + const readAction = await EELogService.createAction({ name: ACTION_READ_SECRETS, - userId, - workspaceId, + userId: new Types.ObjectId(userId), + workspaceId: new Types.ObjectId(workspaceId), secretIds: secrets.map((n: any) => n._id) }); readAction && await EELogService.createLog({ - userId, - workspaceId, + userId: new Types.ObjectId(userId), + workspaceId: new Types.ObjectId(workspaceId), actions: [readAction], channel, ipAddress diff --git a/backend/src/models/LoginSRPDetail.ts b/backend/src/models/LoginSRPDetail.ts deleted file mode 100644 index f65e779c3..000000000 --- a/backend/src/models/LoginSRPDetail.ts +++ /dev/null @@ -1,23 +0,0 @@ -import mongoose, { Schema, model } from 'mongoose'; - -const LoginSRPDetailSchema = new Schema( - { - clientPublicKey: { - type: String, - required: true - }, - email: { - type: String, - required: true, - unique: true - }, - serverBInt: { type: mongoose.Schema.Types.Buffer }, - expireAt: { type: Date } - } -); - -const LoginSRPDetail = model('LoginSRPDetail', LoginSRPDetailSchema); - -// LoginSRPDetailSchema.index({ "expireAt": 1 }, { expireAfterSeconds: 0 }); - -export default LoginSRPDetail; diff --git a/backend/src/models/index.ts b/backend/src/models/index.ts index 72ffca607..ac5f102c0 100644 --- a/backend/src/models/index.ts +++ b/backend/src/models/index.ts @@ -16,6 +16,7 @@ import UserAction, { IUserAction } from './userAction'; import Workspace, { IWorkspace } from './workspace'; import ServiceTokenData, { IServiceTokenData } from './serviceTokenData'; import APIKeyData, { IAPIKeyData } from './apiKeyData'; +import LoginSRPDetail, { ILoginSRPDetail } from './loginSRPDetail'; export { BackupPrivateKey, @@ -53,5 +54,7 @@ export { ServiceTokenData, IServiceTokenData, APIKeyData, - IAPIKeyData + IAPIKeyData, + LoginSRPDetail, + ILoginSRPDetail }; diff --git a/backend/src/models/loginSRPDetail.ts b/backend/src/models/loginSRPDetail.ts new file mode 100644 index 000000000..6f3b579bd --- /dev/null +++ b/backend/src/models/loginSRPDetail.ts @@ -0,0 +1,29 @@ +import mongoose, { Schema, model, Types } from 'mongoose'; + +export interface ILoginSRPDetail { + _id: Types.ObjectId; + clientPublicKey: string; + email: string; + serverBInt: mongoose.Schema.Types.Buffer; + expireAt: Date; +} + +const loginSRPDetailSchema = new Schema( + { + clientPublicKey: { + type: String, + required: true + }, + email: { + type: String, + required: true, + unique: true + }, + serverBInt: { type: mongoose.Schema.Types.Buffer }, + expireAt: { type: Date } + } +); + +const LoginSRPDetail = model('LoginSRPDetail', loginSRPDetailSchema); + +export default LoginSRPDetail; diff --git a/backend/src/models/user.ts b/backend/src/models/user.ts index 7ea988c9d..ba9a59bc5 100644 --- a/backend/src/models/user.ts +++ b/backend/src/models/user.ts @@ -12,6 +12,7 @@ export interface IUser { salt?: string; verifier?: string; refreshVersion?: number; + seenIps: [string]; } const userSchema = new Schema( @@ -54,7 +55,8 @@ const userSchema = new Schema( type: Number, default: 0, select: false - } + }, + seenIps: [String] }, { timestamps: true diff --git a/backend/src/routes/v2/organizations.ts b/backend/src/routes/v2/organizations.ts index 1362a7759..e1488e8a7 100644 --- a/backend/src/routes/v2/organizations.ts +++ b/backend/src/routes/v2/organizations.ts @@ -30,7 +30,7 @@ router.patch( '/:organizationId/memberships/:membershipId', param('organizationId').exists().trim(), param('membershipId').exists().trim(), - body('role').exists().isString().trim().isIn([ADMIN, MEMBER]), + body('role').exists().isString().trim().isIn([OWNER, ADMIN, MEMBER]), validateRequest, requireAuth({ acceptedAuthModes: ['jwt', 'apiKey'] diff --git a/backend/src/variables/action.ts b/backend/src/variables/action.ts index 512eb8e8d..682b357a7 100644 --- a/backend/src/variables/action.ts +++ b/backend/src/variables/action.ts @@ -1,9 +1,13 @@ +const ACTION_LOGIN = 'login'; +const ACTION_LOGOUT = 'logout'; const ACTION_ADD_SECRETS = 'addSecrets'; const ACTION_DELETE_SECRETS = 'deleteSecrets'; const ACTION_UPDATE_SECRETS = 'updateSecrets'; const ACTION_READ_SECRETS = 'readSecrets'; export { + ACTION_LOGIN, + ACTION_LOGOUT, ACTION_ADD_SECRETS, ACTION_DELETE_SECRETS, ACTION_UPDATE_SECRETS, diff --git a/backend/src/variables/index.ts b/backend/src/variables/index.ts index dc1ce6f78..fafab6939 100644 --- a/backend/src/variables/index.ts +++ b/backend/src/variables/index.ts @@ -35,6 +35,8 @@ import { import { SECRET_SHARED, SECRET_PERSONAL } from './secret'; import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event'; import { + ACTION_LOGIN, + ACTION_LOGOUT, ACTION_ADD_SECRETS, ACTION_UPDATE_SECRETS, ACTION_DELETE_SECRETS, @@ -75,6 +77,8 @@ export { INTEGRATION_FLYIO_API_URL, EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS, + ACTION_LOGIN, + ACTION_LOGOUT, ACTION_ADD_SECRETS, ACTION_UPDATE_SECRETS, ACTION_DELETE_SECRETS, diff --git a/cli/main.go b/cli/main.go index 9b1e420fa..a51661ff6 100644 --- a/cli/main.go +++ b/cli/main.go @@ -1,5 +1,5 @@ /* -Copyright Β© 2022 NAME HERE +Copyright (c) 2023 Infisical Inc. */ package main diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index a414b03d3..7417a2594 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -5,6 +5,7 @@ import ( "github.com/Infisical/infisical-merge/packages/config" "github.com/go-resty/resty/v2" + log "github.com/sirupsen/logrus" ) const USER_AGENT = "cli" @@ -144,3 +145,24 @@ func CallGetAllWorkSpacesUserBelongsTo(httpClient *resty.Client) (GetWorkSpacesR return workSpacesResponse, nil } + +func CallIsAuthenticated(httpClient *resty.Client) bool { + var workSpacesResponse GetWorkSpacesResponse + response, err := httpClient. + R(). + SetResult(&workSpacesResponse). + SetHeader("User-Agent", USER_AGENT). + Post(fmt.Sprintf("%v/v1/auth/checkAuth", config.INFISICAL_URL)) + + log.Debugln(fmt.Errorf("CallIsAuthenticated: Unsuccessful response: [response=%v]", response)) + + if err != nil { + return false + } + + if response.IsError() { + return false + } + + return true +} diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go index 2a6fa0dbd..c2963f6bd 100644 --- a/cli/packages/cmd/export.go +++ b/cli/packages/cmd/export.go @@ -1,5 +1,5 @@ /* -Copyright Β© 2022 NAME HERE +Copyright (c) 2023 Infisical Inc. */ package cmd @@ -56,7 +56,12 @@ var exportCmd = &cobra.Command{ util.HandleError(err, "Unable to parse flag") } - secrets, err := util.GetAllEnvironmentVariables(envName) + infisicalToken, err := cmd.Flags().GetString("token") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: envName, InfisicalToken: infisicalToken}) if err != nil { util.HandleError(err, "Unable to fetch secrets") } @@ -91,6 +96,7 @@ func init() { exportCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") exportCmd.Flags().StringP("format", "f", "dotenv", "Set the format of the output file (dotenv, json, csv)") exportCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets") + exportCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token") } // Format according to the format flag diff --git a/cli/packages/cmd/init.go b/cli/packages/cmd/init.go index a9a2d943d..413f61440 100644 --- a/cli/packages/cmd/init.go +++ b/cli/packages/cmd/init.go @@ -1,5 +1,5 @@ /* -Copyright Β© 2022 NAME HERE +Copyright (c) 2023 Infisical Inc. */ package cmd diff --git a/cli/packages/cmd/login.go b/cli/packages/cmd/login.go index c10f38fc4..c37bdda08 100644 --- a/cli/packages/cmd/login.go +++ b/cli/packages/cmd/login.go @@ -1,5 +1,5 @@ /* -Copyright Β© 2022 NAME HERE +Copyright (c) 2023 Infisical Inc. */ package cmd @@ -33,13 +33,13 @@ var loginCmd = &cobra.Command{ PreRun: toggleDebug, Run: func(cmd *cobra.Command, args []string) { currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() - if err != nil && strings.Contains(err.Error(), "The specified item could not be found in the keyring") { // if the key can't be found allow them to override + if err != nil && (strings.Contains(err.Error(), "The specified item could not be found in the keyring") || strings.Contains(err.Error(), "unable to get key from Keyring")) { // if the key can't be found allow them to override log.Debug(err) } else if err != nil { util.HandleError(err) } - if currentLoggedInUserDetails.IsUserLoggedIn { + if currentLoggedInUserDetails.IsUserLoggedIn && !currentLoggedInUserDetails.LoginExpired { // if you are logged in but not expired shouldOverride, err := shouldOverrideLoginPrompt(currentLoggedInUserDetails.UserCredentials.Email) if err != nil { util.HandleError(err) @@ -101,6 +101,9 @@ var loginCmd = &cobra.Command{ util.HandleError(err, "Unable to write write to Infisical Config file. Please try again") } + // clear backed up secrets from prev account + util.DeleteBackupSecrets() + color.Green("Nice! You are logged in as: %v", email) }, diff --git a/cli/packages/cmd/man.go b/cli/packages/cmd/man.go index efe8686b3..86d68c8e7 100644 --- a/cli/packages/cmd/man.go +++ b/cli/packages/cmd/man.go @@ -1,5 +1,5 @@ /* -Copyright Β© 2022 NAME HERE +Copyright (c) 2023 Infisical Inc. */ package cmd diff --git a/cli/packages/cmd/root.go b/cli/packages/cmd/root.go index 93a498860..ac827feb3 100644 --- a/cli/packages/cmd/root.go +++ b/cli/packages/cmd/root.go @@ -1,5 +1,5 @@ /* -Copyright Β© 2022 NAME HERE +Copyright (c) 2023 Infisical Inc. */ package cmd diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go index 4c7c9782f..bd1be4300 100644 --- a/cli/packages/cmd/run.go +++ b/cli/packages/cmd/run.go @@ -1,5 +1,5 @@ /* -Copyright Β© 2022 NAME HERE +Copyright (c) 2023 Infisical Inc. */ package cmd @@ -12,6 +12,7 @@ import ( "strings" "syscall" + "github.com/Infisical/infisical-merge/packages/models" "github.com/Infisical/infisical-merge/packages/util" "github.com/fatih/color" log "github.com/sirupsen/logrus" @@ -58,10 +59,15 @@ var runCmd = &cobra.Command{ util.HandleError(err, "Unable to parse flag") } - if !util.IsSecretEnvironmentValid(envName) { - util.PrintMessageAndExit("Invalid environment name passed. Environment names can only be prod, dev, test or staging") + infisicalToken, err := cmd.Flags().GetString("token") + if err != nil { + util.HandleError(err, "Unable to parse flag") } + // if !util.IsSecretEnvironmentValid(envName) { + // util.PrintMessageAndExit("Invalid environment name passed. Environment names can only be prod, dev, test or staging") + // } + secretOverriding, err := cmd.Flags().GetBool("secret-overriding") if err != nil { util.HandleError(err, "Unable to parse flag") @@ -72,7 +78,8 @@ var runCmd = &cobra.Command{ util.HandleError(err, "Unable to parse flag") } - secrets, err := util.GetAllEnvironmentVariables(envName) + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: envName, InfisicalToken: infisicalToken}) + if err != nil { util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid") } @@ -140,6 +147,7 @@ var runCmd = &cobra.Command{ func init() { rootCmd.AddCommand(runCmd) + runCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token") runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from") runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") runCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets") diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index 912801f5d..c5fb11c1e 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -1,5 +1,5 @@ /* -Copyright Β© 2022 NAME HERE +Copyright (c) 2023 Infisical Inc. */ package cmd @@ -34,12 +34,17 @@ var secretsCmd = &cobra.Command{ util.HandleError(err) } + infisicalToken, err := cmd.Flags().GetString("token") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + shouldExpandSecrets, err := cmd.Flags().GetBool("expand") if err != nil { util.HandleError(err) } - secrets, err := util.GetAllEnvironmentVariables(environmentName) + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken}) if err != nil { util.HandleError(err) } @@ -111,7 +116,7 @@ var secretsSetCmd = &cobra.Command{ plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey) // pull current secrets - secrets, err := util.GetAllEnvironmentVariables(environmentName) + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName}) if err != nil { util.HandleError(err, "unable to retrieve secrets") } @@ -267,7 +272,7 @@ var secretsDeleteCmd = &cobra.Command{ util.HandleError(err, "Unable to get local project details") } - secrets, err := util.GetAllEnvironmentVariables(environmentName) + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName}) if err != nil { util.HandleError(err, "Unable to fetch secrets") } @@ -309,30 +314,6 @@ var secretsDeleteCmd = &cobra.Command{ }, } -func init() { - secretsCmd.AddCommand(secretsGetCmd) - secretsGetCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { - util.RequireLogin() - util.RequireLocalWorkspaceFile() - } - - secretsCmd.AddCommand(secretsSetCmd) - secretsSetCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { - util.RequireLogin() - util.RequireLocalWorkspaceFile() - } - - secretsCmd.AddCommand(secretsDeleteCmd) - secretsDeleteCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { - util.RequireLogin() - util.RequireLocalWorkspaceFile() - } - - secretsCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on") - secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") - rootCmd.AddCommand(secretsCmd) -} - func getSecretsByNames(cmd *cobra.Command, args []string) { environmentName, err := cmd.Flags().GetString("env") if err != nil { @@ -344,7 +325,12 @@ func getSecretsByNames(cmd *cobra.Command, args []string) { util.HandleError(err, "Unable to parse flag") } - secrets, err := util.GetAllEnvironmentVariables(environmentName) + infisicalToken, err := cmd.Flags().GetString("token") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken}) if err != nil { util.HandleError(err, "To fetch all secrets") } @@ -380,3 +366,25 @@ func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]mod return secretMapByName } + +func init() { + secretsGetCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token") + secretsCmd.AddCommand(secretsGetCmd) + + secretsCmd.AddCommand(secretsSetCmd) + secretsSetCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { + util.RequireLogin() + util.RequireLocalWorkspaceFile() + } + + secretsCmd.AddCommand(secretsDeleteCmd) + secretsDeleteCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { + util.RequireLogin() + util.RequireLocalWorkspaceFile() + } + + secretsCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token") + secretsCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on") + secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets") + rootCmd.AddCommand(secretsCmd) +} diff --git a/cli/packages/cmd/vault.go b/cli/packages/cmd/vault.go index a2b03dbec..d9e32769e 100644 --- a/cli/packages/cmd/vault.go +++ b/cli/packages/cmd/vault.go @@ -1,5 +1,5 @@ /* -Copyright Β© 2022 NAME HERE +Copyright (c) 2023 Infisical Inc. */ package cmd diff --git a/cli/packages/models/cli.go b/cli/packages/models/cli.go index 7bc31cab2..67b89b27f 100644 --- a/cli/packages/models/cli.go +++ b/cli/packages/models/cli.go @@ -34,7 +34,12 @@ type WorkspaceConfigFile struct { } type SymmetricEncryptionResult struct { - CipherText []byte - Nonce []byte - AuthTag []byte + CipherText []byte `json:"CipherText"` + Nonce []byte `json:"Nonce"` + AuthTag []byte `json:"AuthTag"` +} + +type GetAllSecretsParameters struct { + Environment string + InfisicalToken string } diff --git a/cli/packages/util/check-for-update.go b/cli/packages/util/check-for-update.go index 5fbf39a1e..a23b90ad1 100644 --- a/cli/packages/util/check-for-update.go +++ b/cli/packages/util/check-for-update.go @@ -2,10 +2,10 @@ package util import ( "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" - "errors" ) func CheckForUpdate() { @@ -42,5 +42,5 @@ func getLatestTag(repoOwner string, repoName string) (string, error) { json.Unmarshal(body, &tags) - return tags[0].Name, nil + return tags[0].Name[1:], nil } diff --git a/cli/packages/util/common.go b/cli/packages/util/common.go index 2d420ac6f..13e76046d 100644 --- a/cli/packages/util/common.go +++ b/cli/packages/util/common.go @@ -2,6 +2,7 @@ package util import ( "fmt" + "net/http" "os" ) @@ -19,3 +20,11 @@ func WriteToFile(fileName string, dataToWrite []byte, filePerm os.FileMode) erro return nil } + +func CheckIsConnectedToInternet() (ok bool) { + _, err := http.Get("http://clients3.google.com/generate_204") + if err != nil { + return false + } + return true +} diff --git a/cli/packages/util/constants.go b/cli/packages/util/constants.go index 1243fb325..5c169f6d5 100644 --- a/cli/packages/util/constants.go +++ b/cli/packages/util/constants.go @@ -11,5 +11,8 @@ const ( KEYRING_SERVICE_NAME = "infisical" PERSONAL_SECRET_TYPE_NAME = "personal" SHARED_SECRET_TYPE_NAME = "shared" - CLI_VERSION = "v0.2.8" +) + +var ( + CLI_VERSION = "devel" ) diff --git a/cli/packages/util/credentials.go b/cli/packages/util/credentials.go index 6410002c0..1766f04c7 100644 --- a/cli/packages/util/credentials.go +++ b/cli/packages/util/credentials.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/99designs/keyring" - "github.com/Infisical/infisical-merge/packages/config" + "github.com/Infisical/infisical-merge/packages/api" "github.com/Infisical/infisical-merge/packages/models" "github.com/go-resty/resty/v2" ) @@ -87,17 +87,10 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) { SetAuthToken(userCreds.JTWToken). SetHeader("Accept", "application/json") - response, err := httpClient. - R(). - Post(fmt.Sprintf("%v/v1/auth/checkAuth", config.INFISICAL_URL)) - - if err != nil { - return LoggedInUserDetails{}, err - } - - if response.StatusCode() > 299 { + isAuthenticated := api.CallIsAuthenticated(httpClient) + if !isAuthenticated { return LoggedInUserDetails{ - IsUserLoggedIn: true, + IsUserLoggedIn: true, // was logged in LoginExpired: true, UserCredentials: userCreds, }, nil diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index 1cf8a7ab5..86f4a5e05 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -2,6 +2,8 @@ package util import ( "encoding/base64" + "encoding/json" + "errors" "fmt" "os" "regexp" @@ -97,13 +99,26 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work return plainTextSecrets, nil } -func GetAllEnvironmentVariables(envName string) ([]models.SingleEnvironmentVariable, error) { - infisicalToken := os.Getenv(INFISICAL_TOKEN_NAME) +func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models.SingleEnvironmentVariable, error) { + var infisicalToken string + if params.InfisicalToken == "" { + infisicalToken = os.Getenv(INFISICAL_TOKEN_NAME) + } else { + infisicalToken = params.InfisicalToken + } + + isConnected := CheckIsConnectedToInternet() + var secretsToReturn []models.SingleEnvironmentVariable + var errorToReturn error if infisicalToken == "" { - RequireLocalWorkspaceFile() - RequireLogin() - log.Debug("Trying to fetch secrets using logged in details") + if isConnected { + log.Debug("GetAllEnvironmentVariables: Connected to internet, checking logged in creds") + RequireLocalWorkspaceFile() + RequireLogin() + } + + log.Debug("GetAllEnvironmentVariables: Trying to fetch secrets using logged in details") loggedInUserDetails, err := GetCurrentLoggedInUserDetails() if err != nil { @@ -115,13 +130,30 @@ func GetAllEnvironmentVariables(envName string) ([]models.SingleEnvironmentVaria return nil, err } - secrets, err := GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, envName) - return secrets, err + secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, params.Environment) + log.Debugf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn) + + backupSecretsEncryptionKey := []byte(loggedInUserDetails.UserCredentials.PrivateKey)[0:32] + if errorToReturn == nil { + WriteBackupSecrets(workspaceFile.WorkspaceId, params.Environment, backupSecretsEncryptionKey, secretsToReturn) + } + + // only attempt to serve cached secrets if no internet connection and if at least one secret cached + if !isConnected { + backedSecrets, err := ReadBackupSecrets(workspaceFile.WorkspaceId, params.Environment, backupSecretsEncryptionKey) + if len(backedSecrets) > 0 { + PrintWarning("Unable to fetch latest secret(s) due to connection error, serving secrets from last successful fetch. For more info, run with --debug") + secretsToReturn = backedSecrets + errorToReturn = err + } + } } else { log.Debug("Trying to fetch secrets using service token") - return GetPlainTextSecretsViaServiceToken(infisicalToken) + secretsToReturn, errorToReturn = GetPlainTextSecretsViaServiceToken(infisicalToken) } + + return secretsToReturn, errorToReturn } func getExpandedEnvVariable(secrets []models.SingleEnvironmentVariable, variableWeAreLookingFor string, hashMapOfCompleteVariables map[string]string, hashMapOfSelfRefs map[string]string) string { @@ -295,3 +327,100 @@ func GetPlainTextSecrets(key []byte, encryptedSecrets api.GetEncryptedSecretsV2R return plainTextSecrets, nil } + +func WriteBackupSecrets(workspace string, environment string, encryptionKey []byte, secrets []models.SingleEnvironmentVariable) error { + fileName := fmt.Sprintf("secrets_%s_%s", workspace, environment) + secrets_backup_folder_name := "secrets-backup" + + _, fullConfigFileDirPath, err := GetFullConfigFilePath() + if err != nil { + return fmt.Errorf("WriteBackupSecrets: unable to get full config folder path [err=%s]", err) + } + + // create secrets backup directory + fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name) + if _, err := os.Stat(fullPathToSecretsBackupFolder); errors.Is(err, os.ErrNotExist) { + err := os.Mkdir(fullPathToSecretsBackupFolder, os.ModePerm) + if err != nil { + return err + } + } + + var encryptedSecrets []models.SymmetricEncryptionResult + for _, secret := range secrets { + marshaledSecrets, _ := json.Marshal(secret) + result, err := crypto.EncryptSymmetric(marshaledSecrets, encryptionKey) + if err != nil { + return err + } + + encryptedSecrets = append(encryptedSecrets, result) + } + + listOfSecretsMarshalled, _ := json.Marshal(encryptedSecrets) + err = os.WriteFile(fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName), listOfSecretsMarshalled, os.ModePerm) + if err != nil { + return fmt.Errorf("WriteBackupSecrets: Unable to write backup secrets to file [err=%s]", err) + } + + return nil +} + +func ReadBackupSecrets(workspace string, environment string, encryptionKey []byte) ([]models.SingleEnvironmentVariable, error) { + fileName := fmt.Sprintf("secrets_%s_%s", workspace, environment) + secrets_backup_folder_name := "secrets-backup" + + _, fullConfigFileDirPath, err := GetFullConfigFilePath() + if err != nil { + return nil, fmt.Errorf("ReadBackupSecrets: unable to write config file because an error occurred when getting config file path [err=%s]", err) + } + + fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name) + if _, err := os.Stat(fullPathToSecretsBackupFolder); errors.Is(err, os.ErrNotExist) { + return nil, nil + } + + encryptedBackupSecretsFilePath := fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName) + + encryptedBackupSecretsAsBytes, err := os.ReadFile(encryptedBackupSecretsFilePath) + if err != nil { + return nil, err + } + + var listOfEncryptedBackupSecrets []models.SymmetricEncryptionResult + + _ = json.Unmarshal(encryptedBackupSecretsAsBytes, &listOfEncryptedBackupSecrets) + + var plainTextSecrets []models.SingleEnvironmentVariable + for _, encryptedSecret := range listOfEncryptedBackupSecrets { + result, err := crypto.DecryptSymmetric(encryptionKey, encryptedSecret.CipherText, encryptedSecret.AuthTag, encryptedSecret.Nonce) + if err != nil { + return nil, err + } + + var plainTextSecret models.SingleEnvironmentVariable + + err = json.Unmarshal(result, &plainTextSecret) + if err != nil { + return nil, err + } + + plainTextSecrets = append(plainTextSecrets, plainTextSecret) + } + + return plainTextSecrets, nil + +} + +func DeleteBackupSecrets() error { + secrets_backup_folder_name := "secrets-backup" + + _, fullConfigFileDirPath, err := GetFullConfigFilePath() + if err != nil { + return fmt.Errorf("ReadBackupSecrets: unable to write config file because an error occurred when getting config file path [err=%s]", err) + } + + fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name) + + return os.RemoveAll(fullPathToSecretsBackupFolder) +} diff --git a/cli/packages/util/vault.go b/cli/packages/util/vault.go index 7e4ce1b31..0852e5f48 100644 --- a/cli/packages/util/vault.go +++ b/cli/packages/util/vault.go @@ -57,7 +57,7 @@ func fileKeyringPassphrasePrompt(prompt string) (string, error) { if password, ok := os.LookupEnv("INFISICAL_VAULT_FILE_PASSPHRASE"); ok { return password, nil } else { - fmt.Println("You may set the `INFISICAL_VAULT_FILE_PASSPHRASE` environment variable to avoid typing password") + fmt.Println("You may set the environment variable `INFISICAL_VAULT_FILE_PASSPHRASE` with your password to avoid typing it") } fmt.Fprintf(os.Stderr, "%s:", prompt) @@ -65,6 +65,7 @@ func fileKeyringPassphrasePrompt(prompt string) (string, error) { if err != nil { return "", err } + fmt.Println("") return string(b), nil } diff --git a/docs/cli/faq.mdx b/docs/cli/faq.mdx index 6a98d8dc3..7eb73258e 100644 --- a/docs/cli/faq.mdx +++ b/docs/cli/faq.mdx @@ -13,4 +13,9 @@ If none of the available stores work for you, you can try using the `file` store If you are still experiencing trouble, please seek support. [Learn more about vault command](./commands/vault) + + + +Yes. If you have previously retrieved secrets for a specific project and environment (such as dev, staging, or prod), the `run`/`secret` command will utilize the saved secrets, even when offline, on subsequent fetch attempts. + \ No newline at end of file diff --git a/docs/integrations/overview.mdx b/docs/integrations/overview.mdx index 4f1e99227..36423e787 100644 --- a/docs/integrations/overview.mdx +++ b/docs/integrations/overview.mdx @@ -12,6 +12,7 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi | [Docker](/integrations/platforms/docker) | Platform | Available | | [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available | | [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available | +| [PM2](/integrations/platforms/pm2) | Platform | Available | | [Heroku](/integrations/cloud/heroku) | Cloud | Available | | [Vercel](/integrations/cloud/vercel) | Cloud | Available | | [Netlify](/integrations/cloud/netlify) | Cloud | Available | diff --git a/docs/integrations/platforms/docker-compose.mdx b/docs/integrations/platforms/docker-compose.mdx index 698316582..e030869e3 100644 --- a/docs/integrations/platforms/docker-compose.mdx +++ b/docs/integrations/platforms/docker-compose.mdx @@ -1,45 +1,38 @@ --- title: "Docker Compose" -description: "How to use Infisical to inject environment variables into container defined in your Docker Compose file." +description: "How to use Infisical to inject environment variables into services defined in your Docker Compose file." --- -The Docker Compose integration enables you to inject environment variables from Infisical into the containers defined in your compose file. +Prerequisites: -## Add the CLI to your Dockerfile(s) start command +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) -Follow the [guide to configure Infisical CLI](./docker) in your your Dockerfile first. +## Configure the Infisical CLI for each service -## Generate Infisical Token +Follow this [guide](./docker) to configure the Infisical CLI for each service that you wish to inject environment variables into; you'll have to update the Dockerfile of each service. -In order for Infisical CLI to authenticate and retrieve your project's secrets without exposing your login credentials, you must generate a Infisical Token. -To learn how, visit [Infisical Token](../../getting-started/dashboard/token). Once you have generated the token, keep it handy. +## Generate Infisical Tokens - - If you have multiple services and they do not use the same secrets, you will - have to generate a Infisical Token for each service. - +Generate a unique [Infisical Token](https://infisical.com/docs/getting-started/dashboard/token) for each service. -## Tell Docker Compose your Infisical Token +## Add Infisical Tokens to your Docker Compose file -For each service you want to inject secrets into, set an environment variable called `INFISICAL_TOKEN` equal to a helpful identifier variable. -This will ensure that you can set Infisical Tokens for multiple services. +For each service you want to inject secrets into, set an environment variable called `INFISICAL_TOKEN` equal to a unique identifier variable. -For the example below, we have set `INFISICAL_TOKEN_FOR_WEB` and `INFISICAL_TOKEN_FOR_API` as the `INFISICAL_TOKEN` for the corresponding service. +In the example below, we set `INFISICAL_TOKEN_FOR_WEB` and `INFISICAL_TOKEN_FOR_API` as the `INFISICAL_TOKEN` for the services. ```yaml # Example Docker Compose file services: web: build: . - image: auledge-frontend - container_name: auledge-frontend + image: example-service-1 environment: - INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_WEB} api: build: . - image: auledge-backend - container_name: auledge-backend + image: example-service-2 environment: - INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_API} ``` diff --git a/docs/integrations/platforms/docker.mdx b/docs/integrations/platforms/docker.mdx index 8f53336ed..b4224d6d0 100644 --- a/docs/integrations/platforms/docker.mdx +++ b/docs/integrations/platforms/docker.mdx @@ -3,9 +3,11 @@ title: "Docker" description: "How to use Infisical to inject environment variables into a Docker container." --- -Infisical can be used in a Dockerfile to inject environment variables into a Docker container. +Prerequisites: -## Add the CLI to your Dockerfile +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) + +## Add the Infisical CLI to your Dockerfile @@ -32,32 +34,32 @@ Infisical can be used in a Dockerfile to inject environment variables into a Doc - ## Modify your Dockerfile start command +## Modify the start command in your Dockerfile - To make your Docker container consume Infisical secrets, you can start your application with Infisical. - This will automatically pull the necessary secrets and make them available to your application as if they were natively exposed within the container. +Starting your service with the Infisical CLI pulls your secrets from Infisical and injects them into your service. - ```dockerfile - CMD ["infisical", "run", "---", "[your application start command]"] +```dockerfile +CMD ["infisical", "run", "---", "[your service start command]"] - # example with single single command - CMD ["infisical", "run", "---", "npm run start"] +# example with single single command +CMD ["infisical", "run", "---", "npm run start"] - # example with multiple commands - CMD ["infisical", "run", "--command", "npm run start && ..."] - ``` +# example with multiple commands +CMD ["infisical", "run", "--command", "npm run start && ..."] +``` - View more options for the `run` command [here](../../cli/commands/run) +## Generate an Infisical Token + +Head to your project settings in Infisical Cloud to generate an [Infisical Token](https://infisical.com/docs/getting-started/dashboard/token). ## Feed Docker your Infisical Token -The CLI looks out for an environment variable called `INFISICAL_TOKEN`. If the token is detected, the CLI will authenticate, retrieve, and inject the environment variables which the token is authorized for. - ```bash docker run --env INFISICAL_TOKEN=[token]... ``` -## Generate an Infisical Token + -[Generate an Infisical Token](../../getting-started/dashboard/token) and keep it handy. +The Infisical CLI uses the detected `INFISICAL_TOKEN` environment variable to authenticate, retrieve, and inject the environment variables which the token is authorized for. + diff --git a/docs/integrations/platforms/pm2.mdx b/docs/integrations/platforms/pm2.mdx new file mode 100644 index 000000000..e9b04bbb2 --- /dev/null +++ b/docs/integrations/platforms/pm2.mdx @@ -0,0 +1,52 @@ +--- +title: "PM2" +description: "How to use Infisical to inject environment variables and secrets with PM2 into a Node.js app" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) +- [Install the CLI](/cli/overview) + +## Initialize Infisical for your Node.js app + +```bash +# navigate to the root of your of your project +cd /path/to/project + +# then initialize infisical +infisical init +``` + +## Create a bash or js script + + + +```bash infisical-run.sh +infisical run -- npm start +``` + +```js infisical-run.js +const spawn = require("child_process").spawn; + +const infisical = spawn("infisical", ["run", "--", "npm", "start"]); + +infisical.stdout.on("data", (data) => console.log(`${data}`)); +infisical.stderr.on("data", (data) => console.error(`${data}`)); +``` + + + +## Start your application as usual but with the script + + + +```bash infisical-run.sh +pm2 start infisical-run.sh +``` + +```bash infisical-run.js +pm2 start infisical-run.js +``` + + diff --git a/docs/mint.json b/docs/mint.json index 4ce1d6c03..441769152 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -209,7 +209,8 @@ "pages": [ "integrations/platforms/docker", "integrations/platforms/docker-compose", - "integrations/platforms/kubernetes" + "integrations/platforms/kubernetes", + "integrations/platforms/pm2" ] }, { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f5bb934fe..283c69b53 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,6 +14,7 @@ "@fortawesome/free-solid-svg-icons": "^6.1.2", "@fortawesome/react-fontawesome": "^0.1.19", "@headlessui/react": "^1.6.6", + "@hookform/resolvers": "^2.9.10", "@radix-ui/react-accordion": "^1.1.0", "@radix-ui/react-alert-dialog": "^1.0.2", "@radix-ui/react-checkbox": "^1.0.1", @@ -51,6 +52,7 @@ "react-code-input": "^3.10.1", "react-dom": "^17.0.2", "react-grid-layout": "^1.3.4", + "react-hook-form": "^7.43.0", "react-i18next": "^12.1.1", "react-mailchimp-subscribe": "^2.1.3", "react-markdown": "^8.0.3", @@ -64,7 +66,8 @@ "tweetnacl-util": "^0.15.1", "uuid": "^8.3.2", "uuidv4": "^6.2.13", - "yaml": "^2.2.0" + "yaml": "^2.2.0", + "yup": "^0.32.11" }, "devDependencies": { "@storybook/addon-essentials": "^7.0.0-beta.30", @@ -108,7 +111,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -121,7 +123,6 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.0", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -157,7 +158,6 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -166,7 +166,6 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", - "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -196,7 +195,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -212,14 +210,12 @@ "node_modules/@babel/core/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -265,7 +261,6 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", - "dev": true, "dependencies": { "@babel/compat-data": "^7.20.5", "@babel/helper-validator-option": "^7.18.6", @@ -284,7 +279,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "dependencies": { "yallist": "^3.0.2" } @@ -293,7 +287,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, "bin": { "semver": "bin/semver.js" } @@ -301,8 +294,7 @@ "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.20.12", @@ -461,7 +453,6 @@ "version": "7.20.11", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", - "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", @@ -535,7 +526,6 @@ "version": "7.20.2", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, "dependencies": { "@babel/types": "^7.20.2" }, @@ -586,7 +576,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -610,7 +599,6 @@ "version": "7.20.6", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", - "dev": true, "dependencies": { "@babel/template": "^7.18.10", "@babel/traverse": "^7.20.5", @@ -2897,6 +2885,14 @@ "react-dom": "^16 || ^17 || ^18" } }, + "node_modules/@hookform/resolvers": { + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.9.10.tgz", + "integrity": "sha512-JIL1DgJIlH9yuxcNGtyhsWX/PgNltz+5Gr6+8SX9fhXc/hPbEIk6wPI82nhgvp3uUb6ZfAM5mqg/x7KR7NAb+A==", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -7005,8 +7001,7 @@ "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 + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==" }, "node_modules/@types/mdast": { "version": "3.0.10", @@ -8997,7 +8992,6 @@ "version": "4.21.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -10680,8 +10674,7 @@ "node_modules/electron-to-chromium": { "version": "1.4.284", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", - "dev": true + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -10977,7 +10970,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -12818,7 +12810,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -14991,7 +14982,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -15210,6 +15200,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", @@ -16183,6 +16178,11 @@ "object-assign": "^4.1.0" } }, + "node_modules/nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "node_modules/nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -16464,8 +16464,7 @@ "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, "node_modules/normalize-package-data": { "version": "2.5.0", @@ -17625,6 +17624,11 @@ "react-is": "^16.13.1" } }, + "node_modules/property-expr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", + "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" + }, "node_modules/property-information": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.1.1.tgz", @@ -18142,6 +18146,21 @@ "react-dom": ">= 16.3.0" } }, + "node_modules/react-hook-form": { + "version": "7.43.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.0.tgz", + "integrity": "sha512-/rVEz7T0gLdSFwPqutJ1kn2e0sQNyb9ci/hmwEYr2YG0KF/LSuRLvNrf9QWJM+gj88CjDpDW5Bh/1AD7B2+z9Q==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/react-i18next": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", @@ -21168,6 +21187,11 @@ "node": ">=0.6" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -21404,7 +21428,7 @@ "version": "4.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21730,7 +21754,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -22480,6 +22503,23 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + }, + "engines": { + "node": ">=10" + } } }, "dependencies": { @@ -22487,7 +22527,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -22497,7 +22536,6 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, "requires": { "@jridgewell/set-array": "^1.0.0", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -22525,14 +22563,12 @@ "@babel/compat-data": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", - "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", - "dev": true + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==" }, "@babel/core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", - "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -22555,7 +22591,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -22563,14 +22598,12 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -22606,7 +22639,6 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", - "dev": true, "requires": { "@babel/compat-data": "^7.20.5", "@babel/helper-validator-option": "^7.18.6", @@ -22619,7 +22651,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "requires": { "yallist": "^3.0.2" } @@ -22627,14 +22658,12 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } }, @@ -22753,7 +22782,6 @@ "version": "7.20.11", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", - "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", @@ -22809,7 +22837,6 @@ "version": "7.20.2", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, "requires": { "@babel/types": "^7.20.2" } @@ -22844,8 +22871,7 @@ "@babel/helper-validator-option": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" }, "@babel/helper-wrap-function": { "version": "7.20.5", @@ -22863,7 +22889,6 @@ "version": "7.20.6", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", - "dev": true, "requires": { "@babel/template": "^7.18.10", "@babel/traverse": "^7.20.5", @@ -24061,7 +24086,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", - "dev": true + "dev": true, + "requires": {} }, "@emotion/utils": { "version": "1.2.0", @@ -24366,7 +24392,14 @@ "@headlessui/react": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.6.6.tgz", - "integrity": "sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==" + "integrity": "sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==", + "requires": {} + }, + "@hookform/resolvers": { + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.9.10.tgz", + "integrity": "sha512-JIL1DgJIlH9yuxcNGtyhsWX/PgNltz+5Gr6+8SX9fhXc/hPbEIk6wPI82nhgvp3uUb6ZfAM5mqg/x7KR7NAb+A==", + "requires": {} }, "@humanwhocodes/config-array": { "version": "0.11.8", @@ -27340,8 +27373,7 @@ "@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 + "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==" }, "@types/mdast": { "version": "3.0.10", @@ -28016,7 +28048,8 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "acorn-node": { "version": "1.8.2", @@ -28150,7 +28183,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "ansi-align": { "version": "3.0.1", @@ -28444,7 +28478,8 @@ "axios-auth-refresh": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.3.tgz", - "integrity": "sha512-2IbDhJ/h6ddNBBnnzn1VFK/qx17pE9aVqiafB8rx5LVHsJ1HtFpUGkbXY7PzTG+8P9HJWcyA3fNZl9BikSuilg==" + "integrity": "sha512-2IbDhJ/h6ddNBBnnzn1VFK/qx17pE9aVqiafB8rx5LVHsJ1HtFpUGkbXY7PzTG+8P9HJWcyA3fNZl9BikSuilg==", + "requires": {} }, "axobject-query": { "version": "3.1.1", @@ -28459,7 +28494,8 @@ "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", - "dev": true + "dev": true, + "requires": {} }, "babel-loader": { "version": "9.1.2", @@ -28838,7 +28874,6 @@ "version": "4.21.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "dev": true, "requires": { "caniuse-lite": "^1.0.30001400", "electron-to-chromium": "^1.4.251", @@ -29668,7 +29703,8 @@ "cva": { "version": "npm:class-variance-authority@0.4.0", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.4.0.tgz", - "integrity": "sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ==" + "integrity": "sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ==", + "requires": {} }, "damerau-levenshtein": { "version": "1.0.8", @@ -30121,8 +30157,7 @@ "electron-to-chromium": { "version": "1.4.284", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", - "dev": true + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, "emoji-regex": { "version": "9.2.2", @@ -30368,8 +30403,7 @@ "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-html": { "version": "1.0.3", @@ -30632,7 +30666,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", - "dev": true + "dev": true, + "requires": {} }, "eslint-import-resolver-node": { "version": "0.3.7", @@ -30890,13 +30925,15 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-simple-import-sort": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz", "integrity": "sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-storybook": { "version": "0.6.10", @@ -31760,8 +31797,7 @@ "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, "get-caller-file": { "version": "2.0.5", @@ -32325,7 +32361,8 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true + "dev": true, + "requires": {} }, "ieee754": { "version": "1.2.1", @@ -33339,8 +33376,7 @@ "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonfile": { "version": "6.1.0", @@ -33508,6 +33544,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", @@ -33638,7 +33679,8 @@ "version": "7.1.8", "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.8.tgz", "integrity": "sha512-rRSa1aFmFnpDRFAhv5vIkWM4nPaoB9vnzIjuIKa1wGupfn2hdCNeaQHKpu4/muoc8n8J7yowjTP2oncA4/Rbgg==", - "dev": true + "dev": true, + "requires": {} }, "md5.js": { "version": "1.3.5", @@ -34155,6 +34197,11 @@ "object-assign": "^4.1.0" } }, + "nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -34344,8 +34391,7 @@ "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, "normalize-package-data": { "version": "2.5.0", @@ -34935,7 +34981,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -35044,7 +35091,8 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.2.tgz", "integrity": "sha512-5RjUbWRe305pUpc48MosoIp6uxZvZxrM6GyOgsbGLTce+ehePKNm7ziW2dLG2air9aXbGuXlHVSQQw4Lbosq3w==", - "dev": true + "dev": true, + "requires": {} }, "pretty-error": { "version": "4.0.0", @@ -35132,6 +35180,11 @@ "react-is": "^16.13.1" } }, + "property-expr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", + "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" + }, "property-information": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.1.1.tgz", @@ -35437,7 +35490,8 @@ "version": "5.6.1", "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", - "dev": true + "dev": true, + "requires": {} }, "react-docgen": { "version": "5.4.3", @@ -35469,7 +35523,8 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", - "dev": true + "dev": true, + "requires": {} }, "react-dom": { "version": "17.0.2", @@ -35521,6 +35576,12 @@ "react-resizable": "^3.0.4" } }, + "react-hook-form": { + "version": "7.43.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.0.tgz", + "integrity": "sha512-/rVEz7T0gLdSFwPqutJ1kn2e0sQNyb9ci/hmwEYr2YG0KF/LSuRLvNrf9QWJM+gj88CjDpDW5Bh/1AD7B2+z9Q==", + "requires": {} + }, "react-i18next": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", @@ -35534,7 +35595,8 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.1.tgz", "integrity": "sha512-cxKSeFTf7jpSSVddm66sKdolG90qURAX3g1roTeaN6x0YEbtWc8JpmFN9+yIqLNH2uEkYerWLtJZIXRIFuBKrg==", - "dev": true + "dev": true, + "requires": {} }, "react-is": { "version": "16.13.1", @@ -35649,7 +35711,8 @@ "react-table": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", - "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==" + "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", + "requires": {} }, "read-cache": { "version": "1.0.0", @@ -35805,7 +35868,8 @@ "redux-thunk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", - "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==" + "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", + "requires": {} }, "regenerate": { "version": "1.4.2", @@ -37258,7 +37322,8 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "dev": true + "dev": true, + "requires": {} }, "style-to-object": { "version": "0.3.0", @@ -37288,7 +37353,8 @@ "styled-jsx": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", - "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==" + "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==", + "requires": {} }, "stylis": { "version": "4.0.13", @@ -37750,6 +37816,11 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -37928,7 +37999,7 @@ "version": "4.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", - "dev": true + "devOptional": true }, "uc.micro": { "version": "1.0.6", @@ -38164,7 +38235,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -38202,12 +38272,14 @@ "use-isomorphic-layout-effect": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "requires": {} }, "use-memo-one": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", - "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==" + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "requires": {} }, "use-sidecar": { "version": "1.1.2", @@ -38221,7 +38293,8 @@ "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} }, "util": { "version": "0.12.5", @@ -38412,7 +38485,8 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true + "dev": true, + "requires": {} }, "eslint-scope": { "version": "5.1.1", @@ -38638,7 +38712,8 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", - "dev": true + "dev": true, + "requires": {} }, "xtend": { "version": "4.0.2", @@ -38698,6 +38773,20 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + } } } } diff --git a/frontend/package.json b/frontend/package.json index f97edd68f..322e60f31 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "@fortawesome/free-solid-svg-icons": "^6.1.2", "@fortawesome/react-fontawesome": "^0.1.19", "@headlessui/react": "^1.6.6", + "@hookform/resolvers": "^2.9.10", "@radix-ui/react-accordion": "^1.1.0", "@radix-ui/react-alert-dialog": "^1.0.2", "@radix-ui/react-checkbox": "^1.0.1", @@ -58,6 +59,7 @@ "react-code-input": "^3.10.1", "react-dom": "^17.0.2", "react-grid-layout": "^1.3.4", + "react-hook-form": "^7.43.0", "react-i18next": "^12.1.1", "react-mailchimp-subscribe": "^2.1.3", "react-markdown": "^8.0.3", @@ -71,7 +73,8 @@ "tweetnacl-util": "^0.15.1", "uuid": "^8.3.2", "uuidv4": "^6.2.13", - "yaml": "^2.2.0" + "yaml": "^2.2.0", + "yup": "^0.32.11" }, "devDependencies": { "@storybook/addon-essentials": "^7.0.0-beta.30", diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index 34cdcaea8..e02c4f474 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -1,5 +1,5 @@ { - "head-title": "{{title}} | Infiscal", + "head-title": "{{title}} | Infisical", "error_project-already-exists": "A project with this name already exists.", "no-mobile": " To use Infisical, please log in through a device with larger dimensions. ", "email": "Email", @@ -28,7 +28,7 @@ "select-event": "Select an event", "event": "Event", "user": "User", - "source": "Source", - "time": "Time", + "source": "Source", + "time": "Time", "timestamp": "Timestamp" -} +} \ No newline at end of file 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/public/locales/en/settings-members.json b/frontend/public/locales/en/settings-members.json index f0b6e586e..128a649a0 100644 --- a/frontend/public/locales/en/settings-members.json +++ b/frontend/public/locales/en/settings-members.json @@ -1,4 +1,4 @@ { "title": "Project Members", - "description": "This page shows the members of the selected project." + "description": "This page shows the members of the selected project, and allows you to modify their permissions." } diff --git a/frontend/public/locales/fr/common.json b/frontend/public/locales/fr/common.json index 8552668aa..1f6a52447 100644 --- a/frontend/public/locales/fr/common.json +++ b/frontend/public/locales/fr/common.json @@ -1,5 +1,5 @@ { - "head-title": "{{title}} | Infiscal", + "head-title": "{{title}} | Infisical", "error_project-already-exists": "Un projet avec ce nom existe dΓ©jΓ .", "no-mobile": " Pour utiliser Infisical, veuillez vous connecter avec un appareil avec des dimensions plus grandes. ", "email": "Email", @@ -28,7 +28,7 @@ "select-event": "SΓ©lectionnez un Γ©vΓ©nement", "event": "Γ‰vΓ©nement", "user": "Utilisateur", - "source": "Source", - "time": "Heure", + "source": "Source", + "time": "Heure", "timestamp": "Horodatage" -} +} \ No newline at end of file diff --git a/frontend/public/locales/ko/common.json b/frontend/public/locales/ko/common.json index 8274dd060..f0692ebf7 100644 --- a/frontend/public/locales/ko/common.json +++ b/frontend/public/locales/ko/common.json @@ -1,5 +1,5 @@ { - "head-title": "{{title}} | Infiscal", + "head-title": "{{title}} | Infisical", "error_project-already-exists": "λ™μΌν•œ 이름을 κ°€μ§„ ν”„λ‘œμ νŠΈκ°€ 이미 μ‘΄μž¬ν•΄μš”.", "no-mobile": " Infisical을 μ‚¬μš©ν•˜λ €λ©΄, 큰 화면을 κ°€μ§„ λ””λ°”μ΄μŠ€λ‘œ λ‘œκ·ΈμΈν•˜μ—¬ μ£Όμ„Έμš”.", "email": "메일", @@ -23,4 +23,4 @@ "language": "μ–Έμ–΄", "search": "κ²€μƒ‰ν•˜κΈ°...", "note": "Note" -} +} \ No newline at end of file diff --git a/frontend/public/locales/pt-BR/common.json b/frontend/public/locales/pt-BR/common.json index 12e657d7c..207f0db29 100644 --- a/frontend/public/locales/pt-BR/common.json +++ b/frontend/public/locales/pt-BR/common.json @@ -1,5 +1,5 @@ { - "head-title": "{{title}} | Infiscal", + "head-title": "{{title}} | Infisical", "error_project-already-exists": "JΓ‘ exite um projeto com este nome.", "no-mobile": "Para usar o Infisical, faΓ§a o login atravΓ©s de um dispositivo com dimensΓ΅es maiores.", "email": "Email", 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 ( <> -
-