diff --git a/.env.example b/.env.example index 6b8639a74..4029cb141 100644 --- a/.env.example +++ b/.env.example @@ -3,16 +3,18 @@ # THIS IS A SAMPLE ENCRYPTION KEY AND SHOULD NEVER BE USED FOR PRODUCTION ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218 +# Required +DB_CONNECTION_URI=postgres://infisical:infisical@db:5432/infisical + # JWT # Required secrets to sign JWT tokens # THIS IS A SAMPLE AUTH_SECRET KEY AND SHOULD NEVER BE USED FOR PRODUCTION AUTH_SECRET=5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE= -# MongoDB -# Backend will connect to the MongoDB instance at connection string MONGO_URL which can either be a ref -# to the MongoDB container instance or Mongo Cloud -# Required -MONGO_URL=mongodb://root:example@mongo:27017/?authSource=admin +# Postgres creds +POSTGRES_PASSWORD=infisical +POSTGRES_USER=infisical +POSTGRES_DB=infisical # Redis REDIS_URL=redis://redis:6379 diff --git a/.env.migration.example b/.env.migration.example new file mode 100644 index 000000000..4d1c8f9ef --- /dev/null +++ b/.env.migration.example @@ -0,0 +1 @@ +DB_CONNECTION_URI= diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fd27d42c1..8ec62ef24 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,6 @@ # Description ๐Ÿ“ฃ - + ## Type โœจ @@ -19,4 +19,6 @@ --- -- [ ] I have read the [contributing guide](https://infisical.com/docs/contributing/overview), agreed and acknowledged the [code of conduct](https://infisical.com/docs/contributing/code-of-conduct). ๐Ÿ“ \ No newline at end of file +- [ ] I have read the [contributing guide](https://infisical.com/docs/contributing/getting-started/overview), agreed and acknowledged the [code of conduct](https://infisical.com/docs/contributing/getting-started/code-of-conduct). ๐Ÿ“ + + \ No newline at end of file diff --git a/.github/values.yaml b/.github/values.yaml index 6c9367736..90bf2ce0a 100644 --- a/.github/values.yaml +++ b/.github/values.yaml @@ -1,49 +1,52 @@ -backend: - enabled: true - name: backend - podAnnotations: {} - deploymentAnnotations: - secrets.infisical.com/auto-reload: "true" - replicaCount: 2 +## @section Common parameters +## + +## @param nameOverride Override release name +## +nameOverride: "" +## @param fullnameOverride Override release fullname +## +fullnameOverride: "" + +## @section Infisical backend parameters +## Documentation : https://infisical.com/docs/self-hosting/deployments/kubernetes +## + +infisical: + ## @param backend.enabled Enable backend + ## + enabled: false + ## @param backend.name Backend name + ## + name: infisical + replicaCount: 3 image: repository: infisical/staging_infisical tag: "latest" pullPolicy: Always - kubeSecretRef: managed-backend-secret - service: - annotations: {} - type: ClusterIP - nodePort: "" - resources: - limits: - memory: 300Mi -backendEnvironmentVariables: null + deploymentAnnotations: + secrets.infisical.com/auto-reload: "true" -## Mongo DB persistence -mongodb: - enabled: false - persistence: - enabled: false - -## By default the backend will be connected to a Mongo instance within the cluster -## However, it is recommended to add a managed document DB connection string for production-use (DBaaS) -## Learn about connection string type here https://www.mongodb.com/docs/manual/reference/connection-string/ -## e.g. "mongodb://:@:/" -mongodbConnection: - externalMongoDBConnectionString: "" + kubeSecretRef: "infisical-gamma-secrets" ingress: + ## @param ingress.enabled Enable ingress + ## enabled: true - # annotations: - # kubernetes.io/ingress.class: "nginx" - # cert-manager.io/issuer: letsencrypt-nginx - hostName: gamma.infisical.com ## <- Replace with your own domain + ## @param ingress.ingressClassName Ingress class name + ## + ingressClassName: nginx + ## @param ingress.nginx.enabled Ingress controller + ## + # nginx: + # enabled: true + ## @param ingress.annotations Ingress annotations + ## + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hostName: "gamma.infisical.com" tls: - [] - # - secretName: letsencrypt-nginx - # hosts: - # - infisical.local - -mailhog: - enabled: false + - secretName: letsencrypt-prod + hosts: + - gamma.infisical.com diff --git a/.github/workflows/build-staging-img.yml b/.github/workflows/build-staging-and-deploy.yml similarity index 72% rename from .github/workflows/build-staging-img.yml rename to .github/workflows/build-staging-and-deploy.yml index 6e094871e..31ffb8729 100644 --- a/.github/workflows/build-staging-img.yml +++ b/.github/workflows/build-staging-and-deploy.yml @@ -35,15 +35,15 @@ jobs: context: . file: Dockerfile.standalone-infisical tags: infisical/infisical:test - - name: โป Spawn backend container and dependencies - run: | - docker compose -f .github/resources/docker-compose.be-test.yml up --wait --quiet-pull - - name: ๐Ÿงช Test backend image - run: | - ./.github/resources/healthcheck.sh infisical-backend-test - - name: โป Shut down backend container and dependencies - run: | - docker compose -f .github/resources/docker-compose.be-test.yml down + # - name: โป Spawn backend container and dependencies + # run: | + # docker compose -f .github/resources/docker-compose.be-test.yml up --wait --quiet-pull + # - name: ๐Ÿงช Test backend image + # run: | + # ./.github/resources/healthcheck.sh infisical-backend-test + # - name: โป Shut down backend container and dependencies + # run: | + # docker compose -f .github/resources/docker-compose.be-test.yml down - name: ๐Ÿ—๏ธ Build backend and push uses: depot/build-push-action@v1 with: @@ -59,11 +59,32 @@ jobs: build-args: | POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} INFISICAL_PLATFORM_VERSION=${{ steps.extract_version.outputs.version }} - + postgres-migration: + name: Run latest migration files + runs-on: ubuntu-latest + needs: [infisical-image] + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Setup Node.js environment + uses: actions/setup-node@v2 + with: + node-version: "20" + - name: Change directory to backend and install dependencies + env: + DB_CONNECTION_URI: ${{ secrets.DB_CONNECTION_URI }} + run: | + cd backend + npm install + npm run migration:latest + # - name: Run postgres DB migration files + # env: + # DB_CONNECTION_URI: ${{ secrets.DB_CONNECTION_URI }} + # run: npm run migration:latest gamma-deployment: name: Deploy to gamma runs-on: ubuntu-latest - needs: [infisical-image] + needs: [postgres-migration] steps: - name: โ˜๏ธ Checkout source uses: actions/checkout@v3 @@ -82,7 +103,7 @@ jobs: with: token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} - name: Save DigitalOcean kubeconfig with short-lived credentials - run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 k8s-1-25-4-do-0-nyc1-1670645170179 + run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 infisical-gamma-postgres - name: switch to gamma namespace run: kubectl config set-context --current --namespace=gamma - name: test kubectl @@ -90,7 +111,7 @@ jobs: - name: Download helm values to file and upgrade gamma deploy run: | wget https://raw.githubusercontent.com/Infisical/infisical/main/.github/values.yaml - helm upgrade infisical infisical-helm-charts/infisical --values values.yaml --wait --install + helm upgrade infisical infisical-helm-charts/infisical-standalone --values values.yaml --wait --install if [[ $(helm status infisical) == *"FAILED"* ]]; then echo "Helm upgrade failed" exit 1 diff --git a/.github/workflows/check-api-for-breaking-changes.yml b/.github/workflows/check-api-for-breaking-changes.yml new file mode 100644 index 000000000..29275abd1 --- /dev/null +++ b/.github/workflows/check-api-for-breaking-changes.yml @@ -0,0 +1,75 @@ +name: "Check API For Breaking Changes" + +on: + pull_request: + types: [opened, synchronize] + paths: + - "backend/src/server/routes/**" + +jobs: + check-be-api-changes: + name: Check API Changes + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout source + uses: actions/checkout@v3 + # - name: Setup Node 20 + # uses: actions/setup-node@v3 + # with: + # node-version: "20" + # uncomment this when testing locally using nektos/act + - uses: KengoTODA/actions-setup-docker-compose@v1 + if: ${{ env.ACT }} + name: Install `docker-compose` for local simulations + with: + version: "2.14.2" + - name: ๐Ÿ“ฆBuild the latest image + run: docker build --tag infisical-api . + working-directory: backend + - name: Start postgres and redis + run: touch .env && docker-compose -f docker-compose.dev.yml up -d db redis + - name: Start the server + run: | + echo "SECRET_SCANNING_GIT_APP_ID=793712" >> .env + echo "SECRET_SCANNING_PRIVATE_KEY=some-random" >> .env + echo "SECRET_SCANNING_WEBHOOK_SECRET=some-random" >> .env + docker run --name infisical-api -d -p 4000:4000 -e DB_CONNECTION_URI=$DB_CONNECTION_URI -e REDIS_URL=$REDIS_URL -e JWT_AUTH_SECRET=$JWT_AUTH_SECRET --env-file .env --entrypoint '/bin/sh' infisical-api -c "npm run migration:latest && ls && node dist/main.mjs" + env: + REDIS_URL: redis://172.17.0.1:6379 + DB_CONNECTION_URI: postgres://infisical:infisical@172.17.0.1:5432/infisical?sslmode=disable + JWT_AUTH_SECRET: something-random + - uses: actions/setup-go@v5 + with: + go-version: '1.21.5' + - name: Wait for container to be stable and check logs + run: | + SECONDS=0 + HEALTHY=0 + while [ $SECONDS -lt 60 ]; do + if docker ps | grep infisical-api | grep -q healthy; then + echo "Container is healthy." + HEALTHY=1 + break + fi + echo "Waiting for container to be healthy... ($SECONDS seconds elapsed)" + + docker logs infisical-api + + sleep 2 + SECONDS=$((SECONDS+2)) + done + + if [ $HEALTHY -ne 1 ]; then + echo "Container did not become healthy in time" + exit 1 + fi + - name: Install openapi-diff + run: go install github.com/tufin/oasdiff@latest + - name: Running OpenAPI Spec diff action + run: oasdiff breaking https://app.infisical.com/api/docs/json http://localhost:4000/api/docs/json --fail-on ERR + - name: cleanup + run: | + docker-compose -f "docker-compose.dev.yml" down + docker stop infisical-api + docker remove infisical-api diff --git a/.github/workflows/check-be-pull-request.yml b/.github/workflows/check-be-pull-request.yml deleted file mode 100644 index 2eb040084..000000000 --- a/.github/workflows/check-be-pull-request.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: "Check Backend Pull Request" - -on: - pull_request: - types: [opened, synchronize] - paths: - - "backend/**" - - "!backend/README.md" - - "!backend/.*" - - "backend/.eslintrc.js" - -jobs: - check-be-pr: - name: Check - runs-on: ubuntu-latest - timeout-minutes: 15 - - steps: - - name: โ˜๏ธ Checkout source - uses: actions/checkout@v3 - - name: ๐Ÿ”ง Setup Node 16 - uses: actions/setup-node@v3 - with: - node-version: "16" - cache: "npm" - cache-dependency-path: backend/package-lock.json - - name: ๐Ÿ“ฆ Install dependencies - run: npm ci --only-production - working-directory: backend - # - name: ๐Ÿงช Run tests - # run: npm run test:ci - # working-directory: backend - # - name: ๐Ÿ“ Upload test results - # uses: actions/upload-artifact@v3 - # if: always() - # with: - # name: be-test-results - # path: | - # ./backend/reports - # ./backend/coverage - - name: ๐Ÿ—๏ธ Run build - run: npm run build - working-directory: backend diff --git a/.github/workflows/check-be-ts-and-lint.yml b/.github/workflows/check-be-ts-and-lint.yml new file mode 100644 index 000000000..4b9b1a1b8 --- /dev/null +++ b/.github/workflows/check-be-ts-and-lint.yml @@ -0,0 +1,35 @@ +name: "Check Backend PR types and lint" + +on: + pull_request: + types: [opened, synchronize] + paths: + - "backend/**" + - "!backend/README.md" + - "!backend/.*" + - "backend/.eslintrc.js" + +jobs: + check-be-pr: + name: Check TS and Lint + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: โ˜๏ธ Checkout source + uses: actions/checkout@v3 + - name: ๐Ÿ”ง Setup Node 20 + uses: actions/setup-node@v3 + with: + node-version: "20" + cache: "npm" + cache-dependency-path: backend/package-lock.json + - name: Install dependencies + run: npm install + working-directory: backend + - name: Run type check + run: npm run type:check + working-directory: backend + - name: Run lint check + run: npm run lint + working-directory: backend diff --git a/.github/workflows/release-standalone-docker-img-postgres.yml b/.github/workflows/release-standalone-docker-img-postgres-offical.yml similarity index 63% rename from .github/workflows/release-standalone-docker-img-postgres.yml rename to .github/workflows/release-standalone-docker-img-postgres-offical.yml index 481b196c9..54f4f4fbe 100644 --- a/.github/workflows/release-standalone-docker-img-postgres.yml +++ b/.github/workflows/release-standalone-docker-img-postgres-offical.yml @@ -1,4 +1,4 @@ -name: Release standalone postgres version +name: Release standalone docker image on: push: tags: @@ -30,28 +30,28 @@ jobs: - name: Save commit hashes for tag id: commit uses: pr-mpt/actions-commit-hash@v2 + - name: ๐Ÿ”ง Set up Docker Buildx + uses: docker/setup-buildx-action@v2 - name: ๐Ÿ‹ Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub - uses: docker/login-action@v3 + - name: Set up Depot CLI + uses: depot/setup-action@v1 + - name: ๐Ÿ“ฆ Build backend and export to Docker + uses: depot/build-push-action@v1 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64 - file: Dockerfile.standalone-infisical + project: 64mmf0n610 + token: ${{ secrets.DEPOT_PROJECT_TOKEN }} push: true + context: . tags: | - akhilmhdh/destruction:latest - akhilmhdh/destruction:${{ steps.commit.outputs.short }} - akhilmhdh/destruction:${{ steps.extract_version.outputs.version }} + infisical/infisical:latest-postgres + infisical/infisical:${{ steps.commit.outputs.short }} + infisical/infisical:${{ steps.extract_version.outputs.version }} + platforms: linux/amd64,linux/arm64 + file: Dockerfile.standalone-infisical + build-args: | + POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} + INFISICAL_PLATFORM_VERSION=${{ steps.extract_version.outputs.version }} diff --git a/.github/workflows/release-standalone-docker-img.yml b/.github/workflows/release-standalone-docker-img.yml deleted file mode 100644 index d60774824..000000000 --- a/.github/workflows/release-standalone-docker-img.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Release standalone docker image -on: - push: - tags: - - "infisical/v*.*.*" - - "!infisical/v*.*.*-postgres" - -jobs: - infisical-standalone: - name: Build infisical standalone image - runs-on: ubuntu-latest - steps: - - name: Extract version from tag - id: extract_version - run: echo "::set-output name=version::${GITHUB_REF_NAME#infisical/}" - - name: โ˜๏ธ Checkout source - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: ๐Ÿ“ฆ Install dependencies to test all dependencies - run: npm ci --only-production - working-directory: backend - - uses: paulhatch/semantic-version@v5.0.2 - id: version - with: - # The prefix to use to identify tags - tag_prefix: "infisical-standalone/v" - # A string which, if present in a git commit, indicates that a change represents a - # major (breaking) change, supports regular expressions wrapped with '/' - major_pattern: "(MAJOR)" - # Same as above except indicating a minor change, supports regular expressions wrapped with '/' - minor_pattern: "(MINOR)" - # A string to determine the format of the version output - version_format: "${major}.${minor}.${patch}-prerelease${increment}" - # Optional path to check for changes. If any changes are detected in the path the - # 'changed' output will true. Enter multiple paths separated by spaces. - change_path: "backend,frontend" - # Prevents pre-v1.0.0 version from automatically incrementing the major version. - # If enabled, when the major version is 0, major releases will be treated as minor and minor as patch. Note that the version_type output is unchanged. - enable_prerelease_mode: true - # - name: ๐Ÿงช Run tests - # run: npm run test:ci - # working-directory: backend - - name: version output - run: | - echo "Output Value: ${{ steps.version.outputs.major }}" - echo "Output Value: ${{ steps.version.outputs.minor }}" - echo "Output Value: ${{ steps.version.outputs.patch }}" - echo "Output Value: ${{ steps.version.outputs.version }}" - echo "Output Value: ${{ steps.version.outputs.version_type }}" - echo "Output Value: ${{ steps.version.outputs.increment }}" - - name: Save commit hashes for tag - id: commit - uses: pr-mpt/actions-commit-hash@v2 - - name: ๐Ÿ”ง Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: ๐Ÿ‹ Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Set up Depot CLI - uses: depot/setup-action@v1 - - name: ๐Ÿ“ฆ Build backend and export to Docker - uses: depot/build-push-action@v1 - with: - project: 64mmf0n610 - token: ${{ secrets.DEPOT_PROJECT_TOKEN }} - push: true - context: . - tags: | - infisical/infisical:latest - infisical/infisical:${{ steps.commit.outputs.short }} - infisical/infisical:${{ steps.extract_version.outputs.version }} - platforms: linux/amd64,linux/arm64 - file: Dockerfile.standalone-infisical - build-args: | - POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} - INFISICAL_PLATFORM_VERSION=${{ steps.extract_version.outputs.version }} diff --git a/.github/workflows/release_build.yml b/.github/workflows/release_build_infisical_cli.yml similarity index 100% rename from .github/workflows/release_build.yml rename to .github/workflows/release_build_infisical_cli.yml diff --git a/.gitignore b/.gitignore index f3c03e814..07322c82f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ node_modules .env.gamma .env.prod .env.infisical - +.env.migration *~ *.swp *.swo diff --git a/Dockerfile.standalone-infisical b/Dockerfile.standalone-infisical index 667221bf0..d4596115e 100644 --- a/Dockerfile.standalone-infisical +++ b/Dockerfile.standalone-infisical @@ -104,7 +104,6 @@ ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \ WORKDIR / COPY --from=backend-runner /app /backend -COPY --from=backend-runner /app/dist/services/smtp/templates /backend/dist/templates COPY --from=frontend-runner /app ./backend/frontend-build diff --git a/Makefile b/Makefile index 544a0256d..2b7f43c85 100644 --- a/Makefile +++ b/Makefile @@ -5,16 +5,10 @@ push: docker-compose -f docker-compose.yml push up-dev: - docker-compose -f docker-compose.dev.yml up --build - -up-pg-dev: - docker compose -f docker-compose.pg.yml up --build - -i-dev: - infisical run -- docker-compose -f docker-compose.dev.yml up --build + docker compose -f docker-compose.dev.yml up --build up-prod: - docker-compose -f docker-compose.yml up --build + docker-compose -f docker-compose.prod.yml up --build down: docker-compose down diff --git a/README.md b/README.md index d48637c0d..3c2fd5387 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ git commit activity - Cloudsmith downloads + Cloudsmith downloads Slack community channel @@ -53,17 +53,19 @@ We're on a mission to make secret management more accessible to everyone, not ju ## Features -- **[User-friendly dashboard](https://infisical.com/docs/documentation/platform/project)** to manage secrets across projects and environments (e.g. development, production, etc.) -- **[Client SDKs](https://infisical.com/docs/sdks/overview)** to fetch secrets for your apps and infrastructure on demand -- **[Infisical CLI](https://infisical.com/docs/cli/overview)** to fetch and inject secrets into any framework in local development -- **[Native integrations](https://infisical.com/docs/integrations/overview)** with platforms like GitHub, Vercel, Netlify, and more -- [**Automatic Kubernetes deployment secret reloads**](https://infisical.com/docs/documentation/getting-started/kubernetes) -- **[Complete control over your data](https://infisical.com/docs/self-hosting/overview)** - host it yourself on any infrastructure -- **[Secret versioning](https://infisical.com/docs/documentation/platform/secret-versioning)** and **[Point-in-Time Recovery]()** to version every secret and project state -- **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)** to record every action taken in a project -- **Role-based Access Controls** per environment -- [**Simple on-premise deployments** to AWS, Digital Ocean, and more](https://infisical.com/docs/self-hosting/overview) -- [**Secret Scanning and Leak Prevention**](https://infisical.com/docs/cli/scanning-overview) +- **[User-friendly dashboard](https://infisical.com/docs/documentation/platform/project)** to manage secrets across projects and environments (e.g. development, production, etc.). +- **[Client SDKs](https://infisical.com/docs/sdks/overview)** to fetch secrets for your apps and infrastructure on demand. +- **[Infisical CLI](https://infisical.com/docs/cli/overview)** to fetch and inject secrets into any framework in local development and CI/CD. +- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** to perform CRUD operation on secrets, users, projects, and any other resource in Infisical. +- **[Native integrations](https://infisical.com/docs/integrations/overview)** with platforms like [GitHub](https://infisical.com/docs/integrations/cicd/githubactions), [Vercel](https://infisical.com/docs/integrations/cloud/vercel), [AWS](https://infisical.com/docs/integrations/cloud/aws-secret-manager), and tools like [Terraform](https://infisical.com/docs/integrations/frameworks/terraform), [Ansible](https://infisical.com/docs/integrations/platforms/ansible), and more. +- **[Infisical Kubernetes operator](https://infisical.com/docs/documentation/getting-started/kubernetes)** to managed secrets in k8s, automatically reload deployments, and more. +- **[Infisical Agent](https://infisical.com/docs/infisical-agent/overview)** to inject secrets into your applications without modifying any code logic. +- **[Self-hosting and on-prem](https://infisical.com/docs/self-hosting/overview)** to get complete control over your data. +- **[Secret versioning](https://infisical.com/docs/documentation/platform/secret-versioning)** and **[Point-in-Time Recovery](https://infisical.com/docs/documentation/platform/pit-recovery)** to version every secret and project state. +- **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)** to record every action taken in a project. +- **[Role-based Access Controls](https://infisical.com/docs/documentation/platform/role-based-access-controls)** to create permission sets on any resource in Infisica and assign those to user or machine identities. +- **[Simple on-premise deployments](https://infisical.com/docs/self-hosting/overview)** to AWS, Digital Ocean, and more. +- **[Secret Scanning and Leak Prevention](https://infisical.com/docs/cli/scanning-overview)** to prevent secrets from leaking to git. And much more. @@ -82,13 +84,13 @@ To set up and run Infisical locally, make sure you have Git and Docker installed Linux/macOS: ```console -git clone https://github.com/Infisical/infisical && cd "$(basename $_ .git)" && cp .env.example .env && docker-compose -f docker-compose.yml up +git clone https://github.com/Infisical/infisical && cd "$(basename $_ .git)" && cp .env.example .env && docker-compose -f docker-compose.prod.yml up ``` Windows Command Prompt: ```console -git clone https://github.com/Infisical/infisical && cd infisical && copy .env.example .env && docker-compose -f docker-compose.yml up +git clone https://github.com/Infisical/infisical && cd infisical && copy .env.example .env && docker-compose -f docker-compose.prod.yml up ``` Create an account at `http://localhost:80` @@ -115,9 +117,9 @@ Lean about Infisical's code scanning feature [here](https://infisical.com/docs/c This repo available under the [MIT expat license](https://github.com/Infisical/infisical/blob/main/LICENSE), with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license. -If you are interested in managed Infisical Cloud of self-hosted Enterprise Offering, take a look at [our website](https://infisical.com/) or [book a meeting with us](https://cal.com/vmatsiiako/infisical-demo): +If you are interested in managed Infisical Cloud of self-hosted Enterprise Offering, take a look at [our website](https://infisical.com/) or [book a meeting with us](https://infisical.cal.com/vlad/infisical-demo): -Schedule a meeting +Schedule a meeting ## Security diff --git a/backend-mongo/.dockerignore b/backend-mongo/.dockerignore deleted file mode 100644 index b484ea02a..000000000 --- a/backend-mongo/.dockerignore +++ /dev/null @@ -1,11 +0,0 @@ -node_modules -.env -.env.* -.git -.gitignore -Dockerfile -.dockerignore -docker-compose.* -.DS_Store -*.swp -*~ diff --git a/backend-mongo/.eslintignore b/backend-mongo/.eslintignore deleted file mode 100644 index 76d195ba3..000000000 --- a/backend-mongo/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -built \ No newline at end of file diff --git a/backend-mongo/.eslintrc b/backend-mongo/.eslintrc deleted file mode 100644 index 31bcc259b..000000000 --- a/backend-mongo/.eslintrc +++ /dev/null @@ -1,41 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint", - "unused-imports" - ], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - "rules": { - "no-empty-function": "off", - "@typescript-eslint/no-empty-function": "off", - "no-console": 2, - "quotes": [ - "error", - "double", - { - "avoidEscape": true - } - ], - "comma-dangle": [ - "error", - "only-multiline" - ], - "@typescript-eslint/no-unused-vars": "off", - "unused-imports/no-unused-imports": "error", - "@typescript-eslint/no-extra-semi": "off", // added to be able to push - "unused-imports/no-unused-vars": [ - "warn", - { - "vars": "all", - "varsIgnorePattern": "^_", - "args": "after-used", - "argsIgnorePattern": "^_" - } - ], - "sort-imports": 1 - } -} \ No newline at end of file diff --git a/backend-mongo/.prettierrc b/backend-mongo/.prettierrc deleted file mode 100644 index 0b8ef54d2..000000000 --- a/backend-mongo/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "singleQuote": false, - "printWidth": 100, - "trailingComma": "none", - "tabWidth": 2, - "semi": true -} diff --git a/backend-mongo/Dockerfile b/backend-mongo/Dockerfile deleted file mode 100644 index 06448ad91..000000000 --- a/backend-mongo/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -# Build stage -FROM node:16-alpine AS build - -WORKDIR /app - -COPY package*.json ./ -RUN npm ci --only-production - -COPY . . -RUN npm run build - -# Production stage -FROM node:16-alpine - -WORKDIR /app - -ENV npm_config_cache /home/node/.npm - -COPY package*.json ./ -RUN npm ci --only-production && npm cache clean --force - -COPY --from=build /app . - -RUN apk add --no-cache bash curl && curl -1sLf \ - 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' | bash \ - && apk add infisical=0.8.1 && apk add --no-cache git - -HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \ - CMD node healthcheck.js - -EXPOSE 4000 - -CMD ["node", "build/index.js"] diff --git a/backend-mongo/environment.d.ts b/backend-mongo/environment.d.ts deleted file mode 100644 index 25f56dcc6..000000000 --- a/backend-mongo/environment.d.ts +++ /dev/null @@ -1,46 +0,0 @@ -export {}; - -declare global { - namespace NodeJS { - interface ProcessEnv { - PORT: string; - ENCRYPTION_KEY: string; - SALT_ROUNDS: string; - JWT_AUTH_LIFETIME: string; - JWT_AUTH_SECRET: string; - JWT_REFRESH_LIFETIME: string; - JWT_REFRESH_SECRET: string; - JWT_SERVICE_SECRET: string; - JWT_SIGNUP_LIFETIME: string; - JWT_SIGNUP_SECRET: string; - MONGO_URL: string; - NODE_ENV: "development" | "staging" | "testing" | "production"; - VERBOSE_ERROR_OUTPUT: string; - LOKI_HOST: string; - CLIENT_ID_HEROKU: string; - CLIENT_ID_VERCEL: string; - CLIENT_ID_NETLIFY: string; - CLIENT_ID_GITHUB: string; - CLIENT_ID_GITLAB: string; - CLIENT_SECRET_HEROKU: string; - CLIENT_SECRET_VERCEL: string; - CLIENT_SECRET_NETLIFY: string; - CLIENT_SECRET_GITHUB: string; - CLIENT_SECRET_GITLAB: string; - CLIENT_SLUG_VERCEL: string; - POSTHOG_HOST: string; - POSTHOG_PROJECT_API_KEY: string; - SENTRY_DSN: string; - SITE_URL: string; - SMTP_HOST: string; - SMTP_SECURE: string; - SMTP_PORT: string; - SMTP_USERNAME: string; - SMTP_PASSWORD: string; - SMTP_FROM_ADDRESS: string; - SMTP_FROM_NAME: string; - TELEMETRY_ENABLED: string; - LICENSE_KEY: string; - } - } -} diff --git a/backend-mongo/healthcheck.js b/backend-mongo/healthcheck.js deleted file mode 100644 index 8cb3dfcaa..000000000 --- a/backend-mongo/healthcheck.js +++ /dev/null @@ -1,24 +0,0 @@ -const http = require('http'); -const PORT = process.env.PORT || 4000; -const options = { - host: 'localhost', - port: PORT, - timeout: 2000, - path: '/healthcheck' -}; - -const healthCheck = http.request(options, (res) => { - console.log(`HEALTHCHECK STATUS: ${res.statusCode}`); - if (res.statusCode == 200) { - process.exit(0); - } else { - process.exit(1); - } -}); - -healthCheck.on('error', function (err) { - console.error(`HEALTH CHECK ERROR: ${err}`); - process.exit(1); -}); - -healthCheck.end(); diff --git a/backend-mongo/img/dashboard.png b/backend-mongo/img/dashboard.png deleted file mode 100644 index 75791f4e9..000000000 Binary files a/backend-mongo/img/dashboard.png and /dev/null differ diff --git a/backend-mongo/jest.config.ts b/backend-mongo/jest.config.ts deleted file mode 100644 index 7c657505b..000000000 --- a/backend-mongo/jest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default { - preset: "ts-jest", - testEnvironment: "node", - collectCoverageFrom: ["src/*.{js,ts}", "!**/node_modules/**"], - modulePaths: ["/src"], - testMatch: ["/tests/**/*.test.ts"], - setupFiles: ["/test-resources/env-vars.js"], - setupFilesAfterEnv: ["/tests/setupTests.ts"], -}; diff --git a/backend-mongo/nodemon.json b/backend-mongo/nodemon.json deleted file mode 100644 index 7ea1ca760..000000000 --- a/backend-mongo/nodemon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "watch": ["src"], - "ext": ".ts,.js", - "ignore": [], - "exec": "ts-node ./src/index.ts" -} \ No newline at end of file diff --git a/backend-mongo/package-lock.json b/backend-mongo/package-lock.json deleted file mode 100644 index 91bc26205..000000000 --- a/backend-mongo/package-lock.json +++ /dev/null @@ -1,32861 +0,0 @@ -{ - "name": "infisical-api", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "infisical-api", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@aws-sdk/client-secrets-manager": "^3.319.0", - "@casl/ability": "^6.5.0", - "@casl/mongoose": "^7.2.1", - "@godaddy/terminus": "^4.12.0", - "@node-saml/passport-saml": "^4.0.4", - "@octokit/rest": "^19.0.5", - "@sentry/node": "^7.77.0", - "@sentry/tracing": "^7.48.0", - "@serdnam/pino-cloudwatch-transport": "^1.0.4", - "@types/crypto-js": "^4.1.1", - "@types/libsodium-wrappers": "^0.7.10", - "@ucast/mongo2js": "^1.3.4", - "ajv": "^8.12.0", - "argon2": "^0.30.3", - "aws-sdk": "^2.1364.0", - "axios": "^1.6.0", - "axios-retry": "^3.4.0", - "bcrypt": "^5.1.0", - "bigint-conversion": "^2.4.0", - "cookie-parser": "^1.4.6", - "cors": "^2.8.5", - "crypto-js": "^4.2.0", - "dotenv": "^16.0.1", - "express": "^4.18.1", - "express-async-errors": "^3.1.1", - "express-rate-limit": "^6.7.0", - "express-validator": "^6.14.2", - "handlebars": "^4.7.7", - "helmet": "^5.1.1", - "infisical-node": "^1.2.1", - "ioredis": "^5.3.2", - "jmespath": "^0.16.0", - "js-yaml": "^4.1.0", - "jsonwebtoken": "^9.0.0", - "jsrp": "^0.2.4", - "libsodium-wrappers": "^0.7.10", - "lodash": "^4.17.21", - "mongoose": "^7.4.1", - "mysql2": "^3.6.2", - "nanoid": "^3.3.6", - "node-cache": "^5.1.2", - "nodemailer": "^6.8.0", - "ora": "^5.4.1", - "passport": "^0.6.0", - "passport-github": "^1.1.0", - "passport-gitlab2": "^5.0.0", - "passport-google-oauth20": "^2.0.0", - "pg": "^8.11.3", - "pino": "^8.16.1", - "pino-http": "^8.5.1", - "posthog-node": "^2.6.0", - "probot": "^12.3.3", - "query-string": "^7.1.3", - "rate-limit-mongo": "^2.3.2", - "rimraf": "^3.0.2", - "swagger-ui-express": "^4.6.2", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1", - "typescript": "^4.9.3", - "utility-types": "^3.10.0", - "zod": "^3.22.3" - }, - "devDependencies": { - "@jest/globals": "^29.3.1", - "@posthog/plugin-scaffold": "^1.3.4", - "@swc/core": "^1.3.99", - "@swc/helpers": "^0.5.3", - "@types/bcrypt": "^5.0.0", - "@types/bcryptjs": "^2.4.2", - "@types/bull": "^4.10.0", - "@types/cookie-parser": "^1.4.3", - "@types/cors": "^2.8.12", - "@types/express": "^4.17.14", - "@types/jest": "^29.5.0", - "@types/jmespath": "^0.15.1", - "@types/jsonwebtoken": "^8.5.9", - "@types/lodash": "^4.14.191", - "@types/node": "^18.11.3", - "@types/nodemailer": "^6.4.6", - "@types/passport": "^1.0.12", - "@types/pg": "^8.10.7", - "@types/picomatch": "^2.3.0", - "@types/pino": "^7.0.5", - "@types/supertest": "^2.0.12", - "@types/swagger-jsdoc": "^6.0.1", - "@types/swagger-ui-express": "^4.1.3", - "@typescript-eslint/eslint-plugin": "^5.54.0", - "@typescript-eslint/parser": "^5.40.1", - "cross-env": "^7.0.3", - "eslint": "^8.26.0", - "eslint-plugin-unused-imports": "^2.0.0", - "install": "^0.13.0", - "jest": "^29.3.1", - "jest-junit": "^15.0.0", - "nodemon": "^2.0.19", - "npm": "^8.19.3", - "pino-pretty": "^10.2.3", - "regenerator-runtime": "^0.14.0", - "smee-client": "^1.2.3", - "supertest": "^6.3.3", - "swagger-autogen": "^2.23.5", - "ts-jest": "^29.0.3", - "ts-node": "^10.9.1" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/crc32/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "dependencies": { - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "dependencies": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "dependencies": { - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/util/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@aws-sdk/client-cloudwatch-logs": { - "version": "3.454.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.454.0.tgz", - "integrity": "sha512-anXMEIZvDvqsFAURYmNHaJU8SH85Rqkahkk0TsDiTLc6/J4Qh8xvcem358qTiXzRpPJmZe4m20XKqL0fXsJgIw==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.454.0", - "@aws-sdk/core": "3.451.0", - "@aws-sdk/credential-provider-node": "3.451.0", - "@aws-sdk/middleware-host-header": "3.451.0", - "@aws-sdk/middleware-logger": "3.451.0", - "@aws-sdk/middleware-recursion-detection": "3.451.0", - "@aws-sdk/middleware-signing": "3.451.0", - "@aws-sdk/middleware-user-agent": "3.451.0", - "@aws-sdk/region-config-resolver": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@aws-sdk/util-user-agent-browser": "3.451.0", - "@aws-sdk/util-user-agent-node": "3.451.0", - "@smithy/config-resolver": "^2.0.18", - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/hash-node": "^2.0.15", - "@smithy/invalid-dependency": "^2.0.13", - "@smithy/middleware-content-length": "^2.0.15", - "@smithy/middleware-endpoint": "^2.2.0", - "@smithy/middleware-retry": "^2.0.20", - "@smithy/middleware-serde": "^2.0.13", - "@smithy/middleware-stack": "^2.0.7", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/protocol-http": "^3.0.9", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.19", - "@smithy/util-defaults-mode-node": "^2.0.25", - "@smithy/util-endpoints": "^1.0.4", - "@smithy/util-retry": "^2.0.6", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sso": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.451.0.tgz", - "integrity": "sha512-KkYSke3Pdv3MfVH/5fT528+MKjMyPKlcLcd4zQb0x6/7Bl7EHrPh1JZYjzPLHelb+UY5X0qN8+cb8iSu1eiwIQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.451.0", - "@aws-sdk/middleware-host-header": "3.451.0", - "@aws-sdk/middleware-logger": "3.451.0", - "@aws-sdk/middleware-recursion-detection": "3.451.0", - "@aws-sdk/middleware-user-agent": "3.451.0", - "@aws-sdk/region-config-resolver": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@aws-sdk/util-user-agent-browser": "3.451.0", - "@aws-sdk/util-user-agent-node": "3.451.0", - "@smithy/config-resolver": "^2.0.18", - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/hash-node": "^2.0.15", - "@smithy/invalid-dependency": "^2.0.13", - "@smithy/middleware-content-length": "^2.0.15", - "@smithy/middleware-endpoint": "^2.2.0", - "@smithy/middleware-retry": "^2.0.20", - "@smithy/middleware-serde": "^2.0.13", - "@smithy/middleware-stack": "^2.0.7", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/protocol-http": "^3.0.9", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.19", - "@smithy/util-defaults-mode-node": "^2.0.25", - "@smithy/util-endpoints": "^1.0.4", - "@smithy/util-retry": "^2.0.6", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sts": { - "version": "3.454.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.454.0.tgz", - "integrity": "sha512-0fDvr8WeB6IYO8BUCzcivWmahgGl/zDbaYfakzGnt4mrl5ztYaXE875WI6b7+oFcKMRvN+KLvwu5TtyFuNY+GQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.451.0", - "@aws-sdk/credential-provider-node": "3.451.0", - "@aws-sdk/middleware-host-header": "3.451.0", - "@aws-sdk/middleware-logger": "3.451.0", - "@aws-sdk/middleware-recursion-detection": "3.451.0", - "@aws-sdk/middleware-sdk-sts": "3.451.0", - "@aws-sdk/middleware-signing": "3.451.0", - "@aws-sdk/middleware-user-agent": "3.451.0", - "@aws-sdk/region-config-resolver": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@aws-sdk/util-user-agent-browser": "3.451.0", - "@aws-sdk/util-user-agent-node": "3.451.0", - "@smithy/config-resolver": "^2.0.18", - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/hash-node": "^2.0.15", - "@smithy/invalid-dependency": "^2.0.13", - "@smithy/middleware-content-length": "^2.0.15", - "@smithy/middleware-endpoint": "^2.2.0", - "@smithy/middleware-retry": "^2.0.20", - "@smithy/middleware-serde": "^2.0.13", - "@smithy/middleware-stack": "^2.0.7", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/protocol-http": "^3.0.9", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.19", - "@smithy/util-defaults-mode-node": "^2.0.25", - "@smithy/util-endpoints": "^1.0.4", - "@smithy/util-retry": "^2.0.6", - "@smithy/util-utf8": "^2.0.2", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.451.0.tgz", - "integrity": "sha512-9dAav7DcRgaF7xCJEQR5ER9ErXxnu/tdnVJ+UPmb1NPeIZdESv1A3lxFDEq1Fs8c4/lzAj9BpshGyJVIZwZDKg==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.451.0.tgz", - "integrity": "sha512-TySt64Ci5/ZbqFw1F9Z0FIGvYx5JSC9e6gqDnizIYd8eMnn8wFRUscRrD7pIHKfrhvVKN5h0GdYovmMO/FMCBw==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.451.0", - "@aws-sdk/credential-provider-process": "3.451.0", - "@aws-sdk/credential-provider-sso": "3.451.0", - "@aws-sdk/credential-provider-web-identity": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.451.0.tgz", - "integrity": "sha512-AEwM1WPyxUdKrKyUsKyFqqRFGU70e4qlDyrtBxJnSU9NRLZI8tfEZ67bN7fHSxBUBODgDXpMSlSvJiBLh5/3pw==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.451.0", - "@aws-sdk/credential-provider-ini": "3.451.0", - "@aws-sdk/credential-provider-process": "3.451.0", - "@aws-sdk/credential-provider-sso": "3.451.0", - "@aws-sdk/credential-provider-web-identity": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.451.0.tgz", - "integrity": "sha512-HQywSdKeD5PErcLLnZfSyCJO+6T+ZyzF+Lm/QgscSC+CbSUSIPi//s15qhBRVely/3KBV6AywxwNH+5eYgt4lQ==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.451.0.tgz", - "integrity": "sha512-Usm/N51+unOt8ID4HnQzxIjUJDrkAQ1vyTOC0gSEEJ7h64NSSPGD5yhN7il5WcErtRd3EEtT1a8/GTC5TdBctg==", - "dependencies": { - "@aws-sdk/client-sso": "3.451.0", - "@aws-sdk/token-providers": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.451.0.tgz", - "integrity": "sha512-Xtg3Qw65EfDjWNG7o2xD6sEmumPfsy3WDGjk2phEzVg8s7hcZGxf5wYwe6UY7RJvlEKrU0rFA+AMn6Hfj5oOzg==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.451.0.tgz", - "integrity": "sha512-j8a5jAfhWmsK99i2k8oR8zzQgXrsJtgrLxc3js6U+525mcZytoiDndkWTmD5fjJ1byU1U2E5TaPq+QJeDip05Q==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-logger": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.451.0.tgz", - "integrity": "sha512-0kHrYEyVeB2QBfP6TfbI240aRtatLZtcErJbhpiNUb+CQPgEL3crIjgVE8yYiJumZ7f0jyjo8HLPkwD1/2APaw==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.451.0.tgz", - "integrity": "sha512-J6jL6gJ7orjHGM70KDRcCP7so/J2SnkN4vZ9YRLTeeZY6zvBuHDjX8GCIgSqPn/nXFXckZO8XSnA7u6+3TAT0w==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.451.0.tgz", - "integrity": "sha512-UJ6UfVUEgp0KIztxpAeelPXI5MLj9wUtUCqYeIMP7C1ZhoEMNm3G39VLkGN43dNhBf1LqjsV9jkKMZbVfYXuwg==", - "dependencies": { - "@aws-sdk/middleware-signing": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-signing": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.451.0.tgz", - "integrity": "sha512-s5ZlcIoLNg1Huj4Qp06iKniE8nJt/Pj1B/fjhWc6cCPCM7XJYUCejCnRh6C5ZJoBEYodjuwZBejPc1Wh3j+znA==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.5.0", - "@smithy/util-middleware": "^2.0.6", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.451.0.tgz", - "integrity": "sha512-8NM/0JiKLNvT9wtAQVl1DFW0cEO7OvZyLSUBLNLTHqyvOZxKaZ8YFk7d8PL6l76LeUKRxq4NMxfZQlUIRe0eSA==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/token-providers": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.451.0.tgz", - "integrity": "sha512-ij1L5iUbn6CwxVOT1PG4NFjsrsKN9c4N1YEM0lkl6DwmaNOscjLKGSNyj9M118vSWsOs1ZDbTwtj++h0O/BWrQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.451.0", - "@aws-sdk/middleware-logger": "3.451.0", - "@aws-sdk/middleware-recursion-detection": "3.451.0", - "@aws-sdk/middleware-user-agent": "3.451.0", - "@aws-sdk/region-config-resolver": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@aws-sdk/util-user-agent-browser": "3.451.0", - "@aws-sdk/util-user-agent-node": "3.451.0", - "@smithy/config-resolver": "^2.0.18", - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/hash-node": "^2.0.15", - "@smithy/invalid-dependency": "^2.0.13", - "@smithy/middleware-content-length": "^2.0.15", - "@smithy/middleware-endpoint": "^2.2.0", - "@smithy/middleware-retry": "^2.0.20", - "@smithy/middleware-serde": "^2.0.13", - "@smithy/middleware-stack": "^2.0.7", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.19", - "@smithy/util-defaults-mode-node": "^2.0.25", - "@smithy/util-endpoints": "^1.0.4", - "@smithy/util-retry": "^2.0.6", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/types": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.451.0.tgz", - "integrity": "sha512-rhK+qeYwCIs+laJfWCcrYEjay2FR/9VABZJ2NRM89jV/fKqGVQR52E5DQqrI+oEIL5JHMhhnr4N4fyECMS35lw==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-endpoints": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.451.0.tgz", - "integrity": "sha512-giqLGBTnRIcKkDqwU7+GQhKbtJ5Ku35cjGQIfMyOga6pwTBUbaK0xW1Sdd8sBQ1GhApscnChzI9o/R9x0368vw==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/util-endpoints": "^1.0.4", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.451.0.tgz", - "integrity": "sha512-Ws5mG3J0TQifH7OTcMrCTexo7HeSAc3cBgjfhS/ofzPUzVCtsyg0G7I6T7wl7vJJETix2Kst2cpOsxygPgPD9w==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/types": "^2.5.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.451.0.tgz", - "integrity": "sha512-TBzm6P+ql4mkGFAjPlO1CI+w3yUT+NulaiALjl/jNX/nnUp6HsJsVxJf4nVFQTG5KRV0iqMypcs7I3KIhH+LmA==", - "dependencies": { - "@aws-sdk/types": "3.451.0", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/abort-controller": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.13.tgz", - "integrity": "sha512-eeOPD+GF9BzF/Mjy3PICLePx4l0f3rG/nQegQHRLTloN5p1lSJJNZsyn+FzDnW8P2AduragZqJdtKNCxXozB1Q==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/config-resolver": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.18.tgz", - "integrity": "sha512-761sJSgNbvsqcsKW6/WZbrZr4H+0Vp/QKKqwyrxCPwD8BsiPEXNHyYnqNgaeK9xRWYswjon0Uxbpe3DWQo0j/g==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/types": "^2.5.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.6", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/credential-provider-imds": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.1.1.tgz", - "integrity": "sha512-gw5G3FjWC6sNz8zpOJgPpH5HGKrpoVFQpToNAwLwJVyI/LJ2jDJRjSKEsM6XI25aRpYjMSE/Qptxx305gN1vHw==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/property-provider": "^2.0.14", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/eventstream-codec": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.13.tgz", - "integrity": "sha512-CExbelIYp+DxAHG8RIs0l9QL7ElqhG4ym9BNoSpkPa4ptBQfzJdep3LbOSVJIE2VUdBAeObdeL6EDB3Jo85n3g==", - "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.5.0", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/fetch-http-handler": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.6.tgz", - "integrity": "sha512-PStY3XO1Ksjwn3wMKye5U6m6zxXpXrXZYqLy/IeCbh3nM9QB3Jgw/B0PUSLUWKdXg4U8qgEu300e3ZoBvZLsDg==", - "dependencies": { - "@smithy/protocol-http": "^3.0.9", - "@smithy/querystring-builder": "^2.0.13", - "@smithy/types": "^2.5.0", - "@smithy/util-base64": "^2.0.1", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/hash-node": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.15.tgz", - "integrity": "sha512-t/qjEJZu/G46A22PAk1k/IiJZT4ncRkG5GOCNWN9HPPy5rCcSZUbh7gwp7CGKgJJ7ATMMg+0Td7i9o1lQTwOfQ==", - "dependencies": { - "@smithy/types": "^2.5.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/invalid-dependency": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.13.tgz", - "integrity": "sha512-XsGYhVhvEikX1Yz0kyIoLssJf2Rs6E0U2w2YuKdT4jSra5A/g8V2oLROC1s56NldbgnpesTYB2z55KCHHbKyjw==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/middleware-content-length": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.15.tgz", - "integrity": "sha512-xH4kRBw01gJgWiU+/mNTrnyFXeozpZHw39gLb3JKGsFDVmSrJZ8/tRqu27tU/ki1gKkxr2wApu+dEYjI3QwV1Q==", - "dependencies": { - "@smithy/protocol-http": "^3.0.9", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/middleware-endpoint": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.2.0.tgz", - "integrity": "sha512-tddRmaig5URk2106PVMiNX6mc5BnKIKajHHDxb7K0J5MLdcuQluHMGnjkv18iY9s9O0tF+gAcPd/pDXA5L9DZw==", - "dependencies": { - "@smithy/middleware-serde": "^2.0.13", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/shared-ini-file-loader": "^2.2.4", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-middleware": "^2.0.6", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/middleware-retry": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.20.tgz", - "integrity": "sha512-X2yrF/SHDk2WDd8LflRNS955rlzQ9daz9UWSp15wW8KtzoTXg3bhHM78HbK1cjr48/FWERSJKh9AvRUUGlIawg==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/protocol-http": "^3.0.9", - "@smithy/service-error-classification": "^2.0.6", - "@smithy/types": "^2.5.0", - "@smithy/util-middleware": "^2.0.6", - "@smithy/util-retry": "^2.0.6", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/middleware-serde": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.13.tgz", - "integrity": "sha512-tBGbeXw+XsE6pPr4UaXOh+UIcXARZeiA8bKJWxk2IjJcD1icVLhBSUQH9myCIZLNNzJIH36SDjUX8Wqk4xJCJg==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/middleware-stack": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.7.tgz", - "integrity": "sha512-L1KLAAWkXbGx1t2jjCI/mDJ2dDNq+rp4/ifr/HcC6FHngxho5O7A5bQLpKHGlkfATH6fUnOEx0VICEVFA4sUzw==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/node-config-provider": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.5.tgz", - "integrity": "sha512-3Omb5/h4tOCuKRx4p4pkYTvEYRCYoKk52bOYbKUyz/G/8gERbagsN8jFm4FjQubkrcIqQEghTpQaUw6uk+0edw==", - "dependencies": { - "@smithy/property-provider": "^2.0.14", - "@smithy/shared-ini-file-loader": "^2.2.4", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/node-http-handler": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.9.tgz", - "integrity": "sha512-+K0q3SlNcocmo9OZj+fz67gY4lwhOCvIJxVbo/xH+hfWObvaxrMTx7JEzzXcluK0thnnLz++K3Qe7Z/8MDUreA==", - "dependencies": { - "@smithy/abort-controller": "^2.0.13", - "@smithy/protocol-http": "^3.0.9", - "@smithy/querystring-builder": "^2.0.13", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/property-provider": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.14.tgz", - "integrity": "sha512-k3D2qp9o6imTrLaXRj6GdLYEJr1sXqS99nLhzq8fYmJjSVOeMg/G+1KVAAc7Oxpu71rlZ2f8SSZxcSxkevuR0A==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/protocol-http": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", - "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/querystring-builder": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.13.tgz", - "integrity": "sha512-JhXKwp3JtsFUe96XLHy/nUPEbaXqn6r7xE4sNaH8bxEyytE5q1fwt0ew/Ke6+vIC7gP87HCHgQpJHg1X1jN2Fw==", - "dependencies": { - "@smithy/types": "^2.5.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/querystring-parser": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.13.tgz", - "integrity": "sha512-TEiT6o8CPZVxJ44Rly/rrsATTQsE+b/nyBVzsYn2sa75xAaZcurNxsFd8z1haoUysONiyex24JMHoJY6iCfLdA==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/service-error-classification": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.6.tgz", - "integrity": "sha512-fCQ36frtYra2fqY2/DV8+3/z2d0VB/1D1hXbjRcM5wkxTToxq6xHbIY/NGGY6v4carskMyG8FHACxgxturJ9Pg==", - "dependencies": { - "@smithy/types": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/shared-ini-file-loader": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.4.tgz", - "integrity": "sha512-9dRknGgvYlRIsoTcmMJXuoR/3ekhGwhRq4un3ns2/byre4Ql5hyUN4iS0x8eITohjU90YOnUCsbRwZRvCkbRfw==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/signature-v4": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.15.tgz", - "integrity": "sha512-SRTEJSEhQYVlBKIIdZ9SZpqW+KFqxqcNnEcBX+8xkDdWx+DItme9VcCDkdN32yTIrICC+irUufnUdV7mmHPjoA==", - "dependencies": { - "@smithy/eventstream-codec": "^2.0.13", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.5.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.6", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/smithy-client": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.15.tgz", - "integrity": "sha512-rngZcQu7Jvs9UbHihK1EI67RMPuzkc3CJmu4MBgB7D7yBnMGuFR86tq5rqHfL2gAkNnMelBN/8kzQVvZjNKefQ==", - "dependencies": { - "@smithy/middleware-stack": "^2.0.7", - "@smithy/types": "^2.5.0", - "@smithy/util-stream": "^2.0.20", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.5.0.tgz", - "integrity": "sha512-/a31lYofrMBkJb3BuPlYJTMKDj0hUmKUP6JFZQu6YVuQVoAjubiY0A52U9S0Uysd33n/djexCUSNJ+G9bf3/aA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/url-parser": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.13.tgz", - "integrity": "sha512-okWx2P/d9jcTsZWTVNnRMpFOE7fMkzloSFyM53fA7nLKJQObxM2T4JlZ5KitKKuXq7pxon9J6SF2kCwtdflIrA==", - "dependencies": { - "@smithy/querystring-parser": "^2.0.13", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-base64": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", - "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "dependencies": { - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.19.tgz", - "integrity": "sha512-VHP8xdFR7/orpiABJwgoTB0t8Zhhwpf93gXhNfUBiwAE9O0rvsv7LwpQYjgvbOUDDO8JfIYQB2GYJNkqqGWsXw==", - "dependencies": { - "@smithy/property-provider": "^2.0.14", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.25", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.25.tgz", - "integrity": "sha512-jkmep6/JyWmn2ADw9VULDeGbugR4N/FJCKOt+gYyVswmN1BJOfzF2umaYxQ1HhQDvna3kzm1Dbo1qIfBW4iuHA==", - "dependencies": { - "@smithy/config-resolver": "^2.0.18", - "@smithy/credential-provider-imds": "^2.1.1", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/property-provider": "^2.0.14", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.6.tgz", - "integrity": "sha512-7W4uuwBvSLgKoLC1x4LfeArCVcbuHdtVaC4g30kKsD1erfICyQ45+tFhhs/dZNeQg+w392fhunCm/+oCcb6BSA==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-retry": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.6.tgz", - "integrity": "sha512-PSO41FofOBmyhPQJwBQJ6mVlaD7Sp9Uff9aBbnfBJ9eqXOE/obrqQjn0PNdkfdvViiPXl49BINfnGcFtSP4kYw==", - "dependencies": { - "@smithy/service-error-classification": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-stream": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.20.tgz", - "integrity": "sha512-tT8VASuD8jJu0yjHEMTCPt1o5E3FVzgdsxK6FQLAjXKqVv5V8InCnc0EOsYrijgspbfDqdAJg7r0o2sySfcHVg==", - "dependencies": { - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/types": "^2.5.0", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@smithy/util-utf8": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", - "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.388.0.tgz", - "integrity": "sha512-5sCogMJ1utRlwLQiameyOrrcyhueknbsC2YK1G9Y7pgmgUl2zzUo7htQS2luW71SeBHiwkTQa3OZjbmGsotJvg==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.388.0", - "@aws-sdk/credential-provider-node": "3.388.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/client-sso": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.387.0.tgz", - "integrity": "sha512-E7uKSvbA0XMKSN5KLInf52hmMpe9/OKo6N9OPffGXdn3fNEQlvyQq3meUkqG7Is0ldgsQMz5EUBNtNybXzr3tQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/client-sts": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.388.0.tgz", - "integrity": "sha512-y9FAcAYHT8O6T/jqhgsIQUb4gLiSTKD3xtzudDvjmFi8gl0oRIY1npbeckSiK6k07VQugm2s64I0nDnDxtWsBg==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.388.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-sdk-sts": "3.387.0", - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.387.0.tgz", - "integrity": "sha512-PVqNk7XPIYe5CMYNvELkcALtkl/pIM8/uPtqEtTg+mgnZBeL4fAmgXZiZMahQo1DxP5t/JaK384f6JG+A0qDjA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.388.0.tgz", - "integrity": "sha512-3dg3A8AiZ5vXkSAYyyI3V/AW3Eo6KQJyE/glA+Nr2M0oAjT4z3vHhS3pf2B+hfKGZBTuKKgxusrrhrQABd/Diw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.388.0.tgz", - "integrity": "sha512-BqWAkIG08gj/wevpesaZhAjALjfUNVjseHQRk+DNUoHIfyibW7Ahf3q/GIPs11dA2o8ECwR9/fo68Sq+sK799A==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-ini": "3.388.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.387.0.tgz", - "integrity": "sha512-tQScLHmDlqkQN+mqw4s3cxepEUeHYDhFl5eH+J8puvPqWjXMYpCEdY79SAtWs6SZd4CWiZ0VLeYU6xQBZengbQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.388.0.tgz", - "integrity": "sha512-RH02+rntaO0UhnSBr42n+7q8HOztc+Dets/hh6cWovf3Yi9s9ghLgYLN9FXpSosfot3XkmT/HOCa+CphAmGN9A==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/client-sso": "3.387.0", - "@aws-sdk/token-providers": "3.388.0", - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.387.0.tgz", - "integrity": "sha512-6ueMPl+J3KWv6ZaAWF4Z138QCuBVFZRVAgwbtP3BNqWrrs4Q6TPksOQJ79lRDMpv0EUoyVl04B6lldNlhN8RdA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.387.0.tgz", - "integrity": "sha512-EWm9PXSr8dSp7hnRth1U7OfelXQp9dLf1yS1kUL+UhppYDJpjhdP7ql3NI4xJKw8e76sP2FuJYEuzWnJHuWoyQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-logger": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.387.0.tgz", - "integrity": "sha512-FjAvJr1XyaInT81RxUwgifnbXoFJrRBFc64XeFJgFanGIQCWLYxRrK2HV9eBpao/AycbmuoHgLd/f0sa4hZFoQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.387.0.tgz", - "integrity": "sha512-ZF45T785ru8OwvYZw6awD9Z76OwSMM1eZzj2eY+FDz1cHfkpLjxEiti2iIH1FxbyK7n9ZqDUx29lVlCv238YyQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.387.0.tgz", - "integrity": "sha512-7ZzRKOJ4V/JDQmKz9z+FjZqw59mrMATEMLR6ff0H0JHMX0Uk5IX8TQB058ss+ar14qeJ4UcteYzCqHNI0O1BHw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-signing": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.387.0.tgz", - "integrity": "sha512-oJXlE0MES8gxNLo137PPNNiOICQGOaETTvq3kBSJgb/gtEAxQajMIlaNT7s1wsjOAruFHt4975nCXuY4lpx7GQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.387.0.tgz", - "integrity": "sha512-hTfFTwDtp86xS98BKa+RFuLfcvGftxwzrbZeisZV8hdb4ZhvNXjSxnvM3vetW0GUEnY9xHPSGyp2ERRTinPKFQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/token-providers": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.388.0.tgz", - "integrity": "sha512-2lo1gFJl624kfjo/YdU6zW+k6dEwhoqjNkDNbOZEFgS1KDofHe9GX8W4/ReKb0Ggho5/EcjzZ53/1CjkzUq4tA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/types": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.387.0.tgz", - "integrity": "sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-endpoints": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.387.0.tgz", - "integrity": "sha512-g7kvuCXehGXHHBw9PkSQdwVyDFmNUZLmfrRmqMyrMDG9QLQrxr4pyWcSaYgTE16yUzhQQOR+QSey+BL6W9/N6g==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.387.0.tgz", - "integrity": "sha512-lpgSVvDqx+JjHZCTYs/yQSS7J71dPlJeAlvxc7bmx5m+vfwKe07HAnIs+929DngS0QbAp/VaXbTiMFsInLkO4Q==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.387.0.tgz", - "integrity": "sha512-r9OVkcWpRYatjLhJacuHFgvO2T5s/Nu5DDbScMrkUD8b4aGIIqsrdZji0vZy9FCjsUFQMM92t9nt4SejrGjChA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/abort-controller": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.2.tgz", - "integrity": "sha512-ln5Cob0mksym62sLr7NiPOSqJ0jKao4qjfcNLDdgINM1lQI12hXrZBlKdPHbXJqpKhKiECDgonMoqCM8bigq4g==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/config-resolver": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.2.tgz", - "integrity": "sha512-0kdsqBL6BdmSbdU6YaDkodVBMua5MuQQluC3nocJ7OJ6PnOuM7i2FEQHE46LBadLqT+CimlDSM+6j91uHNL1ng==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/credential-provider-imds": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.2.tgz", - "integrity": "sha512-mbWFYEZ00LBRDk3WvcXViwpdpkJQcfrM3seuKzFxZnF6wIBLMwrcWcsj+OUC/1L+86m8aQY9imXMAaQsAoGxow==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/node-config-provider": "^2.0.2", - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/eventstream-codec": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.2.tgz", - "integrity": "sha512-PQZiKx7fMnNwx4zxcUCm82VjnqK6wV4MEHSmMy3taj5dKfXV782IjRGyaDT+8TsmNqVdZIkve5zLRAzh+7kOhA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/fetch-http-handler": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.0.2.tgz", - "integrity": "sha512-Wo2m1RaiXNSLF4J3D62LpdSoj/YYb+6tn0H8is1tSrzr7eXAdiYVBc0wIa23N0wT4zmN0iG/yNY6gTCDQ6799A==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/querystring-builder": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/util-base64": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/hash-node": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.2.tgz", - "integrity": "sha512-JKDzZ1YVR7JzOBaJoWy3ToJCE86OQE6D4kOBvvVsu93a3lcF9kv6KYTKBYEWAjwOn/CpK4NH7mKB01OQ8H+aiA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/invalid-dependency": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.2.tgz", - "integrity": "sha512-inQZQ5gCO3WRWuXpsc1YJ4KBjsvj2qsoU32yTIKznBWTCQe/D5Dp+sSaysqBqxe0VTZ+8nFEHdUMWUX2BxQThw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-content-length": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.2.tgz", - "integrity": "sha512-FmHlNfuvYgDZE3fIx0G3rD/wLXfAmBYE4mVc/w6d7RllA7TygPzq2pfHL1iCMzWkWTdoAVnt3h4aavAZnhaxEQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-endpoint": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.2.tgz", - "integrity": "sha512-ropE7/c+g22QeluZ+By/B/WvVep0UFreX+IeRMGIO7EbOUPgqtJRXpbJFdG6JKB1uC+CdaJLn4MnZnVBpcyjuA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/middleware-serde": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-retry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.2.tgz", - "integrity": "sha512-wtBUXqtZVriiXppYaFkUrybAPhFVX7vebnW/yVPliLMWMcguOMS58qhOYPZe3t9Wki2+mASfyu+kO3An8lAg2A==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/service-error-classification": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-retry": "^2.0.0", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-serde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.2.tgz", - "integrity": "sha512-Kw9xLdlueIaivUWslKB67WZ/cCUg3QnzYVIA3t5KfgsseEEuU4UxXw8NSTvIt71gqQloY+Um8ugS+idgxrWWnw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/middleware-stack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.0.tgz", - "integrity": "sha512-31XC1xNF65nlbc16yuh3wwTudmqs6qy4EseQUGF8A/p2m/5wdd/cnXJqpniy/XvXVwkHPz/GwV36HqzHtIKATQ==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-config-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.2.tgz", - "integrity": "sha512-9wVJccASfuCctNWrzR0zrDkf0ox3HCHGEhFlWL2LBoghUYuK28pVRBbG69wvnkhlHnB8dDZHagxH+Nq9dm7eWw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/property-provider": "^2.0.2", - "@smithy/shared-ini-file-loader": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/node-http-handler": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.0.2.tgz", - "integrity": "sha512-lpZjmtmyZqSAtMPsbrLhb7XoAQ2kAHeuLY/csW6I2k+QyFvOk7cZeQsqEngWmZ9SJaeYiDCBINxAIM61i5WGLw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/abort-controller": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/querystring-builder": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/property-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.2.tgz", - "integrity": "sha512-DfaZ8cO+d/mgnMzIllcXcU4OYP+omiOl2LYdn/fTGpw/EAQSVzscYV2muV3sDDnuPYQ/r014hUqIxnF+pzh+SQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/protocol-http": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-2.0.2.tgz", - "integrity": "sha512-qWu8g1FUy+m36KpO1sREJSF7BaLmjw9AqOuwxLVVSdYz+nUQjc9tFAZ9LB6jJXKdsZFSjfkjHJBbhD78QdE7Rw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-builder": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.2.tgz", - "integrity": "sha512-H99LOMWEssfwqkOoTs4Y12UiZ7CTGQSX5Nrx5UkYgRbUEpC1GnnaprHiYrqclC58/xr4K76aNchdPyioxewMzA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/querystring-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.2.tgz", - "integrity": "sha512-L4VtKQ8O4/aWPQJbiFymbhAmxdfLnEaROh/Vs0OstJ7jtOZeBl2QJmuWY2V7hjt64W7V+tEn2sv6vVvnxkm/xQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/service-error-classification": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.0.tgz", - "integrity": "sha512-2z5Nafy1O0cTf69wKyNjGW/sNVMiqDnb4jgwfMG8ye8KnFJ5qmJpDccwIbJNhXIfbsxTg9SEec2oe1cexhMJvw==", - "optional": true, - "peer": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/shared-ini-file-loader": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.2.tgz", - "integrity": "sha512-2VkNOM/82u4vatVdK5nfusgGIlvR48Fkq6me17Oc+V1iyxfR/1x0pG6LzW0br1qlGtzBYFZKmDyviBRcPVFTVw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/signature-v4": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.2.tgz", - "integrity": "sha512-YMooDEw/UmGxcXY4qWnSXkbPFsRloluSvyXVT678YPDN/K2AS1GzKfRsvSU7fbccOB4WF8MHZf2UqcRGEltE3Q==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/eventstream-codec": "^2.0.2", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/smithy-client": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.0.2.tgz", - "integrity": "sha512-mDfokI8WwLU5C0gcQ4ww/zJI/WLGSh2+vdIA42JRnjfYUjJNH/rKfX9YOnn2eBOxl3loATERVUqkHmKe+P8s2Q==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/middleware-stack": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-stream": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.1.0.tgz", - "integrity": "sha512-KLsCsqxX0j2l99iP8s0f7LBlcsp7a7ceXGn0LPYPyVOsqmIKvSaPQajq0YevlL4T9Bm+DtcyXfBTbtBcLX1I7A==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/url-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.2.tgz", - "integrity": "sha512-X1mHCzrSVDlhVy7d3S7Vq+dTfYzwh4n7xGHhyJumu77nJqIss0lazVug85Pwo0DKIoO314wAOvMnBxNYDa+7wA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/querystring-parser": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-base64": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", - "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-body-length-node": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.0.0.tgz", - "integrity": "sha512-ZV7Z/WHTMxHJe/xL/56qZwSUcl63/5aaPAGjkfynJm4poILjdD4GmFI+V+YWabh2WJIjwTKZ5PNsuvPQKt93Mg==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.2.tgz", - "integrity": "sha512-c2tMMjb624XLuzmlRoZpnFOkejVxcgw3WQKdmgdGZYZapcLzXyC0H9JhnXMjQCt30GqLTlsILRNVBYwFRbw/4Q==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.2.tgz", - "integrity": "sha512-gt7m5LLqUtEKldJLyc14DE4kb85vxwomvt9AfEMEvWM4VwfWS1kGJqiStZFb5KNqnQPXw8vvpgLTi8NrWAOXqg==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/config-resolver": "^2.0.2", - "@smithy/credential-provider-imds": "^2.0.2", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-middleware": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.0.tgz", - "integrity": "sha512-eCWX4ECuDHn1wuyyDdGdUWnT4OGyIzV0LN1xRttBFMPI9Ff/4heSHVxneyiMtOB//zpXWCha1/SWHJOZstG7kA==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-retry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.0.tgz", - "integrity": "sha512-/dvJ8afrElasuiiIttRJeoS2sy8YXpksQwiM/TcepqdRVp7u4ejd9C4IQURHNjlfPUT7Y6lCDSa2zQJbdHhVTg==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/service-error-classification": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-stream": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.2.tgz", - "integrity": "sha512-Mg9IJcKIu4YKlbzvpp1KLvh4JZLdcPgpxk+LICuDwzZCfxe47R9enVK8dNEiuyiIGK2ExbfvzCVT8IBru62vZw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-cognito-identity/node_modules/@smithy/util-utf8": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", - "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.370.0.tgz", - "integrity": "sha512-1o1mpWbI1RyzCQ4cVpHQJnm6PziAJ+ptLt4p+wlN74Z330/nnE0JkK3t9l3CxhPqCIW8VjGbTCno5IzwAXnjPw==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.370.0", - "@aws-sdk/credential-provider-node": "3.370.0", - "@aws-sdk/middleware-host-header": "3.370.0", - "@aws-sdk/middleware-logger": "3.370.0", - "@aws-sdk/middleware-recursion-detection": "3.370.0", - "@aws-sdk/middleware-signing": "3.370.0", - "@aws-sdk/middleware-user-agent": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@aws-sdk/util-user-agent-browser": "3.370.0", - "@aws-sdk/util-user-agent-node": "3.370.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.2", - "@smithy/middleware-retry": "^1.0.3", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.3", - "@smithy/util-utf8": "^1.0.1", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.370.0.tgz", - "integrity": "sha512-0Ty1iHuzNxMQtN7nahgkZr4Wcu1XvqGfrQniiGdKKif9jG/4elxsQPiydRuQpFqN6b+bg7wPP7crFP1uTxx2KQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.370.0", - "@aws-sdk/middleware-logger": "3.370.0", - "@aws-sdk/middleware-recursion-detection": "3.370.0", - "@aws-sdk/middleware-user-agent": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@aws-sdk/util-user-agent-browser": "3.370.0", - "@aws-sdk/util-user-agent-node": "3.370.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.2", - "@smithy/middleware-retry": "^1.0.3", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.3", - "@smithy/util-utf8": "^1.0.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.370.0.tgz", - "integrity": "sha512-jAYOO74lmVXylQylqkPrjLzxvUnMKw476JCUTvCO6Q8nv3LzCWd76Ihgv/m9Q4M2Tbqi1iP2roVK5bstsXzEjA==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.370.0", - "@aws-sdk/middleware-logger": "3.370.0", - "@aws-sdk/middleware-recursion-detection": "3.370.0", - "@aws-sdk/middleware-user-agent": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@aws-sdk/util-user-agent-browser": "3.370.0", - "@aws-sdk/util-user-agent-node": "3.370.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.2", - "@smithy/middleware-retry": "^1.0.3", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.3", - "@smithy/util-utf8": "^1.0.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.370.0.tgz", - "integrity": "sha512-utFxOPWIzbN+3kc415Je2o4J72hOLNhgR2Gt5EnRSggC3yOnkC4GzauxG8n7n5gZGBX45eyubHyPOXLOIyoqQA==", - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.370.0", - "@aws-sdk/middleware-host-header": "3.370.0", - "@aws-sdk/middleware-logger": "3.370.0", - "@aws-sdk/middleware-recursion-detection": "3.370.0", - "@aws-sdk/middleware-sdk-sts": "3.370.0", - "@aws-sdk/middleware-signing": "3.370.0", - "@aws-sdk/middleware-user-agent": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@aws-sdk/util-user-agent-browser": "3.370.0", - "@aws-sdk/util-user-agent-node": "3.370.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.2", - "@smithy/middleware-retry": "^1.0.3", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.3", - "@smithy/util-utf8": "^1.0.1", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.451.0.tgz", - "integrity": "sha512-SamWW2zHEf1ZKe3j1w0Piauryl8BQIlej0TBS18A4ACzhjhWXhCs13bO1S88LvPR5mBFXok3XOT6zPOnKDFktw==", - "dependencies": { - "@smithy/smithy-client": "^2.1.15", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/abort-controller": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.13.tgz", - "integrity": "sha512-eeOPD+GF9BzF/Mjy3PICLePx4l0f3rG/nQegQHRLTloN5p1lSJJNZsyn+FzDnW8P2AduragZqJdtKNCxXozB1Q==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/fetch-http-handler": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.6.tgz", - "integrity": "sha512-PStY3XO1Ksjwn3wMKye5U6m6zxXpXrXZYqLy/IeCbh3nM9QB3Jgw/B0PUSLUWKdXg4U8qgEu300e3ZoBvZLsDg==", - "dependencies": { - "@smithy/protocol-http": "^3.0.9", - "@smithy/querystring-builder": "^2.0.13", - "@smithy/types": "^2.5.0", - "@smithy/util-base64": "^2.0.1", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/middleware-stack": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.7.tgz", - "integrity": "sha512-L1KLAAWkXbGx1t2jjCI/mDJ2dDNq+rp4/ifr/HcC6FHngxho5O7A5bQLpKHGlkfATH6fUnOEx0VICEVFA4sUzw==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/node-http-handler": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.9.tgz", - "integrity": "sha512-+K0q3SlNcocmo9OZj+fz67gY4lwhOCvIJxVbo/xH+hfWObvaxrMTx7JEzzXcluK0thnnLz++K3Qe7Z/8MDUreA==", - "dependencies": { - "@smithy/abort-controller": "^2.0.13", - "@smithy/protocol-http": "^3.0.9", - "@smithy/querystring-builder": "^2.0.13", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/protocol-http": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", - "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/querystring-builder": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.13.tgz", - "integrity": "sha512-JhXKwp3JtsFUe96XLHy/nUPEbaXqn6r7xE4sNaH8bxEyytE5q1fwt0ew/Ke6+vIC7gP87HCHgQpJHg1X1jN2Fw==", - "dependencies": { - "@smithy/types": "^2.5.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/smithy-client": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.15.tgz", - "integrity": "sha512-rngZcQu7Jvs9UbHihK1EI67RMPuzkc3CJmu4MBgB7D7yBnMGuFR86tq5rqHfL2gAkNnMelBN/8kzQVvZjNKefQ==", - "dependencies": { - "@smithy/middleware-stack": "^2.0.7", - "@smithy/types": "^2.5.0", - "@smithy/util-stream": "^2.0.20", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.5.0.tgz", - "integrity": "sha512-/a31lYofrMBkJb3BuPlYJTMKDj0hUmKUP6JFZQu6YVuQVoAjubiY0A52U9S0Uysd33n/djexCUSNJ+G9bf3/aA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/util-base64": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", - "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/util-stream": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.20.tgz", - "integrity": "sha512-tT8VASuD8jJu0yjHEMTCPt1o5E3FVzgdsxK6FQLAjXKqVv5V8InCnc0EOsYrijgspbfDqdAJg7r0o2sySfcHVg==", - "dependencies": { - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/types": "^2.5.0", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/core/node_modules/@smithy/util-utf8": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", - "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.388.0.tgz", - "integrity": "sha512-j1oyBc0/O76YouOC2wMZuQUfHOjfrKWgBibIwrwqEqacYWMx/IBxZkk9j2fFerIVaKhhMNkZHAGb+qBx0urR/Q==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.388.0", - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@aws-sdk/types": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.387.0.tgz", - "integrity": "sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/property-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.2.tgz", - "integrity": "sha512-DfaZ8cO+d/mgnMzIllcXcU4OYP+omiOl2LYdn/fTGpw/EAQSVzscYV2muV3sDDnuPYQ/r014hUqIxnF+pzh+SQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-cognito-identity/node_modules/@smithy/types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.1.0.tgz", - "integrity": "sha512-KLsCsqxX0j2l99iP8s0f7LBlcsp7a7ceXGn0LPYPyVOsqmIKvSaPQajq0YevlL4T9Bm+DtcyXfBTbtBcLX1I7A==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.370.0.tgz", - "integrity": "sha512-raR3yP/4GGbKFRPP5hUBNkEmTnzxI9mEc2vJAJrcv4G4J4i/UP6ELiLInQ5eO2/VcV/CeKGZA3t7d1tsJ+jhCg==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.370.0.tgz", - "integrity": "sha512-eJyapFKa4NrC9RfTgxlXnXfS9InG/QMEUPPVL+VhG7YS6nKqetC1digOYgivnEeu+XSKE0DJ7uZuXujN2Y7VAQ==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.370.0", - "@aws-sdk/credential-provider-process": "3.370.0", - "@aws-sdk/credential-provider-sso": "3.370.0", - "@aws-sdk/credential-provider-web-identity": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/credential-provider-imds": "^1.0.1", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.370.0.tgz", - "integrity": "sha512-gkFiotBFKE4Fcn8CzQnMeab9TAR06FEAD02T4ZRYW1xGrBJOowmje9dKqdwQFHSPgnWAP+8HoTA8iwbhTLvjNA==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.370.0", - "@aws-sdk/credential-provider-ini": "3.370.0", - "@aws-sdk/credential-provider-process": "3.370.0", - "@aws-sdk/credential-provider-sso": "3.370.0", - "@aws-sdk/credential-provider-web-identity": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/credential-provider-imds": "^1.0.1", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.370.0.tgz", - "integrity": "sha512-0BKFFZmUO779Xdw3u7wWnoWhYA4zygxJbgGVSyjkOGBvdkbPSTTcdwT1KFkaQy2kOXYeZPl+usVVRXs+ph4ejg==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.370.0.tgz", - "integrity": "sha512-PFroYm5hcPSfC/jkZnCI34QFL3I7WVKveVk6/F3fud/cnP8hp6YjA9NiTNbqdFSzsyoiN/+e5fZgNKih8vVPTA==", - "dependencies": { - "@aws-sdk/client-sso": "3.370.0", - "@aws-sdk/token-providers": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.370.0.tgz", - "integrity": "sha512-CFaBMLRudwhjv1sDzybNV93IaT85IwS+L8Wq6VRMa0mro1q9rrWsIZO811eF+k0NEPfgU1dLH+8Vc2qhw4SARQ==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.388.0.tgz", - "integrity": "sha512-5opHLYjj6rHrw2OaxE+IcLhC9JfiopPH7hRknzKjFnSrJ+HjzcHCML5xghwHLJOLGcoWU40CCSlwJVPLlJluMw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.388.0", - "@aws-sdk/client-sso": "3.387.0", - "@aws-sdk/client-sts": "3.388.0", - "@aws-sdk/credential-provider-cognito-identity": "3.388.0", - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-ini": "3.388.0", - "@aws-sdk/credential-provider-node": "3.388.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/client-sso": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.387.0.tgz", - "integrity": "sha512-E7uKSvbA0XMKSN5KLInf52hmMpe9/OKo6N9OPffGXdn3fNEQlvyQq3meUkqG7Is0ldgsQMz5EUBNtNybXzr3tQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/client-sts": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.388.0.tgz", - "integrity": "sha512-y9FAcAYHT8O6T/jqhgsIQUb4gLiSTKD3xtzudDvjmFi8gl0oRIY1npbeckSiK6k07VQugm2s64I0nDnDxtWsBg==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.388.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-sdk-sts": "3.387.0", - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.387.0.tgz", - "integrity": "sha512-PVqNk7XPIYe5CMYNvELkcALtkl/pIM8/uPtqEtTg+mgnZBeL4fAmgXZiZMahQo1DxP5t/JaK384f6JG+A0qDjA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.388.0.tgz", - "integrity": "sha512-3dg3A8AiZ5vXkSAYyyI3V/AW3Eo6KQJyE/glA+Nr2M0oAjT4z3vHhS3pf2B+hfKGZBTuKKgxusrrhrQABd/Diw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.388.0.tgz", - "integrity": "sha512-BqWAkIG08gj/wevpesaZhAjALjfUNVjseHQRk+DNUoHIfyibW7Ahf3q/GIPs11dA2o8ECwR9/fo68Sq+sK799A==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-ini": "3.388.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.387.0.tgz", - "integrity": "sha512-tQScLHmDlqkQN+mqw4s3cxepEUeHYDhFl5eH+J8puvPqWjXMYpCEdY79SAtWs6SZd4CWiZ0VLeYU6xQBZengbQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.388.0.tgz", - "integrity": "sha512-RH02+rntaO0UhnSBr42n+7q8HOztc+Dets/hh6cWovf3Yi9s9ghLgYLN9FXpSosfot3XkmT/HOCa+CphAmGN9A==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/client-sso": "3.387.0", - "@aws-sdk/token-providers": "3.388.0", - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.387.0.tgz", - "integrity": "sha512-6ueMPl+J3KWv6ZaAWF4Z138QCuBVFZRVAgwbtP3BNqWrrs4Q6TPksOQJ79lRDMpv0EUoyVl04B6lldNlhN8RdA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.387.0.tgz", - "integrity": "sha512-EWm9PXSr8dSp7hnRth1U7OfelXQp9dLf1yS1kUL+UhppYDJpjhdP7ql3NI4xJKw8e76sP2FuJYEuzWnJHuWoyQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-logger": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.387.0.tgz", - "integrity": "sha512-FjAvJr1XyaInT81RxUwgifnbXoFJrRBFc64XeFJgFanGIQCWLYxRrK2HV9eBpao/AycbmuoHgLd/f0sa4hZFoQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.387.0.tgz", - "integrity": "sha512-ZF45T785ru8OwvYZw6awD9Z76OwSMM1eZzj2eY+FDz1cHfkpLjxEiti2iIH1FxbyK7n9ZqDUx29lVlCv238YyQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.387.0.tgz", - "integrity": "sha512-7ZzRKOJ4V/JDQmKz9z+FjZqw59mrMATEMLR6ff0H0JHMX0Uk5IX8TQB058ss+ar14qeJ4UcteYzCqHNI0O1BHw==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-signing": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.387.0.tgz", - "integrity": "sha512-oJXlE0MES8gxNLo137PPNNiOICQGOaETTvq3kBSJgb/gtEAxQajMIlaNT7s1wsjOAruFHt4975nCXuY4lpx7GQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.387.0.tgz", - "integrity": "sha512-hTfFTwDtp86xS98BKa+RFuLfcvGftxwzrbZeisZV8hdb4ZhvNXjSxnvM3vetW0GUEnY9xHPSGyp2ERRTinPKFQ==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/token-providers": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.388.0.tgz", - "integrity": "sha512-2lo1gFJl624kfjo/YdU6zW+k6dEwhoqjNkDNbOZEFgS1KDofHe9GX8W4/ReKb0Ggho5/EcjzZ53/1CjkzUq4tA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/types": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.387.0.tgz", - "integrity": "sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-endpoints": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.387.0.tgz", - "integrity": "sha512-g7kvuCXehGXHHBw9PkSQdwVyDFmNUZLmfrRmqMyrMDG9QLQrxr4pyWcSaYgTE16yUzhQQOR+QSey+BL6W9/N6g==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.387.0.tgz", - "integrity": "sha512-lpgSVvDqx+JjHZCTYs/yQSS7J71dPlJeAlvxc7bmx5m+vfwKe07HAnIs+929DngS0QbAp/VaXbTiMFsInLkO4Q==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.387.0.tgz", - "integrity": "sha512-r9OVkcWpRYatjLhJacuHFgvO2T5s/Nu5DDbScMrkUD8b4aGIIqsrdZji0vZy9FCjsUFQMM92t9nt4SejrGjChA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-sdk/types": "3.387.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/abort-controller": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.2.tgz", - "integrity": "sha512-ln5Cob0mksym62sLr7NiPOSqJ0jKao4qjfcNLDdgINM1lQI12hXrZBlKdPHbXJqpKhKiECDgonMoqCM8bigq4g==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/config-resolver": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.2.tgz", - "integrity": "sha512-0kdsqBL6BdmSbdU6YaDkodVBMua5MuQQluC3nocJ7OJ6PnOuM7i2FEQHE46LBadLqT+CimlDSM+6j91uHNL1ng==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/credential-provider-imds": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.2.tgz", - "integrity": "sha512-mbWFYEZ00LBRDk3WvcXViwpdpkJQcfrM3seuKzFxZnF6wIBLMwrcWcsj+OUC/1L+86m8aQY9imXMAaQsAoGxow==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/node-config-provider": "^2.0.2", - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/eventstream-codec": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.2.tgz", - "integrity": "sha512-PQZiKx7fMnNwx4zxcUCm82VjnqK6wV4MEHSmMy3taj5dKfXV782IjRGyaDT+8TsmNqVdZIkve5zLRAzh+7kOhA==", - "optional": true, - "peer": true, - "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/fetch-http-handler": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.0.2.tgz", - "integrity": "sha512-Wo2m1RaiXNSLF4J3D62LpdSoj/YYb+6tn0H8is1tSrzr7eXAdiYVBc0wIa23N0wT4zmN0iG/yNY6gTCDQ6799A==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/querystring-builder": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/util-base64": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/hash-node": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.2.tgz", - "integrity": "sha512-JKDzZ1YVR7JzOBaJoWy3ToJCE86OQE6D4kOBvvVsu93a3lcF9kv6KYTKBYEWAjwOn/CpK4NH7mKB01OQ8H+aiA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/invalid-dependency": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.2.tgz", - "integrity": "sha512-inQZQ5gCO3WRWuXpsc1YJ4KBjsvj2qsoU32yTIKznBWTCQe/D5Dp+sSaysqBqxe0VTZ+8nFEHdUMWUX2BxQThw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-content-length": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.2.tgz", - "integrity": "sha512-FmHlNfuvYgDZE3fIx0G3rD/wLXfAmBYE4mVc/w6d7RllA7TygPzq2pfHL1iCMzWkWTdoAVnt3h4aavAZnhaxEQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-endpoint": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.2.tgz", - "integrity": "sha512-ropE7/c+g22QeluZ+By/B/WvVep0UFreX+IeRMGIO7EbOUPgqtJRXpbJFdG6JKB1uC+CdaJLn4MnZnVBpcyjuA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/middleware-serde": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-retry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.2.tgz", - "integrity": "sha512-wtBUXqtZVriiXppYaFkUrybAPhFVX7vebnW/yVPliLMWMcguOMS58qhOYPZe3t9Wki2+mASfyu+kO3An8lAg2A==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/service-error-classification": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-retry": "^2.0.0", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-serde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.2.tgz", - "integrity": "sha512-Kw9xLdlueIaivUWslKB67WZ/cCUg3QnzYVIA3t5KfgsseEEuU4UxXw8NSTvIt71gqQloY+Um8ugS+idgxrWWnw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/middleware-stack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.0.tgz", - "integrity": "sha512-31XC1xNF65nlbc16yuh3wwTudmqs6qy4EseQUGF8A/p2m/5wdd/cnXJqpniy/XvXVwkHPz/GwV36HqzHtIKATQ==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-config-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.2.tgz", - "integrity": "sha512-9wVJccASfuCctNWrzR0zrDkf0ox3HCHGEhFlWL2LBoghUYuK28pVRBbG69wvnkhlHnB8dDZHagxH+Nq9dm7eWw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/property-provider": "^2.0.2", - "@smithy/shared-ini-file-loader": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/node-http-handler": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.0.2.tgz", - "integrity": "sha512-lpZjmtmyZqSAtMPsbrLhb7XoAQ2kAHeuLY/csW6I2k+QyFvOk7cZeQsqEngWmZ9SJaeYiDCBINxAIM61i5WGLw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/abort-controller": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/querystring-builder": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/property-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.2.tgz", - "integrity": "sha512-DfaZ8cO+d/mgnMzIllcXcU4OYP+omiOl2LYdn/fTGpw/EAQSVzscYV2muV3sDDnuPYQ/r014hUqIxnF+pzh+SQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/protocol-http": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-2.0.2.tgz", - "integrity": "sha512-qWu8g1FUy+m36KpO1sREJSF7BaLmjw9AqOuwxLVVSdYz+nUQjc9tFAZ9LB6jJXKdsZFSjfkjHJBbhD78QdE7Rw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-builder": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.2.tgz", - "integrity": "sha512-H99LOMWEssfwqkOoTs4Y12UiZ7CTGQSX5Nrx5UkYgRbUEpC1GnnaprHiYrqclC58/xr4K76aNchdPyioxewMzA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/querystring-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.2.tgz", - "integrity": "sha512-L4VtKQ8O4/aWPQJbiFymbhAmxdfLnEaROh/Vs0OstJ7jtOZeBl2QJmuWY2V7hjt64W7V+tEn2sv6vVvnxkm/xQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/service-error-classification": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.0.tgz", - "integrity": "sha512-2z5Nafy1O0cTf69wKyNjGW/sNVMiqDnb4jgwfMG8ye8KnFJ5qmJpDccwIbJNhXIfbsxTg9SEec2oe1cexhMJvw==", - "optional": true, - "peer": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/shared-ini-file-loader": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.2.tgz", - "integrity": "sha512-2VkNOM/82u4vatVdK5nfusgGIlvR48Fkq6me17Oc+V1iyxfR/1x0pG6LzW0br1qlGtzBYFZKmDyviBRcPVFTVw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/signature-v4": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.2.tgz", - "integrity": "sha512-YMooDEw/UmGxcXY4qWnSXkbPFsRloluSvyXVT678YPDN/K2AS1GzKfRsvSU7fbccOB4WF8MHZf2UqcRGEltE3Q==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/eventstream-codec": "^2.0.2", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/smithy-client": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.0.2.tgz", - "integrity": "sha512-mDfokI8WwLU5C0gcQ4ww/zJI/WLGSh2+vdIA42JRnjfYUjJNH/rKfX9YOnn2eBOxl3loATERVUqkHmKe+P8s2Q==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/middleware-stack": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-stream": "^2.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.1.0.tgz", - "integrity": "sha512-KLsCsqxX0j2l99iP8s0f7LBlcsp7a7ceXGn0LPYPyVOsqmIKvSaPQajq0YevlL4T9Bm+DtcyXfBTbtBcLX1I7A==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/url-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.2.tgz", - "integrity": "sha512-X1mHCzrSVDlhVy7d3S7Vq+dTfYzwh4n7xGHhyJumu77nJqIss0lazVug85Pwo0DKIoO314wAOvMnBxNYDa+7wA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/querystring-parser": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-base64": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", - "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-body-length-node": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.0.0.tgz", - "integrity": "sha512-ZV7Z/WHTMxHJe/xL/56qZwSUcl63/5aaPAGjkfynJm4poILjdD4GmFI+V+YWabh2WJIjwTKZ5PNsuvPQKt93Mg==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.2.tgz", - "integrity": "sha512-c2tMMjb624XLuzmlRoZpnFOkejVxcgw3WQKdmgdGZYZapcLzXyC0H9JhnXMjQCt30GqLTlsILRNVBYwFRbw/4Q==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.2.tgz", - "integrity": "sha512-gt7m5LLqUtEKldJLyc14DE4kb85vxwomvt9AfEMEvWM4VwfWS1kGJqiStZFb5KNqnQPXw8vvpgLTi8NrWAOXqg==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/config-resolver": "^2.0.2", - "@smithy/credential-provider-imds": "^2.0.2", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-middleware": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.0.tgz", - "integrity": "sha512-eCWX4ECuDHn1wuyyDdGdUWnT4OGyIzV0LN1xRttBFMPI9Ff/4heSHVxneyiMtOB//zpXWCha1/SWHJOZstG7kA==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-retry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.0.tgz", - "integrity": "sha512-/dvJ8afrElasuiiIttRJeoS2sy8YXpksQwiM/TcepqdRVp7u4ejd9C4IQURHNjlfPUT7Y6lCDSa2zQJbdHhVTg==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/service-error-classification": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-stream": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.2.tgz", - "integrity": "sha512-Mg9IJcKIu4YKlbzvpp1KLvh4JZLdcPgpxk+LICuDwzZCfxe47R9enVK8dNEiuyiIGK2ExbfvzCVT8IBru62vZw==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-providers/node_modules/@smithy/util-utf8": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", - "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", - "optional": true, - "peer": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.370.0.tgz", - "integrity": "sha512-CPXOm/TnOFC7KyXcJglICC7OiA7Kj6mT3ChvEijr56TFOueNHvJdV4aNIFEQy0vGHOWtY12qOWLNto/wYR1BAQ==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.370.0.tgz", - "integrity": "sha512-cQMq9SaZ/ORmTJPCT6VzMML7OxFdQzNkhMAgKpTDl+tdPWynlHF29E5xGoSzROnThHlQPCjogU0NZ8AxI0SWPA==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.370.0.tgz", - "integrity": "sha512-L7ZF/w0lAAY/GK1khT8VdoU0XB7nWHk51rl/ecAg64J70dHnMOAg8n+5FZ9fBu/xH1FwUlHOkwlodJOgzLJjtg==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.370.0.tgz", - "integrity": "sha512-ykbsoVy0AJtVbuhAlTAMcaz/tCE3pT8nAp0L7CQQxSoanRCvOux7au0KwMIQVhxgnYid4dWVF6d00SkqU5MXRA==", - "dependencies": { - "@aws-sdk/middleware-signing": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-signing": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.370.0.tgz", - "integrity": "sha512-Dwr/RTCWOXdm394wCwICGT2VNOTMRe4IGPsBRJAsM24pm+EEqQzSS3Xu/U/zF4exuxqpMta4wec4QpSarPNTxA==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/protocol-http": "^1.1.0", - "@smithy/signature-v4": "^1.0.1", - "@smithy/types": "^1.1.0", - "@smithy/util-middleware": "^1.0.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.370.0.tgz", - "integrity": "sha512-2+3SB6MtMAq1+gVXhw0Y3ONXuljorh6ijnxgTpv+uQnBW5jHCUiAS8WDYiDEm7i9euJPbvJfM8WUrSMDMU6Cog==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.451.0.tgz", - "integrity": "sha512-3iMf4OwzrFb4tAAmoROXaiORUk2FvSejnHIw/XHvf/jjR4EqGGF95NZP/n/MeFZMizJWVssrwS412GmoEyoqhg==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/types": "^2.5.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.6", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/node-config-provider": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.5.tgz", - "integrity": "sha512-3Omb5/h4tOCuKRx4p4pkYTvEYRCYoKk52bOYbKUyz/G/8gERbagsN8jFm4FjQubkrcIqQEghTpQaUw6uk+0edw==", - "dependencies": { - "@smithy/property-provider": "^2.0.14", - "@smithy/shared-ini-file-loader": "^2.2.4", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/property-provider": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.14.tgz", - "integrity": "sha512-k3D2qp9o6imTrLaXRj6GdLYEJr1sXqS99nLhzq8fYmJjSVOeMg/G+1KVAAc7Oxpu71rlZ2f8SSZxcSxkevuR0A==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/shared-ini-file-loader": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.4.tgz", - "integrity": "sha512-9dRknGgvYlRIsoTcmMJXuoR/3ekhGwhRq4un3ns2/byre4Ql5hyUN4iS0x8eITohjU90YOnUCsbRwZRvCkbRfw==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.5.0.tgz", - "integrity": "sha512-/a31lYofrMBkJb3BuPlYJTMKDj0hUmKUP6JFZQu6YVuQVoAjubiY0A52U9S0Uysd33n/djexCUSNJ+G9bf3/aA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver/node_modules/@smithy/util-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.6.tgz", - "integrity": "sha512-7W4uuwBvSLgKoLC1x4LfeArCVcbuHdtVaC4g30kKsD1erfICyQ45+tFhhs/dZNeQg+w392fhunCm/+oCcb6BSA==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.370.0.tgz", - "integrity": "sha512-EyR2ZYr+lJeRiZU2/eLR+mlYU9RXLQvNyGFSAekJKgN13Rpq/h0syzXVFLP/RSod/oZenh/fhVZ2HwlZxuGBtQ==", - "dependencies": { - "@aws-sdk/client-sso-oidc": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", - "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", - "dependencies": { - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.370.0.tgz", - "integrity": "sha512-5ltVAnM79nRlywwzZN5i8Jp4tk245OCGkKwwXbnDU+gq7zT3CIOsct1wNZvmpfZEPGt/bv7/NyRcjP+7XNsX/g==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", - "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.370.0.tgz", - "integrity": "sha512-028LxYZMQ0DANKhW+AKFQslkScZUeYlPmSphrCIXgdIItRZh6ZJHGzE7J/jDsEntZOrZJsjI4z0zZ5W2idj04w==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/types": "^1.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.370.0.tgz", - "integrity": "sha512-33vxZUp8vxTT/DGYIR3PivQm07sSRGWI+4fCv63Rt7Q++fO24E0kQtmVAlikRY810I10poD6rwILVtITtFSzkg==", - "dependencies": { - "@aws-sdk/types": "3.370.0", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "dependencies": { - "tslib": "^2.3.1" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", - "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@casl/ability": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@casl/ability/-/ability-6.5.0.tgz", - "integrity": "sha512-3guc94ugr5ylZQIpJTLz0CDfwNi0mxKVECj1vJUPAvs+Lwunh/dcuUjwzc4MHM9D8JOYX0XUZMEPedpB3vIbOw==", - "dependencies": { - "@ucast/mongo2js": "^1.3.0" - }, - "funding": { - "url": "https://github.com/stalniy/casl/blob/master/BACKERS.md" - } - }, - "node_modules/@casl/mongoose": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@casl/mongoose/-/mongoose-7.2.1.tgz", - "integrity": "sha512-pojgSWYKNIwFM6wWDNct1YD0+8nIxhe2jp5jBbK8JGU60dEs2o0Yw3mCo2y7nBwbvRC2oEots/BlLMVb1Wdo8A==", - "peerDependencies": { - "@casl/ability": "^6.3.2", - "mongoose": "^6.0.13 || ^7.0.0" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@godaddy/terminus": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@godaddy/terminus/-/terminus-4.12.1.tgz", - "integrity": "sha512-Tm+wVu1/V37uZXcT7xOhzdpFoovQReErff8x3y82k6YyWa1gzxWBjTyrx4G2enjEqoXPnUUmJ3MOmwH+TiP6Sw==", - "dependencies": { - "stoppable": "^1.1.0" - } - }, - "node_modules/@hapi/bourne": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.1.0.tgz", - "integrity": "sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q==" - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.1.tgz", - "integrity": "sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.1.tgz", - "integrity": "sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.6.1", - "@jest/reporters": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.6.1", - "jest-haste-map": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-resolve-dependencies": "^29.6.1", - "jest-runner": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "jest-watcher": "^29.6.1", - "micromatch": "^4.0.4", - "pretty-format": "^29.6.1", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.1.tgz", - "integrity": "sha512-RMMXx4ws+Gbvw3DfLSuo2cfQlK7IwGbpuEWXCqyYDcqYTI+9Ju3a5hDnXaxjNsa6uKh9PQF2v+qg+RLe63tz5A==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "jest-mock": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.1.tgz", - "integrity": "sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg==", - "dev": true, - "dependencies": { - "expect": "^29.6.1", - "jest-snapshot": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.1.tgz", - "integrity": "sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.4.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.1.tgz", - "integrity": "sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.1", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.6.1", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.1.tgz", - "integrity": "sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.1", - "@jest/expect": "^29.6.1", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.1.tgz", - "integrity": "sha512-9zuaI9QKr9JnoZtFQlw4GREQbxgmNYXU6QuWtmuODvk5nvPUeBYapVR/VYMyi2WSx3jXTLJTJji8rN6+Cm4+FA==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", - "jest-worker": "^29.6.1", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", - "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.1.tgz", - "integrity": "sha512-Ynr13ZRcpX6INak0TPUukU8GWRfm/vAytE3JbJNGAvINySWYdfE7dGZMbk36oVuK4CigpbhMn8eg1dixZ7ZJOw==", - "dev": true, - "dependencies": { - "@jest/console": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.1.tgz", - "integrity": "sha512-oBkC36PCDf/wb6dWeQIhaviU0l5u6VCsXa119yqdUosYAt7/FbQU2M2UoziO3igj/HBDEgp57ONQ3fm0v9uyyg==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.6.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.1.tgz", - "integrity": "sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.1", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", - "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@juanelas/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@juanelas/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-mr2pfRQpWap0Uq4tlrCgp3W+Yjx1/Bpq4QJsYeAQUh1mExgyQvXz7xUhmYT2HcLLspuAL5dpnos8P2QhaCSXsQ==" - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@maxmind/geoip2-node": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@maxmind/geoip2-node/-/geoip2-node-3.5.0.tgz", - "integrity": "sha512-WG2TNxMwDWDOrljLwyZf5bwiEYubaHuICvQRlgz74lE9OZA/z4o+ZT6OisjDBAZh/yRJVNK6mfHqmP5lLlAwsA==", - "dev": true, - "dependencies": { - "camelcase-keys": "^7.0.0", - "ip6addr": "^0.2.5", - "maxmind": "^4.2.0" - } - }, - "node_modules/@mongodb-js/saslprep": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz", - "integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==", - "optional": true, - "dependencies": { - "sparse-bitfield": "^3.0.3" - } - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz", - "integrity": "sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz", - "integrity": "sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz", - "integrity": "sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz", - "integrity": "sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz", - "integrity": "sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz", - "integrity": "sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@napi-rs/snappy-android-arm-eabi": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm-eabi/-/snappy-android-arm-eabi-7.2.2.tgz", - "integrity": "sha512-H7DuVkPCK5BlAr1NfSU8bDEN7gYs+R78pSHhDng83QxRnCLmVIZk33ymmIwurmoA1HrdTxbkbuNl+lMvNqnytw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-android-arm64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm64/-/snappy-android-arm64-7.2.2.tgz", - "integrity": "sha512-2R/A3qok+nGtpVK8oUMcrIi5OMDckGYNoBLFyli3zp8w6IArPRfg1yOfVUcHvpUDTo9T7LOS1fXgMOoC796eQw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-darwin-arm64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-arm64/-/snappy-darwin-arm64-7.2.2.tgz", - "integrity": "sha512-USgArHbfrmdbuq33bD5ssbkPIoT7YCXCRLmZpDS6dMDrx+iM7eD2BecNbOOo7/v1eu6TRmQ0xOzeQ6I/9FIi5g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-darwin-x64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-x64/-/snappy-darwin-x64-7.2.2.tgz", - "integrity": "sha512-0APDu8iO5iT0IJKblk2lH0VpWSl9zOZndZKnBYIc+ei1npw2L5QvuErFOTeTdHBtzvUHASB+9bvgaWnQo4PvTQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-freebsd-x64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-freebsd-x64/-/snappy-freebsd-x64-7.2.2.tgz", - "integrity": "sha512-mRTCJsuzy0o/B0Hnp9CwNB5V6cOJ4wedDTWEthsdKHSsQlO7WU9W1yP7H3Qv3Ccp/ZfMyrmG98Ad7u7lG58WXA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-arm-gnueabihf": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm-gnueabihf/-/snappy-linux-arm-gnueabihf-7.2.2.tgz", - "integrity": "sha512-v1uzm8+6uYjasBPcFkv90VLZ+WhLzr/tnfkZ/iD9mHYiULqkqpRuC8zvc3FZaJy5wLQE9zTDkTJN1IvUcZ+Vcg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-arm64-gnu": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-gnu/-/snappy-linux-arm64-gnu-7.2.2.tgz", - "integrity": "sha512-LrEMa5pBScs4GXWOn6ZYXfQ72IzoolZw5txqUHVGs8eK4g1HR9HTHhb2oY5ySNaKakG5sOgMsb1rwaEnjhChmQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-arm64-musl": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-musl/-/snappy-linux-arm64-musl-7.2.2.tgz", - "integrity": "sha512-3orWZo9hUpGQcB+3aTLW7UFDqNCQfbr0+MvV67x8nMNYj5eAeUtMmUE/HxLznHO4eZ1qSqiTwLbVx05/Socdlw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-x64-gnu": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-gnu/-/snappy-linux-x64-gnu-7.2.2.tgz", - "integrity": "sha512-jZt8Jit/HHDcavt80zxEkDpH+R1Ic0ssiVCoueASzMXa7vwPJeF4ZxZyqUw4qeSy7n8UUExomu8G8ZbP6VKhgw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-x64-musl": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-musl/-/snappy-linux-x64-musl-7.2.2.tgz", - "integrity": "sha512-Dh96IXgcZrV39a+Tej/owcd9vr5ihiZ3KRix11rr1v0MWtVb61+H1GXXlz6+Zcx9y8jM1NmOuiIuJwkV4vZ4WA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-win32-arm64-msvc": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-arm64-msvc/-/snappy-win32-arm64-msvc-7.2.2.tgz", - "integrity": "sha512-9No0b3xGbHSWv2wtLEn3MO76Yopn1U2TdemZpCaEgOGccz1V+a/1d16Piz3ofSmnA13HGFz3h9NwZH9EOaIgYA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-win32-ia32-msvc": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-ia32-msvc/-/snappy-win32-ia32-msvc-7.2.2.tgz", - "integrity": "sha512-QiGe+0G86J74Qz1JcHtBwM3OYdTni1hX1PFyLRo3HhQUSpmi13Bzc1En7APn+6Pvo7gkrcy81dObGLDSxFAkQQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-win32-x64-msvc": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-x64-msvc/-/snappy-win32-x64-msvc-7.2.2.tgz", - "integrity": "sha512-a43cyx1nK0daw6BZxVcvDEXxKMFLSBSDTAhsFD0VqSKcC7MGUBMaqyoWUcMiI7LBSz4bxUmxDWKfCYzpEmeb3w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-saml/node-saml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@node-saml/node-saml/-/node-saml-4.0.5.tgz", - "integrity": "sha512-J5DglElbY1tjOuaR1NPtjOXkXY5bpUhDoKVoeucYN98A3w4fwgjIOPqIGcb6cQsqFq2zZ6vTCeKn5C/hvefSaw==", - "dependencies": { - "@types/debug": "^4.1.7", - "@types/passport": "^1.0.11", - "@types/xml-crypto": "^1.4.2", - "@types/xml-encryption": "^1.2.1", - "@types/xml2js": "^0.4.11", - "@xmldom/xmldom": "^0.8.6", - "debug": "^4.3.4", - "xml-crypto": "^3.0.1", - "xml-encryption": "^3.0.2", - "xml2js": "^0.5.0", - "xmlbuilder": "^15.1.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@node-saml/passport-saml": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@node-saml/passport-saml/-/passport-saml-4.0.4.tgz", - "integrity": "sha512-xFw3gw0yo+K1mzlkW15NeBF7cVpRHN/4vpjmBKzov5YFImCWh/G0LcTZ8krH3yk2/eRPc3Or8LRPudVJBjmYaw==", - "dependencies": { - "@node-saml/node-saml": "^4.0.4", - "@types/express": "^4.17.14", - "@types/passport": "^1.0.11", - "@types/passport-strategy": "^0.2.35", - "passport": "^0.6.0", - "passport-strategy": "^1.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@octokit/auth-app": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-4.0.13.tgz", - "integrity": "sha512-NBQkmR/Zsc+8fWcVIFrwDgNXS7f4XDrkd9LHdi9DPQw1NdGHLviLzRO2ZBwTtepnwHXW5VTrVU9eFGijMUqllg==", - "dependencies": { - "@octokit/auth-oauth-app": "^5.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "deprecation": "^2.3.1", - "lru-cache": "^9.0.0", - "universal-github-app-jwt": "^1.1.1", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-app/node_modules/lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/@octokit/auth-oauth-app": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-5.0.6.tgz", - "integrity": "sha512-SxyfIBfeFcWd9Z/m1xa4LENTQ3l1y6Nrg31k2Dcb1jS5ov7pmwMJZ6OGX8q3K9slRgVpeAjNA1ipOAMHkieqyw==", - "dependencies": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "@types/btoa-lite": "^1.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-device": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-4.0.5.tgz", - "integrity": "sha512-XyhoWRTzf2ZX0aZ52a6Ew5S5VBAfwwx1QnC2Np6Et3MWQpZjlREIcbcvVZtkNuXp6Z9EeiSLSDUqm3C+aMEHzQ==", - "dependencies": { - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-user": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-2.1.2.tgz", - "integrity": "sha512-kkRqNmFe7s5GQcojE3nSlF+AzYPpPv7kvP/xYEnE57584pixaFBH8Vovt+w5Y3E4zWUEOxjdLItmBTFAWECPAg==", - "dependencies": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-token": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", - "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-unauthenticated": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-3.0.5.tgz", - "integrity": "sha512-yH2GPFcjrTvDWPwJWWCh0tPPtTL5SMgivgKPA+6v/XmYN6hGQkAto8JtZibSKOpf8ipmeYhLNWQ2UgW0GYILCw==", - "dependencies": { - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/core": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", - "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", - "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", - "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/graphql": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", - "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", - "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/oauth-authorization-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-5.0.0.tgz", - "integrity": "sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/oauth-methods": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-2.0.6.tgz", - "integrity": "sha512-l9Uml2iGN2aTWLZcm8hV+neBiFXAQ9+3sKiQe/sgumHlL6HDg0AQ8/l16xX/5jJvfxueqTW5CWbzd0MjnlfHZw==", - "dependencies": { - "@octokit/oauth-authorization-url": "^5.0.0", - "@octokit/request": "^6.2.3", - "@octokit/request-error": "^3.0.3", - "@octokit/types": "^9.0.0", - "btoa-lite": "^1.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", - "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==" - }, - "node_modules/@octokit/plugin-enterprise-compatibility": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-compatibility/-/plugin-enterprise-compatibility-1.3.0.tgz", - "integrity": "sha512-h34sMGdEOER/OKrZJ55v26ntdHb9OPfR1fwOx6Q4qYyyhWA104o11h9tFxnS/l41gED6WEI41Vu2G2zHDVC5lQ==", - "dependencies": { - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.0.3" - } - }, - "node_modules/@octokit/plugin-enterprise-compatibility/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/@octokit/plugin-enterprise-compatibility/node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/plugin-enterprise-compatibility/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz", - "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", - "dependencies": { - "@octokit/tsconfig": "^1.0.2", - "@octokit/types": "^9.2.3" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=4" - } - }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", - "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", - "dependencies": { - "@octokit/types": "^10.0.0" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", - "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" - } - }, - "node_modules/@octokit/plugin-retry": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz", - "integrity": "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ==", - "dependencies": { - "@octokit/types": "^6.0.3", - "bottleneck": "^2.15.3" - } - }, - "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", - "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/rest": { - "version": "19.0.13", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz", - "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", - "dependencies": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.1.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/tsconfig": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", - "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==" - }, - "node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" - } - }, - "node_modules/@octokit/webhooks": { - "version": "9.26.3", - "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.3.tgz", - "integrity": "sha512-DLGk+gzeVq5oK89Bo601txYmyrelMQ7Fi5EnjHE0Xs8CWicy2xkmnJMKptKJrBJpstqbd/9oeDFi/Zj2pudBDQ==", - "dependencies": { - "@octokit/request-error": "^2.0.2", - "@octokit/webhooks-methods": "^2.0.0", - "@octokit/webhooks-types": "5.8.0", - "aggregate-error": "^3.1.0" - } - }, - "node_modules/@octokit/webhooks-methods": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-2.0.0.tgz", - "integrity": "sha512-35cfQ4YWlnZnmZKmIxlGPUPLtbkF8lr/A/1Sk1eC0ddLMwQN06dOuLc+dI3YLQS+T+MoNt3DIQ0NynwgKPilig==" - }, - "node_modules/@octokit/webhooks-types": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-5.8.0.tgz", - "integrity": "sha512-8adktjIb76A7viIdayQSFuBEwOzwhDC+9yxZpKNHjfzrlostHCw0/N7JWpWMObfElwvJMk2fY2l1noENCk9wmw==" - }, - "node_modules/@octokit/webhooks/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/@octokit/webhooks/node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/webhooks/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/@phc/format": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", - "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@posthog/plugin-scaffold": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@posthog/plugin-scaffold/-/plugin-scaffold-1.4.2.tgz", - "integrity": "sha512-/VsRg3CfhQvYhxM2O9+gBOzj4K1QJZClY+yple0npL1Jd2nRn2nT4z7dlPSidTPZvdpFs0+hrnF+m4Kxf1NFvQ==", - "dev": true, - "dependencies": { - "@maxmind/geoip2-node": "^3.4.0" - } - }, - "node_modules/@probot/get-private-key": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@probot/get-private-key/-/get-private-key-1.1.1.tgz", - "integrity": "sha512-hOmBNSAhSZc6PaNkTvj6CO9R5J67ODJ+w5XQlDW9w/6mtcpHWK4L+PZcW0YwVM7PpetLZjN6rsKQIR9yqIaWlA==", - "dependencies": { - "@types/is-base64": "^1.1.0", - "is-base64": "^1.1.0" - } - }, - "node_modules/@probot/octokit-plugin-config": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@probot/octokit-plugin-config/-/octokit-plugin-config-1.1.6.tgz", - "integrity": "sha512-L29wmnFvilzSfWn9tUgItxdLv0LJh2ICjma3FmLr80Spu3wZ9nHyRrKMo9R5/K2m7VuWmgoKnkgRt2zPzAQBEQ==", - "dependencies": { - "@types/js-yaml": "^4.0.5", - "js-yaml": "^4.1.0" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@probot/pino": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@probot/pino/-/pino-2.3.5.tgz", - "integrity": "sha512-IiyiNZonMw1dHC4EAdD55y5owV733d9Gll/IKsrLikB7EJ54+eMCOtL/qo+OmgWN9XV3NTDfziEQF2og/OBKog==", - "dependencies": { - "@sentry/node": "^6.0.0", - "pino-pretty": "^6.0.0", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", - "split2": "^4.0.0" - }, - "bin": { - "pino-probot": "cli.js" - } - }, - "node_modules/@probot/pino/node_modules/@sentry/core": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.19.7.tgz", - "integrity": "sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==", - "dependencies": { - "@sentry/hub": "6.19.7", - "@sentry/minimal": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@probot/pino/node_modules/@sentry/node": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.19.7.tgz", - "integrity": "sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg==", - "dependencies": { - "@sentry/core": "6.19.7", - "@sentry/hub": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", - "cookie": "^0.4.1", - "https-proxy-agent": "^5.0.0", - "lru_map": "^0.3.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@probot/pino/node_modules/@sentry/types": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz", - "integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@probot/pino/node_modules/@sentry/utils": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.19.7.tgz", - "integrity": "sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==", - "dependencies": { - "@sentry/types": "6.19.7", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@probot/pino/node_modules/colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" - }, - "node_modules/@probot/pino/node_modules/jmespath": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/@probot/pino/node_modules/pino-pretty": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-6.0.0.tgz", - "integrity": "sha512-jyeR2fXXWc68st1DTTM5NhkHlx8p+1fKZMfm84Jwq+jSw08IwAjNaZBZR6ts69hhPOfOjg/NiE1HYW7vBRPL3A==", - "dependencies": { - "@hapi/bourne": "^2.0.0", - "args": "^5.0.1", - "colorette": "^1.3.0", - "dateformat": "^4.5.1", - "fast-safe-stringify": "^2.0.7", - "jmespath": "^0.15.0", - "joycon": "^3.0.0", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", - "rfdc": "^1.3.0", - "split2": "^3.1.1", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "pino-pretty": "bin.js" - } - }, - "node_modules/@probot/pino/node_modules/pino-pretty/node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/@probot/pino/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@sentry-internal/tracing": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.59.3.tgz", - "integrity": "sha512-/RkBj/0zQKGsW/UYg6hufrLHHguncLfu4610FCPWpVp0K5Yu5ou8/Aw8D76G3ZxD2TiuSNGwX0o7TYN371ZqTQ==", - "dependencies": { - "@sentry/core": "7.59.3", - "@sentry/types": "7.59.3", - "@sentry/utils": "7.59.3", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/core": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.59.3.tgz", - "integrity": "sha512-cGBOwT9gziIn50fnlBH1WGQlGcHi7wrbvOCyrex4MxKnn1LSBYWBhwU0ymj8DI/9MyPrGDNGkrgpV0WJWBSClg==", - "dependencies": { - "@sentry/types": "7.59.3", - "@sentry/utils": "7.59.3", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/hub": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.19.7.tgz", - "integrity": "sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==", - "dependencies": { - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/hub/node_modules/@sentry/types": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz", - "integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/hub/node_modules/@sentry/utils": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.19.7.tgz", - "integrity": "sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==", - "dependencies": { - "@sentry/types": "6.19.7", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/hub/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@sentry/minimal": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.19.7.tgz", - "integrity": "sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==", - "dependencies": { - "@sentry/hub": "6.19.7", - "@sentry/types": "6.19.7", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/minimal/node_modules/@sentry/types": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz", - "integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/minimal/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@sentry/node": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.77.0.tgz", - "integrity": "sha512-Ob5tgaJOj0OYMwnocc6G/CDLWC7hXfVvKX/ofkF98+BbN/tQa5poL+OwgFn9BA8ud8xKzyGPxGU6LdZ8Oh3z/g==", - "dependencies": { - "@sentry-internal/tracing": "7.77.0", - "@sentry/core": "7.77.0", - "@sentry/types": "7.77.0", - "@sentry/utils": "7.77.0", - "https-proxy-agent": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/node/node_modules/@sentry-internal/tracing": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.77.0.tgz", - "integrity": "sha512-8HRF1rdqWwtINqGEdx8Iqs9UOP/n8E0vXUu3Nmbqj4p5sQPA7vvCfq+4Y4rTqZFc7sNdFpDsRION5iQEh8zfZw==", - "dependencies": { - "@sentry/core": "7.77.0", - "@sentry/types": "7.77.0", - "@sentry/utils": "7.77.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/node/node_modules/@sentry/core": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.77.0.tgz", - "integrity": "sha512-Tj8oTYFZ/ZD+xW8IGIsU6gcFXD/gfE+FUxUaeSosd9KHwBQNOLhZSsYo/tTVf/rnQI/dQnsd4onPZLiL+27aTg==", - "dependencies": { - "@sentry/types": "7.77.0", - "@sentry/utils": "7.77.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/node/node_modules/@sentry/types": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.77.0.tgz", - "integrity": "sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/node/node_modules/@sentry/utils": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.77.0.tgz", - "integrity": "sha512-NmM2kDOqVchrey3N5WSzdQoCsyDkQkiRxExPaNI2oKQ/jMWHs9yt0tSy7otPBcXs0AP59ihl75Bvm1tDRcsp5g==", - "dependencies": { - "@sentry/types": "7.77.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/tracing": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.59.3.tgz", - "integrity": "sha512-+gDsfhYdteAR4NyKl3B5JVQs/bXYT73ajoFrlprfDjAJCEVR9W1P4CULavoLtfASxVqBQcZyT87Hsb9/vbn6bg==", - "dependencies": { - "@sentry-internal/tracing": "7.59.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/types": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.59.3.tgz", - "integrity": "sha512-HQ/Pd3YHyIa4HM0bGfOsfI4ZF+sLVs6II9VtlS4hsVporm4ETl3Obld5HywO3aVYvWOk5j/bpAW9JYsxXjRG5A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/utils": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.59.3.tgz", - "integrity": "sha512-Q57xauMKuzd6S+POA1fmulfjzTsb/z118TNAfZZNkHqVB48hHBqgzdhbEBmN4jPCSKV2Cx7VJUoDZxJfzQyLUQ==", - "dependencies": { - "@sentry/types": "7.59.3", - "tslib": "^2.4.1 || ^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@serdnam/pino-cloudwatch-transport": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@serdnam/pino-cloudwatch-transport/-/pino-cloudwatch-transport-1.0.4.tgz", - "integrity": "sha512-0wtILlFlO/qTFANM1oEMZLKa9REo+mluHN0VTDaOMh15H9Puc+qU4z4jAoZqggFz9Fw9EGG4c+UHpMduZ1EzeQ==", - "dependencies": { - "@aws-sdk/client-cloudwatch-logs": "^3.52.0", - "p-throttle": "^5.0.0", - "pino-abstract-transport": "^0.5.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@serdnam/pino-cloudwatch-transport/node_modules/pino-abstract-transport": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", - "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", - "dependencies": { - "duplexify": "^4.1.2", - "split2": "^4.0.0" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-1.0.2.tgz", - "integrity": "sha512-tb2h0b+JvMee+eAxTmhnyqyNk51UXIK949HnE14lFeezKsVJTB30maan+CO2IMwnig2wVYQH84B5qk6ylmKCuA==", - "dependencies": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-1.0.2.tgz", - "integrity": "sha512-8Bk7CgnVKg1dn5TgnjwPz2ebhxeR7CjGs5yhVYH3S8x0q8yPZZVWwpRIglwXaf5AZBzJlNO1lh+lUhMf2e73zQ==", - "dependencies": { - "@smithy/types": "^1.1.1", - "@smithy/util-config-provider": "^1.0.2", - "@smithy/util-middleware": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-1.0.2.tgz", - "integrity": "sha512-fLjCya+JOu2gPJpCiwSUyoLvT8JdNJmOaTOkKYBZoGf7CzqR6lluSyI+eboZnl/V0xqcfcqBG4tgqCISmWS3/w==", - "dependencies": { - "@smithy/node-config-provider": "^1.0.2", - "@smithy/property-provider": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/url-parser": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-1.0.2.tgz", - "integrity": "sha512-eW/XPiLauR1VAgHKxhVvgvHzLROUgTtqat2lgljztbH8uIYWugv7Nz+SgCavB+hWRazv2iYgqrSy74GvxXq/rg==", - "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^1.1.1", - "@smithy/util-hex-encoding": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-1.0.2.tgz", - "integrity": "sha512-kynyofLf62LvR8yYphPPdyHb8fWG3LepFinM/vWUTG2Q1pVpmPCM530ppagp3+q2p+7Ox0UvSqldbKqV/d1BpA==", - "dependencies": { - "@smithy/protocol-http": "^1.1.1", - "@smithy/querystring-builder": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/util-base64": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-1.0.2.tgz", - "integrity": "sha512-K6PKhcUNrJXtcesyzhIvNlU7drfIU7u+EMQuGmPw6RQDAg/ufUcfKHz4EcUhFAodUmN+rrejhRG9U6wxjeBOQA==", - "dependencies": { - "@smithy/types": "^1.1.1", - "@smithy/util-buffer-from": "^1.0.2", - "@smithy/util-utf8": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-1.0.2.tgz", - "integrity": "sha512-B1Y3Tsa6dfC+Vvb+BJMhTHOfFieeYzY9jWQSTR1vMwKkxsymD0OIAnEw8rD/RiDj/4E4RPGFdx9Mdgnyd6Bv5Q==", - "dependencies": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-1.0.2.tgz", - "integrity": "sha512-pkyBnsBRpe+c/6ASavqIMRBdRtZNJEVJOEzhpxZ9JoAXiZYbkfaSMRA/O1dUxGdJ653GHONunnZ4xMo/LJ7utQ==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-1.0.2.tgz", - "integrity": "sha512-pa1/SgGIrSmnEr2c9Apw7CdU4l/HW0fK3+LKFCPDYJrzM0JdYpqjQzgxi31P00eAkL0EFBccpus/p1n2GF9urw==", - "dependencies": { - "@smithy/protocol-http": "^1.1.1", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.0.3.tgz", - "integrity": "sha512-GsWvTXMFjSgl617PCE2km//kIjjtvMRrR2GAuRDIS9sHiLwmkS46VWaVYy+XE7ubEsEtzZ5yK2e8TKDR6Qr5Lw==", - "dependencies": { - "@smithy/middleware-serde": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/url-parser": "^1.0.2", - "@smithy/util-middleware": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.0.4.tgz", - "integrity": "sha512-G7uRXGFL8c3F7APnoIMTtNAHH8vT4F2qVnAWGAZaervjupaUQuRRHYBLYubK0dWzOZz86BtAXKieJ5p+Ni2Xpg==", - "dependencies": { - "@smithy/protocol-http": "^1.1.1", - "@smithy/service-error-classification": "^1.0.3", - "@smithy/types": "^1.1.1", - "@smithy/util-middleware": "^1.0.2", - "@smithy/util-retry": "^1.0.4", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.0.2.tgz", - "integrity": "sha512-T4PcdMZF4xme6koUNfjmSZ1MLi7eoFeYCtodQNQpBNsS77TuJt1A6kt5kP/qxrTvfZHyFlj0AubACoaUqgzPeg==", - "dependencies": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-1.0.2.tgz", - "integrity": "sha512-H7/uAQEcmO+eDqweEFMJ5YrIpsBwmrXSP6HIIbtxKJSQpAcMGY7KrR2FZgZBi1FMnSUOh+rQrbOyj5HQmSeUBA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-1.0.2.tgz", - "integrity": "sha512-HU7afWpTToU0wL6KseGDR2zojeyjECQfr8LpjAIeHCYIW7r360ABFf4EaplaJRMVoC3hD9FeltgI3/NtShOqCg==", - "dependencies": { - "@smithy/property-provider": "^1.0.2", - "@smithy/shared-ini-file-loader": "^1.0.2", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-1.0.3.tgz", - "integrity": "sha512-PcPUSzTbIb60VCJCiH0PU0E6bwIekttsIEf5Aoo/M0oTfiqsxHTn0Rcij6QoH6qJy6piGKXzLSegspXg5+Kq6g==", - "dependencies": { - "@smithy/abort-controller": "^1.0.2", - "@smithy/protocol-http": "^1.1.1", - "@smithy/querystring-builder": "^1.0.2", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-1.0.2.tgz", - "integrity": "sha512-pXDPyzKX8opzt38B205kDgaxda6LHcTfPvTYQZnwP6BAPp1o9puiCPjeUtkKck7Z6IbpXCPUmUQnzkUzWTA42Q==", - "dependencies": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.1.tgz", - "integrity": "sha512-mFLFa2sSvlUxm55U7B4YCIsJJIMkA6lHxwwqOaBkral1qxFz97rGffP/mmd4JDuin1EnygiO5eNJGgudiUgmDQ==", - "dependencies": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-1.0.2.tgz", - "integrity": "sha512-6P/xANWrtJhMzTPUR87AbXwSBuz1SDHIfL44TFd/GT3hj6rA+IEv7rftEpPjayUiWRocaNnrCPLvmP31mobOyA==", - "dependencies": { - "@smithy/types": "^1.1.1", - "@smithy/util-uri-escape": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.0.2.tgz", - "integrity": "sha512-IWxwxjn+KHWRRRB+K2Ngl+plTwo2WSgc2w+DvLy0DQZJh9UGOpw40d6q97/63GBlXIt4TEt5NbcFrO30CKlrsA==", - "dependencies": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.0.3.tgz", - "integrity": "sha512-2eglIYqrtcUnuI71yweu7rSfCgt6kVvRVf0C72VUqrd0LrV1M0BM0eYN+nitp2CHPSdmMI96pi+dU9U/UqAMSA==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-1.0.2.tgz", - "integrity": "sha512-bdQj95VN+lCXki+P3EsDyrkpeLn8xDYiOISBGnUG/AGPYJXN8dmp4EhRRR7XOoLoSs8anZHR4UcGEOzFv2jwGw==", - "dependencies": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-1.0.2.tgz", - "integrity": "sha512-rpKUhmCuPmpV5dloUkOb9w1oBnJatvKQEjIHGmkjRGZnC3437MTdzWej9TxkagcZ8NRRJavYnEUixzxM1amFig==", - "dependencies": { - "@smithy/eventstream-codec": "^1.0.2", - "@smithy/is-array-buffer": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/util-hex-encoding": "^1.0.2", - "@smithy/util-middleware": "^1.0.2", - "@smithy/util-uri-escape": "^1.0.2", - "@smithy/util-utf8": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-1.0.4.tgz", - "integrity": "sha512-gpo0Xl5Nyp9sgymEfpt7oa9P2q/GlM3VmQIdm+FeH0QEdYOQx3OtvwVmBYAMv2FIPWxkMZlsPYRTnEiBTK5TYg==", - "dependencies": { - "@smithy/middleware-stack": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/util-stream": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.1.tgz", - "integrity": "sha512-tMpkreknl2gRrniHeBtdgQwaOlo39df8RxSrwsHVNIGXULy5XP6KqgScUw2m12D15wnJCKWxVhCX+wbrBW/y7g==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.0.2.tgz", - "integrity": "sha512-0JRsDMQe53F6EHRWksdcavKDRjyqp8vrjakg8EcCUOa7PaFRRB1SO/xGZdzSlW1RSTWQDEksFMTCEcVEKmAoqA==", - "dependencies": { - "@smithy/querystring-parser": "^1.0.2", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-1.0.2.tgz", - "integrity": "sha512-BCm15WILJ3SL93nusoxvJGMVfAMWHZhdeDZPtpAaskozuexd0eF6szdz4kbXaKp38bFCSenA6bkUHqaE3KK0dA==", - "dependencies": { - "@smithy/util-buffer-from": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-1.0.2.tgz", - "integrity": "sha512-Xh8L06H2anF5BHjSYTg8hx+Itcbf4SQZnVMl4PIkCOsKtneMJoGjPRLy17lEzfoh/GOaa0QxgCP6lRMQWzNl4w==", - "dependencies": { - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-1.0.2.tgz", - "integrity": "sha512-nXHbZsUtvZeyfL4Ceds9nmy2Uh2AhWXohG4vWHyjSdmT8cXZlJdmJgnH6SJKDjyUecbu+BpKeVvSrA4cWPSOPA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-1.0.2.tgz", - "integrity": "sha512-lHAYIyrBO9RANrPvccnPjU03MJnWZ66wWuC5GjWWQVfsmPwU6m00aakZkzHdUT6tGCkGacXSgArP5wgTgA+oCw==", - "dependencies": { - "@smithy/is-array-buffer": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-1.0.2.tgz", - "integrity": "sha512-HOdmDm+3HUbuYPBABLLHtn8ittuRyy+BSjKOA169H+EMc+IozipvXDydf+gKBRAxUa4dtKQkLraypwppzi+PRw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-1.0.2.tgz", - "integrity": "sha512-J1u2PO235zxY7dg0+ZqaG96tFg4ehJZ7isGK1pCBEA072qxNPwIpDzUVGnLJkHZvjWEGA8rxIauDtXfB0qxeAg==", - "dependencies": { - "@smithy/property-provider": "^1.0.2", - "@smithy/types": "^1.1.1", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-1.0.2.tgz", - "integrity": "sha512-9/BN63rlIsFStvI+AvljMh873Xw6bbI6b19b+PVYXyycQ2DDQImWcjnzRlHW7eP65CCUNGQ6otDLNdBQCgMXqg==", - "dependencies": { - "@smithy/config-resolver": "^1.0.2", - "@smithy/credential-provider-imds": "^1.0.2", - "@smithy/node-config-provider": "^1.0.2", - "@smithy/property-provider": "^1.0.2", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.0.4.tgz", - "integrity": "sha512-FPry8j1xye5yzrdnf4xKUXVnkQErxdN7bUIaqC0OFoGsv2NfD9b2UUMuZSSt+pr9a8XWAqj0HoyVNUfPiZ/PvQ==", - "dependencies": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@smithy/util-endpoints/node_modules/@smithy/node-config-provider": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.5.tgz", - "integrity": "sha512-3Omb5/h4tOCuKRx4p4pkYTvEYRCYoKk52bOYbKUyz/G/8gERbagsN8jFm4FjQubkrcIqQEghTpQaUw6uk+0edw==", - "dependencies": { - "@smithy/property-provider": "^2.0.14", - "@smithy/shared-ini-file-loader": "^2.2.4", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-endpoints/node_modules/@smithy/property-provider": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.14.tgz", - "integrity": "sha512-k3D2qp9o6imTrLaXRj6GdLYEJr1sXqS99nLhzq8fYmJjSVOeMg/G+1KVAAc7Oxpu71rlZ2f8SSZxcSxkevuR0A==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-endpoints/node_modules/@smithy/shared-ini-file-loader": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.4.tgz", - "integrity": "sha512-9dRknGgvYlRIsoTcmMJXuoR/3ekhGwhRq4un3ns2/byre4Ql5hyUN4iS0x8eITohjU90YOnUCsbRwZRvCkbRfw==", - "dependencies": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-endpoints/node_modules/@smithy/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.5.0.tgz", - "integrity": "sha512-/a31lYofrMBkJb3BuPlYJTMKDj0hUmKUP6JFZQu6YVuQVoAjubiY0A52U9S0Uysd33n/djexCUSNJ+G9bf3/aA==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-1.0.2.tgz", - "integrity": "sha512-Bxydb5rMJorMV6AuDDMOxro3BMDdIwtbQKHpwvQFASkmr52BnpDsWlxgpJi8Iq7nk1Bt4E40oE1Isy/7ubHGzg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.0.2.tgz", - "integrity": "sha512-vtXK7GOR2BoseCX8NCGe9SaiZrm9M2lm/RVexFGyPuafTtry9Vyv7hq/vw8ifd/G/pSJ+msByfJVb1642oQHKw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.0.4.tgz", - "integrity": "sha512-RnZPVFvRoqdj2EbroDo3OsnnQU8eQ4AlnZTOGusbYKybH3269CFdrZfZJloe60AQjX7di3J6t/79PjwCLO5Khw==", - "dependencies": { - "@smithy/service-error-classification": "^1.0.3", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-1.0.2.tgz", - "integrity": "sha512-qyN2M9QFMTz4UCHi6GnBfLOGYKxQZD01Ga6nzaXFFC51HP/QmArU72e4kY50Z/EtW8binPxspP2TAsGbwy9l3A==", - "dependencies": { - "@smithy/fetch-http-handler": "^1.0.2", - "@smithy/node-http-handler": "^1.0.3", - "@smithy/types": "^1.1.1", - "@smithy/util-base64": "^1.0.2", - "@smithy/util-buffer-from": "^1.0.2", - "@smithy/util-hex-encoding": "^1.0.2", - "@smithy/util-utf8": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-1.0.2.tgz", - "integrity": "sha512-k8C0BFNS9HpBMHSgUDnWb1JlCQcFG+PPlVBq9keP4Nfwv6a9Q0yAfASWqUCtzjuMj1hXeLhn/5ADP6JxnID1Pg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-1.0.2.tgz", - "integrity": "sha512-V4cyjKfJlARui0dMBfWJMQAmJzoW77i4N3EjkH/bwnE2Ngbl4tqD2Y0C/xzpzY/J1BdxeCKxAebVFk8aFCaSCw==", - "dependencies": { - "@smithy/util-buffer-from": "^1.0.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@swc/core": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.99.tgz", - "integrity": "sha512-8O996RfuPC4ieb4zbYMfbyCU9k4gSOpyCNnr7qBQ+o7IEmh8JCV6B8wwu+fT/Om/6Lp34KJe1IpJ/24axKS6TQ==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@swc/counter": "^0.1.1", - "@swc/types": "^0.1.5" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.99", - "@swc/core-darwin-x64": "1.3.99", - "@swc/core-linux-arm64-gnu": "1.3.99", - "@swc/core-linux-arm64-musl": "1.3.99", - "@swc/core-linux-x64-gnu": "1.3.99", - "@swc/core-linux-x64-musl": "1.3.99", - "@swc/core-win32-arm64-msvc": "1.3.99", - "@swc/core-win32-ia32-msvc": "1.3.99", - "@swc/core-win32-x64-msvc": "1.3.99" - }, - "peerDependencies": { - "@swc/helpers": "^0.5.0" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.99.tgz", - "integrity": "sha512-Qj7Jct68q3ZKeuJrjPx7k8SxzWN6PqLh+VFxzA+KwLDpQDPzOlKRZwkIMzuFjLhITO4RHgSnXoDk/Syz0ZeN+Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.99.tgz", - "integrity": "sha512-wR7m9QVJjgiBu1PSOHy7s66uJPa45Kf9bZExXUL+JAa9OQxt5y+XVzr+n+F045VXQOwdGWplgPnWjgbUUHEVyw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.99.tgz", - "integrity": "sha512-gcGv1l5t0DScEONmw5OhdVmEI/o49HCe9Ik38zzH0NtDkc+PDYaCcXU5rvfZP2qJFaAAr8cua8iJcOunOSLmnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.99.tgz", - "integrity": "sha512-XL1/eUsTO8BiKsWq9i3iWh7H99iPO61+9HYiWVKhSavknfj4Plbn+XyajDpxsauln5o8t+BRGitymtnAWJM4UQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.99.tgz", - "integrity": "sha512-fGrXYE6DbTfGNIGQmBefYxSk3rp/1lgbD0nVg4rl4mfFRQPi7CgGhrrqSuqZ/ezXInUIgoCyvYGWFSwjLXt/Qg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.99.tgz", - "integrity": "sha512-kvgZp/mqf3IJ806gUOL6gN6VU15+DfzM1Zv4Udn8GqgXiUAvbQehrtruid4Snn5pZTLj4PEpSCBbxgxK1jbssA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.99.tgz", - "integrity": "sha512-yt8RtZ4W/QgFF+JUemOUQAkVW58cCST7mbfKFZ1v16w3pl3NcWd9OrtppFIXpbjU1rrUX2zp2R7HZZzZ2Zk/aQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.99.tgz", - "integrity": "sha512-62p5fWnOJR/rlbmbUIpQEVRconICy5KDScWVuJg1v3GPLBrmacjphyHiJC1mp6dYvvoEWCk/77c/jcQwlXrDXw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.99.tgz", - "integrity": "sha512-PdppWhkoS45VGdMBxvClVgF1hVjqamtvYd82Gab1i4IV45OSym2KinoDCKE1b6j3LwBLOn2J9fvChGSgGfDCHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", - "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==", - "dev": true - }, - "node_modules/@swc/helpers": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", - "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==", - "dev": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@swc/types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", - "dev": true - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/bcrypt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", - "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/bcryptjs": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", - "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==", - "dev": true - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg==" - }, - "node_modules/@types/bull": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@types/bull/-/bull-4.10.0.tgz", - "integrity": "sha512-RkYW8K2H3J76HT6twmHYbzJ0GtLDDotpLP9ah9gtiA7zfF6peBH1l5fEiK0oeIZ3/642M7Jcb9sPmor8Vf4w6g==", - "deprecated": "This is a stub types definition. bull provides its own type definitions, so you do not need this installed.", - "dev": true, - "dependencies": { - "bull": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", - "dev": true - }, - "node_modules/@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==" - }, - "node_modules/@types/debug": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", - "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" - }, - "node_modules/@types/ioredis": { - "version": "4.28.10", - "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz", - "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/is-base64": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/is-base64/-/is-base64-1.1.1.tgz", - "integrity": "sha512-JgnGhP+MeSHEQmvxcobcwPEP4Ew56voiq9/0hmP/41lyQ/3gBw/ZCIRy2v+QkEOdeCl58lRcrf6+Y6WMlJGETA==" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", - "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/jmespath": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@types/jmespath/-/jmespath-0.15.1.tgz", - "integrity": "sha512-RWN1HQ71Hjl2ixw4a8s7/Bcz6S9uaBTaoCQ5cJB7OsjgHBFi3GaWMy0vRgZBPSYXdsMKFNxGLUUEh9uRf00Spw==", - "dev": true - }, - "node_modules/@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" - }, - "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true - }, - "node_modules/@types/jsonwebtoken": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", - "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/libsodium-wrappers": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", - "integrity": "sha512-BqI9B92u+cM3ccp8mpHf+HzJ8fBlRwdmyd6+fz3p99m3V6ifT5O3zmOMi612PGkpeFeG/G6loxUnzlDNhfjPSA==" - }, - "node_modules/@types/lodash": { - "version": "4.14.195", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz", - "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, - "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - }, - "node_modules/@types/node": { - "version": "18.16.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.19.tgz", - "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==" - }, - "node_modules/@types/nodemailer": { - "version": "6.4.8", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.8.tgz", - "integrity": "sha512-oVsJSCkqViCn8/pEu2hfjwVO+Gb3e+eTWjg3PcjeFKRItfKpKwHphQqbYmPQrlMk+op7pNNWPbsJIEthpFN/OQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/passport": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz", - "integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/passport-strategy": { - "version": "0.2.35", - "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", - "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", - "dependencies": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "node_modules/@types/pg": { - "version": "8.10.7", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.7.tgz", - "integrity": "sha512-ksJqHipwYaSEHz9e1fr6H6erjoEdNNaOxwyJgPx9bNeaqOW3iWBQgVHfpwiSAoqGzchfc+ZyRLwEfeCcyYD3uQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^4.0.1" - } - }, - "node_modules/@types/pg/node_modules/pg-types": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz", - "integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==", - "dev": true, - "dependencies": { - "pg-int8": "1.0.1", - "pg-numeric": "1.0.2", - "postgres-array": "~3.0.1", - "postgres-bytea": "~3.0.0", - "postgres-date": "~2.0.1", - "postgres-interval": "^3.0.0", - "postgres-range": "^1.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@types/pg/node_modules/postgres-array": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", - "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/pg/node_modules/postgres-bytea": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", - "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", - "dev": true, - "dependencies": { - "obuf": "~1.1.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/pg/node_modules/postgres-date": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz", - "integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/pg/node_modules/postgres-interval": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", - "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==", - "dev": true - }, - "node_modules/@types/pino": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@types/pino/-/pino-7.0.5.tgz", - "integrity": "sha512-wKoab31pknvILkxAF8ss+v9iNyhw5Iu/0jLtRkUD74cNfOOLJNnqfFKAv0r7wVaTQxRZtWrMpGfShwwBjOcgcg==", - "deprecated": "This is a stub types definition. pino provides its own type definitions, so you do not need this installed.", - "dev": true, - "dependencies": { - "pino": "*" - } - }, - "node_modules/@types/pino-http": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@types/pino-http/-/pino-http-5.8.1.tgz", - "integrity": "sha512-A9MW6VCnx5ii7s+Fs5aFIw+aSZcBCpsZ/atpxamu8tTsvWFacxSf2Hrn1Ohn1jkVRB/LiPGOapRXcFawDBnDnA==", - "dependencies": { - "@types/pino": "6.3" - } - }, - "node_modules/@types/pino-http/node_modules/@types/pino": { - "version": "6.3.12", - "resolved": "https://registry.npmjs.org/@types/pino/-/pino-6.3.12.tgz", - "integrity": "sha512-dsLRTq8/4UtVSpJgl9aeqHvbh6pzdmjYD3C092SYgLD2TyoCqHpTJk6vp8DvCTGGc7iowZ2MoiYiVUUCcu7muw==", - "dependencies": { - "@types/node": "*", - "@types/pino-pretty": "*", - "@types/pino-std-serializers": "*", - "sonic-boom": "^2.1.0" - } - }, - "node_modules/@types/pino-pretty": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/pino-pretty/-/pino-pretty-5.0.0.tgz", - "integrity": "sha512-N1uzqSzioqz8R3AkDbSJwcfDWeI3YMPNapSQQhnB2ISU4NYgUIcAh+hYT5ygqBM+klX4htpEhXMmoJv3J7GrdA==", - "deprecated": "This is a stub types definition. pino-pretty provides its own type definitions, so you do not need this installed.", - "dependencies": { - "pino-pretty": "*" - } - }, - "node_modules/@types/pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-gXfUZx2xIBbFYozGms53fT0nvkacx/+62c8iTxrEqH5PkIGAQvDbXg2774VWOycMPbqn5YJBQ3BMsg4Li3dWbg==", - "deprecated": "This is a stub types definition. pino-std-serializers provides its own type definitions, so you do not need this installed.", - "dependencies": { - "pino-std-serializers": "*" - } - }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", - "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "node_modules/@types/superagent": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.18.tgz", - "integrity": "sha512-LOWgpacIV8GHhrsQU+QMZuomfqXiqzz3ILLkCtKx3Us6AmomFViuzKT9D693QTKgyut2oCytMG8/efOop+DB+w==", - "dev": true, - "dependencies": { - "@types/cookiejar": "*", - "@types/node": "*" - } - }, - "node_modules/@types/supertest": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", - "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", - "dev": true, - "dependencies": { - "@types/superagent": "*" - } - }, - "node_modules/@types/swagger-jsdoc": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.1.tgz", - "integrity": "sha512-+MUpcbyxD528dECUBCEVm6abNuORdbuGjbrUdHDeAQ+rkPuo2a+L4N02WJHF3bonSSE6SJ3dUJwF2V6+cHnf0w==", - "dev": true - }, - "node_modules/@types/swagger-ui-express": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.3.tgz", - "integrity": "sha512-jqCjGU/tGEaqIplPy3WyQg+Nrp6y80DCFnDEAvVKWkJyv0VivSSDCChkppHRHAablvInZe6pijDFMnavtN0vqA==", - "dev": true, - "dependencies": { - "@types/express": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" - }, - "node_modules/@types/whatwg-url": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", - "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", - "dependencies": { - "@types/node": "*", - "@types/webidl-conversions": "*" - } - }, - "node_modules/@types/xml-crypto": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@types/xml-crypto/-/xml-crypto-1.4.2.tgz", - "integrity": "sha512-1kT+3gVkeBDg7Ih8NefxGYfCApwZViMIs5IEs5AXF6Fpsrnf9CLAEIRh0DYb1mIcRcvysVbe27cHsJD6rJi36w==", - "dependencies": { - "@types/node": "*", - "xpath": "0.0.27" - } - }, - "node_modules/@types/xml-encryption": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/xml-encryption/-/xml-encryption-1.2.1.tgz", - "integrity": "sha512-UeyZkfZFZSa9XCGU5uGgUmsSLwQESDJvF076bJGyDf2gkXJjKvK8fW/x4ckvEHB2M/5RHJEkMc5xI+JrdmCTKA==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/xml2js": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.11.tgz", - "integrity": "sha512-JdigeAKmCyoJUiQljjr7tQG3if9NkqGUgwEUqBvV0N7LM4HyQk7UXCnusRa1lnvXAEYJ8mw8GtZWioagNztOwA==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ucast/core": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@ucast/core/-/core-1.10.2.tgz", - "integrity": "sha512-ons5CwXZ/51wrUPfoduC+cO7AS1/wRb0ybpQJ9RrssossDxVy4t49QxWoWgfBDvVKsz9VXzBk9z0wqTdZ+Cq8g==" - }, - "node_modules/@ucast/js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@ucast/js/-/js-3.0.3.tgz", - "integrity": "sha512-jBBqt57T5WagkAjqfCIIE5UYVdaXYgGkOFYv2+kjq2AVpZ2RIbwCo/TujJpDlwTVluUI+WpnRpoGU2tSGlEvFQ==", - "dependencies": { - "@ucast/core": "^1.0.0" - } - }, - "node_modules/@ucast/mongo": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@ucast/mongo/-/mongo-2.4.3.tgz", - "integrity": "sha512-XcI8LclrHWP83H+7H2anGCEeDq0n+12FU2mXCTz6/Tva9/9ddK/iacvvhCyW6cijAAOILmt0tWplRyRhVyZLsA==", - "dependencies": { - "@ucast/core": "^1.4.1" - } - }, - "node_modules/@ucast/mongo2js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@ucast/mongo2js/-/mongo2js-1.3.4.tgz", - "integrity": "sha512-ahazOr1HtelA5AC1KZ9x0UwPMqqimvfmtSm/PRRSeKKeE5G2SCqTgwiNzO7i9jS8zA3dzXpKVPpXMkcYLnyItA==", - "dependencies": { - "@ucast/core": "^1.6.1", - "@ucast/js": "^3.0.0", - "@ucast/mongo": "^2.4.0" - } - }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argon2": { - "version": "0.30.3", - "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.30.3.tgz", - "integrity": "sha512-DoH/kv8c9127ueJSBxAVJXinW9+EuPA3EMUxoV2sAY1qDE5H9BjTyVF/aD2XyHqbqUWabgBkIfcP3ZZuGhbJdg==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.10", - "@phc/format": "^1.0.0", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/args": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", - "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", - "dependencies": { - "camelcase": "5.0.0", - "chalk": "2.4.2", - "leven": "2.1.0", - "mri": "1.1.4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/args/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/args/node_modules/camelcase": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", - "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/args/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/args/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/args/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/args/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/args/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/args/node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/args/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sdk": { - "version": "2.1419.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1419.0.tgz", - "integrity": "sha512-JcD8gb8I5fH/TGdObG8UYyyXfnqVYk50wx9TGao6G/xBYT3YoYeQXj020W568YQpO+dBKRuR4U2LRYdKBNmQ/g==", - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-sdk/node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios-retry": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.5.1.tgz", - "integrity": "sha512-mQRJ4IyAUnYig14BQ4MnnNHHuH1cNH7NW4JxEUD6mNJwK6pwOY66wKLCwZ6Y0o3POpfStalqRC+J4+Hnn6Om7w==", - "dependencies": { - "@babel/runtime": "^7.15.4", - "is-retry-allowed": "^2.2.0" - } - }, - "node_modules/babel-jest": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.1.tgz", - "integrity": "sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.6.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/basic-auth/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.10", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "node_modules/bigint-conversion": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/bigint-conversion/-/bigint-conversion-2.4.1.tgz", - "integrity": "sha512-/DTRevseMZoqN4KLkN5BryOiom0KbwYajiXG5Vo+ZcEPAO0WBZyZoYyDZSgfeq/v/oegLo9bjdndDBlExvAhBQ==", - "dependencies": { - "@juanelas/base64": "^1.1.2" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" - }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/bson": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", - "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==", - "engines": { - "node": ">=14.20.1" - } - }, - "node_modules/btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==" - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/bull": { - "version": "4.10.4", - "resolved": "https://registry.npmjs.org/bull/-/bull-4.10.4.tgz", - "integrity": "sha512-o9m/7HjS/Or3vqRd59evBlWCXd9Lp+ALppKseoSKHaykK46SmRjAilX98PgmOz1yeVaurt8D5UtvEt4bUjM3eA==", - "dev": true, - "dependencies": { - "cron-parser": "^4.2.1", - "debuglog": "^1.0.0", - "get-port": "^5.1.1", - "ioredis": "^5.0.0", - "lodash": "^4.17.21", - "msgpackr": "^1.5.2", - "semver": "^7.3.2", - "uuid": "^8.3.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", - "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", - "dev": true, - "dependencies": { - "camelcase": "^6.3.0", - "map-obj": "^4.1.0", - "quick-lru": "^5.1.1", - "type-fest": "^1.2.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001517", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", - "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", - "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "dependencies": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-parser/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/cookiejar": { - "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": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cron-parser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.8.1.tgz", - "integrity": "sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==", - "dev": true, - "dependencies": { - "luxon": "^3.2.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, - "node_modules/dateformat": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", - "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defaults/node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" - } - }, - "node_modules/duplexify": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", - "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.467", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.467.tgz", - "integrity": "sha512-2qI70O+rR4poYeF2grcuS/bCps5KJh6y1jtZMDDEteyKJQrzLOEhFyXCLcHW6DTBjKjWkk26JhWoAi+Ux9A0fg==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", - "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-unused-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz", - "integrity": "sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==", - "dev": true, - "dependencies": { - "eslint-rule-composer": "^0.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0", - "eslint": "^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - } - } - }, - "node_modules/eslint-rule-composer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", - "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.1.tgz", - "integrity": "sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/eventsource": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", - "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.1.tgz", - "integrity": "sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.6.1", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-async-errors": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", - "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==", - "peerDependencies": { - "express": "^4.16.2" - } - }, - "node_modules/express-handlebars": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-6.0.7.tgz", - "integrity": "sha512-iYeMFpc/hMD+E6FNAZA5fgWeXnXr4rslOSPkeEV6TwdmpJ5lEXuWX0u9vFYs31P2MURctQq2batR09oeNj0LIg==", - "dependencies": { - "glob": "^8.1.0", - "graceful-fs": "^4.2.10", - "handlebars": "^4.7.7" - }, - "engines": { - "node": ">=v12.22.9" - } - }, - "node_modules/express-handlebars/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/express-handlebars/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/express-handlebars/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/express-rate-limit": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.8.0.tgz", - "integrity": "sha512-yVeDWczkh8qgo9INJB1tT4j7LFu+n6ei/oqSMsqpsUIGYjTM+gk+Q3wv19TMUdo8chvus8XohAuOhG7RYRM9ZQ==", - "engines": { - "node": ">= 14.0.0" - }, - "peerDependencies": { - "express": "^4 || ^5" - } - }, - "node_modules/express-validator": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.15.0.tgz", - "integrity": "sha512-r05VYoBL3i2pswuehoFSy+uM8NBuVaY7avp5qrYjQBDzagx2Z5A77FZqPT8/gNLF3HopWkIzaTFaC4JysWXLqg==", - "dependencies": { - "lodash": "^4.17.21", - "validator": "^13.9.0" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-copy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", - "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fast-redact": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.2.0.tgz", - "integrity": "sha512-zaTadChr+NekyzallAMXATXLOR8MNx3zqpZ0MUF2aGf4EathnG0f32VLODNlY8IuGY3HoRO2L6/6fSzNsLaHIw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "node_modules/fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", - "dependencies": { - "punycode": "^1.3.2" - } - }, - "node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatstr": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz", - "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" - }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dev": true, - "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dependencies": { - "is-property": "^1.0.2" - } - }, - "node_modules/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, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/helmet": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-5.1.1.tgz", - "integrity": "sha512-/yX0oVZBggA9cLJh8aw3PPCfedBnbd7J2aowjzsaWwZh7/UFY0nccn/aHAggIgWUFfnykX8GKd3a1pSbrmlcVQ==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/help-me": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", - "integrity": "sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==", - "dependencies": { - "glob": "^8.0.0", - "readable-stream": "^3.6.0" - } - }, - "node_modules/help-me/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/help-me/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/help-me/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/infisical-node": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.3.2.tgz", - "integrity": "sha512-o1rxfOBAmpTiipka9Xnfa2AgTS8CkJHo0aRQwk6UGi+yEkKzXS7dDM7bZD56M/z+yKGLK15QkfFGZXp1VomlHw==", - "dependencies": { - "axios": "^1.3.3", - "dotenv": "^16.0.3", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/install": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", - "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/ioredis": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", - "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", - "dependencies": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/ioredis/node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" - }, - "node_modules/ip6addr": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/ip6addr/-/ip6addr-0.2.5.tgz", - "integrity": "sha512-9RGGSB6Zc9Ox5DpDGFnJdIeF0AsqXzdH+FspCfPPaU/L/4tI6P+5lIoFUFm9JXs9IrJv1boqAaNCQmoDADTSKQ==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-base64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-base64/-/is-base64-1.1.0.tgz", - "integrity": "sha512-Nlhg7Z2dVC4/PTvIFkgVVNvPHSO2eR/Yd0XzhGiXCXEvWnptXlXa/clQ8aePPiMuxEGcWfzWbGw2Fe3d+Y3v1g==", - "bin": { - "is_base64": "bin/is-base64", - "is-base64": "bin/is-base64" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, - "node_modules/is-retry-allowed": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", - "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.1.tgz", - "integrity": "sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.6.1", - "@jest/types": "^29.6.1", - "import-local": "^3.0.2", - "jest-cli": "^29.6.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.1.tgz", - "integrity": "sha512-tPbYLEiBU4MYAL2XoZme/bgfUeotpDBd81lgHLCbDZZFaGmECk0b+/xejPFtmiBP87GgP/y4jplcRpbH+fgCzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.1", - "@jest/expect": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.1", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", - "p-limit": "^3.1.0", - "pretty-format": "^29.6.1", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.1.tgz", - "integrity": "sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing==", - "dev": true, - "dependencies": { - "@jest/core": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.1.tgz", - "integrity": "sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.1", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.1", - "jest-environment-node": "^29.6.1", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-runner": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.6.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.1.tgz", - "integrity": "sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.1.tgz", - "integrity": "sha512-n5eoj5eiTHpKQCAVcNTT7DRqeUmJ01hsAL0Q1SMiBHcBcvTKDELixQOGMCpqhbIuTcfC4kMfSnpmDqRgRJcLNQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.1", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.6.1", - "pretty-format": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.1.tgz", - "integrity": "sha512-ZNIfAiE+foBog24W+2caIldl4Irh8Lx1PUhg/GZ0odM1d/h2qORAsejiFc7zb+SEmYPn1yDZzEDSU5PmDkmVLQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.1.tgz", - "integrity": "sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.1", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.1", - "jest-worker": "^29.6.1", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-junit": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-15.0.0.tgz", - "integrity": "sha512-Z5sVX0Ag3HZdMUnD5DFlG+1gciIFSy7yIVPhOdGUi8YJaI9iLvvBb530gtQL2CHmv0JJeiwRZenr0VrSR7frvg==", - "dev": true, - "dependencies": { - "mkdirp": "^1.0.4", - "strip-ansi": "^6.0.1", - "uuid": "^8.3.2", - "xml": "^1.0.1" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.1.tgz", - "integrity": "sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz", - "integrity": "sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.6.1", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.1.tgz", - "integrity": "sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.6.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.1.tgz", - "integrity": "sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.1", - "@types/node": "*", - "jest-util": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.1.tgz", - "integrity": "sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.1.tgz", - "integrity": "sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.1.tgz", - "integrity": "sha512-tw0wb2Q9yhjAQ2w8rHRDxteryyIck7gIzQE4Reu3JuOBpGp96xWgF0nY8MDdejzrLCZKDcp8JlZrBN/EtkQvPQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.6.1", - "@jest/environment": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.6.1", - "jest-haste-map": "^29.6.1", - "jest-leak-detector": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-resolve": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-util": "^29.6.1", - "jest-watcher": "^29.6.1", - "jest-worker": "^29.6.1", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.1.tgz", - "integrity": "sha512-D6/AYOA+Lhs5e5il8+5pSLemjtJezUr+8zx+Sn8xlmOux3XOqx4d8l/2udBea8CRPqqrzhsKUsN/gBDE/IcaPQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/globals": "^29.6.1", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-mock": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.1.tgz", - "integrity": "sha512-G4UQE1QQ6OaCgfY+A0uR1W2AY0tGXUPQpoUClhWHq1Xdnx1H6JOrC2nH5lqnOEqaDgbHFgIwZ7bNq24HpB180A==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.6.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.1", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", - "natural-compare": "^1.4.0", - "pretty-format": "^29.6.1", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.1.tgz", - "integrity": "sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.1.tgz", - "integrity": "sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "leven": "^3.1.0", - "pretty-format": "^29.6.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-watcher": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.1.tgz", - "integrity": "sha512-d4wpjWTS7HEZPaaj8m36QiaP856JthRZkrgcIY/7ISoUWPIillrXM23WPboZVLbiwZBt4/qn2Jke84Sla6JhFA==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.6.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.1.tgz", - "integrity": "sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.6.1", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "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" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==", - "dependencies": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "node_modules/jsrp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/jsrp/-/jsrp-0.2.4.tgz", - "integrity": "sha512-+CjGAhZaj3k2MMXEy+xWYv7xJGnise/SlL1IIvnRuJ1ZiLtNPJJln/dMDCgORQCq1ouXDnW1FBxW5bkBFhK/8g==", - "dependencies": { - "create-hash": "^1.0.0", - "jsbn": "^1.0.0", - "randombytes": "^2.0.0" - } - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/kareem": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", - "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/libsodium": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.11.tgz", - "integrity": "sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A==" - }, - "node_modules/libsodium-wrappers": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz", - "integrity": "sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q==", - "dependencies": { - "libsodium": "^0.7.11" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", - "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" - }, - "node_modules/lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, - "node_modules/lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" - }, - "node_modules/lru-cache": { - "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" - } - }, - "node_modules/luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/maxmind": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.11.tgz", - "integrity": "sha512-tJDrKbUzN6PSA88tWgg0L2R4Ln00XwecYQJPFI+RvlF2k1sx6VQYtuQ1SVxm8+bw5tF7GWV4xyb+3/KyzEpPUw==", - "dev": true, - "dependencies": { - "mmdb-lib": "2.0.2", - "tiny-lru": "11.0.1" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mmdb-lib": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-2.0.2.tgz", - "integrity": "sha512-shi1I+fCPQonhTi7qyb6hr7hi87R7YS69FlfJiMFuJ12+grx0JyL56gLNzGTYXPU7EhAPkMLliGeyHer0K+AVA==", - "dev": true, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/mongodb": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.0.tgz", - "integrity": "sha512-g+GCMHN1CoRUA+wb1Agv0TI4YTSiWr42B5ulkiAfLLHitGK1R+PkSAf3Lr5rPZwi/3F04LiaZEW0Kxro9Fi2TA==", - "dependencies": { - "bson": "^5.5.0", - "mongodb-connection-string-url": "^2.6.0", - "socks": "^2.7.1" - }, - "engines": { - "node": ">=14.20.1" - }, - "optionalDependencies": { - "@mongodb-js/saslprep": "^1.1.0" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.0.0", - "kerberos": "^1.0.0 || ^2.0.0", - "mongodb-client-encryption": ">=2.3.0 <3", - "snappy": "^7.2.2" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - } - } - }, - "node_modules/mongodb-connection-string-url": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", - "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", - "dependencies": { - "@types/whatwg-url": "^8.2.1", - "whatwg-url": "^11.0.0" - } - }, - "node_modules/mongoose": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.6.3.tgz", - "integrity": "sha512-moYP2qWCOdWRDeBxqB/zYwQmQnTBsF5DoolX5uPyI218BkiA1ujGY27P0NTd4oWIX+LLkZPw0LDzlc/7oh1plg==", - "dependencies": { - "bson": "^5.5.0", - "kareem": "2.5.1", - "mongodb": "5.9.0", - "mpath": "0.9.0", - "mquery": "5.0.0", - "ms": "2.1.3", - "sift": "16.0.1" - }, - "engines": { - "node": ">=14.20.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" - } - }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "dev": true, - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/morgan/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", - "dependencies": { - "debug": "4.x" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/mri": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", - "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", - "engines": { - "node": ">=4" - } - }, - "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==" - }, - "node_modules/msgpackr": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.9.6.tgz", - "integrity": "sha512-50rmb6+ZWvEm0vJn8R8CwI1Eavss3h5rgtKrcdUal3EkZcpqw82+xsmc7RoHb8fYB5V4EOU2NDaOitDAdO0t+w==", - "dev": true, - "optionalDependencies": { - "msgpackr-extract": "^3.0.2" - } - }, - "node_modules/msgpackr-extract": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz", - "integrity": "sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.0.7" - }, - "bin": { - "download-msgpackr-prebuilds": "bin/download-prebuilds.js" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.2" - } - }, - "node_modules/mysql2": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.2.tgz", - "integrity": "sha512-m5erE6bMoWfPXW1D5UrVwlT8PowAoSX69KcZzPuARQ3wY1RJ52NW9PdvdPo076XiSIkQ5IBTis7hxdlrQTlyug==", - "dependencies": { - "denque": "^2.1.0", - "generate-function": "^2.3.1", - "iconv-lite": "^0.6.3", - "long": "^5.2.1", - "lru-cache": "^8.0.0", - "named-placeholders": "^1.1.3", - "seq-queue": "^0.0.5", - "sqlstring": "^2.3.2" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/mysql2/node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/mysql2/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mysql2/node_modules/lru-cache": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", - "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", - "engines": { - "node": ">=16.14" - } - }, - "node_modules/mysql2/node_modules/sqlstring": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/named-placeholders": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", - "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", - "dependencies": { - "lru-cache": "^7.14.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/named-placeholders/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, - "node_modules/node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "dependencies": { - "clone": "2.x" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-gyp-build-optional-packages": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz", - "integrity": "sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==", - "dev": true, - "optional": true, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "node_modules/nodemailer": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.4.tgz", - "integrity": "sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/nodemon": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", - "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", - "dev": true, - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/nodemon/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/nodemon/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/nodemon/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm": { - "version": "8.19.4", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.19.4.tgz", - "integrity": "sha512-3HANl8i9DKnUA89P4KEgVNN28EjSeDCmvEqbzOAuxCFDzdBZzjUl99zgnGpOUumvW5lvJo2HKcjrsc+tfyv1Hw==", - "bundleDependencies": [ - "@isaacs/string-locale-compare", - "@npmcli/arborist", - "@npmcli/ci-detect", - "@npmcli/config", - "@npmcli/fs", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/run-script", - "abbrev", - "archy", - "cacache", - "chalk", - "chownr", - "cli-columns", - "cli-table3", - "columnify", - "fastest-levenshtein", - "fs-minipass", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmhook", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minimatch", - "minipass", - "minipass-pipeline", - "mkdirp", - "mkdirp-infer-owner", - "ms", - "node-gyp", - "nopt", - "npm-audit-report", - "npm-install-checks", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "npmlog", - "opener", - "p-map", - "pacote", - "parse-conflict-json", - "proc-log", - "qrcode-terminal", - "read", - "read-package-json", - "read-package-json-fast", - "readdir-scoped-modules", - "rimraf", - "semver", - "ssri", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which", - "write-file-atomic" - ], - "dev": true, - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/config": "^4.2.1", - "@npmcli/fs": "^2.1.0", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^4.2.1", - "abbrev": "~1.1.1", - "archy": "~1.0.0", - "cacache": "^16.1.3", - "chalk": "^4.1.2", - "chownr": "^2.0.0", - "cli-columns": "^4.0.0", - "cli-table3": "^0.6.2", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.12", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "graceful-fs": "^4.2.10", - "hosted-git-info": "^5.2.1", - "ini": "^3.0.1", - "init-package-json": "^3.0.2", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^6.0.4", - "libnpmdiff": "^4.0.5", - "libnpmexec": "^4.0.14", - "libnpmfund": "^3.0.5", - "libnpmhook": "^8.0.4", - "libnpmorg": "^4.0.4", - "libnpmpack": "^4.1.3", - "libnpmpublish": "^6.0.5", - "libnpmsearch": "^5.0.4", - "libnpmteam": "^4.0.4", - "libnpmversion": "^3.0.7", - "make-fetch-happen": "^10.2.0", - "minimatch": "^5.1.0", - "minipass": "^3.1.6", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "ms": "^2.1.2", - "node-gyp": "^9.1.0", - "nopt": "^6.0.0", - "npm-audit-report": "^3.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.1.0", - "npm-pick-manifest": "^7.0.2", - "npm-profile": "^6.2.0", - "npm-registry-fetch": "^13.3.1", - "npm-user-validate": "^1.0.1", - "npmlog": "^6.0.2", - "opener": "^1.5.2", - "p-map": "^4.0.0", - "pacote": "^13.6.2", - "parse-conflict-json": "^2.0.2", - "proc-log": "^2.0.1", - "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^5.0.2", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.1", - "tar": "^6.1.11", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^2.0.0", - "validate-npm-package-name": "^4.0.0", - "which": "^2.0.2", - "write-file-atomic": "^4.0.1" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/npm/node_modules/@gar/promisify": { - "version": "1.1.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "5.6.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/query": "^1.2.0", - "@npmcli/run-script": "^4.1.3", - "bin-links": "^3.0.3", - "cacache": "^16.1.3", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^5.2.1", - "json-parse-even-better-errors": "^2.3.1", - "json-stringify-nice": "^1.1.4", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.2", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", - "walk-up-path": "^1.0.0" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/ci-detect": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/npm/node_modules/@npmcli/config": { - "version": "4.2.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/map-workspaces": "^2.0.2", - "ini": "^3.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "proc-log": "^2.0.0", - "read-package-json-fast": "^2.0.3", - "semver": "^7.3.5", - "walk-up-path": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ansi-styles": "^4.3.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/fs": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/git": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "2.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/move-file": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "infer-owner": "^1.0.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/query": { - "version": "1.2.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^9.1.0", - "postcss-selector-parser": "^6.0.10", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "4.2.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3", - "which": "^2.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@tootallnate/once": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/abbrev": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/npm/node_modules/agentkeepalive": { - "version": "4.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/npm/node_modules/aproba": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/are-we-there-yet": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/asap": { - "version": "2.0.6", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/bin-links": { - "version": "3.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/npm/node_modules/builtins": { - "version": "5.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "semver": "^7.0.0" - } - }, - "node_modules/npm/node_modules/cacache": { - "version": "16.1.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/npm/node_modules/chownr": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "ip-regex": "^4.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/clean-stack": { - "version": "2.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/cli-table3": { - "version": "0.6.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "mkdirp-infer-owner": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/color-support": { - "version": "1.1.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/npm/node_modules/columnify": { - "version": "1.6.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/debug": { - "version": "4.3.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/debuglog": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/defaults": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/npm/node_modules/delegates": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/depd": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/dezalgo": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/diff": { - "version": "5.1.0", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.12", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/gauge": { - "version": "4.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/glob": { - "version": "8.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.10", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/has": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/npm/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/hosted-git-info": { - "version": "5.2.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/http-proxy-agent": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/ignore-walk": { - "version": "5.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minimatch": "^5.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/npm/node_modules/indent-string": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/infer-owner": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/ini": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/init-package-json": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/ip": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "cidr-regex": "^3.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/is-core-module": { - "version": "2.10.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/is-lambda": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff": { - "version": "5.1.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.4.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/libnpmaccess": { - "version": "6.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmdiff": { - "version": "4.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/disparity-colors": "^2.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", - "diff": "^5.1.0", - "minimatch": "^5.0.1", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1", - "tar": "^6.1.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmexec": { - "version": "4.0.14", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/fs": "^2.1.1", - "@npmcli/run-script": "^4.2.0", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^9.0.1", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "proc-log": "^2.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", - "semver": "^7.3.7", - "walk-up-path": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmfund": { - "version": "3.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^5.6.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmhook": { - "version": "8.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmorg": { - "version": "4.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmpack": { - "version": "4.1.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/run-script": "^4.1.3", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmpublish": { - "version": "6.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "normalize-package-data": "^4.0.0", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0", - "semver": "^7.3.7", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmsearch": { - "version": "5.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmteam": { - "version": "4.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/libnpmversion": { - "version": "3.0.7", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/run-script": "^4.1.3", - "json-parse-even-better-errors": "^2.3.1", - "proc-log": "^2.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/lru-cache": { - "version": "7.13.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/npm/node_modules/make-fetch-happen": { - "version": "10.2.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/minimatch": { - "version": "5.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/minipass": { - "version": "3.3.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-collect": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minipass-fetch": { - "version": "2.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/mkdirp-infer-owner": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/mute-stream": { - "version": "0.0.8", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/negotiator": { - "version": "0.6.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/node-gyp": { - "version": "9.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^12.22 || ^14.13 || >=16" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/nopt": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chalk": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "9.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "5.1.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "bin": { - "npm-packlist": "bin/index.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "7.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^2.0.0", - "npm-package-arg": "^9.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-profile": { - "version": "6.2.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "13.3.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/npmlog": { - "version": "6.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/once": { - "version": "1.4.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/opener": { - "version": "1.5.2", - "dev": true, - "inBundle": true, - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/npm/node_modules/p-map": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/pacote": { - "version": "13.6.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^4.1.0", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/parse-conflict-json": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", - "just-diff-apply": "^5.2.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.10", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/proc-log": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-inflight": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/promzard": { - "version": "0.3.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "read": "1" - } - }, - "node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "dev": true, - "inBundle": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, - "node_modules/npm/node_modules/read": { - "version": "1.0.7", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "mute-stream": "~0.0.4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/npm/node_modules/read-cmd-shim": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/read-package-json": { - "version": "5.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/read-package-json-fast": { - "version": "2.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/readable-stream": { - "version": "3.6.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/npm/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true - }, - "node_modules/npm/node_modules/semver": { - "version": "7.3.7", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks": { - "version": "2.7.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "7.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/spdx-correct": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", - "dev": true, - "inBundle": true, - "license": "CC-BY-3.0" - }, - "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.11", - "dev": true, - "inBundle": true, - "license": "CC0-1.0" - }, - "node_modules/npm/node_modules/ssri": { - "version": "9.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/npm/node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/tar": { - "version": "6.1.11", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/treeverse": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/unique-filename": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/unique-slug": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "builtins": "^5.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/walk-up-path": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/npm/node_modules/which": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/write-file-atomic": { - "version": "4.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/octokit-auth-probot": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/octokit-auth-probot/-/octokit-auth-probot-1.2.9.tgz", - "integrity": "sha512-mMjw6Y760EwJnW2tSVooJK8BMdsG6D40SoCclnefVf/5yWjaNVquEu8NREBVWb60OwbpnMEz4vREXHB5xdMFYQ==", - "dependencies": { - "@octokit/auth-app": "^4.0.2", - "@octokit/auth-token": "^3.0.0", - "@octokit/auth-unauthenticated": "^3.0.0", - "@octokit/types": "^8.0.0" - }, - "peerDependencies": { - "@octokit/core": ">=3.2" - } - }, - "node_modules/octokit-auth-probot/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==" - }, - "node_modules/octokit-auth-probot/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dependencies": { - "@octokit/openapi-types": "^14.0.0" - } - }, - "node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/p-throttle": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/p-throttle/-/p-throttle-5.1.0.tgz", - "integrity": "sha512-+N+s2g01w1Zch4D0K3OpnPDqLOKmLcQ4BvIFq3JC0K29R28vUOjWpO+OJZBNt8X9i3pFCksZJZ0YXkUGjaFE6g==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/passport": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", - "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-github": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz", - "integrity": "sha512-XARXJycE6fFh/dxF+Uut8OjlwbFEXgbPVj/+V+K7cvriRK7VcAOm+NgBmbiLM9Qv3SSxEAV+V6fIk89nYHXa8A==", - "dependencies": { - "passport-oauth2": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-gitlab2": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/passport-gitlab2/-/passport-gitlab2-5.0.0.tgz", - "integrity": "sha512-cXQMgM6JQx9wHVh7JLH30D8fplfwjsDwRz+zS0pqC8JS+4bNmc1J04NGp5g2M4yfwylH9kQRrMN98GxMw7q7cg==", - "dependencies": { - "passport-oauth2": "^1.4.0" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/passport-google-oauth20": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", - "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", - "dependencies": { - "passport-oauth2": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-oauth2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", - "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", - "dependencies": { - "base64url": "3.x.x", - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, - "node_modules/pg": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", - "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", - "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.2", - "pg-pool": "^3.6.1", - "pg-protocol": "^1.6.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - }, - "engines": { - "node": ">= 8.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.1.1" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-numeric": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", - "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pino": { - "version": "8.16.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.1.tgz", - "integrity": "sha512-3bKsVhBmgPjGV9pyn4fO/8RtoVDR8ssW1ev819FsRXlRNgW8gR/9Kx+gCK4UPWd4JjrRDLWpzd/pb1AyWm3MGA==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.1.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.7.0", - "thread-stream": "^2.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", - "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", - "dependencies": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" - } - }, - "node_modules/pino-abstract-transport/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/pino-abstract-transport/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/pino-abstract-transport/node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", - "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/pino-http": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-8.5.1.tgz", - "integrity": "sha512-T/3d9YHKBYpv/QHjNy73P5BNYYkRrC2/D6CxKMecG4fKFLN+B2iC6LsKYzGRTRV+Ld3fjxFC1ca4TUGbPdzk+Q==", - "dependencies": { - "get-caller-file": "^2.0.5", - "pino": "^8.0.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0" - } - }, - "node_modules/pino-pretty": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.2.3.tgz", - "integrity": "sha512-4jfIUc8TC1GPUfDyMSlW1STeORqkoxec71yhxIpLDQapUu8WOuoz2TTCoidrIssyz78LZC69whBMPIKCMbi3cw==", - "dependencies": { - "colorette": "^2.0.7", - "dateformat": "^4.6.3", - "fast-copy": "^3.0.0", - "fast-safe-stringify": "^2.1.1", - "help-me": "^4.0.1", - "joycon": "^3.1.1", - "minimist": "^1.2.6", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.0.0", - "pump": "^3.0.0", - "readable-stream": "^4.0.0", - "secure-json-parse": "^2.4.0", - "sonic-boom": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "pino-pretty": "bin.js" - } - }, - "node_modules/pino-pretty/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/pino-pretty/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/pino-pretty/node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/pino-pretty/node_modules/readable-stream": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", - "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/pino-pretty/node_modules/sonic-boom": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz", - "integrity": "sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/pino-std-serializers": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", - "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" - }, - "node_modules/pino/node_modules/sonic-boom": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz", - "integrity": "sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-conf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", - "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", - "dependencies": { - "find-up": "^3.0.0", - "load-json-file": "^5.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-range": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz", - "integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==", - "dev": true - }, - "node_modules/posthog-node": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.6.0.tgz", - "integrity": "sha512-/BiFw/jwdP0uJSRAIoYqLoBTjZ612xv74b1L/a3T/p1nJVL8e0OrHuxbJW56c6WVW/IKm9gBF/zhbqfaz0XgJQ==", - "dependencies": { - "axios": "^0.27.0" - }, - "engines": { - "node": ">=15.0.0" - } - }, - "node_modules/posthog-node/node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-format": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", - "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/probot": { - "version": "12.3.3", - "resolved": "https://registry.npmjs.org/probot/-/probot-12.3.3.tgz", - "integrity": "sha512-cdtKd+xISzi8sw6++BYBXleRknCA6hqUMoHj/sJqQBrjbNxQLhfeFCq9O2d0Z4eShsy5YFRR3MWwDKJ9uAE0CA==", - "dependencies": { - "@octokit/core": "^3.2.4", - "@octokit/plugin-enterprise-compatibility": "^1.2.8", - "@octokit/plugin-paginate-rest": "^2.6.2", - "@octokit/plugin-rest-endpoint-methods": "^5.0.1", - "@octokit/plugin-retry": "^3.0.6", - "@octokit/plugin-throttling": "^3.3.4", - "@octokit/types": "^8.0.0", - "@octokit/webhooks": "^9.26.3", - "@probot/get-private-key": "^1.1.0", - "@probot/octokit-plugin-config": "^1.0.0", - "@probot/pino": "^2.2.0", - "@types/express": "^4.17.9", - "@types/ioredis": "^4.27.1", - "@types/pino": "^6.3.4", - "@types/pino-http": "^5.0.6", - "commander": "^6.2.0", - "deepmerge": "^4.2.2", - "deprecation": "^2.3.1", - "dotenv": "^8.2.0", - "eventsource": "^2.0.2", - "express": "^4.17.1", - "express-handlebars": "^6.0.3", - "ioredis": "^4.27.8", - "js-yaml": "^3.14.1", - "lru-cache": "^6.0.0", - "octokit-auth-probot": "^1.2.2", - "pino": "^6.7.0", - "pino-http": "^5.3.0", - "pkg-conf": "^3.1.0", - "resolve": "^1.19.0", - "semver": "^7.3.4", - "update-dotenv": "^1.1.1", - "uuid": "^8.3.2" - }, - "bin": { - "probot": "bin/probot.js" - }, - "engines": { - "node": ">=10.21" - } - }, - "node_modules/probot/node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dependencies": { - "@octokit/types": "^6.0.3" - } - }, - "node_modules/probot/node_modules/@octokit/auth-token/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/auth-token/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/probot/node_modules/@octokit/core/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/core/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/probot/node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/endpoint/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/probot/node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/graphql/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==" - }, - "node_modules/probot/node_modules/@octokit/plugin-paginate-rest": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", - "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", - "dependencies": { - "@octokit/types": "^6.40.0" - }, - "peerDependencies": { - "@octokit/core": ">=2" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", - "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", - "dependencies": { - "@octokit/types": "^6.39.0", - "deprecation": "^2.3.1" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-throttling": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-3.7.0.tgz", - "integrity": "sha512-qrKT1Yl/KuwGSC6/oHpLBot3ooC9rq0/ryDYBCpkRtoj+R8T47xTMDT6Tk2CxWopFota/8Pi/2SqArqwC0JPow==", - "dependencies": { - "@octokit/types": "^6.0.1", - "bottleneck": "^2.15.3" - }, - "peerDependencies": { - "@octokit/core": "^3.5.0" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/probot/node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/probot/node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/request-error/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/request/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/request/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dependencies": { - "@octokit/openapi-types": "^14.0.0" - } - }, - "node_modules/probot/node_modules/@types/pino": { - "version": "6.3.12", - "resolved": "https://registry.npmjs.org/@types/pino/-/pino-6.3.12.tgz", - "integrity": "sha512-dsLRTq8/4UtVSpJgl9aeqHvbh6pzdmjYD3C092SYgLD2TyoCqHpTJk6vp8DvCTGGc7iowZ2MoiYiVUUCcu7muw==", - "dependencies": { - "@types/node": "*", - "@types/pino-pretty": "*", - "@types/pino-std-serializers": "*", - "sonic-boom": "^2.1.0" - } - }, - "node_modules/probot/node_modules/@types/pino/node_modules/sonic-boom": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", - "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/probot/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/probot/node_modules/dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/probot/node_modules/ioredis": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.5.tgz", - "integrity": "sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==", - "dependencies": { - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.1", - "denque": "^1.1.0", - "lodash.defaults": "^4.2.0", - "lodash.flatten": "^4.4.0", - "lodash.isarguments": "^3.1.0", - "p-map": "^2.1.0", - "redis-commands": "1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/probot/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/probot/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/probot/node_modules/pino": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-6.14.0.tgz", - "integrity": "sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==", - "dependencies": { - "fast-redact": "^3.0.0", - "fast-safe-stringify": "^2.0.8", - "flatstr": "^1.0.12", - "pino-std-serializers": "^3.1.0", - "process-warning": "^1.0.0", - "quick-format-unescaped": "^4.0.3", - "sonic-boom": "^1.0.2" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/probot/node_modules/pino-http": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-5.8.0.tgz", - "integrity": "sha512-YwXiyRb9y0WCD1P9PcxuJuh3Dc5qmXde/paJE86UGYRdiFOi828hR9iUGmk5gaw6NBT9gLtKANOHFimvh19U5w==", - "dependencies": { - "fast-url-parser": "^1.1.3", - "pino": "^6.13.0", - "pino-std-serializers": "^4.0.0" - } - }, - "node_modules/probot/node_modules/pino-http/node_modules/pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==" - }, - "node_modules/probot/node_modules/pino-std-serializers": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz", - "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==" - }, - "node_modules/probot/node_modules/process-warning": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", - "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" - }, - "node_modules/probot/node_modules/sonic-boom": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", - "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "flatstr": "^1.0.12" - } - }, - "node_modules/probot/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-warning": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.0.tgz", - "integrity": "sha512-N6mp1+2jpQr3oCFMz6SeHRGbv6Slb20bRhj4v3xR99HqNToAcOe1MFOp4tytyzOfJn+QtN8Rf7U/h2KAn4kC6g==" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" - }, - "node_modules/pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/query-string": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", - "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", - "dependencies": { - "decode-uri-component": "^0.2.2", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/rate-limit-mongo": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/rate-limit-mongo/-/rate-limit-mongo-2.3.2.tgz", - "integrity": "sha512-dLck0j5N/AX9ycVHn5lX9Ti2Wrrwi1LfbXitu/mMBZOo2nC26RgYKJVbcb2mYgb9VMaPI2IwJVzIa2hAQrMaDA==", - "dependencies": { - "mongodb": "^3.6.7", - "twostep": "0.4.2", - "underscore": "1.12.1" - } - }, - "node_modules/rate-limit-mongo/node_modules/mongodb": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.8.0.tgz", - "integrity": "sha512-xx4CXmxcj3bNe7iGBlhntVrUqrNARYhUZteXaz4epEESv4oXD/FONAovcyoCaEffdYlw25Yz284OxMfpnPLlgQ==", - "dependencies": { - "bson": "^5.4.0", - "mongodb-connection-string-url": "^2.6.0", - "socks": "^2.7.1" - }, - "engines": { - "node": ">=14.20.1" - }, - "optionalDependencies": { - "@mongodb-js/saslprep": "^1.1.0" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.0.0", - "kerberos": "^1.0.0 || ^2.0.0", - "mongodb-client-encryption": ">=2.3.0 <3", - "snappy": "^7.2.2" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - } - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dependencies": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" - }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/seq-queue": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", - "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sift": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", - "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", - "dev": true, - "dependencies": { - "semver": "~7.0.0" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/smee-client": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/smee-client/-/smee-client-1.2.3.tgz", - "integrity": "sha512-uDrU8u9/Ln7aRXyzGHgVaNUS8onHZZeSwQjCdkMoSL7U85xI+l+Y2NgjibkMJAyXkW7IAbb8rw9RMHIjS6lAwA==", - "dev": true, - "dependencies": { - "commander": "^2.19.0", - "eventsource": "^1.1.0", - "morgan": "^1.9.1", - "superagent": "^7.1.3", - "validator": "^13.7.0" - }, - "bin": { - "smee": "bin/smee.js" - } - }, - "node_modules/smee-client/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/smee-client/node_modules/eventsource": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz", - "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/snappy": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/snappy/-/snappy-7.2.2.tgz", - "integrity": "sha512-iADMq1kY0v3vJmGTuKcFWSXt15qYUz7wFkArOrsSg0IFfI3nJqIJvK2/ZbEIndg7erIJLtAVX2nSOqPz7DcwbA==", - "optional": true, - "peer": true, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@napi-rs/snappy-android-arm-eabi": "7.2.2", - "@napi-rs/snappy-android-arm64": "7.2.2", - "@napi-rs/snappy-darwin-arm64": "7.2.2", - "@napi-rs/snappy-darwin-x64": "7.2.2", - "@napi-rs/snappy-freebsd-x64": "7.2.2", - "@napi-rs/snappy-linux-arm-gnueabihf": "7.2.2", - "@napi-rs/snappy-linux-arm64-gnu": "7.2.2", - "@napi-rs/snappy-linux-arm64-musl": "7.2.2", - "@napi-rs/snappy-linux-x64-gnu": "7.2.2", - "@napi-rs/snappy-linux-x64-musl": "7.2.2", - "@napi-rs/snappy-win32-arm64-msvc": "7.2.2", - "@napi-rs/snappy-win32-ia32-msvc": "7.2.2", - "@napi-rs/snappy-win32-x64-msvc": "7.2.2" - } - }, - "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/sonic-boom": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", - "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "optional": true, - "dependencies": { - "memory-pager": "^1.0.2" - } - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stoppable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", - "engines": { - "node": ">=4", - "npm": ">=6" - } - }, - "node_modules/stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" - }, - "node_modules/superagent": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.5.tgz", - "integrity": "sha512-HQYyGuDRFGmZ6GNC4hq2f37KnsY9Lr0/R1marNZTgMweVDQLTLJJ6DGQ9Tj/xVVs5HEnop9EMmTbywb5P30aqw==", - "dev": true, - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.0.1", - "methods": "^1.1.2", - "mime": "^2.5.0", - "qs": "^6.10.3", - "readable-stream": "^3.6.0", - "semver": "^7.3.7" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/supertest": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", - "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", - "dev": true, - "dependencies": { - "methods": "^1.1.2", - "superagent": "^8.0.5" - }, - "engines": { - "node": ">=6.4.0" - } - }, - "node_modules/supertest/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/supertest/node_modules/superagent": { - "version": "8.0.9", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", - "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", - "dev": true, - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/swagger-autogen": { - "version": "2.23.5", - "resolved": "https://registry.npmjs.org/swagger-autogen/-/swagger-autogen-2.23.5.tgz", - "integrity": "sha512-4Tl2+XhZMyHoBYkABnScHtQE0lKPKUD3NBt09mClrI6UKOUYljKlYw1xiFVwsHCTGR2hAXmhT4PpgjruCtt1ZA==", - "dev": true, - "dependencies": { - "acorn": "^7.4.1", - "deepmerge": "^4.2.2", - "glob": "^7.1.7", - "json5": "^2.2.3" - } - }, - "node_modules/swagger-autogen/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/swagger-ui-dist": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.1.3.tgz", - "integrity": "sha512-W/vZFeZHG+xTN4yu8LXdaIrcnT4Hbr7qRUILYlMEoIiG6nuTylnEGeRcvL64F2eHRA2Jo/fgCSTU06Qfh0lT3g==" - }, - "node_modules/swagger-ui-express": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.3.tgz", - "integrity": "sha512-CDje4PndhTD2HkgyKH3pab+LKspDeB/NhPN2OF1j+piYIamQqBYwAXWESOT1Yju2xFg51bRW9sUng2WxDjzArw==", - "dependencies": { - "swagger-ui-dist": ">=4.11.0" - }, - "engines": { - "node": ">= v0.10.32" - }, - "peerDependencies": { - "express": ">=4.0.0 || >=5.0.0-beta" - } - }, - "node_modules/tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/thread-stream": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", - "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", - "dependencies": { - "real-require": "^0.2.0" - } - }, - "node_modules/tiny-lru": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.0.1.tgz", - "integrity": "sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "dependencies": { - "nopt": "~1.0.10" - }, - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/touch/node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/tr46/node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "node_modules/tweetnacl-util": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" - }, - "node_modules/twostep": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/twostep/-/twostep-0.4.2.tgz", - "integrity": "sha512-O/wdPYk9ey04qcCiw8AQN74DbvLFZLAgnryrNTpV7T/sxB4lcGkCMHynx5xCcA6fCh739ZAqp3HcGhy770X1qA==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uid2": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", - "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, - "node_modules/underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" - }, - "node_modules/universal-github-app-jwt": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz", - "integrity": "sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==", - "dependencies": { - "@types/jsonwebtoken": "^9.0.0", - "jsonwebtoken": "^9.0.0" - } - }, - "node_modules/universal-github-app-jwt/node_modules/@types/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/update-dotenv": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-dotenv/-/update-dotenv-1.1.1.tgz", - "integrity": "sha512-3cIC18In/t0X/yH793c00qqxcKD8jVCgNOPif/fGQkFpYMGecM9YAc+kaAKXuZsM2dE9I9wFI7KvAuNX22SGMQ==", - "peerDependencies": { - "dotenv": "*" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/validator": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", - "dev": true - }, - "node_modules/xml-crypto": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.0.tgz", - "integrity": "sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg==", - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "xpath": "0.0.32" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xml-crypto/node_modules/xpath": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz", - "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==", - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/xml-encryption": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-3.0.2.tgz", - "integrity": "sha512-VxYXPvsWB01/aqVLd6ZMPWZ+qaj0aIdF+cStrVJMcFj3iymwZeI0ABzB3VqMYv48DkSpRhnrXqTUkR34j+UDyg==", - "dependencies": { - "@xmldom/xmldom": "^0.8.5", - "escape-html": "^1.0.3", - "xpath": "0.0.32" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/xml-encryption/node_modules/xpath": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz", - "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==", - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xml2js/node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "engines": { - "node": ">=8.0" - } - }, - "node_modules/xpath": { - "version": "0.0.27", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz", - "integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==", - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.22.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", - "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "requires": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "requires": { - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "requires": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "requires": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "requires": { - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "requires": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@aws-sdk/client-cloudwatch-logs": { - "version": "3.454.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.454.0.tgz", - "integrity": "sha512-anXMEIZvDvqsFAURYmNHaJU8SH85Rqkahkk0TsDiTLc6/J4Qh8xvcem358qTiXzRpPJmZe4m20XKqL0fXsJgIw==", - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.454.0", - "@aws-sdk/core": "3.451.0", - "@aws-sdk/credential-provider-node": "3.451.0", - "@aws-sdk/middleware-host-header": "3.451.0", - "@aws-sdk/middleware-logger": "3.451.0", - "@aws-sdk/middleware-recursion-detection": "3.451.0", - "@aws-sdk/middleware-signing": "3.451.0", - "@aws-sdk/middleware-user-agent": "3.451.0", - "@aws-sdk/region-config-resolver": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@aws-sdk/util-user-agent-browser": "3.451.0", - "@aws-sdk/util-user-agent-node": "3.451.0", - "@smithy/config-resolver": "^2.0.18", - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/hash-node": "^2.0.15", - "@smithy/invalid-dependency": "^2.0.13", - "@smithy/middleware-content-length": "^2.0.15", - "@smithy/middleware-endpoint": "^2.2.0", - "@smithy/middleware-retry": "^2.0.20", - "@smithy/middleware-serde": "^2.0.13", - "@smithy/middleware-stack": "^2.0.7", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/protocol-http": "^3.0.9", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.19", - "@smithy/util-defaults-mode-node": "^2.0.25", - "@smithy/util-endpoints": "^1.0.4", - "@smithy/util-retry": "^2.0.6", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "dependencies": { - "@aws-sdk/client-sso": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.451.0.tgz", - "integrity": "sha512-KkYSke3Pdv3MfVH/5fT528+MKjMyPKlcLcd4zQb0x6/7Bl7EHrPh1JZYjzPLHelb+UY5X0qN8+cb8iSu1eiwIQ==", - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.451.0", - "@aws-sdk/middleware-host-header": "3.451.0", - "@aws-sdk/middleware-logger": "3.451.0", - "@aws-sdk/middleware-recursion-detection": "3.451.0", - "@aws-sdk/middleware-user-agent": "3.451.0", - "@aws-sdk/region-config-resolver": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@aws-sdk/util-user-agent-browser": "3.451.0", - "@aws-sdk/util-user-agent-node": "3.451.0", - "@smithy/config-resolver": "^2.0.18", - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/hash-node": "^2.0.15", - "@smithy/invalid-dependency": "^2.0.13", - "@smithy/middleware-content-length": "^2.0.15", - "@smithy/middleware-endpoint": "^2.2.0", - "@smithy/middleware-retry": "^2.0.20", - "@smithy/middleware-serde": "^2.0.13", - "@smithy/middleware-stack": "^2.0.7", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/protocol-http": "^3.0.9", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.19", - "@smithy/util-defaults-mode-node": "^2.0.25", - "@smithy/util-endpoints": "^1.0.4", - "@smithy/util-retry": "^2.0.6", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/client-sts": { - "version": "3.454.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.454.0.tgz", - "integrity": "sha512-0fDvr8WeB6IYO8BUCzcivWmahgGl/zDbaYfakzGnt4mrl5ztYaXE875WI6b7+oFcKMRvN+KLvwu5TtyFuNY+GQ==", - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.451.0", - "@aws-sdk/credential-provider-node": "3.451.0", - "@aws-sdk/middleware-host-header": "3.451.0", - "@aws-sdk/middleware-logger": "3.451.0", - "@aws-sdk/middleware-recursion-detection": "3.451.0", - "@aws-sdk/middleware-sdk-sts": "3.451.0", - "@aws-sdk/middleware-signing": "3.451.0", - "@aws-sdk/middleware-user-agent": "3.451.0", - "@aws-sdk/region-config-resolver": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@aws-sdk/util-user-agent-browser": "3.451.0", - "@aws-sdk/util-user-agent-node": "3.451.0", - "@smithy/config-resolver": "^2.0.18", - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/hash-node": "^2.0.15", - "@smithy/invalid-dependency": "^2.0.13", - "@smithy/middleware-content-length": "^2.0.15", - "@smithy/middleware-endpoint": "^2.2.0", - "@smithy/middleware-retry": "^2.0.20", - "@smithy/middleware-serde": "^2.0.13", - "@smithy/middleware-stack": "^2.0.7", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/protocol-http": "^3.0.9", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.19", - "@smithy/util-defaults-mode-node": "^2.0.25", - "@smithy/util-endpoints": "^1.0.4", - "@smithy/util-retry": "^2.0.6", - "@smithy/util-utf8": "^2.0.2", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.451.0.tgz", - "integrity": "sha512-9dAav7DcRgaF7xCJEQR5ER9ErXxnu/tdnVJ+UPmb1NPeIZdESv1A3lxFDEq1Fs8c4/lzAj9BpshGyJVIZwZDKg==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.451.0.tgz", - "integrity": "sha512-TySt64Ci5/ZbqFw1F9Z0FIGvYx5JSC9e6gqDnizIYd8eMnn8wFRUscRrD7pIHKfrhvVKN5h0GdYovmMO/FMCBw==", - "requires": { - "@aws-sdk/credential-provider-env": "3.451.0", - "@aws-sdk/credential-provider-process": "3.451.0", - "@aws-sdk/credential-provider-sso": "3.451.0", - "@aws-sdk/credential-provider-web-identity": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.451.0.tgz", - "integrity": "sha512-AEwM1WPyxUdKrKyUsKyFqqRFGU70e4qlDyrtBxJnSU9NRLZI8tfEZ67bN7fHSxBUBODgDXpMSlSvJiBLh5/3pw==", - "requires": { - "@aws-sdk/credential-provider-env": "3.451.0", - "@aws-sdk/credential-provider-ini": "3.451.0", - "@aws-sdk/credential-provider-process": "3.451.0", - "@aws-sdk/credential-provider-sso": "3.451.0", - "@aws-sdk/credential-provider-web-identity": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.451.0.tgz", - "integrity": "sha512-HQywSdKeD5PErcLLnZfSyCJO+6T+ZyzF+Lm/QgscSC+CbSUSIPi//s15qhBRVely/3KBV6AywxwNH+5eYgt4lQ==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.451.0.tgz", - "integrity": "sha512-Usm/N51+unOt8ID4HnQzxIjUJDrkAQ1vyTOC0gSEEJ7h64NSSPGD5yhN7il5WcErtRd3EEtT1a8/GTC5TdBctg==", - "requires": { - "@aws-sdk/client-sso": "3.451.0", - "@aws-sdk/token-providers": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.451.0.tgz", - "integrity": "sha512-Xtg3Qw65EfDjWNG7o2xD6sEmumPfsy3WDGjk2phEzVg8s7hcZGxf5wYwe6UY7RJvlEKrU0rFA+AMn6Hfj5oOzg==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.451.0.tgz", - "integrity": "sha512-j8a5jAfhWmsK99i2k8oR8zzQgXrsJtgrLxc3js6U+525mcZytoiDndkWTmD5fjJ1byU1U2E5TaPq+QJeDip05Q==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.451.0.tgz", - "integrity": "sha512-0kHrYEyVeB2QBfP6TfbI240aRtatLZtcErJbhpiNUb+CQPgEL3crIjgVE8yYiJumZ7f0jyjo8HLPkwD1/2APaw==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.451.0.tgz", - "integrity": "sha512-J6jL6gJ7orjHGM70KDRcCP7so/J2SnkN4vZ9YRLTeeZY6zvBuHDjX8GCIgSqPn/nXFXckZO8XSnA7u6+3TAT0w==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-sdk-sts": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.451.0.tgz", - "integrity": "sha512-UJ6UfVUEgp0KIztxpAeelPXI5MLj9wUtUCqYeIMP7C1ZhoEMNm3G39VLkGN43dNhBf1LqjsV9jkKMZbVfYXuwg==", - "requires": { - "@aws-sdk/middleware-signing": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-signing": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.451.0.tgz", - "integrity": "sha512-s5ZlcIoLNg1Huj4Qp06iKniE8nJt/Pj1B/fjhWc6cCPCM7XJYUCejCnRh6C5ZJoBEYodjuwZBejPc1Wh3j+znA==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.5.0", - "@smithy/util-middleware": "^2.0.6", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.451.0.tgz", - "integrity": "sha512-8NM/0JiKLNvT9wtAQVl1DFW0cEO7OvZyLSUBLNLTHqyvOZxKaZ8YFk7d8PL6l76LeUKRxq4NMxfZQlUIRe0eSA==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/token-providers": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.451.0.tgz", - "integrity": "sha512-ij1L5iUbn6CwxVOT1PG4NFjsrsKN9c4N1YEM0lkl6DwmaNOscjLKGSNyj9M118vSWsOs1ZDbTwtj++h0O/BWrQ==", - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.451.0", - "@aws-sdk/middleware-logger": "3.451.0", - "@aws-sdk/middleware-recursion-detection": "3.451.0", - "@aws-sdk/middleware-user-agent": "3.451.0", - "@aws-sdk/region-config-resolver": "3.451.0", - "@aws-sdk/types": "3.451.0", - "@aws-sdk/util-endpoints": "3.451.0", - "@aws-sdk/util-user-agent-browser": "3.451.0", - "@aws-sdk/util-user-agent-node": "3.451.0", - "@smithy/config-resolver": "^2.0.18", - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/hash-node": "^2.0.15", - "@smithy/invalid-dependency": "^2.0.13", - "@smithy/middleware-content-length": "^2.0.15", - "@smithy/middleware-endpoint": "^2.2.0", - "@smithy/middleware-retry": "^2.0.20", - "@smithy/middleware-serde": "^2.0.13", - "@smithy/middleware-stack": "^2.0.7", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.9", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.19", - "@smithy/util-defaults-mode-node": "^2.0.25", - "@smithy/util-endpoints": "^1.0.4", - "@smithy/util-retry": "^2.0.6", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/types": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.451.0.tgz", - "integrity": "sha512-rhK+qeYwCIs+laJfWCcrYEjay2FR/9VABZJ2NRM89jV/fKqGVQR52E5DQqrI+oEIL5JHMhhnr4N4fyECMS35lw==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.451.0.tgz", - "integrity": "sha512-giqLGBTnRIcKkDqwU7+GQhKbtJ5Ku35cjGQIfMyOga6pwTBUbaK0xW1Sdd8sBQ1GhApscnChzI9o/R9x0368vw==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/util-endpoints": "^1.0.4", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.451.0.tgz", - "integrity": "sha512-Ws5mG3J0TQifH7OTcMrCTexo7HeSAc3cBgjfhS/ofzPUzVCtsyg0G7I6T7wl7vJJETix2Kst2cpOsxygPgPD9w==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/types": "^2.5.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.451.0.tgz", - "integrity": "sha512-TBzm6P+ql4mkGFAjPlO1CI+w3yUT+NulaiALjl/jNX/nnUp6HsJsVxJf4nVFQTG5KRV0iqMypcs7I3KIhH+LmA==", - "requires": { - "@aws-sdk/types": "3.451.0", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/abort-controller": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.13.tgz", - "integrity": "sha512-eeOPD+GF9BzF/Mjy3PICLePx4l0f3rG/nQegQHRLTloN5p1lSJJNZsyn+FzDnW8P2AduragZqJdtKNCxXozB1Q==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/config-resolver": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.18.tgz", - "integrity": "sha512-761sJSgNbvsqcsKW6/WZbrZr4H+0Vp/QKKqwyrxCPwD8BsiPEXNHyYnqNgaeK9xRWYswjon0Uxbpe3DWQo0j/g==", - "requires": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/types": "^2.5.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.6", - "tslib": "^2.5.0" - } - }, - "@smithy/credential-provider-imds": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.1.1.tgz", - "integrity": "sha512-gw5G3FjWC6sNz8zpOJgPpH5HGKrpoVFQpToNAwLwJVyI/LJ2jDJRjSKEsM6XI25aRpYjMSE/Qptxx305gN1vHw==", - "requires": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/property-provider": "^2.0.14", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "tslib": "^2.5.0" - } - }, - "@smithy/eventstream-codec": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.13.tgz", - "integrity": "sha512-CExbelIYp+DxAHG8RIs0l9QL7ElqhG4ym9BNoSpkPa4ptBQfzJdep3LbOSVJIE2VUdBAeObdeL6EDB3Jo85n3g==", - "requires": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.5.0", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/fetch-http-handler": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.6.tgz", - "integrity": "sha512-PStY3XO1Ksjwn3wMKye5U6m6zxXpXrXZYqLy/IeCbh3nM9QB3Jgw/B0PUSLUWKdXg4U8qgEu300e3ZoBvZLsDg==", - "requires": { - "@smithy/protocol-http": "^3.0.9", - "@smithy/querystring-builder": "^2.0.13", - "@smithy/types": "^2.5.0", - "@smithy/util-base64": "^2.0.1", - "tslib": "^2.5.0" - } - }, - "@smithy/hash-node": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.15.tgz", - "integrity": "sha512-t/qjEJZu/G46A22PAk1k/IiJZT4ncRkG5GOCNWN9HPPy5rCcSZUbh7gwp7CGKgJJ7ATMMg+0Td7i9o1lQTwOfQ==", - "requires": { - "@smithy/types": "^2.5.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/invalid-dependency": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.13.tgz", - "integrity": "sha512-XsGYhVhvEikX1Yz0kyIoLssJf2Rs6E0U2w2YuKdT4jSra5A/g8V2oLROC1s56NldbgnpesTYB2z55KCHHbKyjw==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-content-length": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.15.tgz", - "integrity": "sha512-xH4kRBw01gJgWiU+/mNTrnyFXeozpZHw39gLb3JKGsFDVmSrJZ8/tRqu27tU/ki1gKkxr2wApu+dEYjI3QwV1Q==", - "requires": { - "@smithy/protocol-http": "^3.0.9", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-endpoint": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.2.0.tgz", - "integrity": "sha512-tddRmaig5URk2106PVMiNX6mc5BnKIKajHHDxb7K0J5MLdcuQluHMGnjkv18iY9s9O0tF+gAcPd/pDXA5L9DZw==", - "requires": { - "@smithy/middleware-serde": "^2.0.13", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/shared-ini-file-loader": "^2.2.4", - "@smithy/types": "^2.5.0", - "@smithy/url-parser": "^2.0.13", - "@smithy/util-middleware": "^2.0.6", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-retry": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.20.tgz", - "integrity": "sha512-X2yrF/SHDk2WDd8LflRNS955rlzQ9daz9UWSp15wW8KtzoTXg3bhHM78HbK1cjr48/FWERSJKh9AvRUUGlIawg==", - "requires": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/protocol-http": "^3.0.9", - "@smithy/service-error-classification": "^2.0.6", - "@smithy/types": "^2.5.0", - "@smithy/util-middleware": "^2.0.6", - "@smithy/util-retry": "^2.0.6", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - } - }, - "@smithy/middleware-serde": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.13.tgz", - "integrity": "sha512-tBGbeXw+XsE6pPr4UaXOh+UIcXARZeiA8bKJWxk2IjJcD1icVLhBSUQH9myCIZLNNzJIH36SDjUX8Wqk4xJCJg==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-stack": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.7.tgz", - "integrity": "sha512-L1KLAAWkXbGx1t2jjCI/mDJ2dDNq+rp4/ifr/HcC6FHngxho5O7A5bQLpKHGlkfATH6fUnOEx0VICEVFA4sUzw==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/node-config-provider": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.5.tgz", - "integrity": "sha512-3Omb5/h4tOCuKRx4p4pkYTvEYRCYoKk52bOYbKUyz/G/8gERbagsN8jFm4FjQubkrcIqQEghTpQaUw6uk+0edw==", - "requires": { - "@smithy/property-provider": "^2.0.14", - "@smithy/shared-ini-file-loader": "^2.2.4", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/node-http-handler": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.9.tgz", - "integrity": "sha512-+K0q3SlNcocmo9OZj+fz67gY4lwhOCvIJxVbo/xH+hfWObvaxrMTx7JEzzXcluK0thnnLz++K3Qe7Z/8MDUreA==", - "requires": { - "@smithy/abort-controller": "^2.0.13", - "@smithy/protocol-http": "^3.0.9", - "@smithy/querystring-builder": "^2.0.13", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/property-provider": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.14.tgz", - "integrity": "sha512-k3D2qp9o6imTrLaXRj6GdLYEJr1sXqS99nLhzq8fYmJjSVOeMg/G+1KVAAc7Oxpu71rlZ2f8SSZxcSxkevuR0A==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/protocol-http": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", - "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-builder": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.13.tgz", - "integrity": "sha512-JhXKwp3JtsFUe96XLHy/nUPEbaXqn6r7xE4sNaH8bxEyytE5q1fwt0ew/Ke6+vIC7gP87HCHgQpJHg1X1jN2Fw==", - "requires": { - "@smithy/types": "^2.5.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-parser": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.13.tgz", - "integrity": "sha512-TEiT6o8CPZVxJ44Rly/rrsATTQsE+b/nyBVzsYn2sa75xAaZcurNxsFd8z1haoUysONiyex24JMHoJY6iCfLdA==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/service-error-classification": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.6.tgz", - "integrity": "sha512-fCQ36frtYra2fqY2/DV8+3/z2d0VB/1D1hXbjRcM5wkxTToxq6xHbIY/NGGY6v4carskMyG8FHACxgxturJ9Pg==", - "requires": { - "@smithy/types": "^2.5.0" - } - }, - "@smithy/shared-ini-file-loader": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.4.tgz", - "integrity": "sha512-9dRknGgvYlRIsoTcmMJXuoR/3ekhGwhRq4un3ns2/byre4Ql5hyUN4iS0x8eITohjU90YOnUCsbRwZRvCkbRfw==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/signature-v4": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.15.tgz", - "integrity": "sha512-SRTEJSEhQYVlBKIIdZ9SZpqW+KFqxqcNnEcBX+8xkDdWx+DItme9VcCDkdN32yTIrICC+irUufnUdV7mmHPjoA==", - "requires": { - "@smithy/eventstream-codec": "^2.0.13", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.5.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.6", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/smithy-client": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.15.tgz", - "integrity": "sha512-rngZcQu7Jvs9UbHihK1EI67RMPuzkc3CJmu4MBgB7D7yBnMGuFR86tq5rqHfL2gAkNnMelBN/8kzQVvZjNKefQ==", - "requires": { - "@smithy/middleware-stack": "^2.0.7", - "@smithy/types": "^2.5.0", - "@smithy/util-stream": "^2.0.20", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.5.0.tgz", - "integrity": "sha512-/a31lYofrMBkJb3BuPlYJTMKDj0hUmKUP6JFZQu6YVuQVoAjubiY0A52U9S0Uysd33n/djexCUSNJ+G9bf3/aA==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/url-parser": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.13.tgz", - "integrity": "sha512-okWx2P/d9jcTsZWTVNnRMpFOE7fMkzloSFyM53fA7nLKJQObxM2T4JlZ5KitKKuXq7pxon9J6SF2kCwtdflIrA==", - "requires": { - "@smithy/querystring-parser": "^2.0.13", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-base64": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", - "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "requires": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-browser": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.19.tgz", - "integrity": "sha512-VHP8xdFR7/orpiABJwgoTB0t8Zhhwpf93gXhNfUBiwAE9O0rvsv7LwpQYjgvbOUDDO8JfIYQB2GYJNkqqGWsXw==", - "requires": { - "@smithy/property-provider": "^2.0.14", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-node": { - "version": "2.0.25", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.25.tgz", - "integrity": "sha512-jkmep6/JyWmn2ADw9VULDeGbugR4N/FJCKOt+gYyVswmN1BJOfzF2umaYxQ1HhQDvna3kzm1Dbo1qIfBW4iuHA==", - "requires": { - "@smithy/config-resolver": "^2.0.18", - "@smithy/credential-provider-imds": "^2.1.1", - "@smithy/node-config-provider": "^2.1.5", - "@smithy/property-provider": "^2.0.14", - "@smithy/smithy-client": "^2.1.15", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.6.tgz", - "integrity": "sha512-7W4uuwBvSLgKoLC1x4LfeArCVcbuHdtVaC4g30kKsD1erfICyQ45+tFhhs/dZNeQg+w392fhunCm/+oCcb6BSA==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-retry": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.6.tgz", - "integrity": "sha512-PSO41FofOBmyhPQJwBQJ6mVlaD7Sp9Uff9aBbnfBJ9eqXOE/obrqQjn0PNdkfdvViiPXl49BINfnGcFtSP4kYw==", - "requires": { - "@smithy/service-error-classification": "^2.0.6", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-stream": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.20.tgz", - "integrity": "sha512-tT8VASuD8jJu0yjHEMTCPt1o5E3FVzgdsxK6FQLAjXKqVv5V8InCnc0EOsYrijgspbfDqdAJg7r0o2sySfcHVg==", - "requires": { - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/types": "^2.5.0", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-utf8": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", - "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - } - } - }, - "@aws-sdk/client-cognito-identity": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.388.0.tgz", - "integrity": "sha512-5sCogMJ1utRlwLQiameyOrrcyhueknbsC2YK1G9Y7pgmgUl2zzUo7htQS2luW71SeBHiwkTQa3OZjbmGsotJvg==", - "optional": true, - "peer": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.388.0", - "@aws-sdk/credential-provider-node": "3.388.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "@aws-sdk/client-sso": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.387.0.tgz", - "integrity": "sha512-E7uKSvbA0XMKSN5KLInf52hmMpe9/OKo6N9OPffGXdn3fNEQlvyQq3meUkqG7Is0ldgsQMz5EUBNtNybXzr3tQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/client-sts": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.388.0.tgz", - "integrity": "sha512-y9FAcAYHT8O6T/jqhgsIQUb4gLiSTKD3xtzudDvjmFi8gl0oRIY1npbeckSiK6k07VQugm2s64I0nDnDxtWsBg==", - "optional": true, - "peer": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.388.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-sdk-sts": "3.387.0", - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.387.0.tgz", - "integrity": "sha512-PVqNk7XPIYe5CMYNvELkcALtkl/pIM8/uPtqEtTg+mgnZBeL4fAmgXZiZMahQo1DxP5t/JaK384f6JG+A0qDjA==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.388.0.tgz", - "integrity": "sha512-3dg3A8AiZ5vXkSAYyyI3V/AW3Eo6KQJyE/glA+Nr2M0oAjT4z3vHhS3pf2B+hfKGZBTuKKgxusrrhrQABd/Diw==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.388.0.tgz", - "integrity": "sha512-BqWAkIG08gj/wevpesaZhAjALjfUNVjseHQRk+DNUoHIfyibW7Ahf3q/GIPs11dA2o8ECwR9/fo68Sq+sK799A==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-ini": "3.388.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.387.0.tgz", - "integrity": "sha512-tQScLHmDlqkQN+mqw4s3cxepEUeHYDhFl5eH+J8puvPqWjXMYpCEdY79SAtWs6SZd4CWiZ0VLeYU6xQBZengbQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.388.0.tgz", - "integrity": "sha512-RH02+rntaO0UhnSBr42n+7q8HOztc+Dets/hh6cWovf3Yi9s9ghLgYLN9FXpSosfot3XkmT/HOCa+CphAmGN9A==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/client-sso": "3.387.0", - "@aws-sdk/token-providers": "3.388.0", - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.387.0.tgz", - "integrity": "sha512-6ueMPl+J3KWv6ZaAWF4Z138QCuBVFZRVAgwbtP3BNqWrrs4Q6TPksOQJ79lRDMpv0EUoyVl04B6lldNlhN8RdA==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.387.0.tgz", - "integrity": "sha512-EWm9PXSr8dSp7hnRth1U7OfelXQp9dLf1yS1kUL+UhppYDJpjhdP7ql3NI4xJKw8e76sP2FuJYEuzWnJHuWoyQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.387.0.tgz", - "integrity": "sha512-FjAvJr1XyaInT81RxUwgifnbXoFJrRBFc64XeFJgFanGIQCWLYxRrK2HV9eBpao/AycbmuoHgLd/f0sa4hZFoQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.387.0.tgz", - "integrity": "sha512-ZF45T785ru8OwvYZw6awD9Z76OwSMM1eZzj2eY+FDz1cHfkpLjxEiti2iIH1FxbyK7n9ZqDUx29lVlCv238YyQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-sdk-sts": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.387.0.tgz", - "integrity": "sha512-7ZzRKOJ4V/JDQmKz9z+FjZqw59mrMATEMLR6ff0H0JHMX0Uk5IX8TQB058ss+ar14qeJ4UcteYzCqHNI0O1BHw==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-signing": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.387.0.tgz", - "integrity": "sha512-oJXlE0MES8gxNLo137PPNNiOICQGOaETTvq3kBSJgb/gtEAxQajMIlaNT7s1wsjOAruFHt4975nCXuY4lpx7GQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.387.0.tgz", - "integrity": "sha512-hTfFTwDtp86xS98BKa+RFuLfcvGftxwzrbZeisZV8hdb4ZhvNXjSxnvM3vetW0GUEnY9xHPSGyp2ERRTinPKFQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/token-providers": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.388.0.tgz", - "integrity": "sha512-2lo1gFJl624kfjo/YdU6zW+k6dEwhoqjNkDNbOZEFgS1KDofHe9GX8W4/ReKb0Ggho5/EcjzZ53/1CjkzUq4tA==", - "optional": true, - "peer": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/types": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.387.0.tgz", - "integrity": "sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.387.0.tgz", - "integrity": "sha512-g7kvuCXehGXHHBw9PkSQdwVyDFmNUZLmfrRmqMyrMDG9QLQrxr4pyWcSaYgTE16yUzhQQOR+QSey+BL6W9/N6g==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.387.0.tgz", - "integrity": "sha512-lpgSVvDqx+JjHZCTYs/yQSS7J71dPlJeAlvxc7bmx5m+vfwKe07HAnIs+929DngS0QbAp/VaXbTiMFsInLkO4Q==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.387.0.tgz", - "integrity": "sha512-r9OVkcWpRYatjLhJacuHFgvO2T5s/Nu5DDbScMrkUD8b4aGIIqsrdZji0vZy9FCjsUFQMM92t9nt4SejrGjChA==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/abort-controller": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.2.tgz", - "integrity": "sha512-ln5Cob0mksym62sLr7NiPOSqJ0jKao4qjfcNLDdgINM1lQI12hXrZBlKdPHbXJqpKhKiECDgonMoqCM8bigq4g==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/config-resolver": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.2.tgz", - "integrity": "sha512-0kdsqBL6BdmSbdU6YaDkodVBMua5MuQQluC3nocJ7OJ6PnOuM7i2FEQHE46LBadLqT+CimlDSM+6j91uHNL1ng==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/credential-provider-imds": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.2.tgz", - "integrity": "sha512-mbWFYEZ00LBRDk3WvcXViwpdpkJQcfrM3seuKzFxZnF6wIBLMwrcWcsj+OUC/1L+86m8aQY9imXMAaQsAoGxow==", - "optional": true, - "peer": true, - "requires": { - "@smithy/node-config-provider": "^2.0.2", - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/eventstream-codec": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.2.tgz", - "integrity": "sha512-PQZiKx7fMnNwx4zxcUCm82VjnqK6wV4MEHSmMy3taj5dKfXV782IjRGyaDT+8TsmNqVdZIkve5zLRAzh+7kOhA==", - "optional": true, - "peer": true, - "requires": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/fetch-http-handler": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.0.2.tgz", - "integrity": "sha512-Wo2m1RaiXNSLF4J3D62LpdSoj/YYb+6tn0H8is1tSrzr7eXAdiYVBc0wIa23N0wT4zmN0iG/yNY6gTCDQ6799A==", - "optional": true, - "peer": true, - "requires": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/querystring-builder": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/util-base64": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/hash-node": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.2.tgz", - "integrity": "sha512-JKDzZ1YVR7JzOBaJoWy3ToJCE86OQE6D4kOBvvVsu93a3lcF9kv6KYTKBYEWAjwOn/CpK4NH7mKB01OQ8H+aiA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/invalid-dependency": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.2.tgz", - "integrity": "sha512-inQZQ5gCO3WRWuXpsc1YJ4KBjsvj2qsoU32yTIKznBWTCQe/D5Dp+sSaysqBqxe0VTZ+8nFEHdUMWUX2BxQThw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-content-length": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.2.tgz", - "integrity": "sha512-FmHlNfuvYgDZE3fIx0G3rD/wLXfAmBYE4mVc/w6d7RllA7TygPzq2pfHL1iCMzWkWTdoAVnt3h4aavAZnhaxEQ==", - "optional": true, - "peer": true, - "requires": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-endpoint": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.2.tgz", - "integrity": "sha512-ropE7/c+g22QeluZ+By/B/WvVep0UFreX+IeRMGIO7EbOUPgqtJRXpbJFdG6JKB1uC+CdaJLn4MnZnVBpcyjuA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/middleware-serde": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-retry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.2.tgz", - "integrity": "sha512-wtBUXqtZVriiXppYaFkUrybAPhFVX7vebnW/yVPliLMWMcguOMS58qhOYPZe3t9Wki2+mASfyu+kO3An8lAg2A==", - "optional": true, - "peer": true, - "requires": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/service-error-classification": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-retry": "^2.0.0", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - } - }, - "@smithy/middleware-serde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.2.tgz", - "integrity": "sha512-Kw9xLdlueIaivUWslKB67WZ/cCUg3QnzYVIA3t5KfgsseEEuU4UxXw8NSTvIt71gqQloY+Um8ugS+idgxrWWnw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-stack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.0.tgz", - "integrity": "sha512-31XC1xNF65nlbc16yuh3wwTudmqs6qy4EseQUGF8A/p2m/5wdd/cnXJqpniy/XvXVwkHPz/GwV36HqzHtIKATQ==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/node-config-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.2.tgz", - "integrity": "sha512-9wVJccASfuCctNWrzR0zrDkf0ox3HCHGEhFlWL2LBoghUYuK28pVRBbG69wvnkhlHnB8dDZHagxH+Nq9dm7eWw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/property-provider": "^2.0.2", - "@smithy/shared-ini-file-loader": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/node-http-handler": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.0.2.tgz", - "integrity": "sha512-lpZjmtmyZqSAtMPsbrLhb7XoAQ2kAHeuLY/csW6I2k+QyFvOk7cZeQsqEngWmZ9SJaeYiDCBINxAIM61i5WGLw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/abort-controller": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/querystring-builder": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/property-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.2.tgz", - "integrity": "sha512-DfaZ8cO+d/mgnMzIllcXcU4OYP+omiOl2LYdn/fTGpw/EAQSVzscYV2muV3sDDnuPYQ/r014hUqIxnF+pzh+SQ==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/protocol-http": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-2.0.2.tgz", - "integrity": "sha512-qWu8g1FUy+m36KpO1sREJSF7BaLmjw9AqOuwxLVVSdYz+nUQjc9tFAZ9LB6jJXKdsZFSjfkjHJBbhD78QdE7Rw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-builder": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.2.tgz", - "integrity": "sha512-H99LOMWEssfwqkOoTs4Y12UiZ7CTGQSX5Nrx5UkYgRbUEpC1GnnaprHiYrqclC58/xr4K76aNchdPyioxewMzA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.2.tgz", - "integrity": "sha512-L4VtKQ8O4/aWPQJbiFymbhAmxdfLnEaROh/Vs0OstJ7jtOZeBl2QJmuWY2V7hjt64W7V+tEn2sv6vVvnxkm/xQ==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/service-error-classification": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.0.tgz", - "integrity": "sha512-2z5Nafy1O0cTf69wKyNjGW/sNVMiqDnb4jgwfMG8ye8KnFJ5qmJpDccwIbJNhXIfbsxTg9SEec2oe1cexhMJvw==", - "optional": true, - "peer": true - }, - "@smithy/shared-ini-file-loader": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.2.tgz", - "integrity": "sha512-2VkNOM/82u4vatVdK5nfusgGIlvR48Fkq6me17Oc+V1iyxfR/1x0pG6LzW0br1qlGtzBYFZKmDyviBRcPVFTVw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/signature-v4": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.2.tgz", - "integrity": "sha512-YMooDEw/UmGxcXY4qWnSXkbPFsRloluSvyXVT678YPDN/K2AS1GzKfRsvSU7fbccOB4WF8MHZf2UqcRGEltE3Q==", - "optional": true, - "peer": true, - "requires": { - "@smithy/eventstream-codec": "^2.0.2", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/smithy-client": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.0.2.tgz", - "integrity": "sha512-mDfokI8WwLU5C0gcQ4ww/zJI/WLGSh2+vdIA42JRnjfYUjJNH/rKfX9YOnn2eBOxl3loATERVUqkHmKe+P8s2Q==", - "optional": true, - "peer": true, - "requires": { - "@smithy/middleware-stack": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-stream": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.1.0.tgz", - "integrity": "sha512-KLsCsqxX0j2l99iP8s0f7LBlcsp7a7ceXGn0LPYPyVOsqmIKvSaPQajq0YevlL4T9Bm+DtcyXfBTbtBcLX1I7A==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/url-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.2.tgz", - "integrity": "sha512-X1mHCzrSVDlhVy7d3S7Vq+dTfYzwh4n7xGHhyJumu77nJqIss0lazVug85Pwo0DKIoO314wAOvMnBxNYDa+7wA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/querystring-parser": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-base64": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", - "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-node": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.0.0.tgz", - "integrity": "sha512-ZV7Z/WHTMxHJe/xL/56qZwSUcl63/5aaPAGjkfynJm4poILjdD4GmFI+V+YWabh2WJIjwTKZ5PNsuvPQKt93Mg==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-browser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.2.tgz", - "integrity": "sha512-c2tMMjb624XLuzmlRoZpnFOkejVxcgw3WQKdmgdGZYZapcLzXyC0H9JhnXMjQCt30GqLTlsILRNVBYwFRbw/4Q==", - "optional": true, - "peer": true, - "requires": { - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-node": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.2.tgz", - "integrity": "sha512-gt7m5LLqUtEKldJLyc14DE4kb85vxwomvt9AfEMEvWM4VwfWS1kGJqiStZFb5KNqnQPXw8vvpgLTi8NrWAOXqg==", - "optional": true, - "peer": true, - "requires": { - "@smithy/config-resolver": "^2.0.2", - "@smithy/credential-provider-imds": "^2.0.2", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-middleware": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.0.tgz", - "integrity": "sha512-eCWX4ECuDHn1wuyyDdGdUWnT4OGyIzV0LN1xRttBFMPI9Ff/4heSHVxneyiMtOB//zpXWCha1/SWHJOZstG7kA==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-retry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.0.tgz", - "integrity": "sha512-/dvJ8afrElasuiiIttRJeoS2sy8YXpksQwiM/TcepqdRVp7u4ejd9C4IQURHNjlfPUT7Y6lCDSa2zQJbdHhVTg==", - "optional": true, - "peer": true, - "requires": { - "@smithy/service-error-classification": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-stream": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.2.tgz", - "integrity": "sha512-Mg9IJcKIu4YKlbzvpp1KLvh4JZLdcPgpxk+LICuDwzZCfxe47R9enVK8dNEiuyiIGK2ExbfvzCVT8IBru62vZw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-utf8": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", - "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", - "optional": true, - "peer": true, - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - } - } - }, - "@aws-sdk/client-secrets-manager": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.370.0.tgz", - "integrity": "sha512-1o1mpWbI1RyzCQ4cVpHQJnm6PziAJ+ptLt4p+wlN74Z330/nnE0JkK3t9l3CxhPqCIW8VjGbTCno5IzwAXnjPw==", - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.370.0", - "@aws-sdk/credential-provider-node": "3.370.0", - "@aws-sdk/middleware-host-header": "3.370.0", - "@aws-sdk/middleware-logger": "3.370.0", - "@aws-sdk/middleware-recursion-detection": "3.370.0", - "@aws-sdk/middleware-signing": "3.370.0", - "@aws-sdk/middleware-user-agent": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@aws-sdk/util-user-agent-browser": "3.370.0", - "@aws-sdk/util-user-agent-node": "3.370.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.2", - "@smithy/middleware-retry": "^1.0.3", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.3", - "@smithy/util-utf8": "^1.0.1", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - } - }, - "@aws-sdk/client-sso": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.370.0.tgz", - "integrity": "sha512-0Ty1iHuzNxMQtN7nahgkZr4Wcu1XvqGfrQniiGdKKif9jG/4elxsQPiydRuQpFqN6b+bg7wPP7crFP1uTxx2KQ==", - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.370.0", - "@aws-sdk/middleware-logger": "3.370.0", - "@aws-sdk/middleware-recursion-detection": "3.370.0", - "@aws-sdk/middleware-user-agent": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@aws-sdk/util-user-agent-browser": "3.370.0", - "@aws-sdk/util-user-agent-node": "3.370.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.2", - "@smithy/middleware-retry": "^1.0.3", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.3", - "@smithy/util-utf8": "^1.0.1", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/client-sso-oidc": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.370.0.tgz", - "integrity": "sha512-jAYOO74lmVXylQylqkPrjLzxvUnMKw476JCUTvCO6Q8nv3LzCWd76Ihgv/m9Q4M2Tbqi1iP2roVK5bstsXzEjA==", - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.370.0", - "@aws-sdk/middleware-logger": "3.370.0", - "@aws-sdk/middleware-recursion-detection": "3.370.0", - "@aws-sdk/middleware-user-agent": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@aws-sdk/util-user-agent-browser": "3.370.0", - "@aws-sdk/util-user-agent-node": "3.370.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.2", - "@smithy/middleware-retry": "^1.0.3", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.3", - "@smithy/util-utf8": "^1.0.1", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/client-sts": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.370.0.tgz", - "integrity": "sha512-utFxOPWIzbN+3kc415Je2o4J72hOLNhgR2Gt5EnRSggC3yOnkC4GzauxG8n7n5gZGBX45eyubHyPOXLOIyoqQA==", - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.370.0", - "@aws-sdk/middleware-host-header": "3.370.0", - "@aws-sdk/middleware-logger": "3.370.0", - "@aws-sdk/middleware-recursion-detection": "3.370.0", - "@aws-sdk/middleware-sdk-sts": "3.370.0", - "@aws-sdk/middleware-signing": "3.370.0", - "@aws-sdk/middleware-user-agent": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@aws-sdk/util-user-agent-browser": "3.370.0", - "@aws-sdk/util-user-agent-node": "3.370.0", - "@smithy/config-resolver": "^1.0.1", - "@smithy/fetch-http-handler": "^1.0.1", - "@smithy/hash-node": "^1.0.1", - "@smithy/invalid-dependency": "^1.0.1", - "@smithy/middleware-content-length": "^1.0.1", - "@smithy/middleware-endpoint": "^1.0.2", - "@smithy/middleware-retry": "^1.0.3", - "@smithy/middleware-serde": "^1.0.1", - "@smithy/middleware-stack": "^1.0.1", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/node-http-handler": "^1.0.2", - "@smithy/protocol-http": "^1.1.0", - "@smithy/smithy-client": "^1.0.3", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-base64": "^1.0.1", - "@smithy/util-body-length-browser": "^1.0.1", - "@smithy/util-body-length-node": "^1.0.1", - "@smithy/util-defaults-mode-browser": "^1.0.1", - "@smithy/util-defaults-mode-node": "^1.0.1", - "@smithy/util-retry": "^1.0.3", - "@smithy/util-utf8": "^1.0.1", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/core": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.451.0.tgz", - "integrity": "sha512-SamWW2zHEf1ZKe3j1w0Piauryl8BQIlej0TBS18A4ACzhjhWXhCs13bO1S88LvPR5mBFXok3XOT6zPOnKDFktw==", - "requires": { - "@smithy/smithy-client": "^2.1.15", - "tslib": "^2.5.0" - }, - "dependencies": { - "@smithy/abort-controller": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.13.tgz", - "integrity": "sha512-eeOPD+GF9BzF/Mjy3PICLePx4l0f3rG/nQegQHRLTloN5p1lSJJNZsyn+FzDnW8P2AduragZqJdtKNCxXozB1Q==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/fetch-http-handler": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.6.tgz", - "integrity": "sha512-PStY3XO1Ksjwn3wMKye5U6m6zxXpXrXZYqLy/IeCbh3nM9QB3Jgw/B0PUSLUWKdXg4U8qgEu300e3ZoBvZLsDg==", - "requires": { - "@smithy/protocol-http": "^3.0.9", - "@smithy/querystring-builder": "^2.0.13", - "@smithy/types": "^2.5.0", - "@smithy/util-base64": "^2.0.1", - "tslib": "^2.5.0" - } - }, - "@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-stack": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.7.tgz", - "integrity": "sha512-L1KLAAWkXbGx1t2jjCI/mDJ2dDNq+rp4/ifr/HcC6FHngxho5O7A5bQLpKHGlkfATH6fUnOEx0VICEVFA4sUzw==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/node-http-handler": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.9.tgz", - "integrity": "sha512-+K0q3SlNcocmo9OZj+fz67gY4lwhOCvIJxVbo/xH+hfWObvaxrMTx7JEzzXcluK0thnnLz++K3Qe7Z/8MDUreA==", - "requires": { - "@smithy/abort-controller": "^2.0.13", - "@smithy/protocol-http": "^3.0.9", - "@smithy/querystring-builder": "^2.0.13", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/protocol-http": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", - "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-builder": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.13.tgz", - "integrity": "sha512-JhXKwp3JtsFUe96XLHy/nUPEbaXqn6r7xE4sNaH8bxEyytE5q1fwt0ew/Ke6+vIC7gP87HCHgQpJHg1X1jN2Fw==", - "requires": { - "@smithy/types": "^2.5.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/smithy-client": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.15.tgz", - "integrity": "sha512-rngZcQu7Jvs9UbHihK1EI67RMPuzkc3CJmu4MBgB7D7yBnMGuFR86tq5rqHfL2gAkNnMelBN/8kzQVvZjNKefQ==", - "requires": { - "@smithy/middleware-stack": "^2.0.7", - "@smithy/types": "^2.5.0", - "@smithy/util-stream": "^2.0.20", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.5.0.tgz", - "integrity": "sha512-/a31lYofrMBkJb3BuPlYJTMKDj0hUmKUP6JFZQu6YVuQVoAjubiY0A52U9S0Uysd33n/djexCUSNJ+G9bf3/aA==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-base64": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", - "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "requires": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-stream": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.20.tgz", - "integrity": "sha512-tT8VASuD8jJu0yjHEMTCPt1o5E3FVzgdsxK6FQLAjXKqVv5V8InCnc0EOsYrijgspbfDqdAJg7r0o2sySfcHVg==", - "requires": { - "@smithy/fetch-http-handler": "^2.2.6", - "@smithy/node-http-handler": "^2.1.9", - "@smithy/types": "^2.5.0", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-utf8": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", - "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - } - } - }, - "@aws-sdk/credential-provider-cognito-identity": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.388.0.tgz", - "integrity": "sha512-j1oyBc0/O76YouOC2wMZuQUfHOjfrKWgBibIwrwqEqacYWMx/IBxZkk9j2fFerIVaKhhMNkZHAGb+qBx0urR/Q==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.388.0", - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "@aws-sdk/types": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.387.0.tgz", - "integrity": "sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/property-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.2.tgz", - "integrity": "sha512-DfaZ8cO+d/mgnMzIllcXcU4OYP+omiOl2LYdn/fTGpw/EAQSVzscYV2muV3sDDnuPYQ/r014hUqIxnF+pzh+SQ==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.1.0.tgz", - "integrity": "sha512-KLsCsqxX0j2l99iP8s0f7LBlcsp7a7ceXGn0LPYPyVOsqmIKvSaPQajq0YevlL4T9Bm+DtcyXfBTbtBcLX1I7A==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - } - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.370.0.tgz", - "integrity": "sha512-raR3yP/4GGbKFRPP5hUBNkEmTnzxI9mEc2vJAJrcv4G4J4i/UP6ELiLInQ5eO2/VcV/CeKGZA3t7d1tsJ+jhCg==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.370.0.tgz", - "integrity": "sha512-eJyapFKa4NrC9RfTgxlXnXfS9InG/QMEUPPVL+VhG7YS6nKqetC1digOYgivnEeu+XSKE0DJ7uZuXujN2Y7VAQ==", - "requires": { - "@aws-sdk/credential-provider-env": "3.370.0", - "@aws-sdk/credential-provider-process": "3.370.0", - "@aws-sdk/credential-provider-sso": "3.370.0", - "@aws-sdk/credential-provider-web-identity": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/credential-provider-imds": "^1.0.1", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.370.0.tgz", - "integrity": "sha512-gkFiotBFKE4Fcn8CzQnMeab9TAR06FEAD02T4ZRYW1xGrBJOowmje9dKqdwQFHSPgnWAP+8HoTA8iwbhTLvjNA==", - "requires": { - "@aws-sdk/credential-provider-env": "3.370.0", - "@aws-sdk/credential-provider-ini": "3.370.0", - "@aws-sdk/credential-provider-process": "3.370.0", - "@aws-sdk/credential-provider-sso": "3.370.0", - "@aws-sdk/credential-provider-web-identity": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/credential-provider-imds": "^1.0.1", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.370.0.tgz", - "integrity": "sha512-0BKFFZmUO779Xdw3u7wWnoWhYA4zygxJbgGVSyjkOGBvdkbPSTTcdwT1KFkaQy2kOXYeZPl+usVVRXs+ph4ejg==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.370.0.tgz", - "integrity": "sha512-PFroYm5hcPSfC/jkZnCI34QFL3I7WVKveVk6/F3fud/cnP8hp6YjA9NiTNbqdFSzsyoiN/+e5fZgNKih8vVPTA==", - "requires": { - "@aws-sdk/client-sso": "3.370.0", - "@aws-sdk/token-providers": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.370.0.tgz", - "integrity": "sha512-CFaBMLRudwhjv1sDzybNV93IaT85IwS+L8Wq6VRMa0mro1q9rrWsIZO811eF+k0NEPfgU1dLH+8Vc2qhw4SARQ==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-providers": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.388.0.tgz", - "integrity": "sha512-5opHLYjj6rHrw2OaxE+IcLhC9JfiopPH7hRknzKjFnSrJ+HjzcHCML5xghwHLJOLGcoWU40CCSlwJVPLlJluMw==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.388.0", - "@aws-sdk/client-sso": "3.387.0", - "@aws-sdk/client-sts": "3.388.0", - "@aws-sdk/credential-provider-cognito-identity": "3.388.0", - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-ini": "3.388.0", - "@aws-sdk/credential-provider-node": "3.388.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "@aws-sdk/client-sso": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.387.0.tgz", - "integrity": "sha512-E7uKSvbA0XMKSN5KLInf52hmMpe9/OKo6N9OPffGXdn3fNEQlvyQq3meUkqG7Is0ldgsQMz5EUBNtNybXzr3tQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/client-sts": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.388.0.tgz", - "integrity": "sha512-y9FAcAYHT8O6T/jqhgsIQUb4gLiSTKD3xtzudDvjmFi8gl0oRIY1npbeckSiK6k07VQugm2s64I0nDnDxtWsBg==", - "optional": true, - "peer": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.388.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-sdk-sts": "3.387.0", - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.387.0.tgz", - "integrity": "sha512-PVqNk7XPIYe5CMYNvELkcALtkl/pIM8/uPtqEtTg+mgnZBeL4fAmgXZiZMahQo1DxP5t/JaK384f6JG+A0qDjA==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.388.0.tgz", - "integrity": "sha512-3dg3A8AiZ5vXkSAYyyI3V/AW3Eo6KQJyE/glA+Nr2M0oAjT4z3vHhS3pf2B+hfKGZBTuKKgxusrrhrQABd/Diw==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.388.0.tgz", - "integrity": "sha512-BqWAkIG08gj/wevpesaZhAjALjfUNVjseHQRk+DNUoHIfyibW7Ahf3q/GIPs11dA2o8ECwR9/fo68Sq+sK799A==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.387.0", - "@aws-sdk/credential-provider-ini": "3.388.0", - "@aws-sdk/credential-provider-process": "3.387.0", - "@aws-sdk/credential-provider-sso": "3.388.0", - "@aws-sdk/credential-provider-web-identity": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.387.0.tgz", - "integrity": "sha512-tQScLHmDlqkQN+mqw4s3cxepEUeHYDhFl5eH+J8puvPqWjXMYpCEdY79SAtWs6SZd4CWiZ0VLeYU6xQBZengbQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.388.0.tgz", - "integrity": "sha512-RH02+rntaO0UhnSBr42n+7q8HOztc+Dets/hh6cWovf3Yi9s9ghLgYLN9FXpSosfot3XkmT/HOCa+CphAmGN9A==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/client-sso": "3.387.0", - "@aws-sdk/token-providers": "3.388.0", - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.387.0.tgz", - "integrity": "sha512-6ueMPl+J3KWv6ZaAWF4Z138QCuBVFZRVAgwbtP3BNqWrrs4Q6TPksOQJ79lRDMpv0EUoyVl04B6lldNlhN8RdA==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.387.0.tgz", - "integrity": "sha512-EWm9PXSr8dSp7hnRth1U7OfelXQp9dLf1yS1kUL+UhppYDJpjhdP7ql3NI4xJKw8e76sP2FuJYEuzWnJHuWoyQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.387.0.tgz", - "integrity": "sha512-FjAvJr1XyaInT81RxUwgifnbXoFJrRBFc64XeFJgFanGIQCWLYxRrK2HV9eBpao/AycbmuoHgLd/f0sa4hZFoQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.387.0.tgz", - "integrity": "sha512-ZF45T785ru8OwvYZw6awD9Z76OwSMM1eZzj2eY+FDz1cHfkpLjxEiti2iIH1FxbyK7n9ZqDUx29lVlCv238YyQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-sdk-sts": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.387.0.tgz", - "integrity": "sha512-7ZzRKOJ4V/JDQmKz9z+FjZqw59mrMATEMLR6ff0H0JHMX0Uk5IX8TQB058ss+ar14qeJ4UcteYzCqHNI0O1BHw==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/middleware-signing": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-signing": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.387.0.tgz", - "integrity": "sha512-oJXlE0MES8gxNLo137PPNNiOICQGOaETTvq3kBSJgb/gtEAxQajMIlaNT7s1wsjOAruFHt4975nCXuY4lpx7GQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.387.0.tgz", - "integrity": "sha512-hTfFTwDtp86xS98BKa+RFuLfcvGftxwzrbZeisZV8hdb4ZhvNXjSxnvM3vetW0GUEnY9xHPSGyp2ERRTinPKFQ==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/token-providers": { - "version": "3.388.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.388.0.tgz", - "integrity": "sha512-2lo1gFJl624kfjo/YdU6zW+k6dEwhoqjNkDNbOZEFgS1KDofHe9GX8W4/ReKb0Ggho5/EcjzZ53/1CjkzUq4tA==", - "optional": true, - "peer": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.387.0", - "@aws-sdk/middleware-logger": "3.387.0", - "@aws-sdk/middleware-recursion-detection": "3.387.0", - "@aws-sdk/middleware-user-agent": "3.387.0", - "@aws-sdk/types": "3.387.0", - "@aws-sdk/util-endpoints": "3.387.0", - "@aws-sdk/util-user-agent-browser": "3.387.0", - "@aws-sdk/util-user-agent-node": "3.387.0", - "@smithy/config-resolver": "^2.0.2", - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/hash-node": "^2.0.2", - "@smithy/invalid-dependency": "^2.0.2", - "@smithy/middleware-content-length": "^2.0.2", - "@smithy/middleware-endpoint": "^2.0.2", - "@smithy/middleware-retry": "^2.0.2", - "@smithy/middleware-serde": "^2.0.2", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.2", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/smithy-client": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.0.0", - "@smithy/util-defaults-mode-browser": "^2.0.2", - "@smithy/util-defaults-mode-node": "^2.0.2", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/types": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.387.0.tgz", - "integrity": "sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.387.0.tgz", - "integrity": "sha512-g7kvuCXehGXHHBw9PkSQdwVyDFmNUZLmfrRmqMyrMDG9QLQrxr4pyWcSaYgTE16yUzhQQOR+QSey+BL6W9/N6g==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.387.0.tgz", - "integrity": "sha512-lpgSVvDqx+JjHZCTYs/yQSS7J71dPlJeAlvxc7bmx5m+vfwKe07HAnIs+929DngS0QbAp/VaXbTiMFsInLkO4Q==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/types": "^2.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.387.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.387.0.tgz", - "integrity": "sha512-r9OVkcWpRYatjLhJacuHFgvO2T5s/Nu5DDbScMrkUD8b4aGIIqsrdZji0vZy9FCjsUFQMM92t9nt4SejrGjChA==", - "optional": true, - "peer": true, - "requires": { - "@aws-sdk/types": "3.387.0", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/abort-controller": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.2.tgz", - "integrity": "sha512-ln5Cob0mksym62sLr7NiPOSqJ0jKao4qjfcNLDdgINM1lQI12hXrZBlKdPHbXJqpKhKiECDgonMoqCM8bigq4g==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/config-resolver": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.2.tgz", - "integrity": "sha512-0kdsqBL6BdmSbdU6YaDkodVBMua5MuQQluC3nocJ7OJ6PnOuM7i2FEQHE46LBadLqT+CimlDSM+6j91uHNL1ng==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/credential-provider-imds": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.2.tgz", - "integrity": "sha512-mbWFYEZ00LBRDk3WvcXViwpdpkJQcfrM3seuKzFxZnF6wIBLMwrcWcsj+OUC/1L+86m8aQY9imXMAaQsAoGxow==", - "optional": true, - "peer": true, - "requires": { - "@smithy/node-config-provider": "^2.0.2", - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/eventstream-codec": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.2.tgz", - "integrity": "sha512-PQZiKx7fMnNwx4zxcUCm82VjnqK6wV4MEHSmMy3taj5dKfXV782IjRGyaDT+8TsmNqVdZIkve5zLRAzh+7kOhA==", - "optional": true, - "peer": true, - "requires": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/fetch-http-handler": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.0.2.tgz", - "integrity": "sha512-Wo2m1RaiXNSLF4J3D62LpdSoj/YYb+6tn0H8is1tSrzr7eXAdiYVBc0wIa23N0wT4zmN0iG/yNY6gTCDQ6799A==", - "optional": true, - "peer": true, - "requires": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/querystring-builder": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/util-base64": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/hash-node": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.2.tgz", - "integrity": "sha512-JKDzZ1YVR7JzOBaJoWy3ToJCE86OQE6D4kOBvvVsu93a3lcF9kv6KYTKBYEWAjwOn/CpK4NH7mKB01OQ8H+aiA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/invalid-dependency": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.2.tgz", - "integrity": "sha512-inQZQ5gCO3WRWuXpsc1YJ4KBjsvj2qsoU32yTIKznBWTCQe/D5Dp+sSaysqBqxe0VTZ+8nFEHdUMWUX2BxQThw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-content-length": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.2.tgz", - "integrity": "sha512-FmHlNfuvYgDZE3fIx0G3rD/wLXfAmBYE4mVc/w6d7RllA7TygPzq2pfHL1iCMzWkWTdoAVnt3h4aavAZnhaxEQ==", - "optional": true, - "peer": true, - "requires": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-endpoint": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.2.tgz", - "integrity": "sha512-ropE7/c+g22QeluZ+By/B/WvVep0UFreX+IeRMGIO7EbOUPgqtJRXpbJFdG6JKB1uC+CdaJLn4MnZnVBpcyjuA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/middleware-serde": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/url-parser": "^2.0.2", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-retry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.2.tgz", - "integrity": "sha512-wtBUXqtZVriiXppYaFkUrybAPhFVX7vebnW/yVPliLMWMcguOMS58qhOYPZe3t9Wki2+mASfyu+kO3An8lAg2A==", - "optional": true, - "peer": true, - "requires": { - "@smithy/protocol-http": "^2.0.2", - "@smithy/service-error-classification": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-retry": "^2.0.0", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - } - }, - "@smithy/middleware-serde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.2.tgz", - "integrity": "sha512-Kw9xLdlueIaivUWslKB67WZ/cCUg3QnzYVIA3t5KfgsseEEuU4UxXw8NSTvIt71gqQloY+Um8ugS+idgxrWWnw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-stack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.0.tgz", - "integrity": "sha512-31XC1xNF65nlbc16yuh3wwTudmqs6qy4EseQUGF8A/p2m/5wdd/cnXJqpniy/XvXVwkHPz/GwV36HqzHtIKATQ==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/node-config-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.2.tgz", - "integrity": "sha512-9wVJccASfuCctNWrzR0zrDkf0ox3HCHGEhFlWL2LBoghUYuK28pVRBbG69wvnkhlHnB8dDZHagxH+Nq9dm7eWw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/property-provider": "^2.0.2", - "@smithy/shared-ini-file-loader": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/node-http-handler": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.0.2.tgz", - "integrity": "sha512-lpZjmtmyZqSAtMPsbrLhb7XoAQ2kAHeuLY/csW6I2k+QyFvOk7cZeQsqEngWmZ9SJaeYiDCBINxAIM61i5WGLw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/abort-controller": "^2.0.2", - "@smithy/protocol-http": "^2.0.2", - "@smithy/querystring-builder": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/property-provider": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.2.tgz", - "integrity": "sha512-DfaZ8cO+d/mgnMzIllcXcU4OYP+omiOl2LYdn/fTGpw/EAQSVzscYV2muV3sDDnuPYQ/r014hUqIxnF+pzh+SQ==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/protocol-http": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-2.0.2.tgz", - "integrity": "sha512-qWu8g1FUy+m36KpO1sREJSF7BaLmjw9AqOuwxLVVSdYz+nUQjc9tFAZ9LB6jJXKdsZFSjfkjHJBbhD78QdE7Rw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-builder": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.2.tgz", - "integrity": "sha512-H99LOMWEssfwqkOoTs4Y12UiZ7CTGQSX5Nrx5UkYgRbUEpC1GnnaprHiYrqclC58/xr4K76aNchdPyioxewMzA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.2.tgz", - "integrity": "sha512-L4VtKQ8O4/aWPQJbiFymbhAmxdfLnEaROh/Vs0OstJ7jtOZeBl2QJmuWY2V7hjt64W7V+tEn2sv6vVvnxkm/xQ==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/service-error-classification": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.0.tgz", - "integrity": "sha512-2z5Nafy1O0cTf69wKyNjGW/sNVMiqDnb4jgwfMG8ye8KnFJ5qmJpDccwIbJNhXIfbsxTg9SEec2oe1cexhMJvw==", - "optional": true, - "peer": true - }, - "@smithy/shared-ini-file-loader": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.2.tgz", - "integrity": "sha512-2VkNOM/82u4vatVdK5nfusgGIlvR48Fkq6me17Oc+V1iyxfR/1x0pG6LzW0br1qlGtzBYFZKmDyviBRcPVFTVw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/signature-v4": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.2.tgz", - "integrity": "sha512-YMooDEw/UmGxcXY4qWnSXkbPFsRloluSvyXVT678YPDN/K2AS1GzKfRsvSU7fbccOB4WF8MHZf2UqcRGEltE3Q==", - "optional": true, - "peer": true, - "requires": { - "@smithy/eventstream-codec": "^2.0.2", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/smithy-client": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.0.2.tgz", - "integrity": "sha512-mDfokI8WwLU5C0gcQ4ww/zJI/WLGSh2+vdIA42JRnjfYUjJNH/rKfX9YOnn2eBOxl3loATERVUqkHmKe+P8s2Q==", - "optional": true, - "peer": true, - "requires": { - "@smithy/middleware-stack": "^2.0.0", - "@smithy/types": "^2.1.0", - "@smithy/util-stream": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.1.0.tgz", - "integrity": "sha512-KLsCsqxX0j2l99iP8s0f7LBlcsp7a7ceXGn0LPYPyVOsqmIKvSaPQajq0YevlL4T9Bm+DtcyXfBTbtBcLX1I7A==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/url-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.2.tgz", - "integrity": "sha512-X1mHCzrSVDlhVy7d3S7Vq+dTfYzwh4n7xGHhyJumu77nJqIss0lazVug85Pwo0DKIoO314wAOvMnBxNYDa+7wA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/querystring-parser": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-base64": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", - "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", - "optional": true, - "peer": true, - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-node": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.0.0.tgz", - "integrity": "sha512-ZV7Z/WHTMxHJe/xL/56qZwSUcl63/5aaPAGjkfynJm4poILjdD4GmFI+V+YWabh2WJIjwTKZ5PNsuvPQKt93Mg==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-browser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.2.tgz", - "integrity": "sha512-c2tMMjb624XLuzmlRoZpnFOkejVxcgw3WQKdmgdGZYZapcLzXyC0H9JhnXMjQCt30GqLTlsILRNVBYwFRbw/4Q==", - "optional": true, - "peer": true, - "requires": { - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-node": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.2.tgz", - "integrity": "sha512-gt7m5LLqUtEKldJLyc14DE4kb85vxwomvt9AfEMEvWM4VwfWS1kGJqiStZFb5KNqnQPXw8vvpgLTi8NrWAOXqg==", - "optional": true, - "peer": true, - "requires": { - "@smithy/config-resolver": "^2.0.2", - "@smithy/credential-provider-imds": "^2.0.2", - "@smithy/node-config-provider": "^2.0.2", - "@smithy/property-provider": "^2.0.2", - "@smithy/types": "^2.1.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-middleware": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.0.tgz", - "integrity": "sha512-eCWX4ECuDHn1wuyyDdGdUWnT4OGyIzV0LN1xRttBFMPI9Ff/4heSHVxneyiMtOB//zpXWCha1/SWHJOZstG7kA==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-retry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.0.tgz", - "integrity": "sha512-/dvJ8afrElasuiiIttRJeoS2sy8YXpksQwiM/TcepqdRVp7u4ejd9C4IQURHNjlfPUT7Y6lCDSa2zQJbdHhVTg==", - "optional": true, - "peer": true, - "requires": { - "@smithy/service-error-classification": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-stream": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.2.tgz", - "integrity": "sha512-Mg9IJcKIu4YKlbzvpp1KLvh4JZLdcPgpxk+LICuDwzZCfxe47R9enVK8dNEiuyiIGK2ExbfvzCVT8IBru62vZw==", - "optional": true, - "peer": true, - "requires": { - "@smithy/fetch-http-handler": "^2.0.2", - "@smithy/node-http-handler": "^2.0.2", - "@smithy/types": "^2.1.0", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-utf8": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", - "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", - "optional": true, - "peer": true, - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - } - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.370.0.tgz", - "integrity": "sha512-CPXOm/TnOFC7KyXcJglICC7OiA7Kj6mT3ChvEijr56TFOueNHvJdV4aNIFEQy0vGHOWtY12qOWLNto/wYR1BAQ==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.370.0.tgz", - "integrity": "sha512-cQMq9SaZ/ORmTJPCT6VzMML7OxFdQzNkhMAgKpTDl+tdPWynlHF29E5xGoSzROnThHlQPCjogU0NZ8AxI0SWPA==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.370.0.tgz", - "integrity": "sha512-L7ZF/w0lAAY/GK1khT8VdoU0XB7nWHk51rl/ecAg64J70dHnMOAg8n+5FZ9fBu/xH1FwUlHOkwlodJOgzLJjtg==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-sdk-sts": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.370.0.tgz", - "integrity": "sha512-ykbsoVy0AJtVbuhAlTAMcaz/tCE3pT8nAp0L7CQQxSoanRCvOux7au0KwMIQVhxgnYid4dWVF6d00SkqU5MXRA==", - "requires": { - "@aws-sdk/middleware-signing": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-signing": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.370.0.tgz", - "integrity": "sha512-Dwr/RTCWOXdm394wCwICGT2VNOTMRe4IGPsBRJAsM24pm+EEqQzSS3Xu/U/zF4exuxqpMta4wec4QpSarPNTxA==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/protocol-http": "^1.1.0", - "@smithy/signature-v4": "^1.0.1", - "@smithy/types": "^1.1.0", - "@smithy/util-middleware": "^1.0.1", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.370.0.tgz", - "integrity": "sha512-2+3SB6MtMAq1+gVXhw0Y3ONXuljorh6ijnxgTpv+uQnBW5jHCUiAS8WDYiDEm7i9euJPbvJfM8WUrSMDMU6Cog==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@aws-sdk/util-endpoints": "3.370.0", - "@smithy/protocol-http": "^1.1.0", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/region-config-resolver": { - "version": "3.451.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.451.0.tgz", - "integrity": "sha512-3iMf4OwzrFb4tAAmoROXaiORUk2FvSejnHIw/XHvf/jjR4EqGGF95NZP/n/MeFZMizJWVssrwS412GmoEyoqhg==", - "requires": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/types": "^2.5.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.6", - "tslib": "^2.5.0" - }, - "dependencies": { - "@smithy/node-config-provider": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.5.tgz", - "integrity": "sha512-3Omb5/h4tOCuKRx4p4pkYTvEYRCYoKk52bOYbKUyz/G/8gERbagsN8jFm4FjQubkrcIqQEghTpQaUw6uk+0edw==", - "requires": { - "@smithy/property-provider": "^2.0.14", - "@smithy/shared-ini-file-loader": "^2.2.4", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/property-provider": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.14.tgz", - "integrity": "sha512-k3D2qp9o6imTrLaXRj6GdLYEJr1sXqS99nLhzq8fYmJjSVOeMg/G+1KVAAc7Oxpu71rlZ2f8SSZxcSxkevuR0A==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/shared-ini-file-loader": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.4.tgz", - "integrity": "sha512-9dRknGgvYlRIsoTcmMJXuoR/3ekhGwhRq4un3ns2/byre4Ql5hyUN4iS0x8eITohjU90YOnUCsbRwZRvCkbRfw==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.5.0.tgz", - "integrity": "sha512-/a31lYofrMBkJb3BuPlYJTMKDj0hUmKUP6JFZQu6YVuQVoAjubiY0A52U9S0Uysd33n/djexCUSNJ+G9bf3/aA==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.6.tgz", - "integrity": "sha512-7W4uuwBvSLgKoLC1x4LfeArCVcbuHdtVaC4g30kKsD1erfICyQ45+tFhhs/dZNeQg+w392fhunCm/+oCcb6BSA==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - } - } - }, - "@aws-sdk/token-providers": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.370.0.tgz", - "integrity": "sha512-EyR2ZYr+lJeRiZU2/eLR+mlYU9RXLQvNyGFSAekJKgN13Rpq/h0syzXVFLP/RSod/oZenh/fhVZ2HwlZxuGBtQ==", - "requires": { - "@aws-sdk/client-sso-oidc": "3.370.0", - "@aws-sdk/types": "3.370.0", - "@smithy/property-provider": "^1.0.1", - "@smithy/shared-ini-file-loader": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/types": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", - "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", - "requires": { - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.370.0.tgz", - "integrity": "sha512-5ltVAnM79nRlywwzZN5i8Jp4tk245OCGkKwwXbnDU+gq7zT3CIOsct1wNZvmpfZEPGt/bv7/NyRcjP+7XNsX/g==", - "requires": { - "@aws-sdk/types": "3.370.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-locate-window": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", - "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.370.0.tgz", - "integrity": "sha512-028LxYZMQ0DANKhW+AKFQslkScZUeYlPmSphrCIXgdIItRZh6ZJHGzE7J/jDsEntZOrZJsjI4z0zZ5W2idj04w==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@smithy/types": "^1.1.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.370.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.370.0.tgz", - "integrity": "sha512-33vxZUp8vxTT/DGYIR3PivQm07sSRGWI+4fCv63Rt7Q++fO24E0kQtmVAlikRY810I10poD6rwILVtITtFSzkg==", - "requires": { - "@aws-sdk/types": "3.370.0", - "@smithy/node-config-provider": "^1.0.1", - "@smithy/types": "^1.1.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "requires": { - "tslib": "^2.3.1" - } - }, - "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", - "dev": true - }, - "@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.1" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "requires": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" - } - }, - "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/runtime": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", - "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", - "requires": { - "regenerator-runtime": "^0.13.11" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - } - } - }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - }, - "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@casl/ability": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@casl/ability/-/ability-6.5.0.tgz", - "integrity": "sha512-3guc94ugr5ylZQIpJTLz0CDfwNi0mxKVECj1vJUPAvs+Lwunh/dcuUjwzc4MHM9D8JOYX0XUZMEPedpB3vIbOw==", - "requires": { - "@ucast/mongo2js": "^1.3.0" - } - }, - "@casl/mongoose": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@casl/mongoose/-/mongoose-7.2.1.tgz", - "integrity": "sha512-pojgSWYKNIwFM6wWDNct1YD0+8nIxhe2jp5jBbK8JGU60dEs2o0Yw3mCo2y7nBwbvRC2oEots/BlLMVb1Wdo8A==", - "requires": {} - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", - "dev": true - }, - "@godaddy/terminus": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@godaddy/terminus/-/terminus-4.12.1.tgz", - "integrity": "sha512-Tm+wVu1/V37uZXcT7xOhzdpFoovQReErff8x3y82k6YyWa1gzxWBjTyrx4G2enjEqoXPnUUmJ3MOmwH+TiP6Sw==", - "requires": { - "stoppable": "^1.1.0" - } - }, - "@hapi/bourne": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.1.0.tgz", - "integrity": "sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q==" - }, - "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.1.tgz", - "integrity": "sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q==", - "dev": true, - "requires": { - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.1.tgz", - "integrity": "sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ==", - "dev": true, - "requires": { - "@jest/console": "^29.6.1", - "@jest/reporters": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.6.1", - "jest-haste-map": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-resolve-dependencies": "^29.6.1", - "jest-runner": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "jest-watcher": "^29.6.1", - "micromatch": "^4.0.4", - "pretty-format": "^29.6.1", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.1.tgz", - "integrity": "sha512-RMMXx4ws+Gbvw3DfLSuo2cfQlK7IwGbpuEWXCqyYDcqYTI+9Ju3a5hDnXaxjNsa6uKh9PQF2v+qg+RLe63tz5A==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "jest-mock": "^29.6.1" - } - }, - "@jest/expect": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.1.tgz", - "integrity": "sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg==", - "dev": true, - "requires": { - "expect": "^29.6.1", - "jest-snapshot": "^29.6.1" - } - }, - "@jest/expect-utils": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.1.tgz", - "integrity": "sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw==", - "dev": true, - "requires": { - "jest-get-type": "^29.4.3" - } - }, - "@jest/fake-timers": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.1.tgz", - "integrity": "sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg==", - "dev": true, - "requires": { - "@jest/types": "^29.6.1", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.6.1", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1" - } - }, - "@jest/globals": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.1.tgz", - "integrity": "sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A==", - "dev": true, - "requires": { - "@jest/environment": "^29.6.1", - "@jest/expect": "^29.6.1", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.1" - } - }, - "@jest/reporters": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.1.tgz", - "integrity": "sha512-9zuaI9QKr9JnoZtFQlw4GREQbxgmNYXU6QuWtmuODvk5nvPUeBYapVR/VYMyi2WSx3jXTLJTJji8rN6+Cm4+FA==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", - "jest-worker": "^29.6.1", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - } - }, - "@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.27.8" - } - }, - "@jest/source-map": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", - "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.1.tgz", - "integrity": "sha512-Ynr13ZRcpX6INak0TPUukU8GWRfm/vAytE3JbJNGAvINySWYdfE7dGZMbk36oVuK4CigpbhMn8eg1dixZ7ZJOw==", - "dev": true, - "requires": { - "@jest/console": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.1.tgz", - "integrity": "sha512-oBkC36PCDf/wb6dWeQIhaviU0l5u6VCsXa119yqdUosYAt7/FbQU2M2UoziO3igj/HBDEgp57ONQ3fm0v9uyyg==", - "dev": true, - "requires": { - "@jest/test-result": "^29.6.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.1.tgz", - "integrity": "sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.1", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - } - }, - "@jest/types": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", - "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - } - } - }, - "@juanelas/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@juanelas/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-mr2pfRQpWap0Uq4tlrCgp3W+Yjx1/Bpq4QJsYeAQUh1mExgyQvXz7xUhmYT2HcLLspuAL5dpnos8P2QhaCSXsQ==" - }, - "@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "requires": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - } - }, - "@maxmind/geoip2-node": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@maxmind/geoip2-node/-/geoip2-node-3.5.0.tgz", - "integrity": "sha512-WG2TNxMwDWDOrljLwyZf5bwiEYubaHuICvQRlgz74lE9OZA/z4o+ZT6OisjDBAZh/yRJVNK6mfHqmP5lLlAwsA==", - "dev": true, - "requires": { - "camelcase-keys": "^7.0.0", - "ip6addr": "^0.2.5", - "maxmind": "^4.2.0" - } - }, - "@mongodb-js/saslprep": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz", - "integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==", - "optional": true, - "requires": { - "sparse-bitfield": "^3.0.3" - } - }, - "@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz", - "integrity": "sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==", - "dev": true, - "optional": true - }, - "@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz", - "integrity": "sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==", - "dev": true, - "optional": true - }, - "@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz", - "integrity": "sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==", - "dev": true, - "optional": true - }, - "@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz", - "integrity": "sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==", - "dev": true, - "optional": true - }, - "@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz", - "integrity": "sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==", - "dev": true, - "optional": true - }, - "@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz", - "integrity": "sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==", - "dev": true, - "optional": true - }, - "@napi-rs/snappy-android-arm-eabi": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm-eabi/-/snappy-android-arm-eabi-7.2.2.tgz", - "integrity": "sha512-H7DuVkPCK5BlAr1NfSU8bDEN7gYs+R78pSHhDng83QxRnCLmVIZk33ymmIwurmoA1HrdTxbkbuNl+lMvNqnytw==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-android-arm64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm64/-/snappy-android-arm64-7.2.2.tgz", - "integrity": "sha512-2R/A3qok+nGtpVK8oUMcrIi5OMDckGYNoBLFyli3zp8w6IArPRfg1yOfVUcHvpUDTo9T7LOS1fXgMOoC796eQw==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-darwin-arm64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-arm64/-/snappy-darwin-arm64-7.2.2.tgz", - "integrity": "sha512-USgArHbfrmdbuq33bD5ssbkPIoT7YCXCRLmZpDS6dMDrx+iM7eD2BecNbOOo7/v1eu6TRmQ0xOzeQ6I/9FIi5g==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-darwin-x64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-x64/-/snappy-darwin-x64-7.2.2.tgz", - "integrity": "sha512-0APDu8iO5iT0IJKblk2lH0VpWSl9zOZndZKnBYIc+ei1npw2L5QvuErFOTeTdHBtzvUHASB+9bvgaWnQo4PvTQ==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-freebsd-x64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-freebsd-x64/-/snappy-freebsd-x64-7.2.2.tgz", - "integrity": "sha512-mRTCJsuzy0o/B0Hnp9CwNB5V6cOJ4wedDTWEthsdKHSsQlO7WU9W1yP7H3Qv3Ccp/ZfMyrmG98Ad7u7lG58WXA==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-linux-arm-gnueabihf": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm-gnueabihf/-/snappy-linux-arm-gnueabihf-7.2.2.tgz", - "integrity": "sha512-v1uzm8+6uYjasBPcFkv90VLZ+WhLzr/tnfkZ/iD9mHYiULqkqpRuC8zvc3FZaJy5wLQE9zTDkTJN1IvUcZ+Vcg==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-linux-arm64-gnu": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-gnu/-/snappy-linux-arm64-gnu-7.2.2.tgz", - "integrity": "sha512-LrEMa5pBScs4GXWOn6ZYXfQ72IzoolZw5txqUHVGs8eK4g1HR9HTHhb2oY5ySNaKakG5sOgMsb1rwaEnjhChmQ==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-linux-arm64-musl": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-musl/-/snappy-linux-arm64-musl-7.2.2.tgz", - "integrity": "sha512-3orWZo9hUpGQcB+3aTLW7UFDqNCQfbr0+MvV67x8nMNYj5eAeUtMmUE/HxLznHO4eZ1qSqiTwLbVx05/Socdlw==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-linux-x64-gnu": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-gnu/-/snappy-linux-x64-gnu-7.2.2.tgz", - "integrity": "sha512-jZt8Jit/HHDcavt80zxEkDpH+R1Ic0ssiVCoueASzMXa7vwPJeF4ZxZyqUw4qeSy7n8UUExomu8G8ZbP6VKhgw==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-linux-x64-musl": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-musl/-/snappy-linux-x64-musl-7.2.2.tgz", - "integrity": "sha512-Dh96IXgcZrV39a+Tej/owcd9vr5ihiZ3KRix11rr1v0MWtVb61+H1GXXlz6+Zcx9y8jM1NmOuiIuJwkV4vZ4WA==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-win32-arm64-msvc": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-arm64-msvc/-/snappy-win32-arm64-msvc-7.2.2.tgz", - "integrity": "sha512-9No0b3xGbHSWv2wtLEn3MO76Yopn1U2TdemZpCaEgOGccz1V+a/1d16Piz3ofSmnA13HGFz3h9NwZH9EOaIgYA==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-win32-ia32-msvc": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-ia32-msvc/-/snappy-win32-ia32-msvc-7.2.2.tgz", - "integrity": "sha512-QiGe+0G86J74Qz1JcHtBwM3OYdTni1hX1PFyLRo3HhQUSpmi13Bzc1En7APn+6Pvo7gkrcy81dObGLDSxFAkQQ==", - "optional": true, - "peer": true - }, - "@napi-rs/snappy-win32-x64-msvc": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-x64-msvc/-/snappy-win32-x64-msvc-7.2.2.tgz", - "integrity": "sha512-a43cyx1nK0daw6BZxVcvDEXxKMFLSBSDTAhsFD0VqSKcC7MGUBMaqyoWUcMiI7LBSz4bxUmxDWKfCYzpEmeb3w==", - "optional": true, - "peer": true - }, - "@node-saml/node-saml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@node-saml/node-saml/-/node-saml-4.0.5.tgz", - "integrity": "sha512-J5DglElbY1tjOuaR1NPtjOXkXY5bpUhDoKVoeucYN98A3w4fwgjIOPqIGcb6cQsqFq2zZ6vTCeKn5C/hvefSaw==", - "requires": { - "@types/debug": "^4.1.7", - "@types/passport": "^1.0.11", - "@types/xml-crypto": "^1.4.2", - "@types/xml-encryption": "^1.2.1", - "@types/xml2js": "^0.4.11", - "@xmldom/xmldom": "^0.8.6", - "debug": "^4.3.4", - "xml-crypto": "^3.0.1", - "xml-encryption": "^3.0.2", - "xml2js": "^0.5.0", - "xmlbuilder": "^15.1.1" - } - }, - "@node-saml/passport-saml": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@node-saml/passport-saml/-/passport-saml-4.0.4.tgz", - "integrity": "sha512-xFw3gw0yo+K1mzlkW15NeBF7cVpRHN/4vpjmBKzov5YFImCWh/G0LcTZ8krH3yk2/eRPc3Or8LRPudVJBjmYaw==", - "requires": { - "@node-saml/node-saml": "^4.0.4", - "@types/express": "^4.17.14", - "@types/passport": "^1.0.11", - "@types/passport-strategy": "^0.2.35", - "passport": "^0.6.0", - "passport-strategy": "^1.0.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@octokit/auth-app": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-4.0.13.tgz", - "integrity": "sha512-NBQkmR/Zsc+8fWcVIFrwDgNXS7f4XDrkd9LHdi9DPQw1NdGHLviLzRO2ZBwTtepnwHXW5VTrVU9eFGijMUqllg==", - "requires": { - "@octokit/auth-oauth-app": "^5.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "deprecation": "^2.3.1", - "lru-cache": "^9.0.0", - "universal-github-app-jwt": "^1.1.1", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==" - } - } - }, - "@octokit/auth-oauth-app": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-5.0.6.tgz", - "integrity": "sha512-SxyfIBfeFcWd9Z/m1xa4LENTQ3l1y6Nrg31k2Dcb1jS5ov7pmwMJZ6OGX8q3K9slRgVpeAjNA1ipOAMHkieqyw==", - "requires": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "@types/btoa-lite": "^1.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/auth-oauth-device": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-4.0.5.tgz", - "integrity": "sha512-XyhoWRTzf2ZX0aZ52a6Ew5S5VBAfwwx1QnC2Np6Et3MWQpZjlREIcbcvVZtkNuXp6Z9EeiSLSDUqm3C+aMEHzQ==", - "requires": { - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/auth-oauth-user": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-2.1.2.tgz", - "integrity": "sha512-kkRqNmFe7s5GQcojE3nSlF+AzYPpPv7kvP/xYEnE57584pixaFBH8Vovt+w5Y3E4zWUEOxjdLItmBTFAWECPAg==", - "requires": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/auth-token": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", - "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==" - }, - "@octokit/auth-unauthenticated": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-3.0.5.tgz", - "integrity": "sha512-yH2GPFcjrTvDWPwJWWCh0tPPtTL5SMgivgKPA+6v/XmYN6hGQkAto8JtZibSKOpf8ipmeYhLNWQ2UgW0GYILCw==", - "requires": { - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0" - } - }, - "@octokit/core": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", - "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", - "requires": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", - "requires": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", - "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", - "requires": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/oauth-authorization-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-5.0.0.tgz", - "integrity": "sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg==" - }, - "@octokit/oauth-methods": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-2.0.6.tgz", - "integrity": "sha512-l9Uml2iGN2aTWLZcm8hV+neBiFXAQ9+3sKiQe/sgumHlL6HDg0AQ8/l16xX/5jJvfxueqTW5CWbzd0MjnlfHZw==", - "requires": { - "@octokit/oauth-authorization-url": "^5.0.0", - "@octokit/request": "^6.2.3", - "@octokit/request-error": "^3.0.3", - "@octokit/types": "^9.0.0", - "btoa-lite": "^1.0.0" - } - }, - "@octokit/openapi-types": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", - "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==" - }, - "@octokit/plugin-enterprise-compatibility": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-compatibility/-/plugin-enterprise-compatibility-1.3.0.tgz", - "integrity": "sha512-h34sMGdEOER/OKrZJ55v26ntdHb9OPfR1fwOx6Q4qYyyhWA104o11h9tFxnS/l41gED6WEI41Vu2G2zHDVC5lQ==", - "requires": { - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.0.3" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/plugin-paginate-rest": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz", - "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", - "requires": { - "@octokit/tsconfig": "^1.0.2", - "@octokit/types": "^9.2.3" - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "requires": {} - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", - "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", - "requires": { - "@octokit/types": "^10.0.0" - }, - "dependencies": { - "@octokit/types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", - "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", - "requires": { - "@octokit/openapi-types": "^18.0.0" - } - } - } - }, - "@octokit/plugin-retry": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz", - "integrity": "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ==", - "requires": { - "@octokit/types": "^6.0.3", - "bottleneck": "^2.15.3" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", - "requires": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "requires": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "19.0.13", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz", - "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==", - "requires": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.1.2" - } - }, - "@octokit/tsconfig": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", - "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==" - }, - "@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "requires": { - "@octokit/openapi-types": "^18.0.0" - } - }, - "@octokit/webhooks": { - "version": "9.26.3", - "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.3.tgz", - "integrity": "sha512-DLGk+gzeVq5oK89Bo601txYmyrelMQ7Fi5EnjHE0Xs8CWicy2xkmnJMKptKJrBJpstqbd/9oeDFi/Zj2pudBDQ==", - "requires": { - "@octokit/request-error": "^2.0.2", - "@octokit/webhooks-methods": "^2.0.0", - "@octokit/webhooks-types": "5.8.0", - "aggregate-error": "^3.1.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/webhooks-methods": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-2.0.0.tgz", - "integrity": "sha512-35cfQ4YWlnZnmZKmIxlGPUPLtbkF8lr/A/1Sk1eC0ddLMwQN06dOuLc+dI3YLQS+T+MoNt3DIQ0NynwgKPilig==" - }, - "@octokit/webhooks-types": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-5.8.0.tgz", - "integrity": "sha512-8adktjIb76A7viIdayQSFuBEwOzwhDC+9yxZpKNHjfzrlostHCw0/N7JWpWMObfElwvJMk2fY2l1noENCk9wmw==" - }, - "@phc/format": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", - "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==" - }, - "@posthog/plugin-scaffold": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@posthog/plugin-scaffold/-/plugin-scaffold-1.4.2.tgz", - "integrity": "sha512-/VsRg3CfhQvYhxM2O9+gBOzj4K1QJZClY+yple0npL1Jd2nRn2nT4z7dlPSidTPZvdpFs0+hrnF+m4Kxf1NFvQ==", - "dev": true, - "requires": { - "@maxmind/geoip2-node": "^3.4.0" - } - }, - "@probot/get-private-key": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@probot/get-private-key/-/get-private-key-1.1.1.tgz", - "integrity": "sha512-hOmBNSAhSZc6PaNkTvj6CO9R5J67ODJ+w5XQlDW9w/6mtcpHWK4L+PZcW0YwVM7PpetLZjN6rsKQIR9yqIaWlA==", - "requires": { - "@types/is-base64": "^1.1.0", - "is-base64": "^1.1.0" - } - }, - "@probot/octokit-plugin-config": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@probot/octokit-plugin-config/-/octokit-plugin-config-1.1.6.tgz", - "integrity": "sha512-L29wmnFvilzSfWn9tUgItxdLv0LJh2ICjma3FmLr80Spu3wZ9nHyRrKMo9R5/K2m7VuWmgoKnkgRt2zPzAQBEQ==", - "requires": { - "@types/js-yaml": "^4.0.5", - "js-yaml": "^4.1.0" - } - }, - "@probot/pino": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@probot/pino/-/pino-2.3.5.tgz", - "integrity": "sha512-IiyiNZonMw1dHC4EAdD55y5owV733d9Gll/IKsrLikB7EJ54+eMCOtL/qo+OmgWN9XV3NTDfziEQF2og/OBKog==", - "requires": { - "@sentry/node": "^6.0.0", - "pino-pretty": "^6.0.0", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", - "split2": "^4.0.0" - }, - "dependencies": { - "@sentry/core": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.19.7.tgz", - "integrity": "sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==", - "requires": { - "@sentry/hub": "6.19.7", - "@sentry/minimal": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", - "tslib": "^1.9.3" - } - }, - "@sentry/node": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.19.7.tgz", - "integrity": "sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg==", - "requires": { - "@sentry/core": "6.19.7", - "@sentry/hub": "6.19.7", - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", - "cookie": "^0.4.1", - "https-proxy-agent": "^5.0.0", - "lru_map": "^0.3.3", - "tslib": "^1.9.3" - } - }, - "@sentry/types": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz", - "integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==" - }, - "@sentry/utils": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.19.7.tgz", - "integrity": "sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==", - "requires": { - "@sentry/types": "6.19.7", - "tslib": "^1.9.3" - } - }, - "colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" - }, - "jmespath": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w==" - }, - "pino-pretty": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-6.0.0.tgz", - "integrity": "sha512-jyeR2fXXWc68st1DTTM5NhkHlx8p+1fKZMfm84Jwq+jSw08IwAjNaZBZR6ts69hhPOfOjg/NiE1HYW7vBRPL3A==", - "requires": { - "@hapi/bourne": "^2.0.0", - "args": "^5.0.1", - "colorette": "^1.3.0", - "dateformat": "^4.5.1", - "fast-safe-stringify": "^2.0.7", - "jmespath": "^0.15.0", - "joycon": "^3.0.0", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", - "rfdc": "^1.3.0", - "split2": "^3.1.1", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "requires": { - "readable-stream": "^3.0.0" - } - } - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@sentry-internal/tracing": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.59.3.tgz", - "integrity": "sha512-/RkBj/0zQKGsW/UYg6hufrLHHguncLfu4610FCPWpVp0K5Yu5ou8/Aw8D76G3ZxD2TiuSNGwX0o7TYN371ZqTQ==", - "requires": { - "@sentry/core": "7.59.3", - "@sentry/types": "7.59.3", - "@sentry/utils": "7.59.3", - "tslib": "^2.4.1 || ^1.9.3" - } - }, - "@sentry/core": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.59.3.tgz", - "integrity": "sha512-cGBOwT9gziIn50fnlBH1WGQlGcHi7wrbvOCyrex4MxKnn1LSBYWBhwU0ymj8DI/9MyPrGDNGkrgpV0WJWBSClg==", - "requires": { - "@sentry/types": "7.59.3", - "@sentry/utils": "7.59.3", - "tslib": "^2.4.1 || ^1.9.3" - } - }, - "@sentry/hub": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.19.7.tgz", - "integrity": "sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==", - "requires": { - "@sentry/types": "6.19.7", - "@sentry/utils": "6.19.7", - "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/types": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz", - "integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==" - }, - "@sentry/utils": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.19.7.tgz", - "integrity": "sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==", - "requires": { - "@sentry/types": "6.19.7", - "tslib": "^1.9.3" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@sentry/minimal": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.19.7.tgz", - "integrity": "sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==", - "requires": { - "@sentry/hub": "6.19.7", - "@sentry/types": "6.19.7", - "tslib": "^1.9.3" - }, - "dependencies": { - "@sentry/types": { - "version": "6.19.7", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.19.7.tgz", - "integrity": "sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==" - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "@sentry/node": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.77.0.tgz", - "integrity": "sha512-Ob5tgaJOj0OYMwnocc6G/CDLWC7hXfVvKX/ofkF98+BbN/tQa5poL+OwgFn9BA8ud8xKzyGPxGU6LdZ8Oh3z/g==", - "requires": { - "@sentry-internal/tracing": "7.77.0", - "@sentry/core": "7.77.0", - "@sentry/types": "7.77.0", - "@sentry/utils": "7.77.0", - "https-proxy-agent": "^5.0.0" - }, - "dependencies": { - "@sentry-internal/tracing": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.77.0.tgz", - "integrity": "sha512-8HRF1rdqWwtINqGEdx8Iqs9UOP/n8E0vXUu3Nmbqj4p5sQPA7vvCfq+4Y4rTqZFc7sNdFpDsRION5iQEh8zfZw==", - "requires": { - "@sentry/core": "7.77.0", - "@sentry/types": "7.77.0", - "@sentry/utils": "7.77.0" - } - }, - "@sentry/core": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.77.0.tgz", - "integrity": "sha512-Tj8oTYFZ/ZD+xW8IGIsU6gcFXD/gfE+FUxUaeSosd9KHwBQNOLhZSsYo/tTVf/rnQI/dQnsd4onPZLiL+27aTg==", - "requires": { - "@sentry/types": "7.77.0", - "@sentry/utils": "7.77.0" - } - }, - "@sentry/types": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.77.0.tgz", - "integrity": "sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA==" - }, - "@sentry/utils": { - "version": "7.77.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.77.0.tgz", - "integrity": "sha512-NmM2kDOqVchrey3N5WSzdQoCsyDkQkiRxExPaNI2oKQ/jMWHs9yt0tSy7otPBcXs0AP59ihl75Bvm1tDRcsp5g==", - "requires": { - "@sentry/types": "7.77.0" - } - } - } - }, - "@sentry/tracing": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.59.3.tgz", - "integrity": "sha512-+gDsfhYdteAR4NyKl3B5JVQs/bXYT73ajoFrlprfDjAJCEVR9W1P4CULavoLtfASxVqBQcZyT87Hsb9/vbn6bg==", - "requires": { - "@sentry-internal/tracing": "7.59.3" - } - }, - "@sentry/types": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.59.3.tgz", - "integrity": "sha512-HQ/Pd3YHyIa4HM0bGfOsfI4ZF+sLVs6II9VtlS4hsVporm4ETl3Obld5HywO3aVYvWOk5j/bpAW9JYsxXjRG5A==" - }, - "@sentry/utils": { - "version": "7.59.3", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.59.3.tgz", - "integrity": "sha512-Q57xauMKuzd6S+POA1fmulfjzTsb/z118TNAfZZNkHqVB48hHBqgzdhbEBmN4jPCSKV2Cx7VJUoDZxJfzQyLUQ==", - "requires": { - "@sentry/types": "7.59.3", - "tslib": "^2.4.1 || ^1.9.3" - } - }, - "@serdnam/pino-cloudwatch-transport": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@serdnam/pino-cloudwatch-transport/-/pino-cloudwatch-transport-1.0.4.tgz", - "integrity": "sha512-0wtILlFlO/qTFANM1oEMZLKa9REo+mluHN0VTDaOMh15H9Puc+qU4z4jAoZqggFz9Fw9EGG4c+UHpMduZ1EzeQ==", - "requires": { - "@aws-sdk/client-cloudwatch-logs": "^3.52.0", - "p-throttle": "^5.0.0", - "pino-abstract-transport": "^0.5.0" - }, - "dependencies": { - "pino-abstract-transport": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", - "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", - "requires": { - "duplexify": "^4.1.2", - "split2": "^4.0.0" - } - } - } - }, - "@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^3.0.0" - } - }, - "@smithy/abort-controller": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-1.0.2.tgz", - "integrity": "sha512-tb2h0b+JvMee+eAxTmhnyqyNk51UXIK949HnE14lFeezKsVJTB30maan+CO2IMwnig2wVYQH84B5qk6ylmKCuA==", - "requires": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/config-resolver": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-1.0.2.tgz", - "integrity": "sha512-8Bk7CgnVKg1dn5TgnjwPz2ebhxeR7CjGs5yhVYH3S8x0q8yPZZVWwpRIglwXaf5AZBzJlNO1lh+lUhMf2e73zQ==", - "requires": { - "@smithy/types": "^1.1.1", - "@smithy/util-config-provider": "^1.0.2", - "@smithy/util-middleware": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/credential-provider-imds": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-1.0.2.tgz", - "integrity": "sha512-fLjCya+JOu2gPJpCiwSUyoLvT8JdNJmOaTOkKYBZoGf7CzqR6lluSyI+eboZnl/V0xqcfcqBG4tgqCISmWS3/w==", - "requires": { - "@smithy/node-config-provider": "^1.0.2", - "@smithy/property-provider": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/url-parser": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/eventstream-codec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-1.0.2.tgz", - "integrity": "sha512-eW/XPiLauR1VAgHKxhVvgvHzLROUgTtqat2lgljztbH8uIYWugv7Nz+SgCavB+hWRazv2iYgqrSy74GvxXq/rg==", - "requires": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^1.1.1", - "@smithy/util-hex-encoding": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/fetch-http-handler": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-1.0.2.tgz", - "integrity": "sha512-kynyofLf62LvR8yYphPPdyHb8fWG3LepFinM/vWUTG2Q1pVpmPCM530ppagp3+q2p+7Ox0UvSqldbKqV/d1BpA==", - "requires": { - "@smithy/protocol-http": "^1.1.1", - "@smithy/querystring-builder": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/util-base64": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/hash-node": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-1.0.2.tgz", - "integrity": "sha512-K6PKhcUNrJXtcesyzhIvNlU7drfIU7u+EMQuGmPw6RQDAg/ufUcfKHz4EcUhFAodUmN+rrejhRG9U6wxjeBOQA==", - "requires": { - "@smithy/types": "^1.1.1", - "@smithy/util-buffer-from": "^1.0.2", - "@smithy/util-utf8": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/invalid-dependency": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-1.0.2.tgz", - "integrity": "sha512-B1Y3Tsa6dfC+Vvb+BJMhTHOfFieeYzY9jWQSTR1vMwKkxsymD0OIAnEw8rD/RiDj/4E4RPGFdx9Mdgnyd6Bv5Q==", - "requires": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/is-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-1.0.2.tgz", - "integrity": "sha512-pkyBnsBRpe+c/6ASavqIMRBdRtZNJEVJOEzhpxZ9JoAXiZYbkfaSMRA/O1dUxGdJ653GHONunnZ4xMo/LJ7utQ==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-content-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-1.0.2.tgz", - "integrity": "sha512-pa1/SgGIrSmnEr2c9Apw7CdU4l/HW0fK3+LKFCPDYJrzM0JdYpqjQzgxi31P00eAkL0EFBccpus/p1n2GF9urw==", - "requires": { - "@smithy/protocol-http": "^1.1.1", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-endpoint": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.0.3.tgz", - "integrity": "sha512-GsWvTXMFjSgl617PCE2km//kIjjtvMRrR2GAuRDIS9sHiLwmkS46VWaVYy+XE7ubEsEtzZ5yK2e8TKDR6Qr5Lw==", - "requires": { - "@smithy/middleware-serde": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/url-parser": "^1.0.2", - "@smithy/util-middleware": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-retry": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.0.4.tgz", - "integrity": "sha512-G7uRXGFL8c3F7APnoIMTtNAHH8vT4F2qVnAWGAZaervjupaUQuRRHYBLYubK0dWzOZz86BtAXKieJ5p+Ni2Xpg==", - "requires": { - "@smithy/protocol-http": "^1.1.1", - "@smithy/service-error-classification": "^1.0.3", - "@smithy/types": "^1.1.1", - "@smithy/util-middleware": "^1.0.2", - "@smithy/util-retry": "^1.0.4", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - } - }, - "@smithy/middleware-serde": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.0.2.tgz", - "integrity": "sha512-T4PcdMZF4xme6koUNfjmSZ1MLi7eoFeYCtodQNQpBNsS77TuJt1A6kt5kP/qxrTvfZHyFlj0AubACoaUqgzPeg==", - "requires": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-stack": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-1.0.2.tgz", - "integrity": "sha512-H7/uAQEcmO+eDqweEFMJ5YrIpsBwmrXSP6HIIbtxKJSQpAcMGY7KrR2FZgZBi1FMnSUOh+rQrbOyj5HQmSeUBA==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/node-config-provider": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-1.0.2.tgz", - "integrity": "sha512-HU7afWpTToU0wL6KseGDR2zojeyjECQfr8LpjAIeHCYIW7r360ABFf4EaplaJRMVoC3hD9FeltgI3/NtShOqCg==", - "requires": { - "@smithy/property-provider": "^1.0.2", - "@smithy/shared-ini-file-loader": "^1.0.2", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/node-http-handler": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-1.0.3.tgz", - "integrity": "sha512-PcPUSzTbIb60VCJCiH0PU0E6bwIekttsIEf5Aoo/M0oTfiqsxHTn0Rcij6QoH6qJy6piGKXzLSegspXg5+Kq6g==", - "requires": { - "@smithy/abort-controller": "^1.0.2", - "@smithy/protocol-http": "^1.1.1", - "@smithy/querystring-builder": "^1.0.2", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/property-provider": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-1.0.2.tgz", - "integrity": "sha512-pXDPyzKX8opzt38B205kDgaxda6LHcTfPvTYQZnwP6BAPp1o9puiCPjeUtkKck7Z6IbpXCPUmUQnzkUzWTA42Q==", - "requires": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/protocol-http": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.1.tgz", - "integrity": "sha512-mFLFa2sSvlUxm55U7B4YCIsJJIMkA6lHxwwqOaBkral1qxFz97rGffP/mmd4JDuin1EnygiO5eNJGgudiUgmDQ==", - "requires": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-builder": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-1.0.2.tgz", - "integrity": "sha512-6P/xANWrtJhMzTPUR87AbXwSBuz1SDHIfL44TFd/GT3hj6rA+IEv7rftEpPjayUiWRocaNnrCPLvmP31mobOyA==", - "requires": { - "@smithy/types": "^1.1.1", - "@smithy/util-uri-escape": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-parser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.0.2.tgz", - "integrity": "sha512-IWxwxjn+KHWRRRB+K2Ngl+plTwo2WSgc2w+DvLy0DQZJh9UGOpw40d6q97/63GBlXIt4TEt5NbcFrO30CKlrsA==", - "requires": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/service-error-classification": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.0.3.tgz", - "integrity": "sha512-2eglIYqrtcUnuI71yweu7rSfCgt6kVvRVf0C72VUqrd0LrV1M0BM0eYN+nitp2CHPSdmMI96pi+dU9U/UqAMSA==" - }, - "@smithy/shared-ini-file-loader": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-1.0.2.tgz", - "integrity": "sha512-bdQj95VN+lCXki+P3EsDyrkpeLn8xDYiOISBGnUG/AGPYJXN8dmp4EhRRR7XOoLoSs8anZHR4UcGEOzFv2jwGw==", - "requires": { - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/signature-v4": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-1.0.2.tgz", - "integrity": "sha512-rpKUhmCuPmpV5dloUkOb9w1oBnJatvKQEjIHGmkjRGZnC3437MTdzWej9TxkagcZ8NRRJavYnEUixzxM1amFig==", - "requires": { - "@smithy/eventstream-codec": "^1.0.2", - "@smithy/is-array-buffer": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/util-hex-encoding": "^1.0.2", - "@smithy/util-middleware": "^1.0.2", - "@smithy/util-uri-escape": "^1.0.2", - "@smithy/util-utf8": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/smithy-client": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-1.0.4.tgz", - "integrity": "sha512-gpo0Xl5Nyp9sgymEfpt7oa9P2q/GlM3VmQIdm+FeH0QEdYOQx3OtvwVmBYAMv2FIPWxkMZlsPYRTnEiBTK5TYg==", - "requires": { - "@smithy/middleware-stack": "^1.0.2", - "@smithy/types": "^1.1.1", - "@smithy/util-stream": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.1.tgz", - "integrity": "sha512-tMpkreknl2gRrniHeBtdgQwaOlo39df8RxSrwsHVNIGXULy5XP6KqgScUw2m12D15wnJCKWxVhCX+wbrBW/y7g==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/url-parser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.0.2.tgz", - "integrity": "sha512-0JRsDMQe53F6EHRWksdcavKDRjyqp8vrjakg8EcCUOa7PaFRRB1SO/xGZdzSlW1RSTWQDEksFMTCEcVEKmAoqA==", - "requires": { - "@smithy/querystring-parser": "^1.0.2", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/util-base64": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-1.0.2.tgz", - "integrity": "sha512-BCm15WILJ3SL93nusoxvJGMVfAMWHZhdeDZPtpAaskozuexd0eF6szdz4kbXaKp38bFCSenA6bkUHqaE3KK0dA==", - "requires": { - "@smithy/util-buffer-from": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-browser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-1.0.2.tgz", - "integrity": "sha512-Xh8L06H2anF5BHjSYTg8hx+Itcbf4SQZnVMl4PIkCOsKtneMJoGjPRLy17lEzfoh/GOaa0QxgCP6lRMQWzNl4w==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-node": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-1.0.2.tgz", - "integrity": "sha512-nXHbZsUtvZeyfL4Ceds9nmy2Uh2AhWXohG4vWHyjSdmT8cXZlJdmJgnH6SJKDjyUecbu+BpKeVvSrA4cWPSOPA==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-buffer-from": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-1.0.2.tgz", - "integrity": "sha512-lHAYIyrBO9RANrPvccnPjU03MJnWZ66wWuC5GjWWQVfsmPwU6m00aakZkzHdUT6tGCkGacXSgArP5wgTgA+oCw==", - "requires": { - "@smithy/is-array-buffer": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/util-config-provider": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-1.0.2.tgz", - "integrity": "sha512-HOdmDm+3HUbuYPBABLLHtn8ittuRyy+BSjKOA169H+EMc+IozipvXDydf+gKBRAxUa4dtKQkLraypwppzi+PRw==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-browser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-1.0.2.tgz", - "integrity": "sha512-J1u2PO235zxY7dg0+ZqaG96tFg4ehJZ7isGK1pCBEA072qxNPwIpDzUVGnLJkHZvjWEGA8rxIauDtXfB0qxeAg==", - "requires": { - "@smithy/property-provider": "^1.0.2", - "@smithy/types": "^1.1.1", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-node": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-1.0.2.tgz", - "integrity": "sha512-9/BN63rlIsFStvI+AvljMh873Xw6bbI6b19b+PVYXyycQ2DDQImWcjnzRlHW7eP65CCUNGQ6otDLNdBQCgMXqg==", - "requires": { - "@smithy/config-resolver": "^1.0.2", - "@smithy/credential-provider-imds": "^1.0.2", - "@smithy/node-config-provider": "^1.0.2", - "@smithy/property-provider": "^1.0.2", - "@smithy/types": "^1.1.1", - "tslib": "^2.5.0" - } - }, - "@smithy/util-endpoints": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.0.4.tgz", - "integrity": "sha512-FPry8j1xye5yzrdnf4xKUXVnkQErxdN7bUIaqC0OFoGsv2NfD9b2UUMuZSSt+pr9a8XWAqj0HoyVNUfPiZ/PvQ==", - "requires": { - "@smithy/node-config-provider": "^2.1.5", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "@smithy/node-config-provider": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.5.tgz", - "integrity": "sha512-3Omb5/h4tOCuKRx4p4pkYTvEYRCYoKk52bOYbKUyz/G/8gERbagsN8jFm4FjQubkrcIqQEghTpQaUw6uk+0edw==", - "requires": { - "@smithy/property-provider": "^2.0.14", - "@smithy/shared-ini-file-loader": "^2.2.4", - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/property-provider": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.14.tgz", - "integrity": "sha512-k3D2qp9o6imTrLaXRj6GdLYEJr1sXqS99nLhzq8fYmJjSVOeMg/G+1KVAAc7Oxpu71rlZ2f8SSZxcSxkevuR0A==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/shared-ini-file-loader": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.4.tgz", - "integrity": "sha512-9dRknGgvYlRIsoTcmMJXuoR/3ekhGwhRq4un3ns2/byre4Ql5hyUN4iS0x8eITohjU90YOnUCsbRwZRvCkbRfw==", - "requires": { - "@smithy/types": "^2.5.0", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.5.0.tgz", - "integrity": "sha512-/a31lYofrMBkJb3BuPlYJTMKDj0hUmKUP6JFZQu6YVuQVoAjubiY0A52U9S0Uysd33n/djexCUSNJ+G9bf3/aA==", - "requires": { - "tslib": "^2.5.0" - } - } - } - }, - "@smithy/util-hex-encoding": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-1.0.2.tgz", - "integrity": "sha512-Bxydb5rMJorMV6AuDDMOxro3BMDdIwtbQKHpwvQFASkmr52BnpDsWlxgpJi8Iq7nk1Bt4E40oE1Isy/7ubHGzg==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-middleware": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.0.2.tgz", - "integrity": "sha512-vtXK7GOR2BoseCX8NCGe9SaiZrm9M2lm/RVexFGyPuafTtry9Vyv7hq/vw8ifd/G/pSJ+msByfJVb1642oQHKw==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-retry": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.0.4.tgz", - "integrity": "sha512-RnZPVFvRoqdj2EbroDo3OsnnQU8eQ4AlnZTOGusbYKybH3269CFdrZfZJloe60AQjX7di3J6t/79PjwCLO5Khw==", - "requires": { - "@smithy/service-error-classification": "^1.0.3", - "tslib": "^2.5.0" - } - }, - "@smithy/util-stream": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-1.0.2.tgz", - "integrity": "sha512-qyN2M9QFMTz4UCHi6GnBfLOGYKxQZD01Ga6nzaXFFC51HP/QmArU72e4kY50Z/EtW8binPxspP2TAsGbwy9l3A==", - "requires": { - "@smithy/fetch-http-handler": "^1.0.2", - "@smithy/node-http-handler": "^1.0.3", - "@smithy/types": "^1.1.1", - "@smithy/util-base64": "^1.0.2", - "@smithy/util-buffer-from": "^1.0.2", - "@smithy/util-hex-encoding": "^1.0.2", - "@smithy/util-utf8": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/util-uri-escape": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-1.0.2.tgz", - "integrity": "sha512-k8C0BFNS9HpBMHSgUDnWb1JlCQcFG+PPlVBq9keP4Nfwv6a9Q0yAfASWqUCtzjuMj1hXeLhn/5ADP6JxnID1Pg==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-utf8": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-1.0.2.tgz", - "integrity": "sha512-V4cyjKfJlARui0dMBfWJMQAmJzoW77i4N3EjkH/bwnE2Ngbl4tqD2Y0C/xzpzY/J1BdxeCKxAebVFk8aFCaSCw==", - "requires": { - "@smithy/util-buffer-from": "^1.0.2", - "tslib": "^2.5.0" - } - }, - "@swc/core": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.99.tgz", - "integrity": "sha512-8O996RfuPC4ieb4zbYMfbyCU9k4gSOpyCNnr7qBQ+o7IEmh8JCV6B8wwu+fT/Om/6Lp34KJe1IpJ/24axKS6TQ==", - "dev": true, - "requires": { - "@swc/core-darwin-arm64": "1.3.99", - "@swc/core-darwin-x64": "1.3.99", - "@swc/core-linux-arm64-gnu": "1.3.99", - "@swc/core-linux-arm64-musl": "1.3.99", - "@swc/core-linux-x64-gnu": "1.3.99", - "@swc/core-linux-x64-musl": "1.3.99", - "@swc/core-win32-arm64-msvc": "1.3.99", - "@swc/core-win32-ia32-msvc": "1.3.99", - "@swc/core-win32-x64-msvc": "1.3.99", - "@swc/counter": "^0.1.1", - "@swc/types": "^0.1.5" - } - }, - "@swc/core-darwin-arm64": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.99.tgz", - "integrity": "sha512-Qj7Jct68q3ZKeuJrjPx7k8SxzWN6PqLh+VFxzA+KwLDpQDPzOlKRZwkIMzuFjLhITO4RHgSnXoDk/Syz0ZeN+Q==", - "dev": true, - "optional": true - }, - "@swc/core-darwin-x64": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.99.tgz", - "integrity": "sha512-wR7m9QVJjgiBu1PSOHy7s66uJPa45Kf9bZExXUL+JAa9OQxt5y+XVzr+n+F045VXQOwdGWplgPnWjgbUUHEVyw==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm64-gnu": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.99.tgz", - "integrity": "sha512-gcGv1l5t0DScEONmw5OhdVmEI/o49HCe9Ik38zzH0NtDkc+PDYaCcXU5rvfZP2qJFaAAr8cua8iJcOunOSLmnA==", - "dev": true, - "optional": true - }, - "@swc/core-linux-arm64-musl": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.99.tgz", - "integrity": "sha512-XL1/eUsTO8BiKsWq9i3iWh7H99iPO61+9HYiWVKhSavknfj4Plbn+XyajDpxsauln5o8t+BRGitymtnAWJM4UQ==", - "dev": true, - "optional": true - }, - "@swc/core-linux-x64-gnu": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.99.tgz", - "integrity": "sha512-fGrXYE6DbTfGNIGQmBefYxSk3rp/1lgbD0nVg4rl4mfFRQPi7CgGhrrqSuqZ/ezXInUIgoCyvYGWFSwjLXt/Qg==", - "dev": true, - "optional": true - }, - "@swc/core-linux-x64-musl": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.99.tgz", - "integrity": "sha512-kvgZp/mqf3IJ806gUOL6gN6VU15+DfzM1Zv4Udn8GqgXiUAvbQehrtruid4Snn5pZTLj4PEpSCBbxgxK1jbssA==", - "dev": true, - "optional": true - }, - "@swc/core-win32-arm64-msvc": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.99.tgz", - "integrity": "sha512-yt8RtZ4W/QgFF+JUemOUQAkVW58cCST7mbfKFZ1v16w3pl3NcWd9OrtppFIXpbjU1rrUX2zp2R7HZZzZ2Zk/aQ==", - "dev": true, - "optional": true - }, - "@swc/core-win32-ia32-msvc": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.99.tgz", - "integrity": "sha512-62p5fWnOJR/rlbmbUIpQEVRconICy5KDScWVuJg1v3GPLBrmacjphyHiJC1mp6dYvvoEWCk/77c/jcQwlXrDXw==", - "dev": true, - "optional": true - }, - "@swc/core-win32-x64-msvc": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.99.tgz", - "integrity": "sha512-PdppWhkoS45VGdMBxvClVgF1hVjqamtvYd82Gab1i4IV45OSym2KinoDCKE1b6j3LwBLOn2J9fvChGSgGfDCHQ==", - "dev": true, - "optional": true - }, - "@swc/counter": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", - "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==", - "dev": true - }, - "@swc/helpers": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", - "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==", - "dev": true, - "requires": { - "tslib": "^2.4.0" - } - }, - "@swc/types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", - "dev": true - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/bcrypt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", - "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/bcryptjs": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz", - "integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==", - "dev": true - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg==" - }, - "@types/bull": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@types/bull/-/bull-4.10.0.tgz", - "integrity": "sha512-RkYW8K2H3J76HT6twmHYbzJ0GtLDDotpLP9ah9gtiA7zfF6peBH1l5fEiK0oeIZ3/642M7Jcb9sPmor8Vf4w6g==", - "dev": true, - "requires": { - "bull": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==", - "dev": true, - "requires": { - "@types/express": "*" - } - }, - "@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", - "dev": true - }, - "@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==" - }, - "@types/debug": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", - "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", - "requires": { - "@types/ms": "*" - } - }, - "@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" - }, - "@types/ioredis": { - "version": "4.28.10", - "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz", - "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/is-base64": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/is-base64/-/is-base64-1.1.1.tgz", - "integrity": "sha512-JgnGhP+MeSHEQmvxcobcwPEP4Ew56voiq9/0hmP/41lyQ/3gBw/ZCIRy2v+QkEOdeCl58lRcrf6+Y6WMlJGETA==" - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.5.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", - "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/jmespath": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@types/jmespath/-/jmespath-0.15.1.tgz", - "integrity": "sha512-RWN1HQ71Hjl2ixw4a8s7/Bcz6S9uaBTaoCQ5cJB7OsjgHBFi3GaWMy0vRgZBPSYXdsMKFNxGLUUEh9uRf00Spw==", - "dev": true - }, - "@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==" - }, - "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true - }, - "@types/jsonwebtoken": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", - "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/libsodium-wrappers": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", - "integrity": "sha512-BqI9B92u+cM3ccp8mpHf+HzJ8fBlRwdmyd6+fz3p99m3V6ifT5O3zmOMi612PGkpeFeG/G6loxUnzlDNhfjPSA==" - }, - "@types/lodash": { - "version": "4.14.195", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz", - "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", - "dev": true - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" - }, - "@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - }, - "@types/node": { - "version": "18.16.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.19.tgz", - "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==" - }, - "@types/nodemailer": { - "version": "6.4.8", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.8.tgz", - "integrity": "sha512-oVsJSCkqViCn8/pEu2hfjwVO+Gb3e+eTWjg3PcjeFKRItfKpKwHphQqbYmPQrlMk+op7pNNWPbsJIEthpFN/OQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/passport": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz", - "integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==", - "requires": { - "@types/express": "*" - } - }, - "@types/passport-strategy": { - "version": "0.2.35", - "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", - "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", - "requires": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "@types/pg": { - "version": "8.10.7", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.7.tgz", - "integrity": "sha512-ksJqHipwYaSEHz9e1fr6H6erjoEdNNaOxwyJgPx9bNeaqOW3iWBQgVHfpwiSAoqGzchfc+ZyRLwEfeCcyYD3uQ==", - "dev": true, - "requires": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^4.0.1" - }, - "dependencies": { - "pg-types": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz", - "integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==", - "dev": true, - "requires": { - "pg-int8": "1.0.1", - "pg-numeric": "1.0.2", - "postgres-array": "~3.0.1", - "postgres-bytea": "~3.0.0", - "postgres-date": "~2.0.1", - "postgres-interval": "^3.0.0", - "postgres-range": "^1.1.1" - } - }, - "postgres-array": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", - "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", - "dev": true - }, - "postgres-bytea": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", - "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", - "dev": true, - "requires": { - "obuf": "~1.1.2" - } - }, - "postgres-date": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz", - "integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==", - "dev": true - }, - "postgres-interval": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", - "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", - "dev": true - } - } - }, - "@types/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==", - "dev": true - }, - "@types/pino": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@types/pino/-/pino-7.0.5.tgz", - "integrity": "sha512-wKoab31pknvILkxAF8ss+v9iNyhw5Iu/0jLtRkUD74cNfOOLJNnqfFKAv0r7wVaTQxRZtWrMpGfShwwBjOcgcg==", - "dev": true, - "requires": { - "pino": "*" - } - }, - "@types/pino-http": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/@types/pino-http/-/pino-http-5.8.1.tgz", - "integrity": "sha512-A9MW6VCnx5ii7s+Fs5aFIw+aSZcBCpsZ/atpxamu8tTsvWFacxSf2Hrn1Ohn1jkVRB/LiPGOapRXcFawDBnDnA==", - "requires": { - "@types/pino": "6.3" - }, - "dependencies": { - "@types/pino": { - "version": "6.3.12", - "resolved": "https://registry.npmjs.org/@types/pino/-/pino-6.3.12.tgz", - "integrity": "sha512-dsLRTq8/4UtVSpJgl9aeqHvbh6pzdmjYD3C092SYgLD2TyoCqHpTJk6vp8DvCTGGc7iowZ2MoiYiVUUCcu7muw==", - "requires": { - "@types/node": "*", - "@types/pino-pretty": "*", - "@types/pino-std-serializers": "*", - "sonic-boom": "^2.1.0" - } - } - } - }, - "@types/pino-pretty": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/pino-pretty/-/pino-pretty-5.0.0.tgz", - "integrity": "sha512-N1uzqSzioqz8R3AkDbSJwcfDWeI3YMPNapSQQhnB2ISU4NYgUIcAh+hYT5ygqBM+klX4htpEhXMmoJv3J7GrdA==", - "requires": { - "pino-pretty": "*" - } - }, - "@types/pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-gXfUZx2xIBbFYozGms53fT0nvkacx/+62c8iTxrEqH5PkIGAQvDbXg2774VWOycMPbqn5YJBQ3BMsg4Li3dWbg==", - "requires": { - "pino-std-serializers": "*" - } - }, - "@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", - "dev": true - }, - "@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/serve-static": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", - "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", - "requires": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/superagent": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.18.tgz", - "integrity": "sha512-LOWgpacIV8GHhrsQU+QMZuomfqXiqzz3ILLkCtKx3Us6AmomFViuzKT9D693QTKgyut2oCytMG8/efOop+DB+w==", - "dev": true, - "requires": { - "@types/cookiejar": "*", - "@types/node": "*" - } - }, - "@types/supertest": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", - "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", - "dev": true, - "requires": { - "@types/superagent": "*" - } - }, - "@types/swagger-jsdoc": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.1.tgz", - "integrity": "sha512-+MUpcbyxD528dECUBCEVm6abNuORdbuGjbrUdHDeAQ+rkPuo2a+L4N02WJHF3bonSSE6SJ3dUJwF2V6+cHnf0w==", - "dev": true - }, - "@types/swagger-ui-express": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.3.tgz", - "integrity": "sha512-jqCjGU/tGEaqIplPy3WyQg+Nrp6y80DCFnDEAvVKWkJyv0VivSSDCChkppHRHAablvInZe6pijDFMnavtN0vqA==", - "dev": true, - "requires": { - "@types/express": "*", - "@types/serve-static": "*" - } - }, - "@types/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" - }, - "@types/whatwg-url": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", - "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", - "requires": { - "@types/node": "*", - "@types/webidl-conversions": "*" - } - }, - "@types/xml-crypto": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@types/xml-crypto/-/xml-crypto-1.4.2.tgz", - "integrity": "sha512-1kT+3gVkeBDg7Ih8NefxGYfCApwZViMIs5IEs5AXF6Fpsrnf9CLAEIRh0DYb1mIcRcvysVbe27cHsJD6rJi36w==", - "requires": { - "@types/node": "*", - "xpath": "0.0.27" - } - }, - "@types/xml-encryption": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/xml-encryption/-/xml-encryption-1.2.1.tgz", - "integrity": "sha512-UeyZkfZFZSa9XCGU5uGgUmsSLwQESDJvF076bJGyDf2gkXJjKvK8fW/x4ckvEHB2M/5RHJEkMc5xI+JrdmCTKA==", - "requires": { - "@types/node": "*" - } - }, - "@types/xml2js": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.11.tgz", - "integrity": "sha512-JdigeAKmCyoJUiQljjr7tQG3if9NkqGUgwEUqBvV0N7LM4HyQk7UXCnusRa1lnvXAEYJ8mw8GtZWioagNztOwA==", - "requires": { - "@types/node": "*" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@ucast/core": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@ucast/core/-/core-1.10.2.tgz", - "integrity": "sha512-ons5CwXZ/51wrUPfoduC+cO7AS1/wRb0ybpQJ9RrssossDxVy4t49QxWoWgfBDvVKsz9VXzBk9z0wqTdZ+Cq8g==" - }, - "@ucast/js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@ucast/js/-/js-3.0.3.tgz", - "integrity": "sha512-jBBqt57T5WagkAjqfCIIE5UYVdaXYgGkOFYv2+kjq2AVpZ2RIbwCo/TujJpDlwTVluUI+WpnRpoGU2tSGlEvFQ==", - "requires": { - "@ucast/core": "^1.0.0" - } - }, - "@ucast/mongo": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@ucast/mongo/-/mongo-2.4.3.tgz", - "integrity": "sha512-XcI8LclrHWP83H+7H2anGCEeDq0n+12FU2mXCTz6/Tva9/9ddK/iacvvhCyW6cijAAOILmt0tWplRyRhVyZLsA==", - "requires": { - "@ucast/core": "^1.4.1" - } - }, - "@ucast/mongo2js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@ucast/mongo2js/-/mongo2js-1.3.4.tgz", - "integrity": "sha512-ahazOr1HtelA5AC1KZ9x0UwPMqqimvfmtSm/PRRSeKKeE5G2SCqTgwiNzO7i9jS8zA3dzXpKVPpXMkcYLnyItA==", - "requires": { - "@ucast/core": "^1.6.1", - "@ucast/js": "^3.0.0", - "@ucast/mongo": "^2.4.0" - } - }, - "@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==" - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - } - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argon2": { - "version": "0.30.3", - "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.30.3.tgz", - "integrity": "sha512-DoH/kv8c9127ueJSBxAVJXinW9+EuPA3EMUxoV2sAY1qDE5H9BjTyVF/aD2XyHqbqUWabgBkIfcP3ZZuGhbJdg==", - "requires": { - "@mapbox/node-pre-gyp": "^1.0.10", - "@phc/format": "^1.0.0", - "node-addon-api": "^5.0.0" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "args": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", - "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", - "requires": { - "camelcase": "5.0.0", - "chalk": "2.4.2", - "leven": "2.1.0", - "mri": "1.1.4" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "camelcase": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", - "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" - }, - "aws-sdk": { - "version": "2.1419.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1419.0.tgz", - "integrity": "sha512-JcD8gb8I5fH/TGdObG8UYyyXfnqVYk50wx9TGao6G/xBYT3YoYeQXj020W568YQpO+dBKRuR4U2LRYdKBNmQ/g==", - "requires": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" - }, - "dependencies": { - "uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" - } - } - }, - "axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "axios-retry": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.5.1.tgz", - "integrity": "sha512-mQRJ4IyAUnYig14BQ4MnnNHHuH1cNH7NW4JxEUD6mNJwK6pwOY66wKLCwZ6Y0o3POpfStalqRC+J4+Hnn6Om7w==", - "requires": { - "@babel/runtime": "^7.15.4", - "is-retry-allowed": "^2.2.0" - } - }, - "babel-jest": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.1.tgz", - "integrity": "sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A==", - "dev": true, - "requires": { - "@jest/transform": "^29.6.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.5.0", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", - "requires": { - "@mapbox/node-pre-gyp": "^1.0.10", - "node-addon-api": "^5.0.0" - } - }, - "before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "bigint-conversion": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/bigint-conversion/-/bigint-conversion-2.4.1.tgz", - "integrity": "sha512-/DTRevseMZoqN4KLkN5BryOiom0KbwYajiXG5Vo+ZcEPAO0WBZyZoYyDZSgfeq/v/oegLo9bjdndDBlExvAhBQ==", - "requires": { - "@juanelas/base64": "^1.1.2" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - }, - "dependencies": { - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - } - } - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" - }, - "bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "bson": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", - "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==" - }, - "btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==" - }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" - }, - "bull": { - "version": "4.10.4", - "resolved": "https://registry.npmjs.org/bull/-/bull-4.10.4.tgz", - "integrity": "sha512-o9m/7HjS/Or3vqRd59evBlWCXd9Lp+ALppKseoSKHaykK46SmRjAilX98PgmOz1yeVaurt8D5UtvEt4bUjM3eA==", - "dev": true, - "requires": { - "cron-parser": "^4.2.1", - "debuglog": "^1.0.0", - "get-port": "^5.1.1", - "ioredis": "^5.0.0", - "lodash": "^4.17.21", - "msgpackr": "^1.5.2", - "semver": "^7.3.2", - "uuid": "^8.3.0" - } - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "camelcase-keys": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", - "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", - "dev": true, - "requires": { - "camelcase": "^6.3.0", - "map-obj": "^4.1.0", - "quick-lru": "^5.1.1", - "type-fest": "^1.2.1" - } - }, - "caniuse-lite": { - "version": "1.0.30001517", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", - "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", - "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==" - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" - }, - "cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" - }, - "colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" - }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" - }, - "cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "requires": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6" - }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - } - } - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "cookiejar": { - "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": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cron-parser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.8.1.tgz", - "integrity": "sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==", - "dev": true, - "requires": { - "luxon": "^3.2.1" - } - }, - "cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, - "dateformat": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", - "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" - }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "requires": { - "clone": "^1.0.2" - }, - "dependencies": { - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" - }, - "duplexify": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", - "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", - "requires": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "electron-to-chromium": { - "version": "1.4.467", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.467.tgz", - "integrity": "sha512-2qI70O+rR4poYeF2grcuS/bCps5KJh6y1jtZMDDEteyKJQrzLOEhFyXCLcHW6DTBjKjWkk26JhWoAi+Ux9A0fg==", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", - "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "eslint-scope": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.1.tgz", - "integrity": "sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "eslint-plugin-unused-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz", - "integrity": "sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==", - "dev": true, - "requires": { - "eslint-rule-composer": "^0.3.0" - } - }, - "eslint-rule-composer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", - "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "dev": true - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true - }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" - }, - "eventsource": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", - "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==" - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.1.tgz", - "integrity": "sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.6.1", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1" - } - }, - "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "express-async-errors": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", - "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==", - "requires": {} - }, - "express-handlebars": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-6.0.7.tgz", - "integrity": "sha512-iYeMFpc/hMD+E6FNAZA5fgWeXnXr4rslOSPkeEV6TwdmpJ5lEXuWX0u9vFYs31P2MURctQq2batR09oeNj0LIg==", - "requires": { - "glob": "^8.1.0", - "graceful-fs": "^4.2.10", - "handlebars": "^4.7.7" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "express-rate-limit": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.8.0.tgz", - "integrity": "sha512-yVeDWczkh8qgo9INJB1tT4j7LFu+n6ei/oqSMsqpsUIGYjTM+gk+Q3wv19TMUdo8chvus8XohAuOhG7RYRM9ZQ==", - "requires": {} - }, - "express-validator": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.15.0.tgz", - "integrity": "sha512-r05VYoBL3i2pswuehoFSy+uM8NBuVaY7avp5qrYjQBDzagx2Z5A77FZqPT8/gNLF3HopWkIzaTFaC4JysWXLqg==", - "requires": { - "lodash": "^4.17.21", - "validator": "^13.9.0" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true - }, - "fast-copy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", - "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-glob": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fast-redact": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.2.0.tgz", - "integrity": "sha512-zaTadChr+NekyzallAMXATXLOR8MNx3zqpZ0MUF2aGf4EathnG0f32VLODNlY8IuGY3HoRO2L6/6fSzNsLaHIw==" - }, - "fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", - "requires": { - "punycode": "^1.3.2" - } - }, - "fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "requires": { - "strnum": "^1.0.5" - } - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==" - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatstr": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz", - "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "requires": { - "is-callable": "^1.1.3" - } - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dev": true, - "requires": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { - "minipass": "^3.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - } - }, - "generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "requires": { - "is-property": "^1.0.2" - } - }, - "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 - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - }, - "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - } - }, - "helmet": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-5.1.1.tgz", - "integrity": "sha512-/yX0oVZBggA9cLJh8aw3PPCfedBnbd7J2aowjzsaWwZh7/UFY0nccn/aHAggIgWUFfnykX8GKd3a1pSbrmlcVQ==" - }, - "help-me": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", - "integrity": "sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==", - "requires": { - "glob": "^8.0.0", - "readable-stream": "^3.6.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" - }, - "infisical-node": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.3.2.tgz", - "integrity": "sha512-o1rxfOBAmpTiipka9Xnfa2AgTS8CkJHo0aRQwk6UGi+yEkKzXS7dDM7bZD56M/z+yKGLK15QkfFGZXp1VomlHw==", - "requires": { - "axios": "^1.3.3", - "dotenv": "^16.0.3", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "install": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", - "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", - "dev": true - }, - "ioredis": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", - "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", - "requires": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "dependencies": { - "denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" - } - } - }, - "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" - }, - "ip6addr": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/ip6addr/-/ip6addr-0.2.5.tgz", - "integrity": "sha512-9RGGSB6Zc9Ox5DpDGFnJdIeF0AsqXzdH+FspCfPPaU/L/4tI6P+5lIoFUFm9JXs9IrJv1boqAaNCQmoDADTSKQ==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2" - } - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "is-base64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-base64/-/is-base64-1.1.0.tgz", - "integrity": "sha512-Nlhg7Z2dVC4/PTvIFkgVVNvPHSO2eR/Yd0XzhGiXCXEvWnptXlXa/clQ8aePPiMuxEGcWfzWbGw2Fe3d+Y3v1g==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" - }, - "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, - "is-retry-allowed": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", - "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==" - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "requires": { - "which-typed-array": "^1.1.11" - } - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.1.tgz", - "integrity": "sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw==", - "dev": true, - "requires": { - "@jest/core": "^29.6.1", - "@jest/types": "^29.6.1", - "import-local": "^3.0.2", - "jest-cli": "^29.6.1" - } - }, - "jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.1.tgz", - "integrity": "sha512-tPbYLEiBU4MYAL2XoZme/bgfUeotpDBd81lgHLCbDZZFaGmECk0b+/xejPFtmiBP87GgP/y4jplcRpbH+fgCzQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.6.1", - "@jest/expect": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.1", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", - "p-limit": "^3.1.0", - "pretty-format": "^29.6.1", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.1.tgz", - "integrity": "sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing==", - "dev": true, - "requires": { - "@jest/core": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.1.tgz", - "integrity": "sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.1", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.1", - "jest-environment-node": "^29.6.1", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-runner": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.6.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.1.tgz", - "integrity": "sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" - } - }, - "jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.1.tgz", - "integrity": "sha512-n5eoj5eiTHpKQCAVcNTT7DRqeUmJ01hsAL0Q1SMiBHcBcvTKDELixQOGMCpqhbIuTcfC4kMfSnpmDqRgRJcLNQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.1", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.6.1", - "pretty-format": "^29.6.1" - } - }, - "jest-environment-node": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.1.tgz", - "integrity": "sha512-ZNIfAiE+foBog24W+2caIldl4Irh8Lx1PUhg/GZ0odM1d/h2qORAsejiFc7zb+SEmYPn1yDZzEDSU5PmDkmVLQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1" - } - }, - "jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", - "dev": true - }, - "jest-haste-map": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.1.tgz", - "integrity": "sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig==", - "dev": true, - "requires": { - "@jest/types": "^29.6.1", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.1", - "jest-worker": "^29.6.1", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-junit": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-15.0.0.tgz", - "integrity": "sha512-Z5sVX0Ag3HZdMUnD5DFlG+1gciIFSy7yIVPhOdGUi8YJaI9iLvvBb530gtQL2CHmv0JJeiwRZenr0VrSR7frvg==", - "dev": true, - "requires": { - "mkdirp": "^1.0.4", - "strip-ansi": "^6.0.1", - "uuid": "^8.3.2", - "xml": "^1.0.1" - } - }, - "jest-leak-detector": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.1.tgz", - "integrity": "sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ==", - "dev": true, - "requires": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" - } - }, - "jest-matcher-utils": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz", - "integrity": "sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.6.1", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" - } - }, - "jest-message-util": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.1.tgz", - "integrity": "sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.6.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.1.tgz", - "integrity": "sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.1", - "@types/node": "*", - "jest-util": "^29.6.1" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", - "dev": true - }, - "jest-resolve": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.1.tgz", - "integrity": "sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.1.tgz", - "integrity": "sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw==", - "dev": true, - "requires": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.1" - } - }, - "jest-runner": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.1.tgz", - "integrity": "sha512-tw0wb2Q9yhjAQ2w8rHRDxteryyIck7gIzQE4Reu3JuOBpGp96xWgF0nY8MDdejzrLCZKDcp8JlZrBN/EtkQvPQ==", - "dev": true, - "requires": { - "@jest/console": "^29.6.1", - "@jest/environment": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.6.1", - "jest-haste-map": "^29.6.1", - "jest-leak-detector": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-resolve": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-util": "^29.6.1", - "jest-watcher": "^29.6.1", - "jest-worker": "^29.6.1", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - } - }, - "jest-runtime": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.1.tgz", - "integrity": "sha512-D6/AYOA+Lhs5e5il8+5pSLemjtJezUr+8zx+Sn8xlmOux3XOqx4d8l/2udBea8CRPqqrzhsKUsN/gBDE/IcaPQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/globals": "^29.6.1", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-mock": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-snapshot": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.1.tgz", - "integrity": "sha512-G4UQE1QQ6OaCgfY+A0uR1W2AY0tGXUPQpoUClhWHq1Xdnx1H6JOrC2nH5lqnOEqaDgbHFgIwZ7bNq24HpB180A==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.6.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.1", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", - "natural-compare": "^1.4.0", - "pretty-format": "^29.6.1", - "semver": "^7.5.3" - } - }, - "jest-util": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.1.tgz", - "integrity": "sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg==", - "dev": true, - "requires": { - "@jest/types": "^29.6.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.1.tgz", - "integrity": "sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "leven": "^3.1.0", - "pretty-format": "^29.6.1" - } - }, - "jest-watcher": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.1.tgz", - "integrity": "sha512-d4wpjWTS7HEZPaaj8m36QiaP856JthRZkrgcIY/7ISoUWPIillrXM23WPboZVLbiwZBt4/qn2Jke84Sla6JhFA==", - "dev": true, - "requires": { - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.6.1", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.1.tgz", - "integrity": "sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.6.1", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" - }, - "joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==", - "requires": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - } - }, - "jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "jsrp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/jsrp/-/jsrp-0.2.4.tgz", - "integrity": "sha512-+CjGAhZaj3k2MMXEy+xWYv7xJGnise/SlL1IIvnRuJ1ZiLtNPJJln/dMDCgORQCq1ouXDnW1FBxW5bkBFhK/8g==", - "requires": { - "create-hash": "^1.0.0", - "jsbn": "^1.0.0", - "randombytes": "^2.0.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "kareem": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", - "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==" - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "libsodium": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.11.tgz", - "integrity": "sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A==" - }, - "libsodium-wrappers": { - "version": "0.7.11", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz", - "integrity": "sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q==", - "requires": { - "libsodium": "^0.7.11" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", - "requires": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - }, - "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==" - } - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, - "lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" - }, - "lru-cache": { - "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" - } - }, - "luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true - }, - "maxmind": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.11.tgz", - "integrity": "sha512-tJDrKbUzN6PSA88tWgg0L2R4Ln00XwecYQJPFI+RvlF2k1sx6VQYtuQ1SVxm8+bw5tF7GWV4xyb+3/KyzEpPUw==", - "dev": true, - "requires": { - "mmdb-lib": "2.0.2", - "tiny-lru": "11.0.1" - } - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" - }, - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "mmdb-lib": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-2.0.2.tgz", - "integrity": "sha512-shi1I+fCPQonhTi7qyb6hr7hi87R7YS69FlfJiMFuJ12+grx0JyL56gLNzGTYXPU7EhAPkMLliGeyHer0K+AVA==", - "dev": true - }, - "mongodb": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.0.tgz", - "integrity": "sha512-g+GCMHN1CoRUA+wb1Agv0TI4YTSiWr42B5ulkiAfLLHitGK1R+PkSAf3Lr5rPZwi/3F04LiaZEW0Kxro9Fi2TA==", - "requires": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^5.5.0", - "mongodb-connection-string-url": "^2.6.0", - "socks": "^2.7.1" - } - }, - "mongodb-connection-string-url": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", - "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", - "requires": { - "@types/whatwg-url": "^8.2.1", - "whatwg-url": "^11.0.0" - } - }, - "mongoose": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.6.3.tgz", - "integrity": "sha512-moYP2qWCOdWRDeBxqB/zYwQmQnTBsF5DoolX5uPyI218BkiA1ujGY27P0NTd4oWIX+LLkZPw0LDzlc/7oh1plg==", - "requires": { - "bson": "^5.5.0", - "kareem": "2.5.1", - "mongodb": "5.9.0", - "mpath": "0.9.0", - "mquery": "5.0.0", - "ms": "2.1.3", - "sift": "16.0.1" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "dev": true, - "requires": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - } - } - }, - "mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" - }, - "mquery": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", - "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", - "requires": { - "debug": "4.x" - } - }, - "mri": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", - "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "msgpackr": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.9.6.tgz", - "integrity": "sha512-50rmb6+ZWvEm0vJn8R8CwI1Eavss3h5rgtKrcdUal3EkZcpqw82+xsmc7RoHb8fYB5V4EOU2NDaOitDAdO0t+w==", - "dev": true, - "requires": { - "msgpackr-extract": "^3.0.2" - } - }, - "msgpackr-extract": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz", - "integrity": "sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==", - "dev": true, - "optional": true, - "requires": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.2", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.2", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.2", - "node-gyp-build-optional-packages": "5.0.7" - } - }, - "mysql2": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.2.tgz", - "integrity": "sha512-m5erE6bMoWfPXW1D5UrVwlT8PowAoSX69KcZzPuARQ3wY1RJ52NW9PdvdPo076XiSIkQ5IBTis7hxdlrQTlyug==", - "requires": { - "denque": "^2.1.0", - "generate-function": "^2.3.1", - "iconv-lite": "^0.6.3", - "long": "^5.2.1", - "lru-cache": "^8.0.0", - "named-placeholders": "^1.1.3", - "seq-queue": "^0.0.5", - "sqlstring": "^2.3.2" - }, - "dependencies": { - "denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "lru-cache": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", - "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==" - }, - "sqlstring": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==" - } - } - }, - "named-placeholders": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", - "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", - "requires": { - "lru-cache": "^7.14.1" - }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" - } - } - }, - "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, - "node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "requires": { - "clone": "2.x" - } - }, - "node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", - "requires": { - "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } - } - }, - "node-gyp-build-optional-packages": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz", - "integrity": "sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==", - "dev": true, - "optional": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "nodemailer": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.4.tgz", - "integrity": "sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA==" - }, - "nodemon": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", - "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", - "dev": true, - "requires": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm": { - "version": "8.19.4", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.19.4.tgz", - "integrity": "sha512-3HANl8i9DKnUA89P4KEgVNN28EjSeDCmvEqbzOAuxCFDzdBZzjUl99zgnGpOUumvW5lvJo2HKcjrsc+tfyv1Hw==", - "dev": true, - "requires": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/config": "^4.2.1", - "@npmcli/fs": "^2.1.0", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^4.2.1", - "abbrev": "~1.1.1", - "archy": "~1.0.0", - "cacache": "^16.1.3", - "chalk": "^4.1.2", - "chownr": "^2.0.0", - "cli-columns": "^4.0.0", - "cli-table3": "^0.6.2", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.12", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "graceful-fs": "^4.2.10", - "hosted-git-info": "^5.2.1", - "ini": "^3.0.1", - "init-package-json": "^3.0.2", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^6.0.4", - "libnpmdiff": "^4.0.5", - "libnpmexec": "^4.0.14", - "libnpmfund": "^3.0.5", - "libnpmhook": "^8.0.4", - "libnpmorg": "^4.0.4", - "libnpmpack": "^4.1.3", - "libnpmpublish": "^6.0.5", - "libnpmsearch": "^5.0.4", - "libnpmteam": "^4.0.4", - "libnpmversion": "^3.0.7", - "make-fetch-happen": "^10.2.0", - "minimatch": "^5.1.0", - "minipass": "^3.1.6", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "ms": "^2.1.2", - "node-gyp": "^9.1.0", - "nopt": "^6.0.0", - "npm-audit-report": "^3.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.1.0", - "npm-pick-manifest": "^7.0.2", - "npm-profile": "^6.2.0", - "npm-registry-fetch": "^13.3.1", - "npm-user-validate": "^1.0.1", - "npmlog": "^6.0.2", - "opener": "^1.5.2", - "p-map": "^4.0.0", - "pacote": "^13.6.2", - "parse-conflict-json": "^2.0.2", - "proc-log": "^2.0.1", - "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^5.0.2", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.1", - "tar": "^6.1.11", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^2.0.0", - "validate-npm-package-name": "^4.0.0", - "which": "^2.0.2", - "write-file-atomic": "^4.0.1" - }, - "dependencies": { - "@colors/colors": { - "version": "1.5.0", - "bundled": true, - "dev": true, - "optional": true - }, - "@gar/promisify": { - "version": "1.1.3", - "bundled": true, - "dev": true - }, - "@isaacs/string-locale-compare": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "@npmcli/arborist": { - "version": "5.6.3", - "bundled": true, - "dev": true, - "requires": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/query": "^1.2.0", - "@npmcli/run-script": "^4.1.3", - "bin-links": "^3.0.3", - "cacache": "^16.1.3", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^5.2.1", - "json-parse-even-better-errors": "^2.3.1", - "json-stringify-nice": "^1.1.4", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.2", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", - "walk-up-path": "^1.0.0" - } - }, - "@npmcli/ci-detect": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "@npmcli/config": { - "version": "4.2.2", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/map-workspaces": "^2.0.2", - "ini": "^3.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "proc-log": "^2.0.0", - "read-package-json-fast": "^2.0.3", - "semver": "^7.3.5", - "walk-up-path": "^1.0.0" - } - }, - "@npmcli/disparity-colors": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.3.0" - } - }, - "@npmcli/fs": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "requires": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - } - }, - "@npmcli/git": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" - } - }, - "@npmcli/installed-package-contents": { - "version": "1.0.7", - "bundled": true, - "dev": true, - "requires": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "dependencies": { - "npm-bundled": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - } - } - }, - "@npmcli/map-workspaces": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" - } - }, - "@npmcli/metavuln-calculator": { - "version": "3.1.1", - "bundled": true, - "dev": true, - "requires": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", - "semver": "^7.3.5" - } - }, - "@npmcli/move-file": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - } - }, - "@npmcli/name-from-folder": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "@npmcli/node-gyp": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "@npmcli/package-json": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.3.1" - } - }, - "@npmcli/promise-spawn": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "infer-owner": "^1.0.4" - } - }, - "@npmcli/query": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "npm-package-arg": "^9.1.0", - "postcss-selector-parser": "^6.0.10", - "semver": "^7.3.7" - } - }, - "@npmcli/run-script": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3", - "which": "^2.0.2" - } - }, - "@tootallnate/once": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "bundled": true, - "dev": true, - "requires": { - "debug": "4" - } - }, - "agentkeepalive": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "requires": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - } - }, - "aggregate-error": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ansi-regex": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "aproba": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "archy": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "are-we-there-yet": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, - "asap": { - "version": "2.0.6", - "bundled": true, - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "bin-links": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "requires": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" - }, - "dependencies": { - "npm-normalize-package-bin": { - "version": "2.0.0", - "bundled": true, - "dev": true - } - } - }, - "binary-extensions": { - "version": "2.2.0", - "bundled": true, - "dev": true - }, - "brace-expansion": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "builtins": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "semver": "^7.0.0" - } - }, - "cacache": { - "version": "16.1.3", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" - } - }, - "chalk": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chownr": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "cidr-regex": { - "version": "3.1.1", - "bundled": true, - "dev": true, - "requires": { - "ip-regex": "^4.1.0" - } - }, - "clean-stack": { - "version": "2.2.0", - "bundled": true, - "dev": true - }, - "cli-columns": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - } - }, - "cli-table3": { - "version": "0.6.2", - "bundled": true, - "dev": true, - "requires": { - "@colors/colors": "1.5.0", - "string-width": "^4.2.0" - } - }, - "clone": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "cmd-shim": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "mkdirp-infer-owner": "^2.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "color-support": { - "version": "1.1.3", - "bundled": true, - "dev": true - }, - "columnify": { - "version": "1.6.0", - "bundled": true, - "dev": true, - "requires": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" - } - }, - "common-ancestor-path": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "cssesc": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "debug": { - "version": "4.3.4", - "bundled": true, - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "bundled": true, - "dev": true - } - } - }, - "debuglog": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "defaults": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "depd": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "dezalgo": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "diff": { - "version": "5.1.0", - "bundled": true, - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "bundled": true, - "dev": true - }, - "encoding": { - "version": "0.1.13", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "iconv-lite": "^0.6.2" - } - }, - "env-paths": { - "version": "2.2.1", - "bundled": true, - "dev": true - }, - "err-code": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "fastest-levenshtein": { - "version": "1.0.12", - "bundled": true, - "dev": true - }, - "fs-minipass": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "gauge": { - "version": "4.0.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - } - }, - "glob": { - "version": "8.0.3", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - } - }, - "graceful-fs": { - "version": "4.2.10", - "bundled": true, - "dev": true - }, - "has": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "hosted-git-info": { - "version": "5.2.1", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "^7.5.1" - } - }, - "http-cache-semantics": { - "version": "4.1.1", - "bundled": true, - "dev": true - }, - "http-proxy-agent": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "humanize-ms": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "ignore-walk": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "minimatch": "^5.0.1" - } - }, - "imurmurhash": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "bundled": true, - "dev": true - }, - "ini": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "init-package-json": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" - } - }, - "ip": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "ip-regex": { - "version": "4.3.0", - "bundled": true, - "dev": true - }, - "is-cidr": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "cidr-regex": "^3.1.1" - } - }, - "is-core-module": { - "version": "2.10.0", - "bundled": true, - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "is-lambda": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "isexe": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "bundled": true, - "dev": true - }, - "json-stringify-nice": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "bundled": true, - "dev": true - }, - "just-diff": { - "version": "5.1.1", - "bundled": true, - "dev": true - }, - "just-diff-apply": { - "version": "5.4.1", - "bundled": true, - "dev": true - }, - "libnpmaccess": { - "version": "6.0.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0" - } - }, - "libnpmdiff": { - "version": "4.0.5", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/disparity-colors": "^2.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", - "diff": "^5.1.0", - "minimatch": "^5.0.1", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1", - "tar": "^6.1.0" - } - }, - "libnpmexec": { - "version": "4.0.14", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/fs": "^2.1.1", - "@npmcli/run-script": "^4.2.0", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^9.0.1", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "proc-log": "^2.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", - "semver": "^7.3.7", - "walk-up-path": "^1.0.0" - } - }, - "libnpmfund": { - "version": "3.0.5", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/arborist": "^5.6.3" - } - }, - "libnpmhook": { - "version": "8.0.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - } - }, - "libnpmorg": { - "version": "4.0.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - } - }, - "libnpmpack": { - "version": "4.1.3", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/run-script": "^4.1.3", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1" - } - }, - "libnpmpublish": { - "version": "6.0.5", - "bundled": true, - "dev": true, - "requires": { - "normalize-package-data": "^4.0.0", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0", - "semver": "^7.3.7", - "ssri": "^9.0.0" - } - }, - "libnpmsearch": { - "version": "5.0.4", - "bundled": true, - "dev": true, - "requires": { - "npm-registry-fetch": "^13.0.0" - } - }, - "libnpmteam": { - "version": "4.0.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" - } - }, - "libnpmversion": { - "version": "3.0.7", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/git": "^3.0.0", - "@npmcli/run-script": "^4.1.3", - "json-parse-even-better-errors": "^2.3.1", - "proc-log": "^2.0.0", - "semver": "^7.3.7" - } - }, - "lru-cache": { - "version": "7.13.2", - "bundled": true, - "dev": true - }, - "make-fetch-happen": { - "version": "10.2.1", - "bundled": true, - "dev": true, - "requires": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - } - }, - "minimatch": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "minipass": { - "version": "3.3.4", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minipass-collect": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-fetch": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "encoding": "^0.1.13", - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - } - }, - "minipass-flush": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-json-stream": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "minipass-pipeline": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-sized": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "mkdirp-infer-owner": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" - } - }, - "ms": { - "version": "2.1.3", - "bundled": true, - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "negotiator": { - "version": "0.6.3", - "bundled": true, - "dev": true - }, - "node-gyp": { - "version": "9.1.0", - "bundled": true, - "dev": true, - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "nopt": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "abbrev": "1" - } - } - } - }, - "nopt": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "abbrev": "^1.0.0" - } - }, - "normalize-package-data": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - } - }, - "npm-audit-report": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "chalk": "^4.0.0" - } - }, - "npm-bundled": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "npm-normalize-package-bin": "^2.0.0" - }, - "dependencies": { - "npm-normalize-package-bin": { - "version": "2.0.0", - "bundled": true, - "dev": true - } - } - }, - "npm-install-checks": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "semver": "^7.1.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "npm-package-arg": { - "version": "9.1.0", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" - } - }, - "npm-packlist": { - "version": "5.1.3", - "bundled": true, - "dev": true, - "requires": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "dependencies": { - "npm-normalize-package-bin": { - "version": "2.0.0", - "bundled": true, - "dev": true - } - } - }, - "npm-pick-manifest": { - "version": "7.0.2", - "bundled": true, - "dev": true, - "requires": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^2.0.0", - "npm-package-arg": "^9.0.0", - "semver": "^7.3.5" - }, - "dependencies": { - "npm-normalize-package-bin": { - "version": "2.0.0", - "bundled": true, - "dev": true - } - } - }, - "npm-profile": { - "version": "6.2.1", - "bundled": true, - "dev": true, - "requires": { - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0" - } - }, - "npm-registry-fetch": { - "version": "13.3.1", - "bundled": true, - "dev": true, - "requires": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" - } - }, - "npm-user-validate": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "npmlog": { - "version": "6.0.2", - "bundled": true, - "dev": true, - "requires": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - } - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "opener": { - "version": "1.5.2", - "bundled": true, - "dev": true - }, - "p-map": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "pacote": { - "version": "13.6.2", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^4.1.0", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11" - } - }, - "parse-conflict-json": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", - "just-diff-apply": "^5.2.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "postcss-selector-parser": { - "version": "6.0.10", - "bundled": true, - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "proc-log": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "promise-all-reject-late": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "promise-call-limit": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "promise-retry": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - } - }, - "promzard": { - "version": "0.3.0", - "bundled": true, - "dev": true, - "requires": { - "read": "1" - } - }, - "qrcode-terminal": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "read": { - "version": "1.0.7", - "bundled": true, - "dev": true, - "requires": { - "mute-stream": "~0.0.4" - } - }, - "read-cmd-shim": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "read-package-json": { - "version": "5.0.2", - "bundled": true, - "dev": true, - "requires": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, - "dependencies": { - "npm-normalize-package-bin": { - "version": "2.0.0", - "bundled": true, - "dev": true - } - } - }, - "read-package-json-fast": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "retry": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.3" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "glob": { - "version": "7.2.3", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "safe-buffer": { - "version": "5.2.1", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "7.3.7", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "bundled": true, - "dev": true - }, - "smart-buffer": { - "version": "4.2.0", - "bundled": true, - "dev": true - }, - "socks": { - "version": "2.7.0", - "bundled": true, - "dev": true, - "requires": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - } - }, - "socks-proxy-agent": { - "version": "7.0.0", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - } - }, - "spdx-correct": { - "version": "3.1.1", - "bundled": true, - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "bundled": true, - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.11", - "bundled": true, - "dev": true - }, - "ssri": { - "version": "9.0.1", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.1.1" - } - }, - "string_decoder": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "4.2.3", - "bundled": true, - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "supports-color": { - "version": "7.2.0", - "bundled": true, - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tar": { - "version": "6.1.11", - "bundled": true, - "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - } - }, - "text-table": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "tiny-relative-date": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "treeverse": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "unique-filename": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "unique-slug": "^3.0.0" - } - }, - "unique-slug": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "builtins": "^5.0.0" - } - }, - "walk-up-path": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "wcwidth": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "which": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wide-align": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "write-file-atomic": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "yallist": { - "version": "4.0.0", - "bundled": true, - "dev": true - } - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "octokit-auth-probot": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/octokit-auth-probot/-/octokit-auth-probot-1.2.9.tgz", - "integrity": "sha512-mMjw6Y760EwJnW2tSVooJK8BMdsG6D40SoCclnefVf/5yWjaNVquEu8NREBVWb60OwbpnMEz4vREXHB5xdMFYQ==", - "requires": { - "@octokit/auth-app": "^4.0.2", - "@octokit/auth-token": "^3.0.0", - "@octokit/auth-unauthenticated": "^3.0.0", - "@octokit/types": "^8.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==" - }, - "@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "requires": { - "@octokit/openapi-types": "^14.0.0" - } - } - } - }, - "on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" - }, - "p-throttle": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/p-throttle/-/p-throttle-5.1.0.tgz", - "integrity": "sha512-+N+s2g01w1Zch4D0K3OpnPDqLOKmLcQ4BvIFq3JC0K29R28vUOjWpO+OJZBNt8X9i3pFCksZJZ0YXkUGjaFE6g==" - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "passport": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", - "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", - "requires": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - } - }, - "passport-github": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz", - "integrity": "sha512-XARXJycE6fFh/dxF+Uut8OjlwbFEXgbPVj/+V+K7cvriRK7VcAOm+NgBmbiLM9Qv3SSxEAV+V6fIk89nYHXa8A==", - "requires": { - "passport-oauth2": "1.x.x" - } - }, - "passport-gitlab2": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/passport-gitlab2/-/passport-gitlab2-5.0.0.tgz", - "integrity": "sha512-cXQMgM6JQx9wHVh7JLH30D8fplfwjsDwRz+zS0pqC8JS+4bNmc1J04NGp5g2M4yfwylH9kQRrMN98GxMw7q7cg==", - "requires": { - "passport-oauth2": "^1.4.0" - } - }, - "passport-google-oauth20": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", - "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", - "requires": { - "passport-oauth2": "1.x.x" - } - }, - "passport-oauth2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", - "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", - "requires": { - "base64url": "3.x.x", - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - } - }, - "passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, - "pg": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", - "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", - "requires": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-cloudflare": "^1.1.1", - "pg-connection-string": "^2.6.2", - "pg-pool": "^3.6.1", - "pg-protocol": "^1.6.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - } - }, - "pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", - "optional": true - }, - "pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" - }, - "pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" - }, - "pg-numeric": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", - "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", - "dev": true - }, - "pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", - "requires": {} - }, - "pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" - }, - "pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "requires": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - } - }, - "pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "requires": { - "split2": "^4.1.0" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, - "pino": { - "version": "8.16.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.1.tgz", - "integrity": "sha512-3bKsVhBmgPjGV9pyn4fO/8RtoVDR8ssW1ev819FsRXlRNgW8gR/9Kx+gCK4UPWd4JjrRDLWpzd/pb1AyWm3MGA==", - "requires": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "v1.1.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.7.0", - "thread-stream": "^2.0.0" - }, - "dependencies": { - "sonic-boom": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz", - "integrity": "sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==", - "requires": { - "atomic-sleep": "^1.0.0" - } - } - } - }, - "pino-abstract-transport": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", - "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", - "requires": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" - }, - "dependencies": { - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "readable-stream": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", - "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", - "requires": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - } - } - } - }, - "pino-http": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-8.5.1.tgz", - "integrity": "sha512-T/3d9YHKBYpv/QHjNy73P5BNYYkRrC2/D6CxKMecG4fKFLN+B2iC6LsKYzGRTRV+Ld3fjxFC1ca4TUGbPdzk+Q==", - "requires": { - "get-caller-file": "^2.0.5", - "pino": "^8.0.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0" - } - }, - "pino-pretty": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.2.3.tgz", - "integrity": "sha512-4jfIUc8TC1GPUfDyMSlW1STeORqkoxec71yhxIpLDQapUu8WOuoz2TTCoidrIssyz78LZC69whBMPIKCMbi3cw==", - "requires": { - "colorette": "^2.0.7", - "dateformat": "^4.6.3", - "fast-copy": "^3.0.0", - "fast-safe-stringify": "^2.1.1", - "help-me": "^4.0.1", - "joycon": "^3.1.1", - "minimist": "^1.2.6", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.0.0", - "pump": "^3.0.0", - "readable-stream": "^4.0.0", - "secure-json-parse": "^2.4.0", - "sonic-boom": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "readable-stream": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", - "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", - "requires": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - } - }, - "sonic-boom": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz", - "integrity": "sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==", - "requires": { - "atomic-sleep": "^1.0.0" - } - } - } - }, - "pino-std-serializers": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", - "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" - }, - "pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true - }, - "pkg-conf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", - "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", - "requires": { - "find-up": "^3.0.0", - "load-json-file": "^5.2.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" - } - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" - }, - "postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" - }, - "postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" - }, - "postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "requires": { - "xtend": "^4.0.0" - } - }, - "postgres-range": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz", - "integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==", - "dev": true - }, - "posthog-node": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.6.0.tgz", - "integrity": "sha512-/BiFw/jwdP0uJSRAIoYqLoBTjZ612xv74b1L/a3T/p1nJVL8e0OrHuxbJW56c6WVW/IKm9gBF/zhbqfaz0XgJQ==", - "requires": { - "axios": "^0.27.0" - }, - "dependencies": { - "axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "requires": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "pretty-format": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", - "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "probot": { - "version": "12.3.3", - "resolved": "https://registry.npmjs.org/probot/-/probot-12.3.3.tgz", - "integrity": "sha512-cdtKd+xISzi8sw6++BYBXleRknCA6hqUMoHj/sJqQBrjbNxQLhfeFCq9O2d0Z4eShsy5YFRR3MWwDKJ9uAE0CA==", - "requires": { - "@octokit/core": "^3.2.4", - "@octokit/plugin-enterprise-compatibility": "^1.2.8", - "@octokit/plugin-paginate-rest": "^2.6.2", - "@octokit/plugin-rest-endpoint-methods": "^5.0.1", - "@octokit/plugin-retry": "^3.0.6", - "@octokit/plugin-throttling": "^3.3.4", - "@octokit/types": "^8.0.0", - "@octokit/webhooks": "^9.26.3", - "@probot/get-private-key": "^1.1.0", - "@probot/octokit-plugin-config": "^1.0.0", - "@probot/pino": "^2.2.0", - "@types/express": "^4.17.9", - "@types/ioredis": "^4.27.1", - "@types/pino": "^6.3.4", - "@types/pino-http": "^5.0.6", - "commander": "^6.2.0", - "deepmerge": "^4.2.2", - "deprecation": "^2.3.1", - "dotenv": "^8.2.0", - "eventsource": "^2.0.2", - "express": "^4.17.1", - "express-handlebars": "^6.0.3", - "ioredis": "^4.27.8", - "js-yaml": "^3.14.1", - "lru-cache": "^6.0.0", - "octokit-auth-probot": "^1.2.2", - "pino": "^6.7.0", - "pino-http": "^5.3.0", - "pkg-conf": "^3.1.0", - "resolve": "^1.19.0", - "semver": "^7.3.4", - "update-dotenv": "^1.1.1", - "uuid": "^8.3.2" - }, - "dependencies": { - "@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "requires": { - "@octokit/types": "^6.0.3" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "requires": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "requires": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==" - }, - "@octokit/plugin-paginate-rest": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", - "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", - "requires": { - "@octokit/types": "^6.40.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", - "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", - "requires": { - "@octokit/types": "^6.39.0", - "deprecation": "^2.3.1" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/plugin-throttling": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-3.7.0.tgz", - "integrity": "sha512-qrKT1Yl/KuwGSC6/oHpLBot3ooC9rq0/ryDYBCpkRtoj+R8T47xTMDT6Tk2CxWopFota/8Pi/2SqArqwC0JPow==", - "requires": { - "@octokit/types": "^6.0.1", - "bottleneck": "^2.15.3" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - } - } - }, - "@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "requires": { - "@octokit/openapi-types": "^14.0.0" - } - }, - "@types/pino": { - "version": "6.3.12", - "resolved": "https://registry.npmjs.org/@types/pino/-/pino-6.3.12.tgz", - "integrity": "sha512-dsLRTq8/4UtVSpJgl9aeqHvbh6pzdmjYD3C092SYgLD2TyoCqHpTJk6vp8DvCTGGc7iowZ2MoiYiVUUCcu7muw==", - "requires": { - "@types/node": "*", - "@types/pino-pretty": "*", - "@types/pino-std-serializers": "*", - "sonic-boom": "^2.1.0" - }, - "dependencies": { - "sonic-boom": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", - "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", - "requires": { - "atomic-sleep": "^1.0.0" - } - } - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" - }, - "ioredis": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.5.tgz", - "integrity": "sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==", - "requires": { - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.1", - "denque": "^1.1.0", - "lodash.defaults": "^4.2.0", - "lodash.flatten": "^4.4.0", - "lodash.isarguments": "^3.1.0", - "p-map": "^2.1.0", - "redis-commands": "1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "pino": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-6.14.0.tgz", - "integrity": "sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==", - "requires": { - "fast-redact": "^3.0.0", - "fast-safe-stringify": "^2.0.8", - "flatstr": "^1.0.12", - "pino-std-serializers": "^3.1.0", - "process-warning": "^1.0.0", - "quick-format-unescaped": "^4.0.3", - "sonic-boom": "^1.0.2" - } - }, - "pino-http": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-5.8.0.tgz", - "integrity": "sha512-YwXiyRb9y0WCD1P9PcxuJuh3Dc5qmXde/paJE86UGYRdiFOi828hR9iUGmk5gaw6NBT9gLtKANOHFimvh19U5w==", - "requires": { - "fast-url-parser": "^1.1.3", - "pino": "^6.13.0", - "pino-std-serializers": "^4.0.0" - }, - "dependencies": { - "pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==" - } - } - }, - "pino-std-serializers": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz", - "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==" - }, - "process-warning": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", - "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" - }, - "sonic-boom": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", - "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", - "requires": { - "atomic-sleep": "^1.0.0", - "flatstr": "^1.0.12" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" - }, - "process-warning": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.0.tgz", - "integrity": "sha512-N6mp1+2jpQr3oCFMz6SeHRGbv6Slb20bRhj4v3xR99HqNToAcOe1MFOp4tytyzOfJn+QtN8Rf7U/h2KAn4kC6g==" - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" - }, - "pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", - "dev": true - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "query-string": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", - "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", - "requires": { - "decode-uri-component": "^0.2.2", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "rate-limit-mongo": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/rate-limit-mongo/-/rate-limit-mongo-2.3.2.tgz", - "integrity": "sha512-dLck0j5N/AX9ycVHn5lX9Ti2Wrrwi1LfbXitu/mMBZOo2nC26RgYKJVbcb2mYgb9VMaPI2IwJVzIa2hAQrMaDA==", - "requires": { - "mongodb": "5.8.0", - "twostep": "0.4.2", - "underscore": "1.12.1" - }, - "dependencies": { - "mongodb": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.8.0.tgz", - "integrity": "sha512-xx4CXmxcj3bNe7iGBlhntVrUqrNARYhUZteXaz4epEESv4oXD/FONAovcyoCaEffdYlw25Yz284OxMfpnPLlgQ==", - "requires": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^5.4.0", - "mongodb-connection-string-url": "^2.6.0", - "socks": "^2.7.1" - } - } - } - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" - }, - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "requires": { - "redis-errors": "^1.0.0" - } - }, - "regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" - }, - "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "requires": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" - }, - "secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "seq-queue": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", - "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "sift": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", - "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", - "dev": true, - "requires": { - "semver": "~7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" - }, - "smee-client": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/smee-client/-/smee-client-1.2.3.tgz", - "integrity": "sha512-uDrU8u9/Ln7aRXyzGHgVaNUS8onHZZeSwQjCdkMoSL7U85xI+l+Y2NgjibkMJAyXkW7IAbb8rw9RMHIjS6lAwA==", - "dev": true, - "requires": { - "commander": "^2.19.0", - "eventsource": "^1.1.0", - "morgan": "^1.9.1", - "superagent": "^7.1.3", - "validator": "^13.7.0" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "eventsource": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.2.tgz", - "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==", - "dev": true - } - } - }, - "snappy": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/snappy/-/snappy-7.2.2.tgz", - "integrity": "sha512-iADMq1kY0v3vJmGTuKcFWSXt15qYUz7wFkArOrsSg0IFfI3nJqIJvK2/ZbEIndg7erIJLtAVX2nSOqPz7DcwbA==", - "optional": true, - "peer": true, - "requires": { - "@napi-rs/snappy-android-arm-eabi": "7.2.2", - "@napi-rs/snappy-android-arm64": "7.2.2", - "@napi-rs/snappy-darwin-arm64": "7.2.2", - "@napi-rs/snappy-darwin-x64": "7.2.2", - "@napi-rs/snappy-freebsd-x64": "7.2.2", - "@napi-rs/snappy-linux-arm-gnueabihf": "7.2.2", - "@napi-rs/snappy-linux-arm64-gnu": "7.2.2", - "@napi-rs/snappy-linux-arm64-musl": "7.2.2", - "@napi-rs/snappy-linux-x64-gnu": "7.2.2", - "@napi-rs/snappy-linux-x64-musl": "7.2.2", - "@napi-rs/snappy-win32-arm64-msvc": "7.2.2", - "@napi-rs/snappy-win32-ia32-msvc": "7.2.2", - "@napi-rs/snappy-win32-x64-msvc": "7.2.2" - } - }, - "socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "requires": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - } - }, - "sonic-boom": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", - "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", - "requires": { - "atomic-sleep": "^1.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "optional": true, - "requires": { - "memory-pager": "^1.0.2" - } - }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" - }, - "split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "stoppable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==" - }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" - }, - "strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" - }, - "superagent": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.5.tgz", - "integrity": "sha512-HQYyGuDRFGmZ6GNC4hq2f37KnsY9Lr0/R1marNZTgMweVDQLTLJJ6DGQ9Tj/xVVs5HEnop9EMmTbywb5P30aqw==", - "dev": true, - "requires": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.0.1", - "methods": "^1.1.2", - "mime": "^2.5.0", - "qs": "^6.10.3", - "readable-stream": "^3.6.0", - "semver": "^7.3.7" - }, - "dependencies": { - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - } - } - }, - "supertest": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", - "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", - "dev": true, - "requires": { - "methods": "^1.1.2", - "superagent": "^8.0.5" - }, - "dependencies": { - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - }, - "superagent": { - "version": "8.0.9", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", - "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", - "dev": true, - "requires": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - } - } - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "swagger-autogen": { - "version": "2.23.5", - "resolved": "https://registry.npmjs.org/swagger-autogen/-/swagger-autogen-2.23.5.tgz", - "integrity": "sha512-4Tl2+XhZMyHoBYkABnScHtQE0lKPKUD3NBt09mClrI6UKOUYljKlYw1xiFVwsHCTGR2hAXmhT4PpgjruCtt1ZA==", - "dev": true, - "requires": { - "acorn": "^7.4.1", - "deepmerge": "^4.2.2", - "glob": "^7.1.7", - "json5": "^2.2.3" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } - } - }, - "swagger-ui-dist": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.1.3.tgz", - "integrity": "sha512-W/vZFeZHG+xTN4yu8LXdaIrcnT4Hbr7qRUILYlMEoIiG6nuTylnEGeRcvL64F2eHRA2Jo/fgCSTU06Qfh0lT3g==" - }, - "swagger-ui-express": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.3.tgz", - "integrity": "sha512-CDje4PndhTD2HkgyKH3pab+LKspDeB/NhPN2OF1j+piYIamQqBYwAXWESOT1Yju2xFg51bRW9sUng2WxDjzArw==", - "requires": { - "swagger-ui-dist": ">=4.11.0" - } - }, - "tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "dependencies": { - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "thread-stream": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", - "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", - "requires": { - "real-require": "^0.2.0" - } - }, - "tiny-lru": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.0.1.tgz", - "integrity": "sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg==", - "dev": true - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "~1.0.10" - }, - "dependencies": { - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "requires": { - "abbrev": "1" - } - } - } - }, - "tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "requires": { - "punycode": "^2.1.1" - }, - "dependencies": { - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" - } - } - }, - "ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - } - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "tweetnacl-util": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" - }, - "twostep": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/twostep/-/twostep-0.4.2.tgz", - "integrity": "sha512-O/wdPYk9ey04qcCiw8AQN74DbvLFZLAgnryrNTpV7T/sxB4lcGkCMHynx5xCcA6fCh739ZAqp3HcGhy770X1qA==" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" - }, - "uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "optional": true - }, - "uid2": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", - "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" - }, - "undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, - "underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" - }, - "universal-github-app-jwt": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.1.tgz", - "integrity": "sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==", - "requires": { - "@types/jsonwebtoken": "^9.0.0", - "jsonwebtoken": "^9.0.0" - }, - "dependencies": { - "@types/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==", - "requires": { - "@types/node": "*" - } - } - } - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "update-dotenv": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-dotenv/-/update-dotenv-1.1.1.tgz", - "integrity": "sha512-3cIC18In/t0X/yH793c00qqxcKD8jVCgNOPif/fGQkFpYMGecM9YAc+kaAKXuZsM2dE9I9wFI7KvAuNX22SGMQ==", - "requires": {} - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" - } - } - }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" - } - } - }, - "util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "requires": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - } - } - }, - "validator": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "requires": { - "defaults": "^1.0.3" - } - }, - "webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" - }, - "whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "requires": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", - "dev": true - }, - "xml-crypto": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.0.tgz", - "integrity": "sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg==", - "requires": { - "@xmldom/xmldom": "^0.8.8", - "xpath": "0.0.32" - }, - "dependencies": { - "xpath": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz", - "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==" - } - } - }, - "xml-encryption": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-3.0.2.tgz", - "integrity": "sha512-VxYXPvsWB01/aqVLd6ZMPWZ+qaj0aIdF+cStrVJMcFj3iymwZeI0ABzB3VqMYv48DkSpRhnrXqTUkR34j+UDyg==", - "requires": { - "@xmldom/xmldom": "^0.8.5", - "escape-html": "^1.0.3", - "xpath": "0.0.32" - }, - "dependencies": { - "xpath": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz", - "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==" - } - } - }, - "xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "dependencies": { - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" - } - } - }, - "xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==" - }, - "xpath": { - "version": "0.0.27", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz", - "integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - }, - "zod": { - "version": "3.22.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", - "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==" - } - } -} diff --git a/backend-mongo/package.json b/backend-mongo/package.json deleted file mode 100644 index 395696272..000000000 --- a/backend-mongo/package.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "dependencies": { - "@aws-sdk/client-secrets-manager": "^3.319.0", - "@casl/ability": "^6.5.0", - "@casl/mongoose": "^7.2.1", - "@godaddy/terminus": "^4.12.0", - "@node-saml/passport-saml": "^4.0.4", - "@octokit/rest": "^19.0.5", - "@sentry/node": "^7.77.0", - "@sentry/tracing": "^7.48.0", - "@serdnam/pino-cloudwatch-transport": "^1.0.4", - "@types/crypto-js": "^4.1.1", - "@types/libsodium-wrappers": "^0.7.10", - "@ucast/mongo2js": "^1.3.4", - "ajv": "^8.12.0", - "argon2": "^0.30.3", - "aws-sdk": "^2.1364.0", - "axios": "^1.6.0", - "axios-retry": "^3.4.0", - "bcrypt": "^5.1.0", - "bigint-conversion": "^2.4.0", - "cookie-parser": "^1.4.6", - "cors": "^2.8.5", - "crypto-js": "^4.2.0", - "dotenv": "^16.0.1", - "express": "^4.18.1", - "express-async-errors": "^3.1.1", - "express-rate-limit": "^6.7.0", - "express-validator": "^6.14.2", - "handlebars": "^4.7.7", - "helmet": "^5.1.1", - "infisical-node": "^1.2.1", - "ioredis": "^5.3.2", - "jmespath": "^0.16.0", - "js-yaml": "^4.1.0", - "jsonwebtoken": "^9.0.0", - "jsrp": "^0.2.4", - "libsodium-wrappers": "^0.7.10", - "lodash": "^4.17.21", - "mongoose": "^7.4.1", - "mysql2": "^3.6.2", - "nanoid": "^3.3.6", - "node-cache": "^5.1.2", - "nodemailer": "^6.8.0", - "ora": "^5.4.1", - "passport": "^0.6.0", - "passport-github": "^1.1.0", - "passport-gitlab2": "^5.0.0", - "passport-google-oauth20": "^2.0.0", - "pg": "^8.11.3", - "pino": "^8.16.1", - "pino-http": "^8.5.1", - "posthog-node": "^2.6.0", - "probot": "^12.3.3", - "query-string": "^7.1.3", - "rate-limit-mongo": "^2.3.2", - "rimraf": "^3.0.2", - "swagger-ui-express": "^4.6.2", - "tweetnacl": "^1.0.3", - "tweetnacl-util": "^0.15.1", - "typescript": "^4.9.3", - "utility-types": "^3.10.0", - "zod": "^3.22.3" - }, - "overrides": { - "rate-limit-mongo": { - "mongodb": "5.8.0" - } - }, - "name": "infisical-api", - "version": "1.0.0", - "main": "src/index.js", - "scripts": { - "start": "node build/index.js", - "dev": "nodemon index.js", - "swagger-autogen": "node ./swagger/index.ts", - "build": "rimraf ./build && tsc && cp -R ./src/templates ./build && cp -R ./src/data ./build", - "lint": "eslint . --ext .ts", - "lint-and-fix": "eslint . --ext .ts --fix", - "lint-staged": "lint-staged", - "pretest": "docker compose -f test-resources/docker-compose.test.yml up -d", - "test": "cross-env NODE_ENV=test jest --verbose --testTimeout=10000 --detectOpenHandles; npm run posttest", - "test:ci": "npm test -- --watchAll=false --ci --reporters=default --reporters=jest-junit --reporters=github-actions --coverage --testLocationInResults --json --outputFile=coverage/report.json", - "posttest": "docker compose -f test-resources/docker-compose.test.yml down" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Infisical/infisical-api.git" - }, - "author": "", - "license": "ISC", - "bugs": { - "url": "https://github.com/Infisical/infisical-api/issues" - }, - "homepage": "https://github.com/Infisical/infisical-api#readme", - "description": "", - "devDependencies": { - "@jest/globals": "^29.3.1", - "@posthog/plugin-scaffold": "^1.3.4", - "@swc/core": "^1.3.99", - "@swc/helpers": "^0.5.3", - "@types/bcrypt": "^5.0.0", - "@types/bcryptjs": "^2.4.2", - "@types/bull": "^4.10.0", - "@types/cookie-parser": "^1.4.3", - "@types/cors": "^2.8.12", - "@types/express": "^4.17.14", - "@types/jest": "^29.5.0", - "@types/jmespath": "^0.15.1", - "@types/jsonwebtoken": "^8.5.9", - "@types/lodash": "^4.14.191", - "@types/node": "^18.11.3", - "@types/nodemailer": "^6.4.6", - "@types/passport": "^1.0.12", - "@types/pg": "^8.10.7", - "@types/picomatch": "^2.3.0", - "@types/pino": "^7.0.5", - "@types/supertest": "^2.0.12", - "@types/swagger-jsdoc": "^6.0.1", - "@types/swagger-ui-express": "^4.1.3", - "@typescript-eslint/eslint-plugin": "^5.54.0", - "@typescript-eslint/parser": "^5.40.1", - "cross-env": "^7.0.3", - "eslint": "^8.26.0", - "eslint-plugin-unused-imports": "^2.0.0", - "install": "^0.13.0", - "jest": "^29.3.1", - "jest-junit": "^15.0.0", - "nodemon": "^2.0.19", - "npm": "^8.19.3", - "pino-pretty": "^10.2.3", - "regenerator-runtime": "^0.14.0", - "smee-client": "^1.2.3", - "supertest": "^6.3.3", - "swagger-autogen": "^2.23.5", - "ts-jest": "^29.0.3", - "ts-node": "^10.9.1" - }, - "jest-junit": { - "outputDirectory": "reports", - "outputName": "jest-junit.xml", - "ancestorSeparator": " โ€บ ", - "uniqueOutputName": "false", - "suiteNameTemplate": "{filepath}", - "classNameTemplate": "{classname}", - "titleTemplate": "{title}" - } -} diff --git a/backend-mongo/spec.json b/backend-mongo/spec.json deleted file mode 100644 index e5ecb5df0..000000000 --- a/backend-mongo/spec.json +++ /dev/null @@ -1,8047 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Infisical API", - "description": "List of all available APIs that can be consumed", - "version": "1.0.0" - }, - "servers": [ - { - "url": "https://app.infisical.com", - "description": "Production server" - }, - { - "url": "http://localhost:8080", - "description": "Local server" - } - ], - "paths": { - "/api/v1/identities/": { - "post": { - "summary": "Create identity", - "description": "Create identity", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identity": { - "$ref": "#/components/schemas/Identity" - } - }, - "description": "Details of the created identity" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of entity to create", - "example": "development" - }, - "organizationId": { - "type": "string", - "description": "ID of organization where to create identity", - "example": "dev-environment" - }, - "role": { - "type": "string", - "description": "Role to assume for organization membership", - "example": "no-access" - } - }, - "required": [ - "name", - "organizationId", - "role" - ] - } - } - } - } - } - }, - "/api/v1/identities/{identityId}": { - "patch": { - "summary": "Update identity", - "description": "Update identity", - "parameters": [ - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity to update" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identity": { - "$ref": "#/components/schemas/Identity" - } - }, - "description": "Details of the updated identity" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of entity to update to", - "example": "development" - }, - "role": { - "type": "string", - "description": "Role to update to for organization membership", - "example": "no-access" - } - } - } - } - } - } - }, - "delete": { - "summary": "Delete identity", - "description": "Delete identity", - "parameters": [ - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identity": { - "$ref": "#/components/schemas/Identity" - } - }, - "description": "Details of the deleted identity" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/api/v1/secret/{secretId}/secret-versions": { - "get": { - "summary": "Return secret versions", - "description": "Return secret versions", - "parameters": [ - { - "name": "secretId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of secret" - }, - { - "name": "offset", - "description": "Number of versions to skip", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "limit", - "description": "Maximum number of versions to return", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretVersions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SecretVersion" - }, - "description": "Secret versions" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ] - } - }, - "/api/v1/secret/{secretId}/secret-versions/rollback": { - "post": { - "summary": "Roll back secret to a version.", - "description": "Roll back secret to a version.", - "parameters": [ - { - "name": "secretId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of secret" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secret": { - "type": "object", - "$ref": "#/components/schemas/Secret", - "description": "Secret rolled back to" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "version": { - "type": "integer", - "description": "Version of secret to roll back to" - } - } - } - } - } - } - } - }, - "/api/v1/secret-snapshot/{secretSnapshotId}": { - "get": { - "description": "", - "parameters": [ - { - "name": "secretSnapshotId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/users/me/ip": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/secret-snapshots": { - "get": { - "summary": "Return project secret snapshot ids", - "description": "Return project secret snapshots ids", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project where to get secret snapshots for" - }, - { - "name": "environment", - "description": "Slug of environment where to get secret snapshots for", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "directory", - "description": "Path where to get secret snapshots for like / or /foo/bar. Default is /", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "offset", - "description": "Number of secret snapshots to skip", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "limit", - "description": "Maximum number of secret snapshots to return", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretSnapshots": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SecretSnapshot" - }, - "description": "Project secret snapshots" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ] - } - }, - "/api/v1/workspace/{workspaceId}/secret-snapshots/count": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/secret-snapshots/rollback": { - "post": { - "summary": "Roll back project secrets to those captured in a secret snapshot version.", - "description": "Roll back project secrets to those captured in a secret snapshot version.", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project where to roll back" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Secret" - }, - "description": "Secrets rolled back to" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "environment": { - "type": "string", - "description": "Slug of environment where to roll back" - }, - "directory": { - "type": "string", - "description": "Path where to roll back for like / or /foo/bar. Default is /" - }, - "version": { - "type": "integer", - "description": "Version of secret snapshot to roll back to" - } - } - } - } - } - } - } - }, - "/api/v1/workspace/{workspaceId}/audit-logs": { - "get": { - "summary": "Return audit logs", - "description": "Return audit logs", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of the workspace where to get folders from" - }, - { - "name": "offset", - "description": "Number of logs to skip before starting to return logs for pagination", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "limit", - "description": "Maximum number of logs to return for pagination", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "startDate", - "description": "Filter logs from this date in ISO-8601 format", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "endDate", - "description": "Filter logs till this date in ISO-8601 format", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "eventType", - "description": "Filter by type of event such as get-secrets, get-secret, create-secret, update-secret, delete-secret, etc.", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "userAgentType", - "description": "Filter by type of user agent such as web, cli, k8-operator, or other", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "actor", - "description": "Filter by actor such as user or service", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "auditLogs": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AuditLog" - }, - "description": "List of audit log" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ] - } - }, - "/api/v1/workspace/{workspaceId}/audit-logs/filters/actors": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/trusted-ips": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/trusted-ips/{trustedIpId}": { - "patch": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "trustedIpId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "trustedIpId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/organizations/{organizationId}/plans/table": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/plan": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/session/trial": { - "post": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/plan/billing": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/plan/table": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/billing-details": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "patch": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/billing-details/payment-methods": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/billing-details/payment-methods/{pmtMethodId}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "pmtMethodId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/billing-details/tax-ids": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/billing-details/tax-ids/{taxId}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "taxId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/invoices": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organizations/{organizationId}/licenses": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/sso/redirect/saml2/{ssoIdentifier}": { - "get": { - "description": "", - "parameters": [ - { - "name": "ssoIdentifier", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "callback_port", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "default": { - "description": "" - } - } - } - }, - "/api/v1/sso/saml2/{ssoIdentifier}": { - "post": { - "description": "", - "parameters": [ - { - "name": "ssoIdentifier", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "default": { - "description": "" - } - } - } - }, - "/api/v1/sso/config": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - }, - "patch": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/cloud-products/": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/api-key/": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/api-key/{apiKeyDataId}": { - "patch": { - "description": "", - "parameters": [ - { - "name": "apiKeyDataId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "apiKeyDataId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-rotation-providers/{workspaceId}": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-rotations/": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-rotations/restart": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-rotations/{id}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/signup/email/signup": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "403": { - "description": "Forbidden" - } - } - } - }, - "/api/v1/signup/email/verify": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "403": { - "description": "Forbidden" - } - } - } - }, - "/api/v1/auth/token": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/auth/login1": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/auth/login2": { - "post": { - "description": "", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/auth/logout": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/auth/checkAuth": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/auth/sessions": { - "delete": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/auth/token/renew": { - "post": { - "summary": "Renew access token", - "description": "Renew access token", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "accessToken": { - "type": "string", - "description": "(Same) Access token after successful renewal" - }, - "expiresIn": { - "type": "number", - "description": "TTL of access token in seconds" - }, - "tokenType": { - "type": "string", - "description": "Type of access token (e.g. Bearer)" - } - }, - "description": "Access token and its details" - } - } - } - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "accessToken": { - "type": "string", - "description": "Access token to renew", - "example": "..." - } - } - } - } - } - } - } - }, - "/api/v1/auth/universal-auth/login": { - "post": { - "summary": "Login with Universal Auth", - "description": "Login with Universal Auth", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "accessToken": { - "type": "string", - "description": "Access token issued after successful login" - }, - "expiresIn": { - "type": "number", - "description": "TTL of access token in seconds" - }, - "tokenType": { - "type": "string", - "description": "Type of access token (e.g. Bearer)" - } - }, - "description": "Access token and its details" - } - } - } - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "description": "Client ID for identity to login with Universal Auth", - "example": "..." - }, - "clientSecret": { - "type": "string", - "description": "Client Secret for identity to login with Universal Auth", - "example": "..." - } - } - } - } - } - } - } - }, - "/api/v1/auth/universal-auth/identities/{identityId}": { - "post": { - "summary": "Attach Universal Auth configuration onto identity", - "description": "Attach Universal Auth configuration onto identity", - "parameters": [ - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity to attach Universal Auth onto" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityUniversalAuth": { - "$ref": "#/components/schemas/IdentityUniversalAuth" - } - }, - "description": "Details of attached Universal Auth" - } - } - } - }, - "400": { - "description": "Bad Request" - } - }, - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecretTrustedIps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipAddress": { - "type": "string", - "description": "IP address to trust", - "default": "0.0.0.0/0" - } - } - }, - "description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", - "example": "...", - "default": [ - { - "ipAddress": "0.0.0.0/0" - } - ] - }, - "accessTokenTTL": { - "type": "number", - "description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.", - "example": "...", - "default": 100 - }, - "accessTokenMaxTTL": { - "type": "number", - "description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.", - "example": "...", - "default": 2592000 - }, - "accessTokenNumUsesLimit": { - "type": "number", - "description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.", - "example": "...", - "default": 0 - }, - "accessTokenTrustedIps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipAddress": { - "type": "string", - "description": "IP address to trust", - "default": "0.0.0.0/0" - } - } - }, - "description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", - "example": "...", - "default": [ - { - "ipAddress": "0.0.0.0/0" - } - ] - } - } - } - } - } - } - }, - "patch": { - "summary": "Update Universal Auth configuration on identity", - "description": "Update Universal Auth configuration on identity", - "parameters": [ - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity to update Universal Auth on" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityUniversalAuth": { - "$ref": "#/components/schemas/IdentityUniversalAuth" - } - }, - "description": "Details of updated Universal Auth" - } - } - } - }, - "400": { - "description": "Bad Request" - } - }, - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecretTrustedIps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipAddress": { - "type": "string", - "description": "IP address to trust" - } - } - }, - "description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", - "example": "..." - }, - "accessTokenTTL": { - "type": "number", - "description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.", - "example": "..." - }, - "accessTokenMaxTTL": { - "type": "number", - "description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.", - "example": "..." - }, - "accessTokenNumUsesLimit": { - "type": "number", - "description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.", - "example": "..." - }, - "accessTokenTrustedIps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipAddress": { - "type": "string", - "description": "IP address to trust" - } - } - }, - "description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", - "example": "..." - } - } - } - } - } - } - }, - "get": { - "summary": "Retrieve Universal Auth configuration on identity", - "description": "Retrieve Universal Auth configuration on identity", - "parameters": [ - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity to retrieve Universal Auth on" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityUniversalAuth": { - "$ref": "#/components/schemas/IdentityUniversalAuth" - } - }, - "description": "Details of retrieved Universal Auth" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/api/v1/auth/universal-auth/identities/{identityId}/client-secrets": { - "post": { - "summary": "Create Universal Auth Client Secret for identity", - "description": "Create Universal Auth Client Secret for identity", - "parameters": [ - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity to create Universal Auth Client Secret for" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecret": { - "type": "string", - "description": "The created Client Secret" - }, - "clientSecretData": { - "$ref": "#/components/schemas/IdentityUniversalAuthClientSecretData" - } - }, - "description": "Details of the created Client Secret" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "description": { - "type": "string", - "description": "A description for the Client Secret to create.", - "example": "..." - }, - "ttl": { - "type": "number", - "description": "The time-to-live for the Client Secret to create. By default, the TTL will be set to 0 which implies that the Client Secret will never expire; a value of 0 implies an infinite lifetime.", - "example": "...", - "default": 0 - }, - "numUsesLimit": { - "type": "number", - "description": "The maximum number of times that the Client Secret can be used together with the Client ID to get back an access token; a value of 0 implies infinite number of uses.", - "example": "...", - "default": 0 - } - } - } - } - } - } - }, - "get": { - "summary": "List Universal Auth Client Secrets for identity", - "description": "List Universal Auth Client Secrets for identity", - "parameters": [ - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity for which to get Client Secrets for" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecretData": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IdentityUniversalAuthClientSecretData" - } - } - }, - "description": "Details of the Client Secrets" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/api/v1/auth/universal-auth/identities/{identityId}/client-secrets/{clientSecretId}/revoke": { - "post": { - "summary": "Revoke Universal Auth Client Secret for identity", - "description": "Revoke Universal Auth Client Secret for identity", - "parameters": [ - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity under which Client Secret was issued for" - }, - { - "name": "clientSecretId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of Client Secret to revoke" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecretData": { - "$ref": "#/components/schemas/IdentityUniversalAuthClientSecretData" - } - }, - "description": "Details of the revoked Client Secret" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/api/v1/admin/config": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "patch": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/admin/signup": { - "post": { - "description": "", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/bot/{workspaceId}": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/bot/{botId}/active": { - "patch": { - "description": "", - "parameters": [ - { - "name": "botId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/user/": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/user-action/": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organization/": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organization/{organizationId}": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organization/{organizationId}/users": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organization/{organizationId}/my-workspaces": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organization/{organizationId}/name": { - "patch": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organization/{organizationId}/incidentContactOrg": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organization/{organizationId}/customer-portal-session": { - "post": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/organization/{organizationId}/workspace-memberships": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/keys": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/users": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/workspace/{workspaceId}": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/name": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/invite-signup": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/integrations": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/authorizations": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/workspace/{workspaceId}/service-tokens": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/membership-org/membershipOrg/{membershipOrgId}/change-role": { - "post": { - "description": "", - "parameters": [ - { - "name": "membershipOrgId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/membership-org/{membershipOrgId}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "membershipOrgId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "default": { - "description": "" - } - } - } - }, - "/api/v1/membership/{workspaceId}/connect": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/membership/{membershipId}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "membershipId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/membership/{membershipId}/change-role": { - "post": { - "description": "", - "parameters": [ - { - "name": "membershipId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/key/{workspaceId}": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/key/{workspaceId}/latest": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/invite-org/signup": { - "post": { - "description": "", - "parameters": [ - { - "name": "host", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/invite-org/verify": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret/{workspaceId}": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "example": "any" - }, - "keys": { - "example": "any" - }, - "environment": { - "example": "any" - }, - "channel": { - "example": "any" - } - } - } - } - } - } - }, - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "channel", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret/{workspaceId}/service-token": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "channel", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/service-token/": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "example": "any" - }, - "workspaceId": { - "example": "any" - }, - "environment": { - "example": "any" - }, - "expiresIn": { - "example": "any" - }, - "publicKey": { - "example": "any" - }, - "encryptedKey": { - "example": "any" - }, - "nonce": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v1/password/srp1": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/password/change-password": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/password/email/password-reset": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/password/email/password-reset-verify": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "403": { - "description": "Forbidden" - } - } - } - }, - "/api/v1/password/backup-private-key": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/password/password-reset": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration/": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration/{integrationId}": { - "patch": { - "description": "", - "parameters": [ - { - "name": "integrationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "integrationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration/manual-sync": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/integration-options": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/integration-auth/oauth-token": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/access-token": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/apps": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/teams": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/vercel/branches": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/checkly/groups": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/qovery/orgs": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/qovery/projects": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/qovery/environments": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/qovery/apps": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/qovery/containers": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/qovery/jobs": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/railway/environments": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/railway/services": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/bitbucket/workspaces": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/northflank/secret-groups": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/integration-auth/{integrationAuthId}/teamcity/build-configs": { - "get": { - "description": "", - "parameters": [ - { - "name": "integrationAuthId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/folders/": { - "post": { - "summary": "Create folder", - "description": "Create folder", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "folder": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "ID of folder", - "example": "someFolderId" - }, - "name": { - "type": "string", - "description": "Name of folder", - "example": "my_folder" - }, - "version": { - "type": "number", - "description": "Version of folder", - "example": 1 - } - }, - "description": "Details of created folder" - } - } - } - } - } - }, - "400": { - "description": "Bad Request. For example, 'Folder name cannot contain spaces. Only underscore and dashes'" - }, - "401": { - "description": "Unauthorized request. For example, 'Folder Permission Denied'" - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of the workspace where to create folder", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to create folder", - "example": "production" - }, - "folderName": { - "type": "string", - "description": "Name of folder to create", - "example": "my_folder" - }, - "directory": { - "type": "string", - "description": "Path where to create folder like / or /foo/bar. Default is /", - "example": "/foo/bar" - } - }, - "required": [ - "workspaceId", - "environment", - "folderName" - ] - } - } - } - } - }, - "get": { - "summary": "Get folders", - "description": "Get folders", - "parameters": [ - { - "name": "workspaceId", - "description": "ID of the workspace where to get folders from", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "description": "Slug of environment where to get folders from", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "directory", - "description": "Path where to get fodlers from like / or /foo/bar. Default is /", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "folders": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "someFolderId" - }, - "name": { - "type": "string", - "example": "someFolderName" - } - } - }, - "description": "List of folders" - } - } - } - } - } - }, - "400": { - "description": "Bad Request. For instance, 'The folder doesn't exist'" - }, - "401": { - "description": "Unauthorized request. For example, 'Folder Permission Denied'" - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ] - } - }, - "/api/v1/folders/{folderName}": { - "patch": { - "summary": "Update folder", - "description": "Update folder", - "parameters": [ - { - "name": "folderName", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "Name of folder to update" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Success message", - "example": "Successfully updated folder" - }, - "folder": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of updated folder", - "example": "updated_folder_name" - }, - "id": { - "type": "string", - "description": "ID of created folder", - "example": "abc123" - } - }, - "description": "Details of the updated folder" - } - } - } - } - } - }, - "400": { - "description": "Bad Request. Reasons can include 'The folder doesn't exist' or 'Folder name cannot contain spaces. Only underscore and dashes'" - }, - "401": { - "description": "Unauthorized request. For example, 'Folder Permission Denied'" - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of workspace where to update folder", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to update folder", - "example": "production" - }, - "name": { - "type": "string", - "description": "Name of folder to update to", - "example": "updated_folder_name" - }, - "directory": { - "type": "string", - "description": "Path where to update folder like / or /foo/bar. Default is /", - "example": "/foo/bar" - } - }, - "required": [ - "workspaceId", - "environment", - "name" - ] - } - } - } - } - }, - "delete": { - "summary": "Delete folder", - "description": "Delete folder", - "parameters": [ - { - "name": "folderName", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "Name of folder to delete" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Success message", - "example": "successfully deleted folders" - }, - "folders": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "ID of deleted folder", - "example": "abc123" - }, - "name": { - "type": "string", - "description": "Name of deleted folder", - "example": "someFolderName" - } - } - }, - "description": "List of IDs and names of deleted folders" - } - } - } - } - } - }, - "400": { - "description": "Bad Request. Reasons can include 'The folder doesn't exist'" - }, - "401": { - "description": "Unauthorized request. For example, 'Folder Permission Denied'" - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of the workspace where to delete folder", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to delete folder", - "example": "production" - }, - "directory": { - "type": "string", - "description": "Path where to delete folder like / or /foo/bar. Default is /", - "example": "/foo/bar" - } - }, - "required": [ - "workspaceId", - "environment" - ] - } - } - } - } - } - }, - "/api/v1/secret-scanning/create-installation-session/organization/{organizationId}": { - "post": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-scanning/link-installation": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-scanning/installation-status/organization/{organizationId}": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-scanning/organization/{organizationId}/risks": { - "get": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-scanning/organization/{organizationId}/risks/{riskId}/status": { - "post": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "riskId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/webhooks/": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/webhooks/{webhookId}": { - "patch": { - "description": "", - "parameters": [ - { - "name": "webhookId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "webhookId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/webhooks/{webhookId}/test": { - "post": { - "description": "", - "parameters": [ - { - "name": "webhookId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v1/secret-imports/": { - "post": { - "summary": "Create secret import", - "description": "Create secret import", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "successfully created secret import" - } - }, - "description": "Confirmation of secret import creation" - } - } - } - }, - "400": { - "description": "Bad Request. For example, 'Secret import already exist'" - }, - "401": { - "description": "Unauthorized request. For example, 'Folder Permission Denied'" - }, - "404": { - "description": "Resource Not Found. For example, 'Failed to find folder'" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of workspace where to create secret import", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to create secret import", - "example": "dev" - }, - "directory": { - "type": "string", - "description": "Path where to create secret import like / or /foo/bar. Default is /", - "example": "/foo/bar" - }, - "secretImport": { - "type": "object", - "properties": { - "environment": { - "type": "string", - "description": "Slug of environment to import from", - "example": "development" - }, - "secretPath": { - "type": "string", - "description": "Path where to import from like / or /foo/bar.", - "example": "/user/oauth" - } - } - } - }, - "required": [ - "workspaceId", - "environment", - "directory", - "secretImport" - ] - } - } - } - } - }, - "get": { - "summary": "Get secret imports", - "description": "Get secret imports", - "parameters": [ - { - "name": "workspaceId", - "in": "query", - "description": "ID of workspace where to get secret imports from", - "required": true, - "example": "workspace12345", - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "in": "query", - "description": "Slug of environment where to get secret imports from", - "required": true, - "example": "production", - "schema": { - "type": "string" - } - }, - { - "name": "directory", - "in": "query", - "description": "Path where to get secret imports from like / or /foo/bar. Default is /", - "required": false, - "example": "folder12345", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully retrieved secret import", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretImport": { - "$ref": "#/components/schemas/SecretImport" - } - } - } - } - } - }, - "401": { - "description": "Unauthorized access due to invalid token or scope" - }, - "403": { - "description": "Forbidden access due to insufficient permissions" - } - } - } - }, - "/api/v1/secret-imports/{id}": { - "put": { - "summary": "Update secret import", - "description": "Update secret import", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of secret import to update", - "example": "import12345" - } - ], - "responses": { - "200": { - "description": "Successfully updated the secret import", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "successfully updated secret import" - } - } - } - } - } - }, - "400": { - "description": "Bad Request - Import not found" - }, - "401": { - "description": "Unauthorized access due to invalid token or scope" - }, - "403": { - "description": "Forbidden access due to insufficient permissions" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretImports": { - "type": "array", - "description": "List of secret imports to update to", - "items": { - "type": "object", - "properties": { - "environment": { - "type": "string", - "description": "Slug of environment to import from", - "example": "dev" - }, - "secretPath": { - "type": "string", - "description": "Path where to import secrets from like / or /foo/bar", - "example": "/foo/bar" - } - }, - "required": [ - "environment", - "secretPath" - ] - } - } - }, - "required": [ - "secretImports" - ] - } - } - } - } - }, - "delete": { - "summary": "Delete secret import", - "description": "Delete secret import", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of parent secret import document from which to delete secret import", - "example": "12345abcde" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "successfully delete secret import" - } - }, - "description": "Confirmation of secret import deletion" - } - } - } - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretImportEnv": { - "type": "string", - "description": "Slug of environment of import to delete", - "example": "someWorkspaceId" - }, - "secretImportPath": { - "type": "string", - "description": "Path like / or /foo/bar of import to delete", - "example": "production" - } - }, - "required": [ - "id", - "secretImportEnv", - "secretImportPath" - ] - } - } - } - } - } - }, - "/api/v1/secret-imports/secrets": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/roles/": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/roles/{id}": { - "patch": { - "description": "", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/roles/organization/{orgId}/permissions": { - "get": { - "description": "", - "parameters": [ - { - "name": "orgId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/roles/workspace/{workspaceId}/permissions": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-approvals/": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-approvals/board": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-approvals/{id}": { - "patch": { - "description": "", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/sso/redirect/google": { - "get": { - "description": "", - "parameters": [ - { - "name": "callback_port", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "default": { - "description": "" - } - } - } - }, - "/api/v1/sso/google": { - "get": { - "description": "", - "responses": { - "default": { - "description": "" - } - } - } - }, - "/api/v1/sso/redirect/github": { - "get": { - "description": "", - "parameters": [ - { - "name": "callback_port", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "default": { - "description": "" - } - } - } - }, - "/api/v1/sso/github": { - "get": { - "description": "", - "responses": { - "default": { - "description": "" - } - } - } - }, - "/api/v1/sso/redirect/gitlab": { - "get": { - "description": "", - "parameters": [ - { - "name": "callback_port", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "default": { - "description": "" - } - } - } - }, - "/api/v1/sso/gitlab": { - "get": { - "description": "", - "responses": { - "default": { - "description": "" - } - } - } - }, - "/api/v1/secret-approval-requests/": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-approval-requests/count": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-approval-requests/{id}": { - "get": { - "description": "", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-approval-requests/{id}/merge": { - "post": { - "description": "", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-approval-requests/{id}/review": { - "post": { - "description": "", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v1/secret-approval-requests/{id}/status": { - "post": { - "description": "", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/signup/complete-account/signup": { - "post": { - "description": "", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "403": { - "description": "Forbidden" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "example": "any" - }, - "firstName": { - "example": "any" - }, - "lastName": { - "example": "any" - }, - "protectedKey": { - "example": "any" - }, - "protectedKeyIV": { - "example": "any" - }, - "protectedKeyTag": { - "example": "any" - }, - "publicKey": { - "example": "any" - }, - "encryptedPrivateKey": { - "example": "any" - }, - "encryptedPrivateKeyIV": { - "example": "any" - }, - "encryptedPrivateKeyTag": { - "example": "any" - }, - "salt": { - "example": "any" - }, - "verifier": { - "example": "any" - }, - "organizationName": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v2/signup/complete-account/invite": { - "post": { - "description": "", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "403": { - "description": "Forbidden" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "example": "any" - }, - "firstName": { - "example": "any" - }, - "lastName": { - "example": "any" - }, - "protectedKey": { - "example": "any" - }, - "protectedKeyIV": { - "example": "any" - }, - "protectedKeyTag": { - "example": "any" - }, - "publicKey": { - "example": "any" - }, - "encryptedPrivateKey": { - "example": "any" - }, - "encryptedPrivateKeyIV": { - "example": "any" - }, - "encryptedPrivateKeyTag": { - "example": "any" - }, - "salt": { - "example": "any" - }, - "verifier": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v2/auth/login1": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "example": "any" - }, - "clientPublicKey": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v2/auth/login2": { - "post": { - "description": "", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "example": "any" - }, - "clientProof": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v2/auth/mfa/send": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/auth/mfa/verify": { - "post": { - "description": "", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/users/me/mfa": { - "patch": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/users/me/name": { - "patch": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/users/me/auth-methods": { - "put": { - "description": "", - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v2/users/me/organizations": { - "get": { - "summary": "Return organizations that current user is part of", - "description": "Return organizations that current user is part of", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "organizations": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Organization" - }, - "description": "Organizations that user is part of" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ] - } - }, - "/api/v2/users/me/api-keys": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/users/me/api-keys/{apiKeyDataId}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "apiKeyDataId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/users/me/sessions": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/users/me": { - "get": { - "summary": "Retrieve the current user on the request", - "description": "Retrieve the current user on the request", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "user": { - "type": "object", - "$ref": "#/components/schemas/CurrentUser", - "description": "Current user on request" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ] - }, - "delete": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/organizations/{organizationId}/memberships": { - "get": { - "summary": "Return organization user memberships", - "description": "Return organization user memberships", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of organization" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "memberships": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MembershipOrg" - }, - "description": "Memberships of organization" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ] - } - }, - "/api/v2/organizations/{organizationId}/memberships/{membershipId}": { - "patch": { - "summary": "Update organization user membership", - "description": "Update organization user membership", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of organization" - }, - { - "name": "membershipId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of organization membership to update" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "membership": { - "$ref": "#/components/schemas/MembershipOrg", - "description": "Updated organization membership" - } - } - } - } - } - }, - "400": { - "description": "Bad Request" - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "role": { - "type": "string", - "description": "Role of organization membership - either owner, admin, or member" - } - } - } - } - } - } - }, - "delete": { - "summary": "Delete organization user membership", - "description": "Delete organization user membership", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of organization" - }, - { - "name": "membershipId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of organization membership to delete" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "membership": { - "$ref": "#/components/schemas/MembershipOrg", - "description": "Deleted organization membership" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ] - } - }, - "/api/v2/organizations/{organizationId}/workspaces": { - "get": { - "summary": "Return projects in organization that user is part of", - "description": "Return projects in organization that user is part of", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of organization" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaces": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Project" - }, - "description": "Projects of organization" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ] - } - }, - "/api/v2/organizations/": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/organizations/{organizationId}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/organizations/{organizationId}/identity-memberships": { - "get": { - "summary": "Return organization identity memberships", - "description": "Return organization identity memberships", - "parameters": [ - { - "name": "organizationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of organization" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityMemberships": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IdentityMembershipOrg" - }, - "description": "Identity memberships of organization" - } - } - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/api/v2/workspace/{workspaceId}/memberships": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "get": { - "summary": "Return project user memberships", - "description": "Return project user memberships", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "memberships": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Membership" - }, - "description": "Memberships of project" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ] - } - }, - "/api/v2/workspace/{workspaceId}/environments": { - "post": { - "summary": "Create environment", - "description": "Create environment", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of workspace where to create environment" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Sucess message", - "example": "Successfully created environment" - }, - "workspace": { - "type": "string", - "description": "ID of workspace where environment was created", - "example": "abc123" - }, - "environment": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of created environment", - "example": "Staging" - }, - "slug": { - "type": "string", - "description": "Slug of created environment", - "example": "staging" - } - } - } - }, - "description": "Details of the created environment" - } - } - } - }, - "400": { - "description": "Bad Request" - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "environmentName": { - "type": "string", - "description": "Name of the environment to create", - "example": "development" - }, - "environmentSlug": { - "type": "string", - "description": "Slug of environment to create", - "example": "dev-environment" - } - }, - "required": [ - "environmentName", - "environmentSlug" - ] - } - } - } - } - }, - "put": { - "summary": "Update environment", - "description": "Update environment", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of workspace where to update environment" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Success message", - "example": "Successfully update environment" - }, - "workspace": { - "type": "string", - "description": "ID of workspace where environment was updated", - "example": "abc123" - }, - "environment": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of updated environment", - "example": "Staging-Renamed" - }, - "slug": { - "type": "string", - "description": "Slug of updated environment", - "example": "staging-renamed" - } - } - } - }, - "description": "Details of the renamed environment" - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "environmentName": { - "type": "string", - "description": "Name of environment to update to", - "example": "Staging-Renamed" - }, - "environmentSlug": { - "type": "string", - "description": "Slug of environment to update to", - "example": "staging-renamed" - }, - "oldEnvironmentSlug": { - "type": "string", - "description": "Current slug of environment", - "example": "staging-old" - } - }, - "required": [ - "environmentName", - "environmentSlug", - "oldEnvironmentSlug" - ] - } - } - } - } - }, - "patch": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "summary": "Delete environment", - "description": "Delete environment", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of workspace where to delete environment" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Success message", - "example": "Successfully deleted environment" - }, - "workspace": { - "type": "string", - "description": "ID of workspace where environment was deleted", - "example": "abc123" - }, - "environment": { - "type": "string", - "description": "Slug of deleted environment", - "example": "dev" - } - }, - "description": "Response after deleting an environment from a workspace" - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "environmentSlug": { - "type": "string", - "description": "Slug of environment to delete", - "example": "dev" - } - }, - "required": [ - "environmentSlug" - ] - } - } - } - } - } - }, - "/api/v2/workspace/{workspaceId}/tags": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/workspace/tags/{tagId}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "tagId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/workspace/{workspaceId}/secrets": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "example": "any" - }, - "keys": { - "example": "any" - }, - "environment": { - "example": "any" - }, - "channel": { - "example": "any" - } - } - } - } - } - } - }, - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "channel", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/workspace/{workspaceId}/encrypted-key": { - "get": { - "summary": "Return encrypted project key", - "description": "Return encrypted project key", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProjectKey" - }, - "description": "Encrypted project key for the given project" - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ] - } - }, - "/api/v2/workspace/{workspaceId}/service-token-data": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/workspace/{workspaceId}/memberships/{membershipId}": { - "patch": { - "summary": "Update project user membership", - "description": "Update project user membership", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project" - }, - { - "name": "membershipId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project membership to update" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "membership": { - "$ref": "#/components/schemas/Membership", - "description": "Updated membership" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "role": { - "type": "string", - "description": "Role to update to for project membership" - } - } - } - } - } - } - }, - "delete": { - "summary": "Delete project user membership", - "description": "Delete project user membership", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project" - }, - { - "name": "membershipId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project membership to delete" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "membership": { - "$ref": "#/components/schemas/Membership", - "description": "Deleted membership" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ] - } - }, - "/api/v2/workspace/{workspaceId}/auto-capitalization": { - "patch": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/workspace/{workspaceId}/identity-memberships/{identityId}": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "patch": { - "summary": "Update project identity membership", - "description": "Update project identity membership", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project" - }, - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity whose membership to update in project" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityMembership": { - "$ref": "#/components/schemas/IdentityMembership", - "description": "Updated identity membership" - } - } - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "role": { - "type": "string", - "description": "Role to update to for identity project membership" - } - } - } - } - } - } - }, - "delete": { - "summary": "Delete project identity membership", - "description": "Delete project identity membership", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project" - }, - { - "name": "identityId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of identity whose membership to delete in project" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityMembership": { - "$ref": "#/components/schemas/IdentityMembership", - "description": "Deleted identity membership" - } - } - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/api/v2/workspace/{workspaceId}/identity-memberships": { - "get": { - "summary": "Return project identity memberships", - "description": "Return project identity memberships", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "ID of project" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityMemberships": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IdentityMembership" - }, - "description": "Identity memberships of project" - } - } - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/api/v2/secret/batch-create/workspace/{workspaceId}/environment/{environment}": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v2/secret/workspace/{workspaceId}/environment/{environment}": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secret": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v2/secret/workspace/{workspaceId}": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/secret/{secretId}": { - "get": { - "description": "", - "parameters": [ - { - "name": "secretId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "secretId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/secret/batch/workspace/{workspaceId}/environment/{environmentName}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "environmentName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretIds": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v2/secret/batch-modify/workspace/{workspaceId}/environment/{environmentName}": { - "patch": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "environmentName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v2/secret/workspace/{workspaceId}/environment/{environmentName}": { - "patch": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "environmentName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secret": { - "example": "any" - } - } - } - } - } - } - } - }, - "/api/v2/secrets/batch": { - "post": { - "description": "", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/secrets/": { - "post": { - "summary": "Create new secret(s)", - "description": "Create one or many secrets for a given project and environment.", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Secret" - }, - "description": "Newly-created secrets for the given project and environment" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of project" - }, - "environment": { - "type": "string", - "description": "Environment within project" - }, - "secrets": { - "$ref": "#/components/schemas/CreateSecret", - "description": "Secret(s) to create - object or array of objects" - } - } - } - } - } - } - }, - "get": { - "summary": "Read secrets", - "description": "Read secrets from a project and environment", - "parameters": [ - { - "name": "workspaceId", - "description": "ID of project", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "description": "Environment within project", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Secret" - }, - "description": "Secrets for the given project and environment" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ] - }, - "patch": { - "summary": "Update secret(s)", - "description": "Update secret(s)", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Secret" - }, - "description": "Updated secrets" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "$ref": "#/components/schemas/UpdateSecret", - "description": "Secret(s) to update - object or array of objects" - } - } - } - } - } - } - }, - "delete": { - "summary": "Delete secret(s)", - "description": "Delete one or many secrets by their ID(s)", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Secret" - }, - "description": "Deleted secrets" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretIds": { - "type": "string", - "description": "ID(s) of secrets - string or array of strings" - } - } - } - } - } - } - } - }, - "/api/v2/service-token/": { - "get": { - "summary": "Return Infisical Token data", - "description": "Return Infisical Token data", - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "serviceTokenData": { - "type": "object", - "$ref": "#/components/schemas/ServiceTokenData", - "description": "Details of service token" - } - } - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - }, - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v2/service-token/{serviceTokenDataId}": { - "delete": { - "description": "", - "parameters": [ - { - "name": "serviceTokenDataId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/auth/login1": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/auth/login2": { - "post": { - "description": "", - "parameters": [ - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - } - } - } - }, - "/api/v3/secrets/raw": { - "get": { - "summary": "List secrets", - "description": "List secrets", - "parameters": [ - { - "name": "workspaceId", - "description": "ID of workspace where to get secrets from", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "description": "Slug of environment where to get secrets from", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "secretPath", - "description": "Path where to update secret like / or /foo/bar. Default is /", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "include_imports", - "description": "Whether or not to include imported secrets. Default is false", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RawSecret" - }, - "description": "List of secrets" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ] - } - }, - "/api/v3/secrets/raw/{secretName}": { - "get": { - "summary": "Get secret", - "description": "Get secret", - "parameters": [ - { - "name": "secretName", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "Name of secret to get" - }, - { - "name": "workspaceId", - "description": "ID of workspace where to get secret", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "environment", - "description": "Slug of environment where to get secret", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "secretPath", - "description": "Path where to update secret like / or /foo/bar. Default is /", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "type", - "description": "Type of secret to get; either shared or personal. Default is shared.", - "required": true, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "include_imports", - "description": "Whether or not to include imported secrets. Default is false", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secret": { - "$ref": "#/components/schemas/RawSecret" - } - } - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ] - }, - "post": { - "summary": "Create secret", - "description": "Create secret", - "parameters": [ - { - "name": "secretName", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "Name of secret to create" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RawSecret" - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of the workspace where to create secret", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to create secret", - "example": "dev" - }, - "secretPath": { - "type": "string", - "description": "Path where to create secret. Default is /", - "example": "/foo/bar" - }, - "secretValue": { - "type": "string", - "description": "Value of secret to create", - "example": "Some value" - }, - "secretComment": { - "type": "string", - "description": "Comment for secret to create", - "example": "Some comment" - }, - "type": { - "type": "string", - "description": "Type of secret to create; either shared or personal. Default is shared.", - "example": "shared" - }, - "skipMultilineEncoding": { - "type": "boolean", - "description": "Convert multi line secrets into one line by wrapping", - "example": "true" - } - }, - "required": [ - "workspaceId", - "environment", - "secretValue" - ] - } - } - } - } - }, - "patch": { - "summary": "Update secret", - "description": "Update secret", - "parameters": [ - { - "name": "secretName", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "Name of secret to update" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RawSecret" - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of the workspace where to update secret", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to update secret", - "example": "dev" - }, - "secretPath": { - "type": "string", - "description": "Path where to update secret like / or /foo/bar. Default is /", - "example": "/foo/bar" - }, - "secretValue": { - "type": "string", - "description": "Value of secret to update to", - "example": "Some value" - }, - "type": { - "type": "string", - "description": "Type of secret to update; either shared or personal. Default is shared.", - "example": "shared" - }, - "skipMultilineEncoding": { - "type": "boolean", - "description": "Convert multi line secrets into one line by wrapping", - "example": "true" - } - }, - "required": [ - "workspaceId", - "environment", - "secretValue" - ] - } - } - } - } - }, - "delete": { - "summary": "Delete secret", - "description": "Delete secret", - "parameters": [ - { - "name": "secretName", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "Name of secret to delete" - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secret": { - "$ref": "#/components/schemas/RawSecret" - } - }, - "description": "The deleted secret" - } - } - } - } - }, - "security": [ - { - "apiKeyAuth": [], - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of workspace where to delete secret", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of Environment where to delete secret", - "example": "dev" - }, - "secretPath": { - "type": "string", - "description": "Path where to delete secret. Default is /", - "example": "/foo/bar" - }, - "type": { - "type": "string", - "description": "Type of secret to delete; either shared or personal. Default is shared", - "example": "shared" - } - }, - "required": [ - "workspaceId", - "environment" - ] - } - } - } - } - } - }, - "/api/v3/secrets/": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/secrets/batch": { - "post": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "patch": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/secrets/{secretName}": { - "post": { - "description": "", - "parameters": [ - { - "name": "secretName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "get": { - "description": "", - "parameters": [ - { - "name": "secretName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "patch": { - "description": "", - "parameters": [ - { - "name": "secretName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "description": "", - "parameters": [ - { - "name": "secretName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/workspaces/{workspaceId}/secrets/blind-index-status": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/workspaces/{workspaceId}/secrets": { - "get": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/workspaces/{workspaceId}/secrets/names": { - "post": { - "description": "", - "parameters": [ - { - "name": "workspaceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/v3/signup/complete-account/signup": { - "post": { - "description": "", - "parameters": [ - { - "name": "authorization", - "in": "header", - "schema": { - "type": "string" - } - }, - { - "name": "user-agent", - "in": "header", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request" - }, - "403": { - "description": "Forbidden" - } - } - } - }, - "/api/v3/us/me/api-keys": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/api/status": { - "get": { - "description": "", - "responses": { - "200": { - "description": "OK" - } - } - } - } - }, - "components": { - "schemas": { - "CurrentUser": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "email": { - "type": "string", - "example": "johndoe@gmail.com" - }, - "firstName": { - "type": "string", - "example": "John" - }, - "lastName": { - "type": "string", - "example": "Doe" - }, - "publicKey": { - "type": "string", - "example": "johns_nacl_public_key" - }, - "encryptedPrivateKey": { - "type": "string", - "example": "johns_enc_nacl_private_key" - }, - "iv": { - "type": "string", - "example": "iv_of_enc_nacl_private_key" - }, - "tag": { - "type": "string", - "example": "tag_of_enc_nacl_private_key" - }, - "updatedAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - }, - "createdAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - } - } - }, - "Identity": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "name": { - "type": "string", - "example": "Machine 1" - }, - "authMethod": { - "type": "string", - "example": "universal-auth" - } - } - }, - "IdentityUniversalAuth": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "identity": { - "type": "string", - "example": "" - }, - "clientId": { - "type": "string", - "example": "..." - }, - "clientSecretTrustedIps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipAddress": { - "type": "string", - "example": "0.0.0.0" - }, - "type": { - "type": "string", - "example": "ipv4" - }, - "prefix": { - "type": "string", - "example": "0" - } - } - } - }, - "accessTokenTTL": { - "type": "number", - "example": 7200 - }, - "accessTokenMaxTTL": { - "type": "number", - "example": 2592000 - }, - "accessTokenNumUsesLimit": { - "type": "number", - "example": 0 - }, - "accessTokenTrustedIps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "ipAddress": { - "type": "string", - "example": "0.0.0.0" - }, - "type": { - "type": "string", - "example": "ipv4" - }, - "prefix": { - "type": "string", - "example": "0" - } - } - } - } - } - }, - "IdentityUniversalAuthClientSecretData": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "identityUniversalAuth": { - "type": "string", - "example": "" - }, - "isClientSecretRevoked": { - "type": "boolean", - "example": false - }, - "description": { - "type": "string", - "example": "" - }, - "clientSecretPrefix": { - "type": "string", - "example": "abc" - }, - "clientSecretNumUses": { - "type": "number", - "example": 0 - }, - "clientSecretNumUsesLimit": { - "type": "number", - "example": 0 - }, - "clientSecretTTL": { - "type": "number", - "example": 0 - }, - "createdAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - }, - "updatedAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - } - } - }, - "Membership": { - "type": "object", - "properties": { - "user": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "email": { - "type": "string", - "example": "johndoe@gmail.com" - }, - "firstName": { - "type": "string", - "example": "John" - }, - "lastName": { - "type": "string", - "example": "Doe" - }, - "publicKey": { - "type": "string", - "example": "johns_nacl_public_key" - }, - "updatedAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - }, - "createdAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - } - } - }, - "workspace": { - "type": "string", - "example": "" - }, - "role": { - "type": "string", - "example": "admin" - } - } - }, - "MembershipOrg": { - "type": "object", - "properties": { - "user": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "email": { - "type": "string", - "example": "johndoe@gmail.com" - }, - "firstName": { - "type": "string", - "example": "John" - }, - "lastName": { - "type": "string", - "example": "Doe" - }, - "publicKey": { - "type": "string", - "example": "johns_nacl_public_key" - }, - "updatedAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - }, - "createdAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - } - } - }, - "organization": { - "type": "string", - "example": "" - }, - "role": { - "type": "string", - "example": "owner" - }, - "status": { - "type": "string", - "example": "accepted" - } - } - }, - "IdentityMembership": { - "type": "object", - "properties": { - "identity": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "name": { - "type": "string", - "example": "Machine 1" - }, - "authMethod": { - "type": "string", - "example": "universal-auth" - } - } - }, - "workspace": { - "type": "string", - "example": "" - }, - "role": { - "type": "string", - "example": "member" - } - } - }, - "IdentityMembershipOrg": { - "type": "object", - "properties": { - "identity": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "name": { - "type": "string", - "example": "Machine 1" - }, - "authMethod": { - "type": "string", - "example": "universal-auth" - } - } - }, - "organization": { - "type": "string", - "example": "" - }, - "role": { - "type": "string", - "example": "member" - }, - "status": { - "type": "string", - "example": "accepted" - } - } - }, - "Organization": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "name": { - "type": "string", - "example": "Acme Corp." - }, - "customerId": { - "type": "string", - "example": "" - } - } - }, - "Project": { - "type": "object", - "properties": { - "name": { - "type": "string", - "example": "My Project" - }, - "organization": { - "type": "string", - "example": "" - }, - "environments": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "example": "development" - }, - "slug": { - "type": "string", - "example": "dev" - } - } - } - } - } - }, - "ProjectKey": { - "type": "object", - "properties": { - "encryptedkey": { - "type": "string", - "example": "" - }, - "nonce": { - "type": "string", - "example": "" - }, - "sender": { - "type": "object", - "properties": { - "publicKey": { - "type": "string", - "example": "senders_nacl_public_key" - } - } - }, - "receiver": { - "type": "string", - "example": "" - }, - "workspace": { - "type": "string", - "example": "" - } - } - }, - "CreateSecret": { - "type": "object", - "properties": { - "type": { - "type": "string", - "example": "shared" - }, - "secretKeyCiphertext": { - "type": "string", - "example": "" - }, - "secretKeyIV": { - "type": "string", - "example": "" - }, - "secretKeyTag": { - "type": "string", - "example": "" - }, - "secretValueCiphertext": { - "type": "string", - "example": "" - }, - "secretValueIV": { - "type": "string", - "example": "" - }, - "secretValueTag": { - "type": "string", - "example": "" - }, - "secretCommentCiphertext": { - "type": "string", - "example": "" - }, - "secretCommentIV": { - "type": "string", - "example": "" - }, - "secretCommentTag": { - "type": "string", - "example": "" - } - } - }, - "UpdateSecret": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "" - }, - "secretKeyCiphertext": { - "type": "string", - "example": "" - }, - "secretKeyIV": { - "type": "string", - "example": "" - }, - "secretKeyTag": { - "type": "string", - "example": "" - }, - "secretValueCiphertext": { - "type": "string", - "example": "" - }, - "secretValueIV": { - "type": "string", - "example": "" - }, - "secretValueTag": { - "type": "string", - "example": "" - }, - "secretCommentCiphertext": { - "type": "string", - "example": "" - }, - "secretCommentIV": { - "type": "string", - "example": "" - }, - "secretCommentTag": { - "type": "string", - "example": "" - } - } - }, - "Secret": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "version": { - "type": "number", - "example": 1 - }, - "workspace": { - "type": "string", - "example": "" - }, - "type": { - "type": "string", - "example": "shared" - }, - "user": {}, - "secretKeyCiphertext": { - "type": "string", - "example": "" - }, - "secretKeyIV": { - "type": "string", - "example": "" - }, - "secretKeyTag": { - "type": "string", - "example": "" - }, - "secretValueCiphertext": { - "type": "string", - "example": "" - }, - "secretValueIV": { - "type": "string", - "example": "" - }, - "secretValueTag": { - "type": "string", - "example": "" - }, - "secretCommentCiphertext": { - "type": "string", - "example": "" - }, - "secretCommentIV": { - "type": "string", - "example": "" - }, - "secretCommentTag": { - "type": "string", - "example": "" - }, - "updatedAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - }, - "createdAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - } - } - }, - "RawSecret": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "abc123" - }, - "version": { - "type": "number", - "example": 1 - }, - "workspace": { - "type": "string", - "example": "abc123" - }, - "environment": { - "type": "string", - "example": "dev" - }, - "secretKey": { - "type": "string", - "example": "STRIPE_KEY" - }, - "secretValue": { - "type": "string", - "example": "abc123" - }, - "secretComment": { - "type": "string", - "example": "Lorem ipsum" - } - } - }, - "SecretImport": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "workspace": { - "type": "string", - "example": "abc123" - }, - "environment": { - "type": "string", - "example": "dev" - }, - "folderId": { - "type": "string", - "example": "root" - }, - "imports": { - "type": "array", - "example": [], - "items": {} - }, - "updatedAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - }, - "createdAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - } - } - }, - "Log": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "user": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "email": { - "type": "string", - "example": "johndoe@gmail.com" - }, - "firstName": { - "type": "string", - "example": "John" - }, - "lastName": { - "type": "string", - "example": "Doe" - } - } - }, - "workspace": { - "type": "string", - "example": "" - }, - "actionNames": { - "type": "array", - "example": [ - "addSecrets" - ], - "items": { - "type": "string" - } - }, - "actions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "example": "addSecrets" - }, - "user": { - "type": "string", - "example": "" - }, - "workspace": { - "type": "string", - "example": "" - }, - "payload": { - "type": "array", - "items": { - "type": "object", - "properties": { - "oldSecretVersion": { - "type": "string", - "example": "" - }, - "newSecretVersion": { - "type": "string", - "example": "" - } - } - } - } - } - } - }, - "channel": { - "type": "string", - "example": "cli" - }, - "ipAddress": { - "type": "string", - "example": "192.168.0.1" - }, - "updatedAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - }, - "createdAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - } - } - }, - "SecretSnapshot": { - "type": "object", - "properties": { - "workspace": { - "type": "string", - "example": "" - }, - "version": { - "type": "number", - "example": 1 - }, - "secretVersions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - } - } - } - } - } - }, - "SecretVersion": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "secret": { - "type": "string", - "example": "" - }, - "version": { - "type": "number", - "example": 1 - }, - "workspace": { - "type": "string", - "example": "" - }, - "type": { - "type": "string", - "example": "shared" - }, - "user": { - "type": "string", - "example": "" - }, - "environment": { - "type": "string", - "example": "dev" - }, - "isDeleted": { - "type": "string", - "example": "" - }, - "secretKeyCiphertext": { - "type": "string", - "example": "" - }, - "secretKeyIV": { - "type": "string", - "example": "" - }, - "secretKeyTag": { - "type": "string", - "example": "" - }, - "secretValueCiphertext": { - "type": "string", - "example": "" - }, - "secretValueIV": { - "type": "string", - "example": "" - }, - "secretValueTag": { - "type": "string", - "example": "" - } - } - }, - "ServiceTokenData": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "name": { - "type": "string", - "example": "" - }, - "workspace": { - "type": "string", - "example": "" - }, - "environment": { - "type": "string", - "example": "" - }, - "user": { - "type": "object", - "properties": { - "_id": { - "type": "string", - "example": "" - }, - "firstName": { - "type": "string", - "example": "" - }, - "lastName": { - "type": "string", - "example": "" - } - } - }, - "expiresAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - }, - "encryptedKey": { - "type": "string", - "example": "" - }, - "iv": { - "type": "string", - "example": "" - }, - "tag": { - "type": "string", - "example": "" - }, - "updatedAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - }, - "createdAt": { - "type": "string", - "example": "2023-01-13T14:16:12.210Z" - } - } - }, - "AuditLog": { - "type": "object", - "properties": { - "actor": { - "type": "object", - "properties": { - "type": { - "type": "string", - "example": "" - }, - "metadata": { - "type": "object", - "properties": {} - } - } - }, - "organization": { - "type": "string", - "example": "" - }, - "workspace": { - "type": "string", - "example": "" - }, - "ipAddress": { - "type": "string", - "example": "" - }, - "event": { - "type": "object", - "properties": { - "type": { - "type": "string", - "example": "" - }, - "metadata": { - "type": "object", - "properties": {} - } - } - }, - "userAgent": { - "type": "string", - "example": "" - }, - "userAgentType": { - "type": "string", - "example": "" - }, - "expiresAt": { - "type": "string", - "example": "" - } - } - } - }, - "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT", - "description": "An access token in Infisical" - }, - "apiKeyAuth": { - "type": "apiKey", - "in": "header", - "name": "X-API-Key", - "description": "An API Key in Infisical" - } - } - } -} \ No newline at end of file diff --git a/backend-mongo/src/bootstrap.ts b/backend-mongo/src/bootstrap.ts deleted file mode 100644 index a1ea148c2..000000000 --- a/backend-mongo/src/bootstrap.ts +++ /dev/null @@ -1,43 +0,0 @@ -import ora from "ora"; -import nodemailer from "nodemailer"; -import { getSmtpHost, getSmtpPort } from "./config"; -import { logger } from "./utils/logging"; -import mongoose from "mongoose"; -import { redisClient } from "./services/RedisService"; - -type BootstrapOpt = { - transporter: nodemailer.Transporter; -}; - -export const bootstrap = async ({ transporter }: BootstrapOpt) => { - const spinner = ora().start(); - spinner.info("Checking configurations..."); - spinner.info("Testing smtp connection"); - - await transporter - .verify() - .then(async () => { - spinner.succeed("SMTP successfully connected"); - }) - .catch(async (err) => { - spinner.fail(`SMTP - Failed to connect to ${await getSmtpHost()}:${await getSmtpPort()}`); - logger.error(err); - }); - - spinner.info("Testing mongodb connection"); - if (mongoose.connection.readyState !== mongoose.ConnectionStates.connected) { - spinner.fail("Mongo DB - Failed to connect"); - } else { - spinner.succeed("Mongodb successfully connected"); - } - - spinner.info("Testing redis connection"); - const redisPing = await redisClient?.ping(); - if (!redisPing) { - spinner.fail("Redis - Failed to connect"); - } else { - spinner.succeed("Redis successfully connected"); - } - - spinner.stop(); -}; diff --git a/backend-mongo/src/config/index.ts b/backend-mongo/src/config/index.ts deleted file mode 100644 index 8ba445f99..000000000 --- a/backend-mongo/src/config/index.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { GITLAB_URL } from "../variables"; - -import InfisicalClient from "infisical-node"; - -export const client = new InfisicalClient({ - token: process.env.INFISICAL_TOKEN! -}); - -export const getIsMigrationMode = async () => - (await client.getSecret("MIGRATION_MODE")).secretValue === "true"; - -export const getPort = async () => (await client.getSecret("PORT")).secretValue || 4000; -export const getEncryptionKey = async () => { - const secretValue = (await client.getSecret("ENCRYPTION_KEY")).secretValue; - return secretValue === "" ? undefined : secretValue; -}; -export const getRootEncryptionKey = async () => { - const secretValue = (await client.getSecret("ROOT_ENCRYPTION_KEY")).secretValue; - return secretValue === "" ? undefined : secretValue; -}; -export const getInviteOnlySignup = async () => - (await client.getSecret("INVITE_ONLY_SIGNUP")).secretValue === "true"; -export const getSaltRounds = async () => - parseInt((await client.getSecret("SALT_ROUNDS")).secretValue) || 10; -export const getAuthSecret = async () => - (await client.getSecret("JWT_AUTH_SECRET")).secretValue ?? - (await client.getSecret("AUTH_SECRET")).secretValue; -export const getJwtAuthLifetime = async () => - (await client.getSecret("JWT_AUTH_LIFETIME")).secretValue || "10d"; -export const getJwtMfaLifetime = async () => - (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m"; -export const getJwtRefreshLifetime = async () => - (await client.getSecret("JWT_REFRESH_LIFETIME")).secretValue || "90d"; -export const getJwtServiceSecret = async () => - (await client.getSecret("JWT_SERVICE_SECRET")).secretValue; // TODO: deprecate (related to ST V1) -export const getJwtSignupLifetime = async () => - (await client.getSecret("JWT_SIGNUP_LIFETIME")).secretValue || "15m"; -export const getJwtProviderAuthLifetime = async () => - (await client.getSecret("JWT_PROVIDER_AUTH_LIFETIME")).secretValue || "15m"; -export const getMongoURL = async () => (await client.getSecret("MONGO_URL")).secretValue; -export const getNodeEnv = async () => - (await client.getSecret("NODE_ENV")).secretValue || "production"; -export const getVerboseErrorOutput = async () => - (await client.getSecret("VERBOSE_ERROR_OUTPUT")).secretValue === "true" && true; -export const getLokiHost = async () => (await client.getSecret("LOKI_HOST")).secretValue; -export const getClientIdAzure = async () => (await client.getSecret("CLIENT_ID_AZURE")).secretValue; -export const getClientIdHeroku = async () => - (await client.getSecret("CLIENT_ID_HEROKU")).secretValue; -export const getClientIdVercel = async () => - (await client.getSecret("CLIENT_ID_VERCEL")).secretValue; -export const getClientIdNetlify = async () => - (await client.getSecret("CLIENT_ID_NETLIFY")).secretValue; -export const getClientIdGitHub = async () => - (await client.getSecret("CLIENT_ID_GITHUB")).secretValue; -export const getClientIdGitLab = async () => - (await client.getSecret("CLIENT_ID_GITLAB")).secretValue; -export const getClientIdBitBucket = async () => - (await client.getSecret("CLIENT_ID_BITBUCKET")).secretValue; -export const getClientIdGCPSecretManager = async () => - (await client.getSecret("CLIENT_ID_GCP_SECRET_MANAGER")).secretValue; -export const getClientSecretAzure = async () => - (await client.getSecret("CLIENT_SECRET_AZURE")).secretValue; -export const getClientSecretHeroku = async () => - (await client.getSecret("CLIENT_SECRET_HEROKU")).secretValue; -export const getClientSecretVercel = async () => - (await client.getSecret("CLIENT_SECRET_VERCEL")).secretValue; -export const getClientSecretNetlify = async () => - (await client.getSecret("CLIENT_SECRET_NETLIFY")).secretValue; -export const getClientSecretGitHub = async () => - (await client.getSecret("CLIENT_SECRET_GITHUB")).secretValue; -export const getClientSecretGitLab = async () => - (await client.getSecret("CLIENT_SECRET_GITLAB")).secretValue; -export const getClientSecretBitBucket = async () => - (await client.getSecret("CLIENT_SECRET_BITBUCKET")).secretValue; -export const getClientSecretGCPSecretManager = async () => - (await client.getSecret("CLIENT_SECRET_GCP_SECRET_MANAGER")).secretValue; -export const getClientSlugVercel = async () => - (await client.getSecret("CLIENT_SLUG_VERCEL")).secretValue; - -export const getClientIdGoogleLogin = async () => - (await client.getSecret("CLIENT_ID_GOOGLE_LOGIN")).secretValue; -export const getClientSecretGoogleLogin = async () => - (await client.getSecret("CLIENT_SECRET_GOOGLE_LOGIN")).secretValue; -export const getClientIdGitHubLogin = async () => - (await client.getSecret("CLIENT_ID_GITHUB_LOGIN")).secretValue; -export const getClientSecretGitHubLogin = async () => - (await client.getSecret("CLIENT_SECRET_GITHUB_LOGIN")).secretValue; -export const getClientIdGitLabLogin = async () => - (await client.getSecret("CLIENT_ID_GITLAB_LOGIN")).secretValue; -export const getClientSecretGitLabLogin = async () => - (await client.getSecret("CLIENT_SECRET_GITLAB_LOGIN")).secretValue; -export const getUrlGitLabLogin = async () => - (await client.getSecret("URL_GITLAB_LOGIN")).secretValue || GITLAB_URL; - -export const getAwsCloudWatchLog = async () => { - const logGroupName = - (await client.getSecret("AWS_CLOUDWATCH_LOG_GROUP_NAME")).secretValue || "infisical-log-stream"; - const region = (await client.getSecret("AWS_CLOUDWATCH_LOG_REGION")).secretValue; - const accessKeyId = (await client.getSecret("AWS_CLOUDWATCH_LOG_ACCESS_KEY_ID")).secretValue; - const accessKeySecret = (await client.getSecret("AWS_CLOUDWATCH_LOG_ACCESS_KEY_SECRET")) - .secretValue; - const interval = parseInt( - (await client.getSecret("AWS_CLOUDWATCH_LOG_INTERVAL")).secretValue || 1000, - 10 - ); - if (!region || !accessKeyId || !accessKeySecret) return; - return { logGroupName, region, accessKeySecret, accessKeyId, interval }; -}; - -export const getPostHogHost = async () => - (await client.getSecret("POSTHOG_HOST")).secretValue || "https://app.posthog.com"; -export const getPostHogProjectApiKey = async () => - (await client.getSecret("POSTHOG_PROJECT_API_KEY")).secretValue || - "phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE"; -export const getSentryDSN = async () => (await client.getSecret("SENTRY_DSN")).secretValue; -export const getSiteURL = async () => (await client.getSecret("SITE_URL")).secretValue; -export const getSmtpHost = async () => (await client.getSecret("SMTP_HOST")).secretValue; -export const getSmtpSecure = async () => - (await client.getSecret("SMTP_SECURE")).secretValue === "true" || false; -export const getSmtpPort = async () => - parseInt((await client.getSecret("SMTP_PORT")).secretValue) || 587; -export const getSmtpUsername = async () => (await client.getSecret("SMTP_USERNAME")).secretValue; -export const getSmtpPassword = async () => (await client.getSecret("SMTP_PASSWORD")).secretValue; -export const getSmtpFromAddress = async () => - (await client.getSecret("SMTP_FROM_ADDRESS")).secretValue; -export const getSmtpFromName = async () => - (await client.getSecret("SMTP_FROM_NAME")).secretValue || "Infisical"; - -export const getSecretScanningWebhookProxy = async () => - (await client.getSecret("SECRET_SCANNING_WEBHOOK_PROXY")).secretValue; -export const getSecretScanningWebhookSecret = async () => - (await client.getSecret("SECRET_SCANNING_WEBHOOK_SECRET")).secretValue; -export const getSecretScanningGitAppId = async () => - (await client.getSecret("SECRET_SCANNING_GIT_APP_ID")).secretValue; -export const getSecretScanningPrivateKey = async () => - (await client.getSecret("SECRET_SCANNING_PRIVATE_KEY")).secretValue; - -export const getRedisUrl = async () => (await client.getSecret("REDIS_URL")).secretValue; -export const getIsInfisicalCloud = async () => - (await client.getSecret("INFISICAL_CLOUD")).secretValue === "true"; - -export const getLicenseKey = async () => { - const secretValue = (await client.getSecret("LICENSE_KEY")).secretValue; - return secretValue === "" ? undefined : secretValue; -}; -export const getLicenseServerKey = async () => { - const secretValue = (await client.getSecret("LICENSE_SERVER_KEY")).secretValue; - return secretValue === "" ? undefined : secretValue; -}; -export const getLicenseServerUrl = async () => - (await client.getSecret("LICENSE_SERVER_URL")).secretValue || "https://portal.infisical.com"; - -export const getTelemetryEnabled = async () => - (await client.getSecret("TELEMETRY_ENABLED")).secretValue !== "false" && true; -export const getLoopsApiKey = async () => (await client.getSecret("LOOPS_API_KEY")).secretValue; -export const getSmtpConfigured = async () => - (await client.getSecret("SMTP_HOST")).secretValue == "" || - (await client.getSecret("SMTP_HOST")).secretValue == undefined - ? false - : true; -export const getHttpsEnabled = async () => { - if ((await getNodeEnv()) != "production") { - // no https for anything other than prod - return false; - } - - if ( - (await client.getSecret("HTTPS_ENABLED")).secretValue == undefined || - (await client.getSecret("HTTPS_ENABLED")).secretValue == "" - ) { - // default when no value present - return true; - } - - return (await client.getSecret("HTTPS_ENABLED")).secretValue === "true" && true; -}; diff --git a/backend-mongo/src/config/request.ts b/backend-mongo/src/config/request.ts deleted file mode 100644 index e69b1baff..000000000 --- a/backend-mongo/src/config/request.ts +++ /dev/null @@ -1,124 +0,0 @@ -import axios from "axios"; -import axiosRetry from "axios-retry"; -import { - getLicenseKeyAuthToken, - getLicenseServerKeyAuthToken, - setLicenseKeyAuthToken, - setLicenseServerKeyAuthToken, -} from "./storage"; -import { - getLicenseKey, - getLicenseServerKey, - getLicenseServerUrl, -} from "./index"; - -// should have JWT to interact with the license server -export const licenseServerKeyRequest = axios.create(); -export const licenseKeyRequest = axios.create(); -export const standardRequest = axios.create(); - -// add retry functionality to the axios instance -axiosRetry(standardRequest, { - retries: 3, - retryDelay: axiosRetry.exponentialDelay, // exponential back-off delay between retries - retryCondition: (error) => { - // only retry if the error is a network error or a 5xx server error - return axiosRetry.isNetworkError(error) || axiosRetry.isRetryableError(error); - }, -}); - -export const refreshLicenseServerKeyToken = async () => { - const licenseServerKey = await getLicenseServerKey(); - const licenseServerUrl = await getLicenseServerUrl(); - - const { data: { token } } = await standardRequest.post( - `${licenseServerUrl}/api/auth/v1/license-server-login`, {}, - { - headers: { - "X-API-KEY": licenseServerKey, - }, - } - ); - - setLicenseServerKeyAuthToken(token); - - return token; -} - -export const refreshLicenseKeyToken = async () => { - const licenseKey = await getLicenseKey(); - const licenseServerUrl = await getLicenseServerUrl(); - - const { data: { token } } = await standardRequest.post( - `${licenseServerUrl}/api/auth/v1/license-login`, {}, - { - headers: { - "X-API-KEY": licenseKey, - }, - } - ); - - setLicenseKeyAuthToken(token); - - return token; -} - -licenseServerKeyRequest.interceptors.request.use((config) => { - const token = getLicenseServerKeyAuthToken(); - - if (token && config.headers) { - // eslint-disable-next-line no-param-reassign - config.headers.Authorization = `Bearer ${token}`; - } - return config; -}, (err) => { - return Promise.reject(err); -}); - -licenseServerKeyRequest.interceptors.response.use((response) => { - return response -}, async function (err) { - const originalRequest = err.config; - - if (err.response.status === 401 && !originalRequest._retry) { - originalRequest._retry = true; - - // refresh - const token = await refreshLicenseServerKeyToken(); - - axios.defaults.headers.common["Authorization"] = "Bearer " + token; - return licenseServerKeyRequest(originalRequest); - } - - return Promise.reject(err); -}); - -licenseKeyRequest.interceptors.request.use((config) => { - const token = getLicenseKeyAuthToken(); - - if (token && config.headers) { - // eslint-disable-next-line no-param-reassign - config.headers.Authorization = `Bearer ${token}`; - } - return config; -}, (err) => { - return Promise.reject(err); -}); - -licenseKeyRequest.interceptors.response.use((response) => { - return response -}, async function (err) { - const originalRequest = err.config; - - if (err.response.status === 401 && !originalRequest._retry) { - originalRequest._retry = true; - - // refresh - const token = await refreshLicenseKeyToken(); - - axios.defaults.headers.common["Authorization"] = "Bearer " + token; - return licenseKeyRequest(originalRequest); - } - - return Promise.reject(err); -}); \ No newline at end of file diff --git a/backend-mongo/src/config/serverConfig.ts b/backend-mongo/src/config/serverConfig.ts deleted file mode 100644 index 0c63cf6e8..000000000 --- a/backend-mongo/src/config/serverConfig.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IServerConfig, ServerConfig } from "../models/serverConfig"; - -let serverConfig: IServerConfig; - -export const serverConfigInit = async () => { - const cfg = await ServerConfig.findOne({}).lean(); - if (!cfg) { - const cfg = new ServerConfig(); - await cfg.save(); - serverConfig = cfg.toObject(); - } else { - serverConfig = cfg; - } - return serverConfig; -}; - -export const getServerConfig = () => serverConfig; - -export const updateServerConfig = async (data: Partial) => { - const cfg = await ServerConfig.findByIdAndUpdate(serverConfig._id, data, { new: true }); - if (!cfg) throw new Error("Failed to update server config"); - serverConfig = cfg.toObject(); - return serverConfig; -}; diff --git a/backend-mongo/src/config/storage.ts b/backend-mongo/src/config/storage.ts deleted file mode 100644 index f3cf27196..000000000 --- a/backend-mongo/src/config/storage.ts +++ /dev/null @@ -1,30 +0,0 @@ -const MemoryLicenseServerKeyTokenStorage = () => { - let authToken: string; - - return { - setToken: (token: string) => { - authToken = token; - }, - getToken: () => authToken, - }; -}; - -const MemoryLicenseKeyTokenStorage = () => { - let authToken: string; - - return { - setToken: (token: string) => { - authToken = token; - }, - getToken: () => authToken, - }; -}; - -const licenseServerTokenStorage = MemoryLicenseServerKeyTokenStorage(); -const licenseTokenStorage = MemoryLicenseKeyTokenStorage(); - -export const getLicenseServerKeyAuthToken = licenseServerTokenStorage.getToken; -export const setLicenseServerKeyAuthToken = licenseServerTokenStorage.setToken; - -export const getLicenseKeyAuthToken = licenseTokenStorage.getToken; -export const setLicenseKeyAuthToken = licenseTokenStorage.setToken; \ No newline at end of file diff --git a/backend-mongo/src/controllers/v1/adminController.ts b/backend-mongo/src/controllers/v1/adminController.ts deleted file mode 100644 index ebe0d4aa7..000000000 --- a/backend-mongo/src/controllers/v1/adminController.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Request, Response } from "express"; -import { getHttpsEnabled, getIsMigrationMode } from "../../config"; -import { getServerConfig, updateServerConfig as setServerConfig } from "../../config/serverConfig"; -import { initializeDefaultOrg, issueAuthTokens } from "../../helpers"; -import { validateRequest } from "../../helpers/validation"; -import { User } from "../../models"; -import { TelemetryService } from "../../services"; -import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; -import * as reqValidator from "../../validation/admin"; - -export const getServerConfigInfo = async (_req: Request, res: Response) => { - const config = getServerConfig(); - const isMigrationModeOn = await getIsMigrationMode(); - return res.send({ config: { ...config, isMigrationModeOn } }); -}; - -export const updateServerConfig = async (req: Request, res: Response) => { - const { - body: { allowSignUp } - } = await validateRequest(reqValidator.UpdateServerConfigV1, req); - const config = await setServerConfig({ allowSignUp }); - return res.send({ config }); -}; - -export const adminSignUp = async (req: Request, res: Response) => { - const cfg = getServerConfig(); - if (cfg.initialized) throw UnauthorizedRequestError({ message: "Admin has been created" }); - const { - body: { - email, - publicKey, - salt, - lastName, - verifier, - firstName, - protectedKey, - protectedKeyIV, - protectedKeyTag, - encryptedPrivateKey, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag - } - } = await validateRequest(reqValidator.SignupV1, req); - let user = await User.findOne({ email }); - if (user) throw BadRequestError({ message: "User already exist" }); - user = new User({ - email, - firstName, - lastName, - encryptionVersion: 2, - protectedKey, - protectedKeyIV, - protectedKeyTag, - publicKey, - encryptedPrivateKey, - iv: encryptedPrivateKeyIV, - tag: encryptedPrivateKeyTag, - salt, - verifier, - superAdmin: true - }); - await user.save(); - await initializeDefaultOrg({ organizationName: "Admin Org", user }); - - await setServerConfig({ initialized: true }); - - // issue tokens - const tokens = await issueAuthTokens({ - userId: user._id, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - const token = tokens.token; - - const postHogClient = await TelemetryService.getPostHogClient(); - if (postHogClient) { - postHogClient.capture({ - event: "admin initialization", - properties: { - email: user.email, - lastName, - firstName - } - }); - } - - // store (refresh) token in httpOnly cookie - res.cookie("jid", tokens.refreshToken, { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: await getHttpsEnabled() - }); - - return res.status(200).send({ - message: "Successfully set up admin account", - user, - token - }); -}; diff --git a/backend-mongo/src/controllers/v1/authController.ts b/backend-mongo/src/controllers/v1/authController.ts deleted file mode 100644 index c27175bb1..000000000 --- a/backend-mongo/src/controllers/v1/authController.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { Request, Response } from "express"; -import jwt from "jsonwebtoken"; -import * as bigintConversion from "bigint-conversion"; -// eslint-disable-next-line @typescript-eslint/no-var-requires -const jsrp = require("jsrp"); -import { - LoginSRPDetail, - TokenVersion, - User -} from "../../models"; -import { clearTokens, createToken, issueAuthTokens } from "../../helpers/auth"; -import { checkUserDevice } from "../../helpers/user"; -import { AuthTokenType } from "../../variables"; -import { - BadRequestError, - UnauthorizedRequestError -} from "../../utils/errors"; -import { - getAuthSecret, - getHttpsEnabled, - getJwtAuthLifetime, -} from "../../config"; -import { ActorType } from "../../ee/models"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/auth"; - -declare module "jsonwebtoken" { - export interface AuthnJwtPayload extends jwt.JwtPayload { - authTokenType: AuthTokenType; - } - export interface UserIDJwtPayload extends jwt.JwtPayload { - userId: string; - refreshVersion?: number; - } - export interface IdentityAccessTokenJwtPayload extends jwt.JwtPayload { - _id: string; - clientSecretId: string; - identityAccessTokenId: string; - authTokenType: string; - } -} - -/** - * Log in user step 1: Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol - * @param req - * @param res - * @returns - */ -export const login1 = async (req: Request, res: Response) => { - const { - body: { email, clientPublicKey } - } = await validateRequest(reqValidator.Login1V1, req); - - const user = await User.findOne({ - email - }).select("+salt +verifier"); - - if (!user) throw new Error("Failed to find user"); - - const server = new jsrp.server(); - server.init( - { - salt: user.salt, - verifier: user.verifier - }, - async () => { - // generate server-side public key - const serverPublicKey = server.getPublicKey(); - - await LoginSRPDetail.findOneAndReplace( - { email: email }, - { - email: email, - clientPublicKey: clientPublicKey, - serverBInt: bigintConversion.bigintToBuf(server.bInt) - }, - { upsert: true, returnNewDocument: false } - ); - - return res.status(200).send({ - serverPublicKey, - salt: user.salt - }); - } - ); -}; - -/** - * Log in user step 2: complete step 2 of SRP protocol and return token and their (encrypted) - * private key - * @param req - * @param res - * @returns - */ -export const login2 = async (req: Request, res: Response) => { - const { - body: { email, clientProof } - } = await validateRequest(reqValidator.Login2V1, req); - - const user = await User.findOne({ - email - }).select("+salt +verifier +publicKey +encryptedPrivateKey +iv +tag"); - - if (!user) throw new Error("Failed to find user"); - - const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: email }); - - if (!loginSRPDetailFromDB) { - return BadRequestError( - Error( - "It looks like some details from the first login are not found. Please try login one again" - ) - ); - } - - const server = new jsrp.server(); - server.init( - { - salt: user.salt, - verifier: user.verifier, - b: loginSRPDetailFromDB.serverBInt - }, - async () => { - server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey); - - // compare server and client shared keys - if (server.checkClientProof(clientProof)) { - // issue tokens - - await checkUserDevice({ - user, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - const tokens = await issueAuthTokens({ - userId: user._id, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - // store (refresh) token in httpOnly cookie - res.cookie("jid", tokens.refreshToken, { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: await getHttpsEnabled() - }); - - // return (access) token in response - return res.status(200).send({ - token: tokens.token, - publicKey: user.publicKey, - encryptedPrivateKey: user.encryptedPrivateKey, - iv: user.iv, - tag: user.tag - }); - } - - return res.status(400).send({ - message: "Failed to authenticate. Try again?" - }); - } - ); -}; - -/** - * Log out user - * @param req - * @param res - * @returns - */ -export const logout = async (req: Request, res: Response) => { - if (req.authData.actor.type === ActorType.USER && req.authData.tokenVersionId) { - await clearTokens(req.authData.tokenVersionId); - } - - // clear httpOnly cookie - res.cookie("jid", "", { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: (await getHttpsEnabled()) as boolean - }); - - return res.status(200).send({ - message: "Successfully logged out." - }); -}; - -export const revokeAllSessions = async (req: Request, res: Response) => { - await TokenVersion.updateMany( - { - user: req.user._id - }, - { - $inc: { - refreshVersion: 1, - accessVersion: 1 - } - } - ); - - return res.status(200).send({ - message: "Successfully revoked all sessions." - }); -}; - -/** - * Return user is authenticated - * @param req - * @param res - * @returns - */ -export const checkAuth = async (req: Request, res: Response) => { - return res.status(200).send({ - message: "Authenticated" - }); -}; - -/** - * Return new JWT access token by first validating the refresh token - * @param req - * @param res - * @returns - */ -export const getNewToken = async (req: Request, res: Response) => { - - const refreshToken = req.cookies.jid; - - if (!refreshToken) - throw BadRequestError({ - message: "Failed to find refresh token in request cookies" - }); - - const decodedToken = jwt.verify(refreshToken, await getAuthSecret()); - - if (decodedToken.authTokenType !== AuthTokenType.REFRESH_TOKEN) throw UnauthorizedRequestError(); - - const user = await User.findOne({ - _id: decodedToken.userId - }).select("+publicKey +refreshVersion +accessVersion"); - - if (!user) throw new Error("Failed to authenticate unfound user"); - if (!user?.publicKey) throw new Error("Failed to authenticate not fully set up account"); - - const tokenVersion = await TokenVersion.findById(decodedToken.tokenVersionId); - - if (!tokenVersion) - throw UnauthorizedRequestError({ - message: "Failed to validate refresh token" - }); - - if (decodedToken.refreshVersion !== tokenVersion.refreshVersion) - throw BadRequestError({ - message: "Failed to validate refresh token" - }); - - const token = createToken({ - payload: { - authTokenType: AuthTokenType.ACCESS_TOKEN, - userId: decodedToken.userId, - tokenVersionId: tokenVersion._id.toString(), - accessVersion: tokenVersion.refreshVersion - }, - expiresIn: await getJwtAuthLifetime(), - secret: await getAuthSecret() - }); - - return res.status(200).send({ - token - }); -}; - -export const handleAuthProviderCallback = (req: Request, res: Response) => { - res.redirect(`/login/provider/success?token=${encodeURIComponent(req.providerAuthToken)}`); -}; \ No newline at end of file diff --git a/backend-mongo/src/controllers/v1/botController.ts b/backend-mongo/src/controllers/v1/botController.ts deleted file mode 100644 index 3a2ff606a..000000000 --- a/backend-mongo/src/controllers/v1/botController.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { Bot, BotKey } from "../../models"; -import { createBot } from "../../helpers/bot"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/bot"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; -import { BadRequestError } from "../../utils/errors"; - -interface BotKey { - encryptedKey: string; - nonce: string; -} - -/** - * Return bot for workspace with id [workspaceId]. If a workspace bot doesn't exist, - * then create and return a new bot. - * @param req - * @param res - * @returns - */ -export const getBotByWorkspaceId = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetBotByWorkspaceIdV1, req); - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - let bot = await Bot.findOne({ - workspace: workspaceId - }); - - if (!bot) { - // case: bot doesn't exist for workspace with id [workspaceId] - // -> create a new bot and return it - bot = await createBot({ - name: "Infisical Bot", - workspaceId: new Types.ObjectId(workspaceId) - }); - } - - return res.status(200).send({ - bot - }); -}; - -/** - * Return bot with id [req.bot._id] with active state set to [isActive]. - * @param req - * @param res - * @returns - */ -export const setBotActiveState = async (req: Request, res: Response) => { - const { - body: { botKey, isActive }, - params: { botId } - } = await validateRequest(reqValidator.SetBotActiveStateV1, req); - - const bot = await Bot.findById(botId); - if (!bot) { - throw BadRequestError({ message: "Bot not found" }); - } - const userId = req.user._id; - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: bot.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Integrations - ); - - if (isActive) { - // bot state set to active -> share workspace key with bot - if (!botKey?.encryptedKey || !botKey?.nonce) { - return res.status(400).send({ - message: "Failed to set bot state to active - missing bot key" - }); - } - - await BotKey.findOneAndUpdate( - { - workspace: bot.workspace - }, - { - encryptedKey: botKey.encryptedKey, - nonce: botKey.nonce, - sender: userId, - bot: bot._id, - workspace: bot.workspace - }, - { - upsert: true, - new: true - } - ); - } else { - // case: bot state set to inactive -> delete bot's workspace key - await BotKey.deleteOne({ - bot: bot._id - }); - } - - const updatedBot = await Bot.findOneAndUpdate( - { - _id: bot._id - }, - { - isActive - }, - { - new: true - } - ); - - if (!updatedBot) throw new Error("Failed to update bot active state"); - - return res.status(200).send({ - bot - }); -}; diff --git a/backend-mongo/src/controllers/v1/index.ts b/backend-mongo/src/controllers/v1/index.ts deleted file mode 100644 index 937936416..000000000 --- a/backend-mongo/src/controllers/v1/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as authController from "./authController"; -import * as universalAuthController from "./universalAuthController"; -import * as botController from "./botController"; -import * as integrationAuthController from "./integrationAuthController"; -import * as integrationController from "./integrationController"; -import * as keyController from "./keyController"; -import * as membershipController from "./membershipController"; -import * as membershipOrgController from "./membershipOrgController"; -import * as organizationController from "./organizationController"; -import * as passwordController from "./passwordController"; -import * as secretController from "./secretController"; -import * as serviceTokenController from "./serviceTokenController"; -import * as signupController from "./signupController"; -import * as userActionController from "./userActionController"; -import * as userController from "./userController"; -import * as workspaceController from "./workspaceController"; -import * as secretScanningController from "./secretScanningController"; -import * as webhookController from "./webhookController"; -import * as secretImpsController from "./secretImpsController"; -import * as adminController from "./adminController"; - -export { - authController, - universalAuthController, - botController, - integrationAuthController, - integrationController, - keyController, - membershipController, - membershipOrgController, - organizationController, - passwordController, - secretController, - serviceTokenController, - signupController, - userActionController, - userController, - workspaceController, - secretScanningController, - webhookController, - secretImpsController, - adminController -}; diff --git a/backend-mongo/src/controllers/v1/integrationAuthController.ts b/backend-mongo/src/controllers/v1/integrationAuthController.ts deleted file mode 100644 index 857fadda5..000000000 --- a/backend-mongo/src/controllers/v1/integrationAuthController.ts +++ /dev/null @@ -1,1303 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { standardRequest } from "../../config/request"; -import { getApps, getTeams, revokeAccess } from "../../integrations"; -import { Bot, IIntegrationAuth, Integration, IntegrationAuth, Workspace } from "../../models"; -import { EventType } from "../../ee/models"; -import { IntegrationService } from "../../services"; -import { EEAuditLogService } from "../../ee/services"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_UTF8, - INTEGRATION_BITBUCKET_API_URL, - INTEGRATION_CHECKLY_API_URL, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_NORTHFLANK_API_URL, - INTEGRATION_QOVERY_API_URL, - INTEGRATION_RAILWAY_API_URL, - INTEGRATION_SET, - INTEGRATION_VERCEL_API_URL, - getIntegrationOptions as getIntegrationOptionsFunc -} from "../../variables"; -import { exchangeRefresh } from "../../integrations"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/integrationAuth"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; -import { getIntegrationAuthAccessHelper } from "../../helpers"; - -/*** - * Return integration authorization with id [integrationAuthId] - */ -export const getIntegrationAuth = async (req: Request, res: Response) => { - const { - params: { integrationAuthId } - } = await validateRequest(reqValidator.GetIntegrationAuthV1, req); - - const integrationAuth = await IntegrationAuth.findById(integrationAuthId); - - if (!integrationAuth) return res.status(400).send({ - message: "Failed to find integration authorization" - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - return res.status(200).send({ - integrationAuth - }); -}; - -export const getIntegrationOptions = async (req: Request, res: Response) => { - const INTEGRATION_OPTIONS = await getIntegrationOptionsFunc(); - - return res.status(200).send({ - integrationOptions: INTEGRATION_OPTIONS - }); -}; - -/** - * Perform OAuth2 code-token exchange as part of integration [integration] for workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const oAuthExchange = async (req: Request, res: Response) => { - const { - body: { integration, workspaceId, code, url } - } = await validateRequest(reqValidator.OauthExchangeV1, req); - if (!INTEGRATION_SET.has(integration)) throw new Error("Failed to validate integration"); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ); - - const workspace = await Workspace.findById(workspaceId); - const environments = workspace?.environments || []; - if (environments.length === 0) { - throw new Error("Failed to get environments"); - } - - const integrationAuth = await IntegrationService.handleOAuthExchange({ - workspaceId, - integration, - code, - environment: environments[0].slug, - url - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.AUTHORIZE_INTEGRATION, - metadata: { - integration: integrationAuth.integration - } - }, - { - workspaceId: integrationAuth.workspace - } - ); - - return res.status(200).send({ - integrationAuth - }); -}; - -/** - * Save integration access token and (optionally) access id as part of integration - * [integration] for workspace with id [workspaceId] - * @param req - * @param res - */ -export const saveIntegrationToken = async (req: Request, res: Response) => { - // TODO: refactor - // TODO: check if access token is valid for each integration - const { - body: { workspaceId, integration, url, accessId, namespace, accessToken, refreshToken } - } = await validateRequest(reqValidator.SaveIntegrationAccessTokenV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ); - - const bot = await Bot.findOne({ - workspace: new Types.ObjectId(workspaceId), - isActive: true - }); - - if (!bot) throw new Error("Bot must be enabled to save integration access token"); - - let integrationAuth = await new IntegrationAuth({ - workspace: new Types.ObjectId(workspaceId), - integration, - url, - namespace, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - ...(integration === INTEGRATION_GCP_SECRET_MANAGER - ? { - metadata: { - authMethod: "serviceAccount" - } - } - : {}) - }).save(); - - // encrypt and save integration access details - if (refreshToken) { - await exchangeRefresh({ - integrationAuth, - refreshToken - }); - } - - // encrypt and save integration access details - if (accessId || accessToken) { - integrationAuth = (await IntegrationService.setIntegrationAuthAccess({ - integrationAuthId: integrationAuth._id.toString(), - accessId, - accessToken, - accessExpiresAt: undefined - })) as IIntegrationAuth; - } - - if (!integrationAuth) throw new Error("Failed to save integration access token"); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.AUTHORIZE_INTEGRATION, - metadata: { - integration: integrationAuth.integration - } - }, - { - workspaceId: integrationAuth.workspace - } - ); - - return res.status(200).send({ - integrationAuth - }); -}; - -/** - * Return list of applications allowed for integration with integration authorization id [integrationAuthId] - * @param req - * @param res - * @returns - */ -export const getIntegrationAuthApps = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { teamId, workspaceSlug } - } = await validateRequest(reqValidator.GetIntegrationAuthAppsV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken, accessId } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - const apps = await getApps({ - integrationAuth: integrationAuth, - accessToken: accessToken, - accessId: accessId, - ...(teamId && { teamId }), - ...(workspaceSlug && { workspaceSlug }) - }); - - return res.status(200).send({ - apps - }); -}; - -/** - * Return list of teams allowed for integration with integration authorization id [integrationAuthId] - * @param req - * @param res - * @returns - */ -export const getIntegrationAuthTeams = async (req: Request, res: Response) => { - const { - params: { integrationAuthId } - } = await validateRequest(reqValidator.GetIntegrationAuthTeamsV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - const teams = await getTeams({ - integrationAuth: integrationAuth, - accessToken: accessToken - }); - - return res.status(200).send({ - teams - }); -}; - -/** - * Return list of available Vercel (preview) branches for Vercel project with - * id [appId] - * @param req - * @param res - */ -export const getIntegrationAuthVercelBranches = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { appId } - } = await validateRequest(reqValidator.GetIntegrationAuthVercelBranchesV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface VercelBranch { - ref: string; - lastCommit: string; - isProtected: boolean; - } - - const params = new URLSearchParams({ - projectId: appId, - ...(integrationAuth.teamId - ? { - teamId: integrationAuth.teamId - } - : {}) - }); - - let branches: string[] = []; - - if (appId && appId !== "") { - const { data }: { data: VercelBranch[] } = await standardRequest.get( - `${INTEGRATION_VERCEL_API_URL}/v1/integrations/git-branches`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - - branches = data.map((b) => b.ref); - } - - return res.status(200).send({ - branches - }); -}; - -/** - * Return list of Checkly groups for a specific user - * @param req - * @param res - */ -export const getIntegrationAuthChecklyGroups = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { accountId } - } = await validateRequest(reqValidator.GetIntegrationAuthChecklyGroupsV1, req); - - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface ChecklyGroup { - id: number; - name: string; - } - - if (accountId && accountId !== "") { - const { data }: { data: ChecklyGroup[] } = ( - await standardRequest.get(`${INTEGRATION_CHECKLY_API_URL}/v1/check-groups`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "X-Checkly-Account": accountId - } - }) - ); - - return res.status(200).send({ - groups: data.map((g: ChecklyGroup) => ({ - name: g.name, - groupId: g.id, - })) - }); - } - - return res.status(200).send({ - groups: [] - }); -} - -/** - * Return list of Qovery Orgs for a specific user - * @param req - * @param res - */ -export const getIntegrationAuthQoveryOrgs = async (req: Request, res: Response) => { - const { - params: { integrationAuthId } - } = await validateRequest(reqValidator.GetIntegrationAuthQoveryOrgsV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - const { data } = await standardRequest.get( - `${INTEGRATION_QOVERY_API_URL}/organization`, - { - headers: { - Authorization: `Token ${accessToken}`, - "Accept": "application/json", - }, - } - ); - - interface QoveryOrg { - id: string; - name: string; - } - - const orgs = data.results.map((a: QoveryOrg) => { - return { - name: a.name, - orgId: a.id, - }; - }); - - return res.status(200).send({ - orgs - }); -}; - -/** - * Return list of Qovery Projects for a specific orgId - * @param req - * @param res - */ -export const getIntegrationAuthQoveryProjects = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { orgId } - } = await validateRequest(reqValidator.GetIntegrationAuthQoveryProjectsV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface Project { - name: string; - projectId: string; - } - - interface QoveryProject { - id: string; - name: string; - } - - let projects: Project[] = []; - - if (orgId && orgId !== "") { - const { data } = await standardRequest.get( - `${INTEGRATION_QOVERY_API_URL}/organization/${orgId}/project`, - { - headers: { - Authorization: `Token ${accessToken}`, - "Accept": "application/json", - }, - } - ); - - projects = data.results.map((a: QoveryProject) => { - return { - name: a.name, - projectId: a.id, - }; - }); - } - - return res.status(200).send({ - projects - }); -}; - -/** - * Return list of Qovery environments for project with id [projectId] - * @param req - * @param res - */ -export const getIntegrationAuthQoveryEnvironments = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { projectId } - } = await validateRequest(reqValidator.GetIntegrationAuthQoveryEnvironmentsV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface Environment { - name: string; - environmentId: string; - } - - interface QoveryEnvironment { - id: string; - name: string; - } - - let environments: Environment[] = []; - - if (projectId && projectId !== "" && projectId !== "none") { // TODO: fix - const { data } = await standardRequest.get( - `${INTEGRATION_QOVERY_API_URL}/project/${projectId}/environment`, - { - headers: { - Authorization: `Token ${accessToken}`, - "Accept": "application/json", - }, - } - ); - - environments = data.results.map((a: QoveryEnvironment) => { - return { - name: a.name, - environmentId: a.id, - }; - }); - } - - return res.status(200).send({ - environments - }); -}; - -/** - * Return list of Qovery apps for environment with id [environmentId] - * @param req - * @param res - */ -export const getIntegrationAuthQoveryApps = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { environmentId } - } = await validateRequest(reqValidator.GetIntegrationAuthQoveryScopesV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface App { - name: string; - appId: string; - } - - interface QoveryApp { - id: string; - name: string; - } - - let apps: App[] = []; - - if (environmentId && environmentId !== "") { - const { data } = await standardRequest.get( - `${INTEGRATION_QOVERY_API_URL}/environment/${environmentId}/application`, - { - headers: { - Authorization: `Token ${accessToken}`, - "Accept": "application/json", - }, - } - ); - - apps = data.results.map((a: QoveryApp) => { - return { - name: a.name, - appId: a.id, - }; - }); - } - - return res.status(200).send({ - apps - }); -}; - -/** - * Return list of Qovery containers for environment with id [environmentId] - * @param req - * @param res - */ -export const getIntegrationAuthQoveryContainers = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { environmentId } - } = await validateRequest(reqValidator.GetIntegrationAuthQoveryScopesV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface Container { - name: string; - appId: string; - } - - interface QoveryContainer { - id: string; - name: string; - } - - let containers: Container[] = []; - - if (environmentId && environmentId !== "") { - const { data } = await standardRequest.get( - `${INTEGRATION_QOVERY_API_URL}/environment/${environmentId}/container`, - { - headers: { - Authorization: `Token ${accessToken}`, - "Accept": "application/json", - }, - } - ); - - containers = data.results.map((a: QoveryContainer) => { - return { - name: a.name, - appId: a.id, - }; - }); - } - - return res.status(200).send({ - containers - }); -}; - -/** - * Return list of Qovery jobs for environment with id [environmentId] - * @param req - * @param res - */ -export const getIntegrationAuthQoveryJobs = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { environmentId } - } = await validateRequest(reqValidator.GetIntegrationAuthQoveryScopesV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface Job { - name: string; - appId: string; - } - - interface QoveryJob { - id: string; - name: string; - } - - let jobs: Job[] = []; - - if (environmentId && environmentId !== "") { - const { data } = await standardRequest.get( - `${INTEGRATION_QOVERY_API_URL}/environment/${environmentId}/job`, - { - headers: { - Authorization: `Token ${accessToken}`, - "Accept": "application/json", - }, - } - ); - - jobs = data.results.map((a: QoveryJob) => { - return { - name: a.name, - appId: a.id, - }; - }); - } - - return res.status(200).send({ - jobs - }); -}; - -/** - * Return list of Railway environments for Railway project with - * id [appId] - * @param req - * @param res - */ -export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { appId } - } = await validateRequest(reqValidator.GetIntegrationAuthRailwayEnvironmentsV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface RailwayEnvironment { - node: { - id: string; - name: string; - isEphemeral: boolean; - }; - } - - interface Environment { - environmentId: string; - name: string; - } - - let environments: Environment[] = []; - - if (appId && appId !== "") { - const query = ` - query GetEnvironments($projectId: String!, $after: String, $before: String, $first: Int, $isEphemeral: Boolean, $last: Int) { - environments(projectId: $projectId, after: $after, before: $before, first: $first, isEphemeral: $isEphemeral, last: $last) { - edges { - node { - id - name - isEphemeral - } - } - } - } - `; - - const variables = { - projectId: appId - }; - - const { - data: { - data: { - environments: { edges } - } - } - } = await standardRequest.post( - INTEGRATION_RAILWAY_API_URL, - { - query, - variables - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json" - } - } - ); - - environments = edges.map((e: RailwayEnvironment) => { - return { - name: e.node.name, - environmentId: e.node.id - }; - }); - } - - return res.status(200).send({ - environments - }); -}; - -/** - * Return list of Railway services for Railway project with id - * [appId] - * @param req - * @param res - */ -export const getIntegrationAuthRailwayServices = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { appId } - } = await validateRequest(reqValidator.GetIntegrationAuthRailwayServicesV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface RailwayService { - node: { - id: string; - name: string; - }; - } - - interface Service { - name: string; - serviceId: string; - } - - let services: Service[] = []; - - const query = ` - query project($id: String!) { - project(id: $id) { - createdAt - deletedAt - id - description - expiredAt - isPublic - isTempProject - isUpdatable - name - prDeploys - teamId - updatedAt - upstreamUrl - services { - edges { - node { - id - name - } - } - } - } - } - `; - - if (appId && appId !== "") { - const variables = { - id: appId - }; - - const { - data: { - data: { - project: { - services: { edges } - } - } - } - } = await standardRequest.post( - INTEGRATION_RAILWAY_API_URL, - { - query, - variables - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json" - } - } - ); - - services = edges.map((e: RailwayService) => ({ - name: e.node.name, - serviceId: e.node.id - })); - } - - return res.status(200).send({ - services - }); -}; - -/** - * Return list of workspaces allowed for Bitbucket integration - * @param req - * @param res - * @returns - */ -export const getIntegrationAuthBitBucketWorkspaces = async (req: Request, res: Response) => { - interface WorkspaceResponse { - size: number; - page: number; - pageLen: number; - next: string; - previous: string; - values: Array; - } - - interface Workspace { - type: string; - uuid: string; - name: string; - slug: string; - is_private: boolean; - created_on: string; - updated_on: string; - } - - const { - params: { integrationAuthId } - } = await validateRequest(reqValidator.GetIntegrationAuthBitbucketWorkspacesV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - const workspaces: Workspace[] = []; - let hasNextPage = true; - let workspaceUrl = `${INTEGRATION_BITBUCKET_API_URL}/2.0/workspaces`; - - while (hasNextPage) { - const { data }: { data: WorkspaceResponse } = await standardRequest.get(workspaceUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }); - - if (data?.values.length > 0) { - data.values.forEach((workspace) => { - workspaces.push(workspace); - }); - } - - if (data.next) { - workspaceUrl = data.next; - } else { - hasNextPage = false; - } - } - - return res.status(200).send({ - workspaces - }); -}; - -/** - * Return list of secret groups for Northflank project with id [appId] - * @param req - * @param res - * @returns - */ -export const getIntegrationAuthNorthflankSecretGroups = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { appId } - } = await validateRequest(reqValidator.GetIntegrationAuthNorthflankSecretGroupsV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface NorthflankSecretGroup { - id: string; - name: string; - description: string; - priority: number; - projectId: string; - } - - interface SecretGroup { - name: string; - groupId: string; - } - - const secretGroups: SecretGroup[] = []; - - if (appId && appId !== "") { - let page = 1; - const perPage = 10; - let hasMorePages = true; - - while (hasMorePages) { - const params = new URLSearchParams({ - page: String(page), - per_page: String(perPage), - filter: "all" - }); - - const { - data: { - data: { secrets } - } - } = await standardRequest.get<{ data: { secrets: NorthflankSecretGroup[] } }>( - `${INTEGRATION_NORTHFLANK_API_URL}/v1/projects/${appId}/secrets`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - - secrets.forEach((a: any) => { - secretGroups.push({ - name: a.name, - groupId: a.id - }); - }); - - if (secrets.length < perPage) { - hasMorePages = false; - } - - page++; - } - } - - return res.status(200).send({ - secretGroups - }); -}; - -/** - * Return list of build configs for TeamCity project with id [appId] - * @param req - * @param res - * @returns - */ -export const getIntegrationAuthTeamCityBuildConfigs = async (req: Request, res: Response) => { - const { - params: { integrationAuthId }, - query: { appId } - } = await validateRequest(reqValidator.GetIntegrationAuthTeamCityBuildConfigsV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - interface TeamCityBuildConfig { - id: string; - name: string; - projectName: string; - projectId: string; - href: string; - webUrl: string; - } - - interface GetTeamCityBuildConfigsRes { - count: number; - href: string; - buildType: TeamCityBuildConfig[]; - } - - if (appId && appId !== "") { - const { - data: { buildType } - } = await standardRequest.get( - `${integrationAuth.url}/app/rest/buildTypes`, - { - params: { - locator: `project:${appId}` - }, - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - - return res.status(200).send({ - buildConfigs: buildType.map((buildConfig) => ({ - name: buildConfig.name, - buildConfigId: buildConfig.id - })) - }); - } - - return res.status(200).send({ - buildConfigs: [] - }); -}; - -/** - * Delete all integration authorizations and integrations for workspace with id [workspaceId] - * with integration name [integration] - * @param req - * @param res - * @returns - */ -export const deleteIntegrationAuths = async (req: Request, res: Response) => { - const { - query: { integration, workspaceId } - } = await validateRequest(reqValidator.DeleteIntegrationAuthsV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Integrations - ); - - const integrationAuths = await IntegrationAuth.deleteMany({ - integration, - workspace: new Types.ObjectId(workspaceId) - }); - - const integrations = await Integration.deleteMany({ - integration, - workspace: new Types.ObjectId(workspaceId) - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UNAUTHORIZE_INTEGRATION, - metadata: { - integration - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.status(200).send({ - integrationAuths, - integrations - }); -} - -/** - * Delete integration authorization with id [integrationAuthId] - * @param req - * @param res - * @returns - */ -export const deleteIntegrationAuthById = async (req: Request, res: Response) => { - const { - params: { integrationAuthId } - } = await validateRequest(reqValidator.DeleteIntegrationAuthV1, req); - - // TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions - const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({ - integrationAuthId: new Types.ObjectId(integrationAuthId) - }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Integrations - ); - - const deletedIntegrationAuth = await revokeAccess({ - integrationAuth: integrationAuth, - accessToken: accessToken - }); - - if (!deletedIntegrationAuth) - return res.status(400).send({ - message: "Failed to find integration authorization" - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UNAUTHORIZE_INTEGRATION, - metadata: { - integration: deletedIntegrationAuth.integration - } - }, - { - workspaceId: deletedIntegrationAuth.workspace - } - ); - - return res.status(200).send({ - integrationAuth: deletedIntegrationAuth - }); -}; diff --git a/backend-mongo/src/controllers/v1/integrationController.ts b/backend-mongo/src/controllers/v1/integrationController.ts deleted file mode 100644 index 837936af7..000000000 --- a/backend-mongo/src/controllers/v1/integrationController.ts +++ /dev/null @@ -1,322 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { Folder, IWorkspace, Integration, IntegrationAuth } from "../../models"; -import { EventService } from "../../services"; -import { eventStartIntegration } from "../../events"; -import { getFolderByPath } from "../../services/FolderService"; -import { BadRequestError } from "../../utils/errors"; -import { EEAuditLogService } from "../../ee/services"; -import { EventType } from "../../ee/models"; -import { syncSecretsToActiveIntegrationsQueue } from "../../queues/integrations/syncSecretsToThirdPartyServices"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/integration"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; - -/** - * Create/initialize an (empty) integration for integration authorization - * @param req - * @param res - * @returns - */ -export const createIntegration = async (req: Request, res: Response) => { - const { - body: { - isActive, - sourceEnvironment, - secretPath, - app, - path, - appId, - owner, - region, - scope, - targetService, - targetServiceId, - integrationAuthId, - targetEnvironment, - targetEnvironmentId, - metadata - } - } = await validateRequest(reqValidator.CreateIntegrationV1, req); - - const integrationAuth = await IntegrationAuth.findById(integrationAuthId) - .populate<{ workspace: IWorkspace }>("workspace") - .select( - "+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt" - ); - - if (!integrationAuth) throw BadRequestError({ message: "Integration auth not found" }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integrationAuth.workspace._id - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ); - - const folders = await Folder.findOne({ - workspace: integrationAuth.workspace._id, - environment: sourceEnvironment - }); - - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath); - if (!folder) { - throw BadRequestError({ - message: "Folder path doesn't exist" - }); - } - } - - // TODO: validate [sourceEnvironment] and [targetEnvironment] - - // initialize new integration after saving integration access token - const integration = await new Integration({ - workspace: integrationAuth.workspace._id, - environment: sourceEnvironment, - isActive, - app, - appId, - targetEnvironment, - targetEnvironmentId, - targetService, - targetServiceId, - owner, - path, - region, - scope, - secretPath, - integration: integrationAuth.integration, - integrationAuth: new Types.ObjectId(integrationAuthId), - metadata - }).save(); - - if (integration) { - // trigger event - push secrets - EventService.handleEvent({ - event: eventStartIntegration({ - workspaceId: integration.workspace, - environment: sourceEnvironment - }) - }); - } - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_INTEGRATION, - metadata: { - integrationId: integration._id.toString(), - integration: integration.integration, - environment: integration.environment, - secretPath, - url: integration.url, - app: integration.app, - appId: integration.appId, - targetEnvironment: integration.targetEnvironment, - targetEnvironmentId: integration.targetEnvironmentId, - targetService: integration.targetService, - targetServiceId: integration.targetServiceId, - path: integration.path, - region: integration.region - } - }, - { - workspaceId: integration.workspace - } - ); - - return res.status(200).send({ - integration - }); -}; - -/** - * Change environment or name of integration with id [integrationId] - * @param req - * @param res - * @returns - */ -export const updateIntegration = async (req: Request, res: Response) => { - // TODO: add integration-specific validation to ensure that each - // integration has the correct fields populated in [Integration] - - const { - body: { - environment, - isActive, - app, - appId, - targetEnvironment, - owner, // github-specific integration param - secretPath - }, - params: { integrationId } - } = await validateRequest(reqValidator.UpdateIntegrationV1, req); - - const integration = await Integration.findById(integrationId); - if (!integration) throw BadRequestError({ message: "Integration not found" }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integration.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Integrations - ); - - const folders = await Folder.findOne({ - workspace: integration.workspace, - environment - }); - - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath); - if (!folder) { - throw BadRequestError({ - message: "Path for service token does not exist" - }); - } - } - - const updatedIntegration = await Integration.findOneAndUpdate( - { - _id: integration._id - }, - { - environment, - isActive, - app, - appId, - targetEnvironment, - owner, - secretPath - }, - { - new: true - } - ); - - if (updatedIntegration) { - // trigger event - push secrets - EventService.handleEvent({ - event: eventStartIntegration({ - workspaceId: updatedIntegration.workspace, - environment - }) - }); - } - - return res.status(200).send({ - integration: updatedIntegration - }); -}; - -/** - * Delete integration with id [integrationId] - * @param req - * @param res - * @returns - */ -export const deleteIntegration = async (req: Request, res: Response) => { - const { - params: { integrationId } - } = await validateRequest(reqValidator.DeleteIntegrationV1, req); - - const integration = await Integration.findById(integrationId); - if (!integration) throw BadRequestError({ message: "Integration not found" }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: integration.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Integrations - ); - - const deletedIntegration = await Integration.findOneAndDelete({ - _id: integrationId - }); - - if (!deletedIntegration) throw new Error("Failed to find integration"); - - const numOtherIntegrationsUsingSameAuth = await Integration.countDocuments({ - integrationAuth: deletedIntegration.integrationAuth, - _id: { - $nin: [deletedIntegration._id] - } - }); - - if (numOtherIntegrationsUsingSameAuth === 0) { - // no other integrations are using the same integration auth - // -> delete integration auth associated with the integration being deleted - await IntegrationAuth.deleteOne({ - _id: deletedIntegration.integrationAuth - }); - } - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.DELETE_INTEGRATION, - metadata: { - integrationId: integration._id.toString(), - integration: integration.integration, - environment: integration.environment, - secretPath: integration.secretPath, - url: integration.url, - app: integration.app, - appId: integration.appId, - targetEnvironment: integration.targetEnvironment, - targetEnvironmentId: integration.targetEnvironmentId, - targetService: integration.targetService, - targetServiceId: integration.targetServiceId, - path: integration.path, - region: integration.region - } - }, - { - workspaceId: integration.workspace - } - ); - - return res.status(200).send({ - integration - }); -}; - -// Will trigger sync for all integrations within the given env and workspace id -export const manualSync = async (req: Request, res: Response) => { - const { - body: { workspaceId, environment } - } = await validateRequest(reqValidator.ManualSyncV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Integrations - ); - - syncSecretsToActiveIntegrationsQueue({ - workspaceId, - environment - }); - - res.status(200).send(); -}; diff --git a/backend-mongo/src/controllers/v1/keyController.ts b/backend-mongo/src/controllers/v1/keyController.ts deleted file mode 100644 index 956487814..000000000 --- a/backend-mongo/src/controllers/v1/keyController.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Types } from "mongoose"; -import { Request, Response } from "express"; -import { Key } from "../../models"; -import { findMembership } from "../../helpers/membership"; -import { EventType } from "../../ee/models"; -import { EEAuditLogService } from "../../ee/services"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/key"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; - -/** - * Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with - * id [key.userId] - * @param req - * @param res - * @returns - */ -export const uploadKey = async (req: Request, res: Response) => { - const { - params: { workspaceId }, - body: { key } - } = await validateRequest(reqValidator.UploadKeyV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Member - ); - - // validate membership of receiver - const receiverMembership = await findMembership({ - user: key.userId, - workspace: workspaceId - }); - - if (!receiverMembership) { - throw new Error("Failed receiver membership validation for workspace"); - } - - await new Key({ - encryptedKey: key.encryptedKey, - nonce: key.nonce, - sender: req.user._id, - receiver: key.userId, - workspace: workspaceId - }).save(); - - return res.status(200).send({ - message: "Successfully uploaded key to workspace" - }); -}; - -/** - * Return latest (encrypted) copy of workspace key for user - * @param req - * @param res - * @returns - */ -export const getLatestKey = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetLatestKeyV1, req); - - // get latest key - const latestKey = await Key.find({ - workspace: workspaceId, - receiver: req.user._id - }) - .sort({ createdAt: -1 }) - .limit(1) - .populate("sender", "+publicKey"); - - const resObj: any = {}; - - if (latestKey.length > 0) { - resObj["latestKey"] = latestKey[0]; - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.GET_WORKSPACE_KEY, - metadata: { - keyId: latestKey[0]._id.toString() - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - } - - return res.status(200).send(resObj); -}; diff --git a/backend-mongo/src/controllers/v1/membershipController.ts b/backend-mongo/src/controllers/v1/membershipController.ts deleted file mode 100644 index 350cddc5f..000000000 --- a/backend-mongo/src/controllers/v1/membershipController.ts +++ /dev/null @@ -1,286 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { IUser, Key, Membership, MembershipOrg, User, Workspace } from "../../models"; -import { EventType, Role } from "../../ee/models"; -import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership"; -import { sendMail } from "../../helpers/nodemailer"; -import { ACCEPTED, ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../../variables"; -import { getSiteURL } from "../../config"; -import { EEAuditLogService, EELicenseService } from "../../ee/services"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/membership"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; -import { BadRequestError } from "../../utils/errors"; -import { InviteUserToWorkspaceV1 } from "../../validation/workspace"; - -/** - * Check that user is a member of workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const validateMembership = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.ValidateMembershipV1, req); - - // validate membership - const membership = await findMembership({ - user: req.user._id, - workspace: workspaceId - }); - - if (!membership) { - throw new Error("Failed to validate membership"); - } - - return res.status(200).send({ - message: "Workspace membership confirmed" - }); -}; - -/** - * Delete membership with id [membershipId] - * @param req - * @param res - * @returns - */ -export const deleteMembership = async (req: Request, res: Response) => { - const { - params: { membershipId } - } = await validateRequest(reqValidator.DeleteMembershipV1, req); - - // check if membership to delete exists - const membershipToDelete = await Membership.findOne({ - _id: membershipId - }).populate<{ user: IUser }>("user"); - - if (!membershipToDelete) { - throw new Error("Failed to delete workspace membership that doesn't exist"); - } - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: membershipToDelete.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Member - ); - - // delete workspace membership - const deletedMembership = await deleteMember({ - membershipId: membershipToDelete._id.toString() - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.REMOVE_WORKSPACE_MEMBER, - metadata: { - userId: membershipToDelete.user._id.toString(), - email: membershipToDelete.user.email - } - }, - { - workspaceId: membershipToDelete.workspace - } - ); - - return res.status(200).send({ - deletedMembership - }); -}; - -/** - * Change and return workspace membership role - * @param req - * @param res - * @returns - */ -export const changeMembershipRole = async (req: Request, res: Response) => { - const { - body: { role }, - params: { membershipId } - } = await validateRequest(reqValidator.ChangeMembershipRoleV1, req); - - // validate target membership - const membershipToChangeRole = await Membership.findById(membershipId).populate<{ user: IUser }>( - "user" - ); - - if (!membershipToChangeRole) { - throw new Error("Failed to find membership to change role"); - } - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: membershipToChangeRole.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Member - ); - - const isCustomRole = ![ADMIN, MEMBER, VIEWER, NO_ACCESS].includes(role); - if (isCustomRole) { - const wsRole = await Role.findOne({ - slug: role, - isOrgRole: false, - workspace: membershipToChangeRole.workspace - }); - if (!wsRole) throw BadRequestError({ message: "Role not found" }); - - const plan = await EELicenseService.getPlan(wsRole.organization); - - if (!plan.rbac) return res.status(400).send({ - message: "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." - }); - - const membership = await Membership.findByIdAndUpdate(membershipId, { - role: CUSTOM, - customRole: wsRole - }); - return res.status(200).send({ - membership - }); - } - - const membership = await Membership.findByIdAndUpdate( - membershipId, - { - $set: { - role - }, - $unset: { - customRole: 1 - } - }, - { - new: true - } - ); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_USER_WORKSPACE_ROLE, - metadata: { - userId: membershipToChangeRole.user._id.toString(), - email: membershipToChangeRole.user.email, - oldRole: membershipToChangeRole.role, - newRole: role - } - }, - { - workspaceId: membershipToChangeRole.workspace - } - ); - - return res.status(200).send({ - membership - }); -}; - -/** - * Add user with email [email] to workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const inviteUserToWorkspace = async (req: Request, res: Response) => { - const { - params: { workspaceId }, - body: { email } - } = await validateRequest(InviteUserToWorkspaceV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Member - ); - - const invitee = await User.findOne({ - email - }).select("+publicKey"); - - if (!invitee || !invitee?.publicKey) throw new Error("Failed to validate invitee"); - - // validate invitee's workspace membership - ensure member isn't - // already a member of the workspace - const inviteeMembership = await Membership.findOne({ - user: invitee._id, - workspace: workspaceId - }).populate<{ user: IUser }>("user"); - - if (inviteeMembership) throw new Error("Failed to add existing member of workspace"); - - const workspace = await Workspace.findById(workspaceId); - if (!workspace) throw new Error("Failed to find workspace"); - // validate invitee's organization membership - ensure that only - // (accepted) organization members can be added to the workspace - const membershipOrg = await MembershipOrg.findOne({ - user: invitee._id, - organization: workspace.organization, - status: ACCEPTED - }); - - if (!membershipOrg) throw new Error("Failed to validate invitee's organization membership"); - - // get latest key - const latestKey = await Key.findOne({ - workspace: workspaceId, - receiver: req.user._id - }) - .sort({ createdAt: -1 }) - .populate("sender", "+publicKey"); - - // create new workspace membership - await new Membership({ - user: invitee._id, - workspace: workspaceId, - role: MEMBER - }).save(); - - await sendMail({ - template: "workspaceInvitation.handlebars", - subjectLine: "Infisical workspace invitation", - recipients: [invitee.email], - substitutions: { - inviterFirstName: req.user.firstName, - inviterEmail: req.user.email, - workspaceName: workspace.name, - callback_url: (await getSiteURL()) + "/login" - } - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.ADD_WORKSPACE_MEMBER, - metadata: { - userId: invitee._id.toString(), - email: invitee.email - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.status(200).send({ - invitee, - latestKey - }); -}; diff --git a/backend-mongo/src/controllers/v1/membershipOrgController.ts b/backend-mongo/src/controllers/v1/membershipOrgController.ts deleted file mode 100644 index f212892ee..000000000 --- a/backend-mongo/src/controllers/v1/membershipOrgController.ts +++ /dev/null @@ -1,292 +0,0 @@ -import { Types } from "mongoose"; -import { Request, Response } from "express"; -import { MembershipOrg, Organization, User } from "../../models"; -import { SSOConfig } from "../../ee/models"; -import { deleteMembershipOrg as deleteMemberFromOrg } from "../../helpers/membershipOrg"; -import { createToken } from "../../helpers/auth"; -import { updateSubscriptionOrgQuantity } from "../../helpers/organization"; -import { sendMail } from "../../helpers/nodemailer"; -import { TokenService } from "../../services"; -import { EELicenseService } from "../../ee/services"; -import { ACCEPTED, AuthTokenType, INVITED, MEMBER, TOKEN_EMAIL_ORG_INVITATION } from "../../variables"; -import * as reqValidator from "../../validation/membershipOrg"; -import { - getAuthSecret, - getJwtSignupLifetime, - getSiteURL, - getSmtpConfigured -} from "../../config"; -import { validateUserEmail } from "../../validation"; -import { validateRequest } from "../../helpers/validation"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - getAuthDataOrgPermissions -} from "../../ee/services/RoleService"; -import { ForbiddenError } from "@casl/ability"; - -/** - * Delete organization membership with id [membershipOrgId] from organization - * @param req - * @param res - * @returns - */ -export const deleteMembershipOrg = async (req: Request, _res: Response) => { - const { - params: { membershipOrgId } - } = await validateRequest(reqValidator.DelOrgMembershipv1, req); - - // check if organization membership to delete exists - const membershipOrgToDelete = await MembershipOrg.findOne({ - _id: membershipOrgId - }).populate("user"); - - if (!membershipOrgToDelete) { - throw new Error("Failed to delete organization membership that doesn't exist"); - } - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: membershipOrgToDelete.organization - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Member - ); - - // delete organization membership - await deleteMemberFromOrg({ - membershipOrgId: membershipOrgToDelete._id.toString() - }); - - await updateSubscriptionOrgQuantity({ - organizationId: membershipOrgToDelete.organization.toString() - }); - - return membershipOrgToDelete; -}; - -/** - * Change and return organization membership role - * @param req - * @param res - * @returns - */ -export const changeMembershipOrgRole = async (req: Request, res: Response) => { - // change role for (target) organization membership with id - // [membershipOrgId] - - let membershipToChangeRole; - - return res.status(200).send({ - membershipOrg: membershipToChangeRole - }); -}; - -/** - * Organization invitation step 1: Send email invitation to user with email [email] - * for organization with id [organizationId] containing magic link - * @param req - * @param res - * @returns - */ -export const inviteUserToOrganization = async (req: Request, res: Response) => { - let inviteeMembershipOrg, completeInviteLink; - const { - body: { inviteeEmail, organizationId } - } = await validateRequest(reqValidator.InviteUserToOrgv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Member - ); - - const host = req.headers.host; - const siteUrl = `${req.protocol}://${host}`; - const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId)); - - const ssoConfig = await SSOConfig.findOne({ - organization: new Types.ObjectId(organizationId) - }); - - if (ssoConfig && ssoConfig.isActive) { - // case: SAML SSO is enabled for the organization - return res.status(400).send({ - message: "Failed to invite member due to SAML SSO configured for organization" - }); - } - - if (plan.memberLimit !== null) { - // case: limit imposed on number of members allowed - - if (plan.membersUsed >= plan.memberLimit) { - // case: number of members used exceeds the number of members allowed - return res.status(400).send({ - message: - "Failed to invite member due to member limit reached. Upgrade plan to invite more members." - }); - } - } - - const invitee = await User.findOne({ - email: inviteeEmail - }).select("+publicKey"); - - if (invitee) { - // case: invitee is an existing user - - inviteeMembershipOrg = await MembershipOrg.findOne({ - user: invitee._id, - organization: organizationId - }); - - if (inviteeMembershipOrg && inviteeMembershipOrg.status === ACCEPTED) { - throw new Error("Failed to invite an existing member of the organization"); - } - - if (!inviteeMembershipOrg) { - await new MembershipOrg({ - user: invitee, - inviteEmail: inviteeEmail, - organization: organizationId, - role: MEMBER, - status: INVITED - }).save(); - } - } else { - // check if invitee has been invited before - inviteeMembershipOrg = await MembershipOrg.findOne({ - inviteEmail: inviteeEmail, - organization: organizationId - }); - - if (!inviteeMembershipOrg) { - // case: invitee has never been invited before - - // validate that email is not disposable - validateUserEmail(inviteeEmail); - - await new MembershipOrg({ - inviteEmail: inviteeEmail, - organization: organizationId, - role: MEMBER, - status: INVITED - }).save(); - } - } - - const organization = await Organization.findOne({ _id: organizationId }); - - if (organization) { - const token = await TokenService.createToken({ - type: TOKEN_EMAIL_ORG_INVITATION, - email: inviteeEmail, - organizationId: organization._id - }); - - await sendMail({ - template: "organizationInvitation.handlebars", - subjectLine: "Infisical organization invitation", - recipients: [inviteeEmail], - substitutions: { - inviterFirstName: req.user.firstName, - inviterEmail: req.user.email, - organizationName: organization.name, - email: inviteeEmail, - organizationId: organization._id.toString(), - token, - callback_url: (await getSiteURL()) + "/signupinvite" - } - }); - - if (!(await getSmtpConfigured())) { - completeInviteLink = `${ - siteUrl + "/signupinvite" - }?token=${token}&to=${inviteeEmail}&organization_id=${organization._id}`; - } - } - - await updateSubscriptionOrgQuantity({ organizationId }); - - return res.status(200).send({ - message: `Sent an invite link to ${req.body.inviteeEmail}`, - completeInviteLink - }); -}; - -/** - * Organization invitation step 2: Verify that code [code] was sent to email [email] as part of - * magic link and issue a temporary signup token for user to complete setting up their account - * @param req - * @param res - * @returns - */ -export const verifyUserToOrganization = async (req: Request, res: Response) => { - let user; - - const { - body: { organizationId, email, code } - } = await validateRequest(reqValidator.VerifyUserToOrgv1, req); - - user = await User.findOne({ email }).select("+publicKey"); - - const membershipOrg = await MembershipOrg.findOne({ - inviteEmail: email, - status: INVITED, - organization: new Types.ObjectId(organizationId) - }); - - if (!membershipOrg) throw new Error("Failed to find any invitations for email"); - - await TokenService.validateToken({ - type: TOKEN_EMAIL_ORG_INVITATION, - email, - organizationId: membershipOrg.organization, - token: code - }); - - if (user && user?.publicKey) { - // case: user has already completed account - // membership can be approved and redirected to login/dashboard - membershipOrg.status = ACCEPTED; - await membershipOrg.save(); - - await updateSubscriptionOrgQuantity({ - organizationId - }); - - return res.status(200).send({ - message: "Successfully verified email", - user - }); - } - - if (!user) { - // initialize user account - user = await new User({ - email - }).save(); - } - - // generate temporary signup token - const token = createToken({ - payload: { - authTokenType: AuthTokenType.SIGNUP_TOKEN, - userId: user._id.toString() - }, - expiresIn: await getJwtSignupLifetime(), - secret: await getAuthSecret() - }); - - return res.status(200).send({ - message: "Successfully verified email", - user, - token - }); -}; diff --git a/backend-mongo/src/controllers/v1/organizationController.ts b/backend-mongo/src/controllers/v1/organizationController.ts deleted file mode 100644 index 676cb5572..000000000 --- a/backend-mongo/src/controllers/v1/organizationController.ts +++ /dev/null @@ -1,387 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { - IncidentContactOrg, - Membership, - MembershipOrg, - Organization, - Workspace -} from "../../models"; -import { getLicenseServerUrl, getSiteURL } from "../../config"; -import { licenseServerKeyRequest } from "../../config/request"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/organization"; -import { ACCEPTED } from "../../variables"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - getAuthDataOrgPermissions -} from "../../ee/services/RoleService"; -import { OrganizationNotFoundError } from "../../utils/errors"; -import { ForbiddenError } from "@casl/ability"; - -export const getOrganizations = async (req: Request, res: Response) => { - const organizations = ( - await MembershipOrg.find({ - user: req.user._id, - status: ACCEPTED - }).populate("organization") - ).map((m) => m.organization); - - return res.status(200).send({ - organizations - }); -}; - -/** - * Return organization with id [organizationId] - * @param req - * @param res - * @returns - */ -export const getOrganization = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgv1, req); - - // ensure user has membership - await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }) - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - return res.status(200).send({ - organization - }); -}; - -/** - * Return organization memberships for organization with id [organizationId] - * @param req - * @param res - * @returns - */ -export const getOrganizationMembers = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgMembersv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Member - ); - - const users = await MembershipOrg.find({ - organization: organizationId - }).populate("user", "+publicKey"); - - return res.status(200).send({ - users - }); -}; - -/** - * Return workspaces that user is part of in organization with id [organizationId] - * @param req - * @param res - * @returns - */ -export const getOrganizationWorkspaces = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgWorkspacesv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }) - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Workspace - ); - - const workspacesSet = new Set( - ( - await Workspace.find( - { - organization: organizationId - }, - "_id" - ) - ).map((w) => w._id.toString()) - ); - - const workspaces = ( - await Membership.find({ - user: req.user._id - }).populate("workspace") - ) - .filter((m) => workspacesSet.has(m.workspace._id.toString())) - .map((m) => m.workspace); - - return res.status(200).send({ - workspaces - }); -}; - -/** - * Change name of organization with id [organizationId] to [name] - * @param req - * @param res - * @returns - */ -export const changeOrganizationName = async (req: Request, res: Response) => { - const { - params: { organizationId }, - body: { name } - } = await validateRequest(reqValidator.ChangeOrgNamev1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Settings - ); - - const organization = await Organization.findOneAndUpdate( - { - _id: organizationId - }, - { - name - }, - { - new: true - } - ); - - return res.status(200).send({ - message: "Successfully changed organization name", - organization - }); -}; - -/** - * Return incident contacts of organization with id [organizationId] - * @param req - * @param res - * @returns - */ -export const getOrganizationIncidentContacts = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgIncidentContactv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.IncidentAccount - ); - - const incidentContactsOrg = await IncidentContactOrg.find({ - organization: organizationId - }); - - return res.status(200).send({ - incidentContactsOrg - }); -}; - -/** - * Add and return new incident contact with email [email] for organization with id [organizationId] - * @param req - * @param res - * @returns - */ -export const addOrganizationIncidentContact = async (req: Request, res: Response) => { - const { - params: { organizationId }, - body: { email } - } = await validateRequest(reqValidator.CreateOrgIncideContact, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.IncidentAccount - ); - - const incidentContactOrg = await IncidentContactOrg.findOneAndUpdate( - { email, organization: organizationId }, - { email, organization: organizationId }, - { upsert: true, new: true } - ); - - return res.status(200).send({ - incidentContactOrg - }); -}; - -/** - * Delete incident contact with email [email] for organization with id [organizationId] - * @param req - * @param res - * @returns - */ -export const deleteOrganizationIncidentContact = async (req: Request, res: Response) => { - const { - params: { organizationId }, - body: { email } - } = await validateRequest(reqValidator.DelOrgIncideContact, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.IncidentAccount - ); - - const incidentContactOrg = await IncidentContactOrg.findOneAndDelete({ - email, - organization: organizationId - }); - - return res.status(200).send({ - message: "Successfully deleted organization incident contact", - incidentContactOrg - }); -}; - -/** - * Redirect user to billing portal or add card page depending on - * if there is a card on file - * @param req - * @param res - * @returns - */ -export const createOrganizationPortalSession = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgPlanBillingInfov1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { - data: { pmtMethods } - } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details/payment-methods` - ); - - if (pmtMethods.length < 1) { - // case: organization has no payment method on file - // -> redirect to add payment method portal - const { - data: { url } - } = await licenseServerKeyRequest.post( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details/payment-methods`, - { - success_url: (await getSiteURL()) + "/dashboard", - cancel_url: (await getSiteURL()) + "/dashboard" - } - ); - return res.status(200).send({ url }); - } else { - // case: organization has payment method on file - // -> redirect to billing portal - const { - data: { url } - } = await licenseServerKeyRequest.post( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details/billing-portal`, - { - return_url: (await getSiteURL()) + "/dashboard" - } - ); - return res.status(200).send({ url }); - } -}; - -/** - * Given a org id, return the projects each member of the org belongs to - * @param req - * @param res - * @returns - */ -export const getOrganizationMembersAndTheirWorkspaces = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgMembersv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Member - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Workspace - ); - - const workspacesSet = ( - await Workspace.find( - { - organization: organizationId - }, - "_id" - ) - ).map((w) => w._id.toString()); - - const memberships = await Membership.find({ - workspace: { $in: workspacesSet } - }).populate("workspace"); - const userToWorkspaceIds: any = {}; - - memberships.forEach((membership) => { - const user = membership.user.toString(); - if (userToWorkspaceIds[user]) { - userToWorkspaceIds[user].push(membership.workspace); - } else { - userToWorkspaceIds[user] = [membership.workspace]; - } - }); - - return res.json(userToWorkspaceIds); -}; diff --git a/backend-mongo/src/controllers/v1/passwordController.ts b/backend-mongo/src/controllers/v1/passwordController.ts deleted file mode 100644 index d0b59f317..000000000 --- a/backend-mongo/src/controllers/v1/passwordController.ts +++ /dev/null @@ -1,370 +0,0 @@ -import { Request, Response } from "express"; -// eslint-disable-next-line @typescript-eslint/no-var-requires -const jsrp = require("jsrp"); -import * as bigintConversion from "bigint-conversion"; -import { BackupPrivateKey, LoginSRPDetail, User } from "../../models"; -import { clearTokens, createToken, sendMail } from "../../helpers"; -import { TokenService } from "../../services"; -import { AuthTokenType, TOKEN_EMAIL_PASSWORD_RESET } from "../../variables"; -import { BadRequestError } from "../../utils/errors"; -import { - getAuthSecret, - getHttpsEnabled, - getJwtSignupLifetime, - getSiteURL -} from "../../config"; -import { ActorType } from "../../ee/models"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/auth"; - -/** - * Password reset step 1: Send email verification link to email [email] - * for account recovery. - * @param req - * @param res - * @returns - */ -export const emailPasswordReset = async (req: Request, res: Response) => { - const { - body: { email } - } = await validateRequest(reqValidator.EmailPasswordResetV1, req); - - const user = await User.findOne({ email }).select("+publicKey"); - if (!user || !user?.publicKey) { - // case: user has already completed account - - return res.status(200).send({ - message: "If an account exists with this email, a password reset link has been sent" - }); - } - - const token = await TokenService.createToken({ - type: TOKEN_EMAIL_PASSWORD_RESET, - email - }); - - await sendMail({ - template: "passwordReset.handlebars", - subjectLine: "Infisical password reset", - recipients: [email], - substitutions: { - email, - token, - callback_url: (await getSiteURL()) + "/password-reset" - } - }); - - return res.status(200).send({ - message: "If an account exists with this email, a password reset link has been sent" - }); -}; - -/** - * Password reset step 2: Verify email verification link sent to email [email] - * @param req - * @param res - * @returns - */ -export const emailPasswordResetVerify = async (req: Request, res: Response) => { - const { - body: { email, code } - } = await validateRequest(reqValidator.EmailPasswordResetVerifyV1, req); - - const user = await User.findOne({ email }).select("+publicKey"); - if (!user || !user?.publicKey) { - // case: user doesn't exist with email [email] or - // hasn't even completed their account - return res.status(403).send({ - error: "Failed email verification for password reset" - }); - } - - await TokenService.validateToken({ - type: TOKEN_EMAIL_PASSWORD_RESET, - email, - token: code - }); - - // generate temporary password-reset token - const token = createToken({ - payload: { - authTokenType: AuthTokenType.SIGNUP_TOKEN, - userId: user._id.toString() - }, - expiresIn: await getJwtSignupLifetime(), - secret: await getAuthSecret() - }); - - return res.status(200).send({ - message: "Successfully verified email", - user, - token - }); -}; - -/** - * Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol - * @param req - * @param res - * @returns - */ -export const srp1 = async (req: Request, res: Response) => { - // return salt, serverPublicKey as part of first step of SRP protocol - const { - body: { clientPublicKey } - } = await validateRequest(reqValidator.Srp1V1, req); - - const user = await User.findOne({ - email: req.user.email - }).select("+salt +verifier"); - - if (!user) throw new Error("Failed to find user"); - - const server = new jsrp.server(); - server.init( - { - salt: user.salt, - verifier: user.verifier - }, - async () => { - // generate server-side public key - const serverPublicKey = server.getPublicKey(); - - await LoginSRPDetail.findOneAndReplace( - { email: req.user.email }, - { - email: req.user.email, - clientPublicKey: clientPublicKey, - serverBInt: bigintConversion.bigintToBuf(server.bInt) - }, - { upsert: true, returnNewDocument: false } - ); - - return res.status(200).send({ - serverPublicKey, - salt: user.salt - }); - } - ); -}; - -/** - * Change account SRP authentication information for user - * Requires verifying [clientProof] as part of step 2 of SRP protocol - * as initiated in POST /srp1 - * @param req - * @param res - * @returns - */ -export const changePassword = async (req: Request, res: Response) => { - const { - body: { - clientProof, - protectedKey, - protectedKeyIV, - protectedKeyTag, - encryptedPrivateKey, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag, - salt, - verifier - } - } = await validateRequest(reqValidator.ChangePasswordV1, req); - - const user = await User.findOne({ - email: req.user.email - }).select("+salt +verifier"); - - if (!user) throw new Error("Failed to find user"); - - const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email }); - - if (!loginSRPDetailFromDB) { - return BadRequestError( - Error( - "It looks like some details from the first login are not found. Please try login one again" - ) - ); - } - - const server = new jsrp.server(); - server.init( - { - salt: user.salt, - verifier: user.verifier, - b: loginSRPDetailFromDB.serverBInt - }, - async () => { - server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey); - - // compare server and client shared keys - if (server.checkClientProof(clientProof)) { - // change password - - await User.findByIdAndUpdate( - req.user._id.toString(), - { - encryptionVersion: 2, - protectedKey, - protectedKeyIV, - protectedKeyTag, - encryptedPrivateKey, - iv: encryptedPrivateKeyIV, - tag: encryptedPrivateKeyTag, - salt, - verifier - }, - { - new: true - } - ); - - if (req.authData.actor.type === ActorType.USER && req.authData.tokenVersionId) { - await clearTokens(req.authData.tokenVersionId); - } - - // clear httpOnly cookie - - res.cookie("jid", "", { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: (await getHttpsEnabled()) as boolean - }); - - return res.status(200).send({ - message: "Successfully changed password" - }); - } - - return res.status(400).send({ - error: "Failed to change password. Try again?" - }); - } - ); -}; - -/** - * Create or change backup private key for user - * @param req - * @param res - * @returns - */ -export const createBackupPrivateKey = async (req: Request, res: Response) => { - // create/change backup private key - // requires verifying [clientProof] as part of second step of SRP protocol - // as initiated in /srp1 - const { - body: { clientProof, encryptedPrivateKey, salt, verifier, iv, tag } - } = await validateRequest(reqValidator.CreateBackupPrivateKeyV1, req); - const user = await User.findOne({ - email: req.user.email - }).select("+salt +verifier"); - - if (!user) throw new Error("Failed to find user"); - - const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email }); - - if (!loginSRPDetailFromDB) { - return BadRequestError( - Error( - "It looks like some details from the first login are not found. Please try login one again" - ) - ); - } - - const server = new jsrp.server(); - server.init( - { - salt: user.salt, - verifier: user.verifier, - b: loginSRPDetailFromDB.serverBInt - }, - async () => { - server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey); - - // compare server and client shared keys - if (server.checkClientProof(clientProof)) { - // create new or replace backup private key - - const backupPrivateKey = await BackupPrivateKey.findOneAndUpdate( - { user: req.user._id }, - { - user: req.user._id, - encryptedPrivateKey, - iv, - tag, - salt, - verifier - }, - { upsert: true, new: true } - ).select("+user, encryptedPrivateKey"); - - // issue tokens - return res.status(200).send({ - message: "Successfully updated backup private key", - backupPrivateKey - }); - } - - return res.status(400).send({ - message: "Failed to update backup private key" - }); - } - ); -}; - -/** - * Return backup private key for user - * @param req - * @param res - * @returns - */ -export const getBackupPrivateKey = async (req: Request, res: Response) => { - const backupPrivateKey = await BackupPrivateKey.findOne({ - user: req.user._id - }).select("+encryptedPrivateKey +iv +tag"); - - if (!backupPrivateKey) throw new Error("Failed to find backup private key"); - - return res.status(200).send({ - backupPrivateKey - }); -}; - -export const resetPassword = async (req: Request, res: Response) => { - const { - body: { - encryptedPrivateKey, - protectedKeyTag, - protectedKey, - protectedKeyIV, - salt, - verifier, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag - } - } = await validateRequest(reqValidator.ResetPasswordV1, req); - - await User.findByIdAndUpdate( - req.user._id.toString(), - { - encryptionVersion: 2, - protectedKey, - protectedKeyIV, - protectedKeyTag, - encryptedPrivateKey, - iv: encryptedPrivateKeyIV, - tag: encryptedPrivateKeyTag, - salt, - verifier - }, - { - new: true - } - ); - - return res.status(200).send({ - message: "Successfully reset password" - }); -}; diff --git a/backend-mongo/src/controllers/v1/secretController.ts b/backend-mongo/src/controllers/v1/secretController.ts deleted file mode 100644 index cda7b5576..000000000 --- a/backend-mongo/src/controllers/v1/secretController.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { Key } from "../../models"; -import { - pullSecrets as pull, - v1PushSecrets as push, - reformatPullSecrets -} from "../../helpers/secret"; -import { pushKeys } from "../../helpers/key"; -import { eventPushSecrets } from "../../events"; -import { EventService } from "../../services"; -import { TelemetryService } from "../../services"; - -interface PushSecret { - ciphertextKey: string; - ivKey: string; - tagKey: string; - hashKey: string; - ciphertextValue: string; - ivValue: string; - tagValue: string; - hashValue: string; - ciphertextComment: string; - ivComment: string; - tagComment: string; - hashComment: string; - type: "shared" | "personal"; -} - -/** - * Upload (encrypted) secrets to workspace with id [workspaceId] - * for environment [environment] - * @param req - * @param res - * @returns - */ -export const pushSecrets = async (req: Request, res: Response) => { - // upload (encrypted) secrets to workspace with id [workspaceId] - const postHogClient = await TelemetryService.getPostHogClient(); - let { secrets }: { secrets: PushSecret[] } = req.body; - const { keys, environment, channel } = req.body; - const { workspaceId } = req.params; - - // validate environment - const workspaceEnvs = req.membership.workspace.environments; - if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { - throw new Error("Failed to validate environment"); - } - - // sanitize secrets - secrets = secrets.filter((s: PushSecret) => s.ciphertextKey !== "" && s.ciphertextValue !== ""); - - await push({ - userId: req.user._id, - workspaceId, - environment, - secrets - }); - - await pushKeys({ - userId: req.user._id, - workspaceId, - keys - }); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets pushed", - distinctId: req.user.email, - properties: { - numberOfSecrets: secrets.length, - environment, - workspaceId, - channel: channel ? channel : "cli" - } - }); - } - - // trigger event - push secrets - EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath: "/" - }) - }); - - return res.status(200).send({ - message: "Successfully uploaded workspace secrets" - }); -}; - -/** - * Return (encrypted) secrets for workspace with id [workspaceId] - * for environment [environment] and (encrypted) workspace key - * @param req - * @param res - * @returns - */ -export const pullSecrets = async (req: Request, res: Response) => { - let secrets; - - const postHogClient = await TelemetryService.getPostHogClient(); - const environment: string = req.query.environment as string; - const channel: string = req.query.channel as string; - const { workspaceId } = req.params; - - // validate environment - const workspaceEnvs = req.membership.workspace.environments; - if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { - throw new Error("Failed to validate environment"); - } - - secrets = await pull({ - userId: req.user._id.toString(), - workspaceId, - environment, - channel: channel ? channel : "cli", - ipAddress: req.realIP - }); - - const key = await Key.findOne({ - workspace: workspaceId, - receiver: req.user._id - }) - .sort({ createdAt: -1 }) - .populate("sender", "+publicKey"); - - if (channel !== "cli") { - secrets = reformatPullSecrets({ secrets }); - } - - if (postHogClient) { - // capture secrets pushed event in production - postHogClient.capture({ - distinctId: req.user.email, - event: "secrets pulled", - properties: { - numberOfSecrets: secrets.length, - environment, - workspaceId, - channel: channel ? channel : "cli" - } - }); - } - - return res.status(200).send({ - secrets, - key - }); -}; - -/** - * Return (encrypted) secrets for workspace with id [workspaceId] - * for environment [environment] and (encrypted) workspace key - * via service token - * @param req - * @param res - * @returns - */ -export const pullSecretsServiceToken = async (req: Request, res: Response) => { - const postHogClient = await TelemetryService.getPostHogClient(); - const environment: string = req.query.environment as string; - const channel: string = req.query.channel as string; - const { workspaceId } = req.params; - - // validate environment - const workspaceEnvs = req.membership.workspace.environments; - if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { - throw new Error("Failed to validate environment"); - } - - const secrets = await pull({ - userId: req.serviceToken.user._id.toString(), - workspaceId, - environment, - channel: "cli", - ipAddress: req.realIP - }); - - const key = { - encryptedKey: req.serviceToken.encryptedKey, - nonce: req.serviceToken.nonce, - sender: { - publicKey: req.serviceToken.publicKey - }, - receiver: req.serviceToken.user, - workspace: req.serviceToken.workspace - }; - - if (postHogClient) { - // capture secrets pulled event in production - postHogClient.capture({ - distinctId: req.serviceToken.user.email, - event: "secrets pulled", - properties: { - numberOfSecrets: secrets.length, - environment, - workspaceId, - channel: channel ? channel : "cli" - } - }); - } - - return res.status(200).send({ - secrets: reformatPullSecrets({ secrets }), - key - }); -}; diff --git a/backend-mongo/src/controllers/v1/secretImpsController.ts b/backend-mongo/src/controllers/v1/secretImpsController.ts deleted file mode 100644 index 5db7a2f0a..000000000 --- a/backend-mongo/src/controllers/v1/secretImpsController.ts +++ /dev/null @@ -1,734 +0,0 @@ - -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { isValidScope } from "../../helpers"; -import { Folder, IServiceTokenData, SecretImport, ServiceTokenData } from "../../models"; -import { getAllImportedSecrets } from "../../services/SecretImportService"; -import { getFolderByPath, getFolderWithPathFromId } from "../../services/FolderService"; -import { - BadRequestError, - ResourceNotFoundError, - UnauthorizedRequestError -} from "../../utils/errors"; -import { EEAuditLogService } from "../../ee/services"; -import { EventType } from "../../ee/models"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/secretImports"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError, subject } from "@casl/ability"; - -export const createSecretImp = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Create secret import' - #swagger.description = 'Create secret import' - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of workspace where to create secret import", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to create secret import", - "example": "dev" - }, - "directory": { - "type": "string", - "description": "Path where to create secret import like / or /foo/bar. Default is /", - "example": "/foo/bar" - }, - "secretImport": { - "type": "object", - "properties": { - "environment": { - "type": "string", - "description": "Slug of environment to import from", - "example": "development" - }, - "secretPath": { - "type": "string", - "description": "Path where to import from like / or /foo/bar.", - "example": "/user/oauth" - } - } - } - }, - "required": ["workspaceId", "environment", "directory", "secretImport"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "successfully created secret import" - } - }, - "description": "Confirmation of secret import creation" - } - } - } - } - #swagger.responses[400] = { - description: "Bad Request. For example, 'Secret import already exist'" - } - #swagger.responses[401] = { - description: "Unauthorized request. For example, 'Folder Permission Denied'" - } - #swagger.responses[404] = { - description: "Resource Not Found. For example, 'Failed to find folder'" - } - */ - - const { - body: { workspaceId, environment, directory, secretImport } - } = await validateRequest(reqValidator.CreateSecretImportV1, req); - - if (req.authData.authPayload instanceof ServiceTokenData) { - // root check - const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, directory); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory }) - ); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - subject(ProjectPermissionSub.Secrets, { environment: secretImport.environment, secretPath: secretImport.secretPath }) - ); - - } - - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }).lean(); - - if (!folders && directory !== "/") - throw ResourceNotFoundError({ message: "Failed to find folder" }); - - let folderId = "root"; - if (folders) { - const folder = getFolderByPath(folders.nodes, directory); - if (!folder) throw BadRequestError({ message: "Folder not found" }); - folderId = folder.id; - } - - const importSecDoc = await SecretImport.findOne({ - workspace: workspaceId, - environment, - folderId - }); - - if (!importSecDoc) { - const doc = new SecretImport({ - workspace: workspaceId, - environment, - folderId, - imports: [{ environment: secretImport.environment, secretPath: secretImport.secretPath }] - }); - - await doc.save(); - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_SECRET_IMPORT, - metadata: { - secretImportId: doc._id.toString(), - folderId: doc.folderId.toString(), - importFromEnvironment: secretImport.environment, - importFromSecretPath: secretImport.secretPath, - importToEnvironment: environment, - importToSecretPath: directory - } - }, - { - workspaceId: doc.workspace - } - ); - return res.status(200).json({ message: "successfully created secret import" }); - } - - const doesImportExist = importSecDoc.imports.find( - (el) => el.environment === secretImport.environment && el.secretPath === secretImport.secretPath - ); - if (doesImportExist) { - throw BadRequestError({ message: "Secret import already exist" }); - } - - importSecDoc.imports.push({ - environment: secretImport.environment, - secretPath: secretImport.secretPath - }); - await importSecDoc.save(); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_SECRET_IMPORT, - metadata: { - secretImportId: importSecDoc._id.toString(), - folderId: importSecDoc.folderId.toString(), - importFromEnvironment: secretImport.environment, - importFromSecretPath: secretImport.secretPath, - importToEnvironment: environment, - importToSecretPath: directory - } - }, - { - workspaceId: importSecDoc.workspace - } - ); - return res.status(200).json({ message: "successfully created secret import" }); -}; - -// to keep the ordering, you must pass all the imports in here not the only updated one -// this is because the order decide which import gets overriden - -/** - * Update secret import - * @param req - * @param res - * @returns - */ -export const updateSecretImport = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update secret import' - #swagger.description = 'Update secret import' - - #swagger.parameters['id'] = { - in: 'path', - description: 'ID of secret import to update', - required: true, - type: 'string', - example: 'import12345' - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretImports": { - "type": "array", - "description": "List of secret imports to update to", - "items": { - "type": "object", - "properties": { - "environment": { - "type": "string", - "description": "Slug of environment to import from", - "example": "dev" - }, - "secretPath": { - "type": "string", - "description": "Path where to import secrets from like / or /foo/bar", - "example": "/foo/bar" - } - }, - "required": ["environment", "secretPath"] - } - } - }, - "required": ["secretImports"] - } - } - } - } - - #swagger.responses[200] = { - description: 'Successfully updated the secret import', - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "successfully updated secret import" - } - } - } - } - } - } - - #swagger.responses[400] = { - description: 'Bad Request - Import not found', - } - - #swagger.responses[403] = { - description: 'Forbidden access due to insufficient permissions', - } - - #swagger.responses[401] = { - description: 'Unauthorized access due to invalid token or scope', - } - */ - const { - body: { secretImports }, - params: { id } - } = await validateRequest(reqValidator.UpdateSecretImportV1, req); - - const importSecDoc = await SecretImport.findById(id); - if (!importSecDoc) { - throw BadRequestError({ message: "Import not found" }); - } - - // check for service token validity - const folders = await Folder.findOne({ - workspace: importSecDoc.workspace, - environment: importSecDoc.environment - }).lean(); - - let secretPath = "/"; - if (folders) { - const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId); - secretPath = folderPath; - } - - if (req.authData.authPayload instanceof ServiceTokenData) { - // token permission check - const isValidScopeAccess = isValidScope( - req.authData.authPayload, - importSecDoc.environment, - secretPath - ); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } else { - // non token entry check - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: importSecDoc.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { - environment: importSecDoc.environment, - secretPath - }) - ); - - secretImports.forEach(({ environment, secretPath }) => { - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - subject(ProjectPermissionSub.Secrets, { environment, secretPath }) - ); - }) - } - - const orderBefore = importSecDoc.imports; - importSecDoc.imports = secretImports; - - await importSecDoc.save(); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_SECRET_IMPORT, - metadata: { - importToEnvironment: importSecDoc.environment, - importToSecretPath: secretPath, - secretImportId: importSecDoc._id.toString(), - folderId: importSecDoc.folderId.toString(), - orderBefore, - orderAfter: secretImports - } - }, - { - workspaceId: importSecDoc.workspace - } - ); - return res.status(200).json({ message: "successfully updated secret import" }); -}; - -/** - * Delete secret import - * @param req - * @param res - * @returns - */ -export const deleteSecretImport = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Delete secret import' - #swagger.description = 'Delete secret import' - - #swagger.parameters['id'] = { - in: 'path', - description: 'ID of parent secret import document from which to delete secret import', - required: true, - type: 'string', - example: '12345abcde' - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretImportEnv": { - "type": "string", - "description": "Slug of environment of import to delete", - "example": "someWorkspaceId" - }, - "secretImportPath": { - "type": "string", - "description": "Path like / or /foo/bar of import to delete", - "example": "production" - } - }, - "required": ["id", "secretImportEnv", "secretImportPath"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "successfully delete secret import" - } - }, - "description": "Confirmation of secret import deletion" - } - } - } - } - */ - const { - params: { id }, - body: { secretImportEnv, secretImportPath } - } = await validateRequest(reqValidator.DeleteSecretImportV1, req); - - const importSecDoc = await SecretImport.findById(id); - if (!importSecDoc) { - throw BadRequestError({ message: "Import not found" }); - } - - // check for service token validity - const folders = await Folder.findOne({ - workspace: importSecDoc.workspace, - environment: importSecDoc.environment - }).lean(); - - let secretPath = "/"; - if (folders) { - const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId); - secretPath = folderPath; - } - - if (req.authData.authPayload instanceof ServiceTokenData) { - const isValidScopeAccess = isValidScope( - req.authData.authPayload, - importSecDoc.environment, - secretPath - ); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: importSecDoc.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - subject(ProjectPermissionSub.Secrets, { - environment: importSecDoc.environment, - secretPath - }) - ); - } - importSecDoc.imports = importSecDoc.imports.filter( - ({ environment, secretPath }) => - !(environment === secretImportEnv && secretPath === secretImportPath) - ); - await importSecDoc.save(); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.DELETE_SECRET_IMPORT, - metadata: { - secretImportId: importSecDoc._id.toString(), - folderId: importSecDoc.folderId.toString(), - importFromEnvironment: secretImportEnv, - importFromSecretPath: secretImportPath, - importToEnvironment: importSecDoc.environment, - importToSecretPath: secretPath - } - }, - { - workspaceId: importSecDoc.workspace - } - ); - - return res.status(200).json({ message: "successfully delete secret import" }); -}; - -/** - * Get secret imports - * @param req - * @param res - * @returns - */ -export const getSecretImports = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Get secret imports' - #swagger.description = 'Get secret imports' - - #swagger.parameters['workspaceId'] = { - in: 'query', - description: 'ID of workspace where to get secret imports from', - required: true, - type: 'string', - example: 'workspace12345' - } - - #swagger.parameters['environment'] = { - in: 'query', - description: 'Slug of environment where to get secret imports from', - required: true, - type: 'string', - example: 'production' - } - - #swagger.parameters['directory'] = { - in: 'query', - description: 'Path where to get secret imports from like / or /foo/bar. Default is /', - required: false, - type: 'string', - example: 'folder12345' - } - - #swagger.responses[200] = { - description: 'Successfully retrieved secret import', - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretImport": { - $ref: '#/definitions/SecretImport' - } - } - } - } - } - } - - #swagger.responses[403] = { - description: 'Forbidden access due to insufficient permissions', - } - - #swagger.responses[401] = { - description: 'Unauthorized access due to invalid token or scope', - } - */ - const { - query: { workspaceId, environment, directory } - } = await validateRequest(reqValidator.GetSecretImportsV1, req); - - if (req.authData.authPayload instanceof ServiceTokenData) { - const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, directory); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath: directory - }) - ); - } - - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }).lean(); - if (!folders && directory !== "/") throw BadRequestError({ message: "Folder not found" }); - - let folderId = "root"; - if (folders) { - const folder = getFolderByPath(folders.nodes, directory); - if (!folder) throw BadRequestError({ message: "Folder not found" }); - folderId = folder.id; - } - - const importSecDoc = await SecretImport.findOne({ - workspace: workspaceId, - environment, - folderId - }); - - if (!importSecDoc) { - return res.status(200).json({ secretImport: {} }); - } - - return res.status(200).json({ secretImport: importSecDoc }); -}; - -/** - * Get all secret imports - * @param req - * @param res - * @returns - */ -export const getAllSecretsFromImport = async (req: Request, res: Response) => { - const { - query: { workspaceId, environment, directory } - } = await validateRequest(reqValidator.GetAllSecretsFromImportV1, req); - - if (req.authData.authPayload instanceof ServiceTokenData) { - // check for service token validity - const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, directory); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath: directory - }) - ); - } - - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }).lean(); - if (!folders && directory !== "/") throw BadRequestError({ message: "Folder not found" }); - - let folderId = "root"; - if (folders) { - const folder = getFolderByPath(folders.nodes, directory); - if (!folder) throw BadRequestError({ message: "Folder not found" }); - folderId = folder.id; - } - - const importSecDoc = await SecretImport.findOne({ - workspace: workspaceId, - environment, - folderId - }); - - if (!importSecDoc) { - return res.status(200).json({ secrets: [] }); - } - - let secretPath = "/"; - if (folders) { - const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId); - secretPath = folderPath; - } - - let permissionCheckFn: (env: string, secPath: string) => boolean; // used to pass as callback function to import secret - if (req.authData.authPayload instanceof ServiceTokenData) { - // check for service token validity - const isValidScopeAccess = isValidScope( - req.authData.authPayload, - importSecDoc.environment, - secretPath - ); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - permissionCheckFn = (env: string, secPath: string) => - isValidScope(req.authData.authPayload as IServiceTokenData, env, secPath); - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: importSecDoc.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - subject(ProjectPermissionSub.Secrets, { - environment: importSecDoc.environment, - secretPath - }) - ); - permissionCheckFn = (env: string, secPath: string) => - permission.can( - ProjectPermissionActions.Read, - subject(ProjectPermissionSub.Secrets, { - environment: env, - secretPath: secPath - }) - ); - } - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.GET_SECRET_IMPORTS, - metadata: { - environment, - secretImportId: importSecDoc._id.toString(), - folderId, - numberOfImports: importSecDoc.imports.length - } - }, - { - workspaceId: importSecDoc.workspace - } - ); - - const secrets = await getAllImportedSecrets( - workspaceId, - environment, - folderId, - permissionCheckFn - ); - return res.status(200).json({ secrets }); -}; diff --git a/backend-mongo/src/controllers/v1/secretScanningController.ts b/backend-mongo/src/controllers/v1/secretScanningController.ts deleted file mode 100644 index 292ce94b3..000000000 --- a/backend-mongo/src/controllers/v1/secretScanningController.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { Request, Response } from "express"; -import { - GitAppInstallationSession, - GitAppOrganizationInstallation, - GitRisks -} from "../../ee/models"; -import crypto from "crypto"; -import { Types } from "mongoose"; -import { OrganizationNotFoundError, UnauthorizedRequestError } from "../../utils/errors"; -import { scanGithubFullRepoForSecretLeaks } from "../../queues/secret-scanning/githubScanFullRepository"; -import { getSecretScanningGitAppId, getSecretScanningPrivateKey } from "../../config"; -import { - STATUS_RESOLVED_FALSE_POSITIVE, - STATUS_RESOLVED_NOT_REVOKED, - STATUS_RESOLVED_REVOKED -} from "../../ee/models/gitRisks"; -import { ProbotOctokit } from "probot"; -import { Organization } from "../../models"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/secretScanning"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - getAuthDataOrgPermissions -} from "../../ee/services/RoleService"; -import { ForbiddenError } from "@casl/ability"; - -export const createInstallationSession = async (req: Request, res: Response) => { - const sessionId = crypto.randomBytes(16).toString("hex"); - const { - params: { organizationId } - } = await validateRequest(reqValidator.CreateInstalLSessionv1, req); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.SecretScanning - ); - - await GitAppInstallationSession.findByIdAndUpdate( - organization, - { - organization: organization.id, - sessionId: sessionId, - user: new Types.ObjectId(req.user._id) - }, - { upsert: true } - ).lean(); - - res.send({ - sessionId: sessionId - }); -}; - -export const linkInstallationToOrganization = async (req: Request, res: Response) => { - const { - body: { sessionId, installationId } - } = await validateRequest(reqValidator.LinkInstallationToOrgv1, req); - - const installationSession = await GitAppInstallationSession.findOneAndDelete({ - sessionId: sessionId - }); - if (!installationSession) { - throw UnauthorizedRequestError(); - } - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: installationSession.organization - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.SecretScanning - ); - - const installationLink = await GitAppOrganizationInstallation.findOneAndUpdate( - { - organizationId: installationSession.organization - }, - { - installationId: installationId, - organizationId: installationSession.organization, - user: installationSession.user - }, - { - upsert: true - } - ).lean(); - - const octokit = new ProbotOctokit({ - auth: { - appId: await getSecretScanningGitAppId(), - privateKey: await getSecretScanningPrivateKey(), - installationId: installationId.toString() - } - }); - - const { - data: { repositories } - } = await octokit.apps.listReposAccessibleToInstallation(); - for (const repository of repositories) { - scanGithubFullRepoForSecretLeaks({ - organizationId: installationSession.organization.toString(), - installationId, - repository: { id: repository.id, fullName: repository.full_name } - }); - } - res.json(installationLink); -}; - -export const getCurrentOrganizationInstallationStatus = async (req: Request, res: Response) => { - const { organizationId } = req.params; - try { - const appInstallation = await GitAppOrganizationInstallation.findOne({ - organizationId: organizationId - }).lean(); - if (!appInstallation) { - res.json({ - appInstallationComplete: false - }); - } - - res.json({ - appInstallationComplete: true - }); - } catch { - res.json({ - appInstallationComplete: false - }); - } -}; - -export const getRisksForOrganization = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgRisksv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.SecretScanning - ); - - const risks = await GitRisks.find({ organization: organizationId }) - .sort({ createdAt: -1 }) - .lean(); - res.json({ - risks: risks - }); -}; - -export const updateRisksStatus = async (req: Request, res: Response) => { - const { - params: { organizationId, riskId }, - body: { status } - } = await validateRequest(reqValidator.UpdateRiskStatusv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.SecretScanning - ); - - const isRiskResolved = - status == STATUS_RESOLVED_FALSE_POSITIVE || - status == STATUS_RESOLVED_REVOKED || - status == STATUS_RESOLVED_NOT_REVOKED - ? true - : false; - const risk = await GitRisks.findByIdAndUpdate(riskId, { - status: status, - isResolved: isRiskResolved - }).lean(); - - res.json(risk); -}; diff --git a/backend-mongo/src/controllers/v1/secretsFolderController.ts b/backend-mongo/src/controllers/v1/secretsFolderController.ts deleted file mode 100644 index 52627b2e3..000000000 --- a/backend-mongo/src/controllers/v1/secretsFolderController.ts +++ /dev/null @@ -1,680 +0,0 @@ -import { ForbiddenError, subject } from "@casl/ability"; -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { EventType, FolderVersion } from "../../ee/models"; -import { EEAuditLogService, EESecretService } from "../../ee/services"; -import { isValidScope } from "../../helpers/secrets"; -import { validateRequest } from "../../helpers/validation"; -import { Secret, ServiceTokenData } from "../../models"; -import { Folder } from "../../models/folder"; -import { - appendFolder, - getAllFolderIds, - getFolderByPath, - getFolderWithPathFromId, - validateFolderName -} from "../../services/FolderService"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; -import * as reqValidator from "../../validation/folders"; - -const ERR_FOLDER_NOT_FOUND = BadRequestError({ message: "The folder doesn't exist" }); - -// verify workspace id/environment -export const createFolder = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Create folder' - #swagger.description = 'Create folder' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of the workspace where to create folder", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to create folder", - "example": "production" - }, - "folderName": { - "type": "string", - "description": "Name of folder to create", - "example": "my_folder" - }, - "directory": { - "type": "string", - "description": "Path where to create folder like / or /foo/bar. Default is /", - "example": "/foo/bar" - } - }, - "required": ["workspaceId", "environment", "folderName"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "folder": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "ID of folder", - "example": "someFolderId" - }, - "name": { - "type": "string", - "description": "Name of folder", - "example": "my_folder" - }, - "version": { - "type": "number", - "description": "Version of folder", - "example": 1 - } - }, - "description": "Details of created folder" - } - } - } - } - } - } - #swagger.responses[400] = { - description: "Bad Request. For example, 'Folder name cannot contain spaces. Only underscore and dashes'" - } - #swagger.responses[401] = { - description: "Unauthorized request. For example, 'Folder Permission Denied'" - } - */ - const { - body: { workspaceId, environment, folderName, directory } - } = await validateRequest(reqValidator.CreateFolderV1, req); - - if (!validateFolderName(folderName)) { - throw BadRequestError({ - message: "Folder name cannot contain spaces. Only underscore and dashes" - }); - } - - if (req.authData.authPayload instanceof ServiceTokenData) { - // token check - const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, directory); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } else { - // user check - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory }) - ); - } - - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }).lean(); - - // space has no folders initialized - if (!folders) { - const folder = new Folder({ - workspace: workspaceId, - environment, - nodes: { - id: "root", - name: "root", - version: 1, - children: [] - } - }); - const { parent, child } = appendFolder(folder.nodes, { folderName, directory }); - await folder.save(); - const folderVersion = new FolderVersion({ - workspace: workspaceId, - environment, - nodes: parent - }); - await folderVersion.save(); - await EESecretService.takeSecretSnapshot({ - workspaceId: new Types.ObjectId(workspaceId), - environment - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_FOLDER, - metadata: { - environment, - folderId: child.id, - folderName, - folderPath: directory - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.json({ folder: { id: child.id, name: folderName } }); - } - - const { parent, child, hasCreated } = appendFolder(folders.nodes, { folderName, directory }); - - if (!hasCreated) return res.json({ folder: child }); - - await Folder.findByIdAndUpdate(folders._id, folders); - - const folderVersion = new FolderVersion({ - workspace: workspaceId, - environment, - nodes: parent - }); - await folderVersion.save(); - - await EESecretService.takeSecretSnapshot({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - folderId: child.id - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_FOLDER, - metadata: { - environment, - folderId: child.id, - folderName, - folderPath: directory - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.json({ folder: child }); -}; - -/** - * Update folder with id [folderId] - * @param req - * @param res - * @returns - */ -export const updateFolderById = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update folder' - #swagger.description = 'Update folder' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['folderName'] = { - "description": "Name of folder to update", - "required": true, - "type": "string" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of workspace where to update folder", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to update folder", - "example": "production" - }, - "name": { - "type": "string", - "description": "Name of folder to update to", - "example": "updated_folder_name" - }, - "directory": { - "type": "string", - "description": "Path where to update folder like / or /foo/bar. Default is /", - "example": "/foo/bar" - } - }, - "required": ["workspaceId", "environment", "name"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Success message", - "example": "Successfully updated folder" - }, - "folder": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of updated folder", - "example": "updated_folder_name" - }, - "id": { - "type": "string", - "description": "ID of created folder", - "example": "abc123" - } - }, - "description": "Details of the updated folder" - } - } - } - } - } - } - - #swagger.responses[400] = { - description: "Bad Request. Reasons can include 'The folder doesn't exist' or 'Folder name cannot contain spaces. Only underscore and dashes'" - } - - #swagger.responses[401] = { - description: "Unauthorized request. For example, 'Folder Permission Denied'" - } - */ - const { - body: { workspaceId, environment, name, directory }, - params: { folderName } - } = await validateRequest(reqValidator.UpdateFolderV1, req); - - if (!validateFolderName(name)) { - throw BadRequestError({ - message: "Folder name cannot contain spaces. Only underscore and dashes" - }); - } - - if (req.authData.authPayload instanceof ServiceTokenData) { - const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, directory); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory }) - ); - } - - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - if (!folders) { - throw BadRequestError({ message: "The folder doesn't exist" }); - } - - const parentFolder = getFolderByPath(folders.nodes, directory); - if (!parentFolder) { - throw BadRequestError({ message: "The folder doesn't exist" }); - } - - const folder = parentFolder.children.find(({ name }) => name === folderName); - if (!folder) throw ERR_FOLDER_NOT_FOUND; - - const oldFolderName = folder.name; - parentFolder.version += 1; - folder.name = name; - - await Folder.findByIdAndUpdate(folders._id, folders); - const folderVersion = new FolderVersion({ - workspace: workspaceId, - environment, - nodes: parentFolder - }); - await folderVersion.save(); - - await EESecretService.takeSecretSnapshot({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - folderId: parentFolder.id - }); - - const { folderPath } = getFolderWithPathFromId(folders.nodes, folder.id); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_FOLDER, - metadata: { - environment, - folderId: folder.id, - oldFolderName, - newFolderName: name, - folderPath - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.json({ - message: "Successfully updated folder", - folder: { name: folder.name, id: folder.id } - }); -}; - -/** - * Delete folder with id [folderId] - * @param req - * @param res - * @returns - */ -export const deleteFolder = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Delete folder' - #swagger.description = 'Delete folder' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['folderName'] = { - "description": "Name of folder to delete", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of the workspace where to delete folder", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to delete folder", - "example": "production" - }, - "directory": { - "type": "string", - "description": "Path where to delete folder like / or /foo/bar. Default is /", - "example": "/foo/bar" - } - }, - "required": ["workspaceId", "environment"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Success message", - "example": "successfully deleted folders" - }, - "folders": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "ID of deleted folder", - "example": "abc123" - }, - "name": { - "type": "string", - "description": "Name of deleted folder", - "example": "someFolderName" - } - } - }, - "description": "List of IDs and names of deleted folders" - } - } - } - } - } - } - - #swagger.responses[400] = { - description: "Bad Request. Reasons can include 'The folder doesn't exist'" - } - - #swagger.responses[401] = { - description: "Unauthorized request. For example, 'Folder Permission Denied'" - } - */ - const { - params: { folderName }, - body: { environment, workspaceId, directory } - } = await validateRequest(reqValidator.DeleteFolderV1, req); - - if (req.authData.authPayload instanceof ServiceTokenData) { - const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, directory); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } else { - // check that user is a member of the workspace - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: directory }) - ); - } - - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - if (!folders) throw ERR_FOLDER_NOT_FOUND; - - const parentFolder = getFolderByPath(folders.nodes, directory); - if (!parentFolder) throw ERR_FOLDER_NOT_FOUND; - - const index = parentFolder.children.findIndex(({ name }) => name === folderName); - if (index === -1) throw ERR_FOLDER_NOT_FOUND; - - const deletedFolder = parentFolder.children.splice(index, 1)[0]; - - parentFolder.version += 1; - const delFolderIds = getAllFolderIds(deletedFolder); - - await Folder.findByIdAndUpdate(folders._id, folders); - const folderVersion = new FolderVersion({ - workspace: workspaceId, - environment, - nodes: parentFolder - }); - await folderVersion.save(); - if (delFolderIds.length) { - await Secret.deleteMany({ - folder: { $in: delFolderIds.map(({ id }) => id) }, - workspace: workspaceId, - environment - }); - } - - await EESecretService.takeSecretSnapshot({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - folderId: parentFolder.id - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.DELETE_FOLDER, - metadata: { - environment, - folderId: deletedFolder.id, - folderName: deletedFolder.name, - folderPath: directory - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.send({ message: "successfully deleted folders", folders: delFolderIds }); -}; - -/** - * Get folders for workspace with id [workspaceId] and environment [environment] - * considering directory/path [directory] - * @param req - * @param res - * @returns - */ -export const getFolders = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Get folders' - #swagger.description = 'Get folders' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of the workspace where to get folders from", - "required": true, - "type": "string", - "in": "query" - } - - #swagger.parameters['environment'] = { - "description": "Slug of environment where to get folders from", - "required": true, - "type": "string", - "in": "query" - } - - #swagger.parameters['directory'] = { - "description": "Path where to get fodlers from like / or /foo/bar. Default is /", - "required": false, - "type": "string", - "in": "query" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "folders": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "example": "someFolderId" - }, - "name": { - "type": "string", - "example": "someFolderName" - } - } - }, - "description": "List of folders" - } - } - } - } - } - } - - #swagger.responses[400] = { - description: "Bad Request. For instance, 'The folder doesn't exist'" - } - - #swagger.responses[401] = { - description: "Unauthorized request. For example, 'Folder Permission Denied'" - } - */ - const { - query: { workspaceId, environment, directory } - } = await validateRequest(reqValidator.GetFoldersV1, req); - - if (req.authData.authPayload instanceof ServiceTokenData) { - const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, directory); - if (!isValidScopeAccess) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } else { - // check that user is a member of the workspace - await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - } - - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - if (!folders) { - return res.send({ folders: [], dir: [] }); - } - - const folder = getFolderByPath(folders.nodes, directory); - - return res.send({ - folders: folder?.children?.map(({ id, name }) => ({ id, name })) || [] - }); -}; diff --git a/backend-mongo/src/controllers/v1/serviceTokenController.ts b/backend-mongo/src/controllers/v1/serviceTokenController.ts deleted file mode 100644 index c1b753a90..000000000 --- a/backend-mongo/src/controllers/v1/serviceTokenController.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Request, Response } from "express"; -import { ServiceToken } from "../../models"; -import { createToken } from "../../helpers/auth"; -import { getJwtServiceSecret } from "../../config"; - -/** - * Return service token on request - * @param req - * @param res - * @returns - */ -export const getServiceToken = async (req: Request, res: Response) => { - return res.status(200).send({ - serviceToken: req.serviceToken, - }); -}; - -/** - * Create and return a new service token - * @param req - * @param res - * @returns - */ -export const createServiceToken = async (req: Request, res: Response) => { - let token; - try { - const { - name, - workspaceId, - environment, - expiresIn, - publicKey, - encryptedKey, - nonce, - } = req.body; - - // validate environment - const workspaceEnvs = req.membership.workspace.environments; - if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { - throw new Error("Failed to validate environment"); - } - - // compute access token expiration date - const expiresAt = new Date(); - expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); - - const serviceToken = await new ServiceToken({ - name, - user: req.user._id, - workspace: workspaceId, - environment, - expiresAt, - publicKey, - encryptedKey, - nonce, - }).save(); - - token = createToken({ - payload: { - serviceTokenId: serviceToken._id.toString(), - workspaceId, - }, - expiresIn: expiresIn, - secret: await getJwtServiceSecret(), - }); - } catch (err) { - return res.status(400).send({ - message: "Failed to create service token", - }); - } - - return res.status(200).send({ - token, - }); -}; \ No newline at end of file diff --git a/backend-mongo/src/controllers/v1/signupController.ts b/backend-mongo/src/controllers/v1/signupController.ts deleted file mode 100644 index 6422808ab..000000000 --- a/backend-mongo/src/controllers/v1/signupController.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Request, Response } from "express"; -import { AuthMethod, User } from "../../models"; -import { checkEmailVerification, sendEmailVerification } from "../../helpers/signup"; -import { createToken } from "../../helpers/auth"; -import { - getAuthSecret, - getJwtSignupLifetime, - getSmtpConfigured -} from "../../config"; -import { validateUserEmail } from "../../validation"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/auth"; -import { AuthTokenType } from "../../variables"; - -/** - * Signup step 1: Initialize account for user under email [email] and send a verification code - * to that email - * @param req - * @param res - * @returns - */ -export const beginEmailSignup = async (req: Request, res: Response) => { - const { - body: { email } - } = await validateRequest(reqValidator.BeginEmailSignUpV1, req); - - // validate that email is not disposable - validateUserEmail(email); - - const user = await User.findOne({ email }).select("+publicKey"); - if (user && user?.publicKey) { - // case: user has already completed account - - return res.status(403).send({ - error: "Failed to send email verification code for complete account" - }); - } - - // send send verification email - await sendEmailVerification({ email }); - - return res.status(200).send({ - message: `Sent an email verification code to ${email}` - }); -}; - -/** - * Signup step 2: Verify that code [code] was sent to email [email] and issue - * a temporary signup token for user to complete setting up their account - * @param req - * @param res - * @returns - */ -export const verifyEmailSignup = async (req: Request, res: Response) => { - let user; - const { - body: { email, code } - } = await validateRequest(reqValidator.VerifyEmailSignUpV1, req); - - // initialize user account - user = await User.findOne({ email }).select("+publicKey"); - if (user && user?.publicKey) { - // case: user has already completed account - return res.status(403).send({ - error: "Failed email verification for complete user" - }); - } - - // verify email - if (await getSmtpConfigured()) { - await checkEmailVerification({ - email, - code - }); - } - - if (!user) { - user = await new User({ - email, - authMethods: [AuthMethod.EMAIL] - }).save(); - } - - // generate temporary signup token - const token = createToken({ - payload: { - authTokenType: AuthTokenType.SIGNUP_TOKEN, - userId: user._id.toString() - }, - expiresIn: await getJwtSignupLifetime(), - secret: await getAuthSecret() - }); - - return res.status(200).send({ - message: "Successfuly verified email", - user, - token - }); -}; diff --git a/backend-mongo/src/controllers/v1/universalAuthController.ts b/backend-mongo/src/controllers/v1/universalAuthController.ts deleted file mode 100644 index 9e5bba715..000000000 --- a/backend-mongo/src/controllers/v1/universalAuthController.ts +++ /dev/null @@ -1,1269 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import jwt from "jsonwebtoken"; -import crypto from "crypto"; -import bcrypt from "bcrypt"; -import { - IIdentity, - IIdentityTrustedIp, - IIdentityUniversalAuthClientSecret, - Identity, - IdentityAccessToken, - IdentityAuthMethod, - IdentityMembershipOrg, - IdentityUniversalAuth, - IdentityUniversalAuthClientSecret, -} from "../../models"; -import { createToken } from "../../helpers/auth"; -import { AuthTokenType } from "../../variables"; -import { - BadRequestError, - ForbiddenRequestError, - ResourceNotFoundError, - UnauthorizedRequestError -} from "../../utils/errors"; -import { - getAuthSecret, - getSaltRounds -} from "../../config"; -import { ActorType, EventType, IRole } from "../../ee/models"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/auth"; -import { checkIPAgainstBlocklist, extractIPDetails, isValidIpOrCidr } from "../../utils/ip"; -import { getUserAgentType } from "../../utils/posthog"; -import { EEAuditLogService, EELicenseService } from "../../ee/services"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - getAuthDataOrgPermissions, - getOrgRolePermissions, - isAtLeastAsPrivilegedOrg -} from "../../ee/services/RoleService"; -import { ForbiddenError } from "@casl/ability"; - -const packageUniversalAuthClientSecretData = (identityUniversalAuthClientSecret: IIdentityUniversalAuthClientSecret) => ({ - _id: identityUniversalAuthClientSecret._id, - identityUniversalAuth: identityUniversalAuthClientSecret.identityUniversalAuth, - isClientSecretRevoked: identityUniversalAuthClientSecret.isClientSecretRevoked, - description: identityUniversalAuthClientSecret.description, - clientSecretPrefix: identityUniversalAuthClientSecret.clientSecretPrefix, - clientSecretNumUses: identityUniversalAuthClientSecret.clientSecretNumUses, - clientSecretNumUsesLimit: identityUniversalAuthClientSecret.clientSecretNumUsesLimit, - clientSecretTTL: identityUniversalAuthClientSecret.clientSecretTTL, - createdAt: identityUniversalAuthClientSecret.createdAt, - updatedAt: identityUniversalAuthClientSecret.updatedAt -}); - -/** - * Renews an access token by its TTL - * @param req - * @param res - */ -export const renewAccessToken = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Renew access token' - #swagger.description = 'Renew access token' - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "accessToken": { - "type": "string", - "description": "Access token to renew", - "example": "..." - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "accessToken": { - "type": "string", - "description": "(Same) Access token after successful renewal" - }, - "expiresIn": { - "type": "number", - "description": "TTL of access token in seconds" - }, - "tokenType": { - "type": "string", - "description": "Type of access token (e.g. Bearer)" - } - }, - "description": "Access token and its details" - } - } - } - } - */ - const { - body: { - accessToken - } - } = await validateRequest(reqValidator.RenewAccessTokenV1, req); - - const decodedToken = ( - jwt.verify(accessToken, await getAuthSecret()) - ); - - if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) throw UnauthorizedRequestError(); - - const identityAccessToken = await IdentityAccessToken.findOne({ - _id: decodedToken.identityAccessTokenId, - isAccessTokenRevoked: false - }); - - if (!identityAccessToken) throw UnauthorizedRequestError(); - - const { - accessTokenTTL, - accessTokenLastRenewedAt, - accessTokenMaxTTL, - createdAt: accessTokenCreatedAt, - accessTokenNumUses, - accessTokenNumUsesLimit - } = identityAccessToken; - - if (accessTokenNumUses >= accessTokenNumUsesLimit) { - throw BadRequestError({ message: "Unable to renew because access token number of uses limit reached" }) - } - - // ttl check - if (accessTokenTTL > 0) { - const currentDate = new Date(); - if (accessTokenLastRenewedAt) { - // access token has been renewed - const accessTokenRenewed = new Date(accessTokenLastRenewedAt); - const ttlInMilliseconds = accessTokenTTL * 1000; - const expirationDate = new Date(accessTokenRenewed.getTime() + ttlInMilliseconds); - - if (currentDate > expirationDate) throw UnauthorizedRequestError({ - message: "Failed to renew MI access token due to TTL expiration" - }); - } else { - // access token has never been renewed - const accessTokenCreated = new Date(accessTokenCreatedAt); - const ttlInMilliseconds = accessTokenTTL * 1000; - const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds); - - if (currentDate > expirationDate) throw UnauthorizedRequestError({ - message: "Failed to renew MI access token due to TTL expiration" - }); - } - } - - // max ttl checks - if (accessTokenMaxTTL > 0) { - const accessTokenCreated = new Date(accessTokenCreatedAt); - const ttlInMilliseconds = accessTokenMaxTTL * 1000; - const currentDate = new Date(); - const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds); - - if (currentDate > expirationDate) throw UnauthorizedRequestError({ - message: "Failed to renew MI access token due to Max TTL expiration" - }); - - const extendToDate = new Date(currentDate.getTime() + accessTokenTTL); - if (extendToDate > expirationDate) throw UnauthorizedRequestError({ - message: "Failed to renew MI access token past its Max TTL expiration" - }); - } - - await IdentityAccessToken.findByIdAndUpdate( - identityAccessToken._id, - { - accessTokenLastRenewedAt: new Date() - } - ); - - return res.status(200).send({ - accessToken, - expiresIn: identityAccessToken.accessTokenTTL, - accessTokenMaxTTL: identityAccessToken.accessTokenMaxTTL, - tokenType: "Bearer" - }); -} - -/** - * Return access token for identity with client id [clientId] - * and client secret [clientSecret] - * @param req - * @param res - */ -export const loginIdentityUniversalAuth = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Login with Universal Auth' - #swagger.description = 'Login with Universal Auth' - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientId": { - "type": "string", - "description": "Client ID for identity to login with Universal Auth", - "example": "..." - }, - "clientSecret": { - "type": "string", - "description": "Client Secret for identity to login with Universal Auth", - "example": "..." - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "accessToken": { - "type": "string", - "description": "Access token issued after successful login" - }, - "expiresIn": { - "type": "number", - "description": "TTL of access token in seconds" - }, - "tokenType": { - "type": "string", - "description": "Type of access token (e.g. Bearer)" - } - }, - "description": "Access token and its details" - } - } - } - } - */ - const { - body: { - clientId, - clientSecret - } - } = await validateRequest(reqValidator.LoginUniversalAuthV1, req); - - const identityUniversalAuth = await IdentityUniversalAuth.findOne({ - clientId - }).populate<{ identity: IIdentity }>("identity"); - - if (!identityUniversalAuth) throw UnauthorizedRequestError(); - - checkIPAgainstBlocklist({ - ipAddress: req.realIP, - trustedIps: identityUniversalAuth.clientSecretTrustedIps - }); - - const clientSecretData = await IdentityUniversalAuthClientSecret.find({ - identity: identityUniversalAuth.identity, - isClientSecretRevoked: false - }); - - let validatedClientSecretDatum: IIdentityUniversalAuthClientSecret | undefined; - - for (const clientSecretDatum of clientSecretData) { - const isSecretValid = await bcrypt.compare( - clientSecret, - clientSecretDatum.clientSecretHash - ); - - if (isSecretValid) { - validatedClientSecretDatum = clientSecretDatum; - break; - } - } - - if (!validatedClientSecretDatum) throw UnauthorizedRequestError(); - - const { - clientSecretTTL, - clientSecretNumUses, - clientSecretNumUsesLimit, - } = validatedClientSecretDatum; - - if (clientSecretTTL > 0) { - const clientSecretCreated = new Date(validatedClientSecretDatum.createdAt) - const ttlInMilliseconds = clientSecretTTL * 1000; - const currentDate = new Date(); - const expirationTime = new Date(clientSecretCreated.getTime() + ttlInMilliseconds); - - if (currentDate > expirationTime) { - await IdentityUniversalAuthClientSecret.findByIdAndUpdate( - validatedClientSecretDatum._id, - { - isClientSecretRevoked: true - } - ); - - throw UnauthorizedRequestError({ - message: "Failed to authenticate identity credentials due to expired client secret" - }); - } - } - - if (clientSecretNumUsesLimit > 0 && clientSecretNumUses === clientSecretNumUsesLimit) { - // number of times client secret can be used for - // a login operation reached - await IdentityUniversalAuthClientSecret.findByIdAndUpdate( - validatedClientSecretDatum._id, - { - isClientSecretRevoked: true - }, - { - new: true - } - ); - - throw UnauthorizedRequestError({ - message: "Failed to authenticate identity credentials due to client secret number of uses limit reached" - }); - } - - // increment usage count by 1 - await IdentityUniversalAuthClientSecret - .findByIdAndUpdate( - validatedClientSecretDatum._id, - { - clientSecretLastUsedAt: new Date(), - $inc: { clientSecretNumUses: 1 } - }, - { - new: true - } - ); - - const identityAccessToken = await new IdentityAccessToken({ - identity: identityUniversalAuth.identity, - identityUniversalAuthClientSecret: validatedClientSecretDatum._id, - accessTokenNumUses: 0, - accessTokenNumUsesLimit: identityUniversalAuth.accessTokenNumUsesLimit, - accessTokenTTL: identityUniversalAuth.accessTokenTTL, - accessTokenMaxTTL: identityUniversalAuth.accessTokenMaxTTL, - accessTokenTrustedIps: identityUniversalAuth.accessTokenTrustedIps, - isAccessTokenRevoked: false - }).save(); - - // token version - const accessToken = createToken({ - payload: { - identityId: identityUniversalAuth.identity.toString(), - clientSecretId: validatedClientSecretDatum._id.toString(), - identityAccessTokenId: identityAccessToken._id.toString(), - authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN - }, - secret: await getAuthSecret() - }); - - const userAgent = req.headers["user-agent"] ?? ""; - - await EEAuditLogService.createAuditLog( - { - actor: { - type: ActorType.IDENTITY, - metadata: { - identityId: identityUniversalAuth.identity._id.toString(), - name: identityUniversalAuth.identity.name - } - }, - authPayload: identityUniversalAuth.identity, - ipAddress: req.realIP, - userAgent, - userAgentType: getUserAgentType(userAgent) - }, - { - type: EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH, - metadata: { - identityId: identityUniversalAuth.identity._id.toString(), - identityUniversalAuthId: identityUniversalAuth._id.toString(), - clientSecretId: validatedClientSecretDatum._id.toString(), - identityAccessTokenId: identityAccessToken._id.toString() - } - } - ); - - return res.status(200).send({ - accessToken, - expiresIn: identityUniversalAuth.accessTokenTTL, - accessTokenMaxTTL: identityUniversalAuth.accessTokenMaxTTL, - tokenType: "Bearer", - }); -} - -/** - * Attach identity universal auth method onto identity with id [identityId] - * @param req - * @param res - */ -export const attachIdentityUniversalAuth = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Attach Universal Auth configuration onto identity' - #swagger.description = 'Attach Universal Auth configuration onto identity' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['identityId'] = { - "description": "ID of identity to attach Universal Auth onto", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecretTrustedIps": { - type: "array", - items: { - type: "object", - "properties": { - "ipAddress": { - type: "string", - description: "IP address to trust", - default: "0.0.0.0/0" - } - } - }, - "description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", - "example": "...", - "default": [{ ipAddress: "0.0.0.0/0" }] - }, - "accessTokenTTL": { - "type": "number", - "description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.", - "example": "...", - "default": 100 - }, - "accessTokenMaxTTL": { - "type": "number", - "description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.", - "example": "...", - "default": 2592000 - }, - "accessTokenNumUsesLimit": { - "type": "number", - "description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.", - "example": "...", - "default": 0 - }, - "accessTokenTrustedIps": { - type: "array", - items: { - type: "object", - "properties": { - "ipAddress": { - type: "string", - description: "IP address to trust", - default: "0.0.0.0/0" - } - } - }, - "description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", - "example": "...", - "default": [{ ipAddress: "0.0.0.0/0" }] - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityUniversalAuth": { - $ref: '#/definitions/IdentityUniversalAuth' - } - }, - "description": "Details of attached Universal Auth" - } - } - } - } - */ - const { - params: { identityId }, - body: { - clientSecretTrustedIps, - accessTokenTTL, - accessTokenMaxTTL, - accessTokenNumUsesLimit, - accessTokenTrustedIps, - } - } = await validateRequest(reqValidator.AddUniversalAuthToIdentityV1, req); - - const identityMembershipOrg = await IdentityMembershipOrg - .findOne({ - identity: new Types.ObjectId(identityId) - }) - .populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembershipOrg) throw ResourceNotFoundError({ - message: `Failed to find identity with id ${identityId}` - }); - - if (identityMembershipOrg.identity?.authMethod) throw BadRequestError({ - message: "Failed to add universal auth to already-configured identity" - }); - - if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) { - throw BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }) - } - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: identityMembershipOrg.organization - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Identity - ); - - const plan = await EELicenseService.getPlan(identityMembershipOrg.organization); - - // validate trusted ips - const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map((clientSecretTrustedIp) => { - if (!plan.ipAllowlisting && (clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && clientSecretTrustedIp.ipAddress !== "::/0")) return res.status(400).send({ - message: "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." - }); - - const isValidIPOrCidr = isValidIpOrCidr(clientSecretTrustedIp.ipAddress); - - if (!isValidIPOrCidr) return res.status(400).send({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - - return extractIPDetails(clientSecretTrustedIp.ipAddress); - }); - - const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { - if (!plan.ipAllowlisting && (accessTokenTrustedIp.ipAddress !== "0.0.0.0/0" && accessTokenTrustedIp.ipAddress !== "::/0")) return res.status(400).send({ - message: "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." - }); - - const isValidIPOrCidr = isValidIpOrCidr(accessTokenTrustedIp.ipAddress); - - if (!isValidIPOrCidr) return res.status(400).send({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - - return extractIPDetails(accessTokenTrustedIp.ipAddress); - }); - - const identityUniversalAuth = await new IdentityUniversalAuth({ - identity: identityMembershipOrg.identity._id, - clientId: crypto.randomUUID(), - clientSecretTrustedIps: reformattedClientSecretTrustedIps, - accessTokenTTL, - accessTokenMaxTTL, - accessTokenNumUsesLimit, - accessTokenTrustedIps: reformattedAccessTokenTrustedIps, - }).save(); - - await Identity.findByIdAndUpdate( - identityMembershipOrg.identity._id, - { - authMethod: IdentityAuthMethod.UNIVERSAL_AUTH - } - ); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.ADD_IDENTITY_UNIVERSAL_AUTH, - metadata: { - identityId: identityMembershipOrg.identity._id.toString(), - clientSecretTrustedIps: reformattedClientSecretTrustedIps as Array, - accessTokenTTL, - accessTokenMaxTTL, - accessTokenNumUsesLimit, - accessTokenTrustedIps: reformattedAccessTokenTrustedIps as Array - } - } - ); - - return res.status(200).send({ - identityUniversalAuth - }); -} - -/** - * Update identity universal auth method on identity with id [identityId] - * @param req - * @param res - */ -export const updateIdentityUniversalAuth = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update Universal Auth configuration on identity' - #swagger.description = 'Update Universal Auth configuration on identity' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['identityId'] = { - "description": "ID of identity to update Universal Auth on", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecretTrustedIps": { - type: "array", - items: { - type: "object", - "properties": { - "ipAddress": { - type: "string", - description: "IP address to trust" - } - } - }, - "description": "List of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. By default, Client Secrets are given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", - "example": "...", - }, - "accessTokenTTL": { - "type": "number", - "description": "The incremental lifetime for an acccess token in seconds; a value of 0 implies an infinite incremental lifetime.", - "example": "...", - }, - "accessTokenMaxTTL": { - "type": "number", - "description": "The maximum lifetime for an acccess token in seconds; a value of 0 implies an infinite maximum lifetime.", - "example": "...", - }, - "accessTokenNumUsesLimit": { - "type": "number", - "description": "The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses.", - "example": "...", - }, - "accessTokenTrustedIps": { - type: "array", - items: { - type: "object", - "properties": { - "ipAddress": { - type: "string", - description: "IP address to trust" - } - } - }, - "description": "List of IPs or CIDR ranges that access tokens can be used from. By default, each token is given the 0.0.0.0/0 entry representing all possible IPv4 addresses.", - "example": "...", - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityUniversalAuth": { - $ref: '#/definitions/IdentityUniversalAuth' - } - }, - "description": "Details of updated Universal Auth" - } - } - } - } - */ - const { - params: { identityId }, - body: { - clientSecretTrustedIps, - accessTokenTTL, // TODO: validate this and max TTL - accessTokenMaxTTL, - accessTokenNumUsesLimit, - accessTokenTrustedIps, - } - } = await validateRequest(reqValidator.UpdateUniversalAuthToIdentityV1, req); - - const identityMembershipOrg = await IdentityMembershipOrg - .findOne({ - identity: new Types.ObjectId(identityId) - }) - .populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembershipOrg) throw ResourceNotFoundError({ - message: `Failed to find identity with id ${identityId}` - }); - - if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.UNIVERSAL_AUTH) throw BadRequestError({ - message: "Failed to add universal auth to already-configured identity" - }); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: identityMembershipOrg.organization - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Identity - ); - - const plan = await EELicenseService.getPlan(identityMembershipOrg.organization); - - // validate trusted ips - let reformattedClientSecretTrustedIps; - if (clientSecretTrustedIps) { - reformattedClientSecretTrustedIps = clientSecretTrustedIps.map((clientSecretTrustedIp) => { - if (!plan.ipAllowlisting && (clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && clientSecretTrustedIp.ipAddress !== "::/0")) return res.status(400).send({ - message: "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." - }); - - const isValidIPOrCidr = isValidIpOrCidr(clientSecretTrustedIp.ipAddress); - - if (!isValidIPOrCidr) return res.status(400).send({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - - return extractIPDetails(clientSecretTrustedIp.ipAddress); - }); - } - - let reformattedAccessTokenTrustedIps; - if (accessTokenTrustedIps) { - reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { - if (!plan.ipAllowlisting && (accessTokenTrustedIp.ipAddress !== "0.0.0.0/0" && accessTokenTrustedIp.ipAddress !== "::/0")) return res.status(400).send({ - message: "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." - }); - - const isValidIPOrCidr = isValidIpOrCidr(accessTokenTrustedIp.ipAddress); - - if (!isValidIPOrCidr) return res.status(400).send({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - - return extractIPDetails(accessTokenTrustedIp.ipAddress); - }); - } - - const identityUniversalAuth = await IdentityUniversalAuth.findOneAndUpdate( - { - identity: identityMembershipOrg.identity._id, - }, - { - clientSecretTrustedIps: reformattedClientSecretTrustedIps, - accessTokenTTL, - accessTokenMaxTTL, - accessTokenNumUsesLimit, - accessTokenTrustedIps: reformattedAccessTokenTrustedIps, - }, - { - new: true - } - ); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_IDENTITY_UNIVERSAL_AUTH, - metadata: { - identityId: identityMembershipOrg.identity._id.toString(), - clientSecretTrustedIps: reformattedClientSecretTrustedIps as Array, - accessTokenTTL, - accessTokenMaxTTL, - accessTokenNumUsesLimit, - accessTokenTrustedIps: reformattedAccessTokenTrustedIps as Array - } - } - ); - - return res.status(200).send({ - identityUniversalAuth - }); -} - -/** - * Return identity universal auth method on identity with id [identityId] - * @param req - * @param res - */ -export const getIdentityUniversalAuth = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Retrieve Universal Auth configuration on identity' - #swagger.description = 'Retrieve Universal Auth configuration on identity' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['identityId'] = { - "description": "ID of identity to retrieve Universal Auth on", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityUniversalAuth": { - $ref: '#/definitions/IdentityUniversalAuth' - } - }, - "description": "Details of retrieved Universal Auth" - } - } - } - } - */ - const { - params: { identityId } - } = await validateRequest(reqValidator.GetUniversalAuthForIdentityV1, req); - - const identityMembershipOrg = await IdentityMembershipOrg - .findOne({ - identity: new Types.ObjectId(identityId) - }) - .populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembershipOrg) throw ResourceNotFoundError({ - message: `Failed to find identity with id ${identityId}` - }); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: identityMembershipOrg.organization - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Identity - ); - - if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.UNIVERSAL_AUTH) throw BadRequestError({ - message: "The identity does not have universal auth configured" - }); - - const identityUniversalAuth = await IdentityUniversalAuth.findOne({ - identity: identityMembershipOrg.identity._id, - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.GET_IDENTITY_UNIVERSAL_AUTH, - metadata: { - identityId: identityMembershipOrg.identity._id.toString(), - } - } - ); - - return res.status(200).send({ - identityUniversalAuth - }); -} - - -/** - * Create client secret for identity universal auth method on identity with id [identityId] - * @param req - * @param res - */ -export const createUniversalAuthClientSecret = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Create Universal Auth Client Secret for identity' - #swagger.description = 'Create Universal Auth Client Secret for identity' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['identityId'] = { - "description": "ID of identity to create Universal Auth Client Secret for", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "description": { - "type": "string", - "description": "A description for the Client Secret to create.", - "example": "..." - }, - "ttl": { - "type": "number", - "description": "The time-to-live for the Client Secret to create. By default, the TTL will be set to 0 which implies that the Client Secret will never expire; a value of 0 implies an infinite lifetime.", - "example": "...", - "default": 0 - }, - "numUsesLimit": { - "type": "number", - "description": "The maximum number of times that the Client Secret can be used together with the Client ID to get back an access token; a value of 0 implies infinite number of uses.", - "example": "...", - "default": 0 - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecret": { - "type": "string", - "description": "The created Client Secret" - }, - "clientSecretData": { - $ref: '#/definitions/IdentityUniversalAuthClientSecretData' - } - }, - "description": "Details of the created Client Secret" - } - } - } - } - */ - const { - params: { identityId }, - body: { - description, - numUsesLimit, - ttl - } - } = await validateRequest(reqValidator.CreateUniversalAuthClientSecretV1, req); - - const identityMembershipOrg = await IdentityMembershipOrg.findOne({ - identity: new Types.ObjectId(identityId) - }).populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembershipOrg) throw ResourceNotFoundError({ - message: `Failed to find identity with id ${identityId}` - }); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: identityMembershipOrg.organization - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Identity - ); - - if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.UNIVERSAL_AUTH) throw BadRequestError({ - message: "The identity does not have universal auth configured" - }); - - const rolePermission = await getOrgRolePermissions( - identityMembershipOrg?.customRole?.slug ?? identityMembershipOrg.role, - identityMembershipOrg.organization.toString() - ); - const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission); - - if (!hasRequiredPrivileges) throw ForbiddenRequestError({ - message: "Failed to create client secret for more privileged identity" - }); - - const clientSecret = crypto.randomBytes(32).toString("hex"); - const clientSecretHash = await bcrypt.hash(clientSecret, await getSaltRounds()); - - const identityUniversalAuth = await IdentityUniversalAuth.findOne({ - identity: identityMembershipOrg.identity._id - }); - - if (!identityUniversalAuth) throw ResourceNotFoundError(); - - const identityUniversalAuthClientSecret = await new IdentityUniversalAuthClientSecret({ - identity: identityMembershipOrg.identity._id, - identityUniversalAuth: identityUniversalAuth._id, - description, - clientSecretPrefix: clientSecret.slice(0, 4), - clientSecretHash, - clientSecretNumUses: 0, - clientSecretNumUsesLimit: numUsesLimit, - clientSecretTTL: ttl, - isClientSecretRevoked: false - }).save(); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET, - metadata: { - identityId: identityMembershipOrg.identity._id.toString(), - clientSecretId: identityUniversalAuthClientSecret._id.toString() - } - } - ); - - return res.status(200).send({ - clientSecret, - clientSecretData: packageUniversalAuthClientSecretData(identityUniversalAuthClientSecret) - }); -} - -/** - * Return list of client secret details for identity universal auth method on identity with id [identityId] - * @param req - * @param res - */ -export const getUniversalAuthClientSecretsDetails = async (req: Request, res: Response) => { - /* - #swagger.summary = 'List Universal Auth Client Secrets for identity' - #swagger.description = 'List Universal Auth Client Secrets for identity' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['identityId'] = { - "description": "ID of identity for which to get Client Secrets for", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecretData": { - type: "array", - items: { - $ref: '#/definitions/IdentityUniversalAuthClientSecretData' - } - } - }, - "description": "Details of the Client Secrets" - } - } - } - } - */ - const { - params: { identityId } - } = await validateRequest(reqValidator.GetUniversalAuthClientSecretsV1, req); - - const identityMembershipOrg = await IdentityMembershipOrg.findOne({ - identity: new Types.ObjectId(identityId) - }).populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembershipOrg) throw ResourceNotFoundError(); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: identityMembershipOrg.organization - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Identity - ); - - if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.UNIVERSAL_AUTH) throw BadRequestError({ - message: "The identity does not have universal auth configured" - }); - - const rolePermission = await getOrgRolePermissions( - identityMembershipOrg?.customRole?.slug ?? identityMembershipOrg.role, - identityMembershipOrg.organization.toString() - ); - const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission); - - if (!hasRequiredPrivileges) throw ForbiddenRequestError({ - message: "Failed to get client secrets for more privileged MI" - }); - - const clientSecretData = await IdentityUniversalAuthClientSecret - .find({ - identity: identityMembershipOrg.identity, - isClientSecretRevoked: false - }) - .sort({ createdAt: -1 }) - .limit(5); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS, - metadata: { - identityId: identityMembershipOrg.identity._id.toString() - } - } - ); - - return res.status(200).send({ - clientSecretData: clientSecretData.map((clientSecretDatum) => packageUniversalAuthClientSecretData(clientSecretDatum)) - }); -} - -/** - * Revoke client secret for identity universal auth method on identity with id [identityId] - * @param req - * @param res - */ -export const revokeUniversalAuthClientSecret = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Revoke Universal Auth Client Secret for identity' - #swagger.description = 'Revoke Universal Auth Client Secret for identity' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['identityId'] = { - "description": "ID of identity under which Client Secret was issued for", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.parameters['clientSecretId'] = { - "description": "ID of Client Secret to revoke", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "clientSecretData": { - $ref: '#/definitions/IdentityUniversalAuthClientSecretData' - } - }, - "description": "Details of the revoked Client Secret" - } - } - } - } - */ - const { - params: { identityId, clientSecretId } - } = await validateRequest(reqValidator.RevokeUniversalAuthClientSecretV1, req); - - const identityMembershipOrg = await IdentityMembershipOrg - .findOne({ - identity: new Types.ObjectId(identityId) - }) - .populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembershipOrg) throw ResourceNotFoundError({ - message: `Failed to find identity with id ${identityId}` - }); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: identityMembershipOrg.organization - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Identity - ); - - const rolePermission = await getOrgRolePermissions( - identityMembershipOrg?.customRole?.slug ?? identityMembershipOrg.role, - identityMembershipOrg.organization.toString() - ); - const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission); - - if (!hasRequiredPrivileges) throw ForbiddenRequestError({ - message: "Failed to delete client secrets for more privileged identity" - }); - - const clientSecretData = await IdentityUniversalAuthClientSecret.findOneAndUpdate( - { - _id: new Types.ObjectId(clientSecretId), - identity: identityMembershipOrg.identity._id - }, - { - isClientSecretRevoked: true - }, - { - new: true - } - ); - - if (!clientSecretData) throw ResourceNotFoundError(); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET, - metadata: { - identityId: identityMembershipOrg.identity._id.toString(), - clientSecretId: clientSecretId - } - } - ); - - return res.status(200).send({ - clientSecretData: packageUniversalAuthClientSecretData(clientSecretData) - }) -} \ No newline at end of file diff --git a/backend-mongo/src/controllers/v1/userActionController.ts b/backend-mongo/src/controllers/v1/userActionController.ts deleted file mode 100644 index 9a151f8dd..000000000 --- a/backend-mongo/src/controllers/v1/userActionController.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Request, Response } from "express"; -import { validateRequest } from "../../helpers/validation"; -import { UserAction } from "../../models"; -import * as reqValidator from "../../validation/action"; - -/** - * Add user action [action] - * @param req - * @param res - * @returns - */ -export const addUserAction = async (req: Request, res: Response) => { - // add/record new action [action] for user with id [req.user._id] - const { - body: { action } - } = await validateRequest(reqValidator.AddUserActionV1, req); - - const userAction = await UserAction.findOneAndUpdate( - { - user: req.user._id, - action - }, - { user: req.user._id, action }, - { - new: true, - upsert: true - } - ); - - return res.status(200).send({ - message: "Successfully recorded user action", - userAction - }); -}; - -/** - * Return user action [action] for user - * @param req - * @param res - * @returns - */ -export const getUserAction = async (req: Request, res: Response) => { - // get user action [action] for user with id [req.user._id] - const { - query: { action } - } = await validateRequest(reqValidator.GetUserActionV1, req); - - const userAction = await UserAction.findOne({ - user: req.user._id, - action - }); - - return res.status(200).send({ - userAction - }); -}; diff --git a/backend-mongo/src/controllers/v1/userController.ts b/backend-mongo/src/controllers/v1/userController.ts deleted file mode 100644 index 398b24f08..000000000 --- a/backend-mongo/src/controllers/v1/userController.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Request, Response } from "express"; - -/** - * Return user on request - * @param req - * @param res - * @returns - */ -export const getUser = async (req: Request, res: Response) => { - return res.status(200).send({ - user: req.user, - }); -}; diff --git a/backend-mongo/src/controllers/v1/webhookController.ts b/backend-mongo/src/controllers/v1/webhookController.ts deleted file mode 100644 index 852a3aeed..000000000 --- a/backend-mongo/src/controllers/v1/webhookController.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { client, getEncryptionKey, getRootEncryptionKey } from "../../config"; -import { Webhook } from "../../models"; -import { getWebhookPayload, triggerWebhookRequest } from "../../services/WebhookService"; -import { BadRequestError, ResourceNotFoundError } from "../../utils/errors"; -import { EEAuditLogService } from "../../ee/services"; -import { EventType } from "../../ee/models"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8 -} from "../../variables"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/webhooks"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; -import { encryptSymmetric128BitHexKeyUTF8 } from "../../utils/crypto"; - -export const createWebhook = async (req: Request, res: Response) => { - const { - body: { webhookUrl, webhookSecretKey, environment, workspaceId, secretPath } - } = await validateRequest(reqValidator.CreateWebhookV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Webhooks - ); - - const webhook = new Webhook({ - workspace: workspaceId, - environment, - secretPath, - url: webhookUrl - }); - - if (webhookSecretKey) { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - if (rootEncryptionKey) { - const { ciphertext, iv, tag } = client.encryptSymmetric(webhookSecretKey, rootEncryptionKey); - webhook.iv = iv; - webhook.tag = tag; - webhook.encryptedSecretKey = ciphertext; - webhook.algorithm = ALGORITHM_AES_256_GCM; - webhook.keyEncoding = ENCODING_SCHEME_BASE64; - } else if (encryptionKey) { - const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: webhookSecretKey, - key: encryptionKey - }); - webhook.iv = iv; - webhook.tag = tag; - webhook.encryptedSecretKey = ciphertext; - webhook.algorithm = ALGORITHM_AES_256_GCM; - webhook.keyEncoding = ENCODING_SCHEME_UTF8; - } - } - - await webhook.save(); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_WEBHOOK, - metadata: { - webhookId: webhook._id.toString(), - environment, - secretPath, - webhookUrl, - isDisabled: false - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.status(200).send({ - webhook, - message: "successfully created webhook" - }); -}; - -export const updateWebhook = async (req: Request, res: Response) => { - const { - body: { isDisabled }, - params: { webhookId } - } = await validateRequest(reqValidator.UpdateWebhookV1, req); - - const webhook = await Webhook.findById(webhookId); - if (!webhook) { - throw BadRequestError({ message: "Webhook not found!!" }); - } - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: webhook.workspace - }); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Webhooks - ); - - if (typeof isDisabled !== undefined) { - webhook.isDisabled = isDisabled; - } - await webhook.save(); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_WEBHOOK_STATUS, - metadata: { - webhookId: webhook._id.toString(), - environment: webhook.environment, - secretPath: webhook.secretPath, - webhookUrl: webhook.url, - isDisabled - } - }, - { - workspaceId: webhook.workspace - } - ); - - return res.status(200).send({ - webhook, - message: "successfully updated webhook" - }); -}; - -export const deleteWebhook = async (req: Request, res: Response) => { - const { - params: { webhookId } - } = await validateRequest(reqValidator.DeleteWebhookV1, req); - let webhook = await Webhook.findById(webhookId); - - if (!webhook) { - throw ResourceNotFoundError({ message: "Webhook not found!!" }); - } - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: webhook.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Webhooks - ); - - webhook = await Webhook.findByIdAndDelete(webhookId); - - if (!webhook) { - throw ResourceNotFoundError({ message: "Webhook not found!!" }); - } - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.DELETE_WEBHOOK, - metadata: { - webhookId: webhook._id.toString(), - environment: webhook.environment, - secretPath: webhook.secretPath, - webhookUrl: webhook.url, - isDisabled: webhook.isDisabled - } - }, - { - workspaceId: webhook.workspace - } - ); - - return res.status(200).send({ - message: "successfully removed webhook" - }); -}; - -export const testWebhook = async (req: Request, res: Response) => { - const { - params: { webhookId } - } = await validateRequest(reqValidator.TestWebhookV1, req); - - const webhook = await Webhook.findById(webhookId); - if (!webhook) { - throw BadRequestError({ message: "Webhook not found!!" }); - } - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: webhook.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Webhooks - ); - - try { - await triggerWebhookRequest( - webhook, - getWebhookPayload( - "test", - webhook.workspace.toString(), - webhook.environment, - webhook.secretPath - ) - ); - await Webhook.findByIdAndUpdate(webhookId, { - lastStatus: "success", - lastRunErrorMessage: null - }); - } catch (err) { - await Webhook.findByIdAndUpdate(webhookId, { - lastStatus: "failed", - lastRunErrorMessage: (err as Error).message - }); - return res.status(400).send({ - message: "Failed to receive response", - error: (err as Error).message - }); - } - - return res.status(200).send({ - message: "Successfully received response" - }); -}; - -export const listWebhooks = async (req: Request, res: Response) => { - const { - query: { environment, workspaceId, secretPath } - } = await validateRequest(reqValidator.ListWebhooksV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Webhooks - ); - - const optionalFilters: Record = {}; - if (environment) optionalFilters.environment = environment as string; - if (secretPath) optionalFilters.secretPath = secretPath as string; - - const webhooks = await Webhook.find({ - workspace: new Types.ObjectId(workspaceId as string), - ...optionalFilters - }); - - return res.status(200).send({ - webhooks - }); -}; diff --git a/backend-mongo/src/controllers/v1/workspaceController.ts b/backend-mongo/src/controllers/v1/workspaceController.ts deleted file mode 100644 index 2d6e9776d..000000000 --- a/backend-mongo/src/controllers/v1/workspaceController.ts +++ /dev/null @@ -1,359 +0,0 @@ -import { Types } from "mongoose"; -import { Request, Response } from "express"; -import { - IUser, - Integration, - IntegrationAuth, - Membership, - Organization, - ServiceToken, - Workspace -} from "../../models"; -import { createWorkspace as create, deleteWorkspace as deleteWork } from "../../helpers/workspace"; -import { EELicenseService } from "../../ee/services"; -import { addMemberships } from "../../helpers/membership"; -import { ADMIN } from "../../variables"; -import { OrganizationNotFoundError } from "../../utils/errors"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - getAuthDataOrgPermissions -} from "../../ee/services/RoleService"; -import { ForbiddenError } from "@casl/ability"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; - -/** - * Return public keys of members of workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const getWorkspacePublicKeys = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspacePublicKeysV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Member - ); - - const publicKeys = ( - await Membership.find({ - workspace: workspaceId - }).populate<{ user: IUser }>("user", "publicKey") - ).map((member) => { - return { - publicKey: member.user.publicKey, - userId: member.user._id - }; - }); - - return res.status(200).send({ - publicKeys - }); -}; - -/** - * Return memberships for workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const getWorkspaceMemberships = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceMembershipsV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Member - ); - - const users = await Membership.find({ - workspace: workspaceId - }).populate("user", "+publicKey"); - - return res.status(200).send({ - users - }); -}; - -/** - * Return workspaces that user is part of - * @param req - * @param res - * @returns - */ -export const getWorkspaces = async (req: Request, res: Response) => { - const workspaces = ( - await Membership.find({ - user: req.user._id - }).populate("workspace") - ).map((m) => m.workspace); - - return res.status(200).send({ - workspaces - }); -}; - -/** - * Return workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const getWorkspace = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceV1, req); - - const workspace = await Workspace.findOne({ - _id: workspaceId - }); - - return res.status(200).send({ - workspace - }); -}; - -/** - * Create new workspace named [workspaceName] under organization with id - * [organizationId] and add user as admin - * @param req - * @param res - * @returns - */ -export const createWorkspace = async (req: Request, res: Response) => { - const { - body: { organizationId, workspaceName } - } = await validateRequest(reqValidator.CreateWorkspaceV1, req); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Workspace - ); - - const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId)); - - if (plan.workspaceLimit !== null) { - // case: limit imposed on number of workspaces allowed - if (plan.workspacesUsed >= plan.workspaceLimit) { - // case: number of workspaces used exceeds the number of workspaces allowed - return res.status(400).send({ - message: - "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces." - }); - } - } - - if (workspaceName.length < 1) { - throw new Error("Workspace names must be at least 1-character long"); - } - - // create workspace and add user as member - const workspace = await create({ - name: workspaceName, - organizationId: new Types.ObjectId(organizationId) - }); - - await addMemberships({ - userIds: [req.user._id], - workspaceId: workspace._id.toString(), - roles: [ADMIN] - }); - - return res.status(200).send({ - workspace - }); -}; - -/** - * Delete workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const deleteWorkspace = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.DeleteWorkspaceV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Workspace - ); - - // delete workspace - const workspace = await deleteWork({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - return res.status(200).send({ - workspace - }); -}; - -/** - * Change name of workspace with id [workspaceId] to [name] - * @param req - * @param res - * @returns - */ -export const changeWorkspaceName = async (req: Request, res: Response) => { - const { - params: { workspaceId }, - body: { name } - } = await validateRequest(reqValidator.ChangeWorkspaceNameV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Workspace - ); - - const workspace = await Workspace.findOneAndUpdate( - { - _id: workspaceId - }, - { - name - }, - { - new: true - } - ); - - return res.status(200).send({ - message: "Successfully changed workspace name", - workspace - }); -}; - -/** - * Return integrations for workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const getWorkspaceIntegrations = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceIntegrationsV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - const integrations = await Integration.find({ - workspace: workspaceId - }); - - return res.status(200).send({ - integrations - }); -}; - -/** - * Return (integration) authorizations for workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const getWorkspaceIntegrationAuthorizations = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceIntegrationAuthorizationsV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); - - const authorizations = await IntegrationAuth.find({ - workspace: workspaceId - }); - - return res.status(200).send({ - authorizations - }); -}; - -/** - * Return service service tokens for workspace [workspaceId] belonging to user - * @param req - * @param res - * @returns - */ -export const getWorkspaceServiceTokens = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceServiceTokensV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.ServiceTokens - ); - - // ?? FIX. - const serviceTokens = await ServiceToken.find({ - user: req.user._id, - workspace: workspaceId - }); - - return res.status(200).send({ - serviceTokens - }); -}; diff --git a/backend-mongo/src/controllers/v2/authController.ts b/backend-mongo/src/controllers/v2/authController.ts deleted file mode 100644 index ce2fae34f..000000000 --- a/backend-mongo/src/controllers/v2/authController.ts +++ /dev/null @@ -1,315 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -import { Request, Response } from "express"; -import jwt from "jsonwebtoken"; -import * as bigintConversion from "bigint-conversion"; -const jsrp = require("jsrp"); -import { LoginSRPDetail, User } from "../../models"; -import { createToken, issueAuthTokens } from "../../helpers/auth"; -import { checkUserDevice } from "../../helpers/user"; -import { sendMail } from "../../helpers/nodemailer"; -import { TokenService } from "../../services"; -import { BadRequestError, InternalServerError } from "../../utils/errors"; -import { AuthTokenType, TOKEN_EMAIL_MFA } from "../../variables"; -import { getAuthSecret, getHttpsEnabled, getJwtMfaLifetime } from "../../config"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/auth"; - -declare module "jsonwebtoken" { - export interface UserIDJwtPayload extends jwt.JwtPayload { - userId: string; - } -} - -/** - * Log in user step 1: Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol - * @param req - * @param res - * @returns - */ -export const login1 = async (req: Request, res: Response) => { - const { email, clientPublicKey }: { email: string; clientPublicKey: string } = req.body; - - const user = await User.findOne({ - email - }).select("+salt +verifier"); - - if (!user) throw new Error("Failed to find user"); - - const server = new jsrp.server(); - server.init( - { - salt: user.salt, - verifier: user.verifier - }, - async () => { - // generate server-side public key - const serverPublicKey = server.getPublicKey(); - - await LoginSRPDetail.findOneAndReplace( - { email: email }, - { - email: email, - clientPublicKey: clientPublicKey, - serverBInt: bigintConversion.bigintToBuf(server.bInt) - }, - { upsert: true, returnNewDocument: false } - ); - - return res.status(200).send({ - serverPublicKey, - salt: user.salt - }); - } - ); -}; - -/** - * Log in user step 2: complete step 2 of SRP protocol and return token and their (encrypted) - * private key - * @param req - * @param res - * @returns - */ -export const login2 = async (req: Request, res: Response) => { - if (!req.headers["user-agent"]) - throw InternalServerError({ message: "User-Agent header is required" }); - - const { email, clientProof } = req.body; - const user = await User.findOne({ - email - }).select( - "+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices" - ); - - if (!user) throw new Error("Failed to find user"); - - const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email }); - - if (!loginSRPDetail) { - return BadRequestError(Error("Failed to find login details for SRP")); - } - - const server = new jsrp.server(); - server.init( - { - salt: user.salt, - verifier: user.verifier, - b: loginSRPDetail.serverBInt - }, - async () => { - server.setClientPublicKey(loginSRPDetail.clientPublicKey); - - // compare server and client shared keys - if (server.checkClientProof(clientProof)) { - if (user.isMfaEnabled) { - // case: user has MFA enabled - - // generate temporary MFA token - const token = createToken({ - payload: { - authTokenType: AuthTokenType.MFA_TOKEN, - userId: user._id.toString() - }, - expiresIn: await getJwtMfaLifetime(), - secret: await getAuthSecret() - }); - - const code = await TokenService.createToken({ - type: TOKEN_EMAIL_MFA, - email - }); - - // send MFA code [code] to [email] - await sendMail({ - template: "emailMfa.handlebars", - subjectLine: "Infisical MFA code", - recipients: [email], - substitutions: { - code - } - }); - - return res.status(200).send({ - mfaEnabled: true, - token - }); - } - - await checkUserDevice({ - user, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - // issue tokens - const tokens = await issueAuthTokens({ - userId: user._id, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - // store (refresh) token in httpOnly cookie - res.cookie("jid", tokens.refreshToken, { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: await getHttpsEnabled() - }); - - // case: user does not have MFA enabled - // return (access) token in response - - interface ResponseData { - mfaEnabled: boolean; - encryptionVersion: any; - protectedKey?: string; - protectedKeyIV?: string; - protectedKeyTag?: string; - token: string; - publicKey?: string; - encryptedPrivateKey?: string; - iv?: string; - tag?: string; - } - - const response: ResponseData = { - mfaEnabled: false, - encryptionVersion: user.encryptionVersion, - token: tokens.token, - publicKey: user.publicKey, - encryptedPrivateKey: user.encryptedPrivateKey, - iv: user.iv, - tag: user.tag - }; - - if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) { - response.protectedKey = user.protectedKey; - response.protectedKeyIV = user.protectedKeyIV; - response.protectedKeyTag = user.protectedKeyTag; - } - - return res.status(200).send(response); - } - - return res.status(400).send({ - message: "Failed to authenticate. Try again?" - }); - } - ); -}; - -/** - * Send MFA token to email [email] - * @param req - * @param res - */ -export const sendMfaToken = async (req: Request, res: Response) => { - const code = await TokenService.createToken({ - type: TOKEN_EMAIL_MFA, - email: req.user.email - }); - - // send MFA code [code] to [email] - await sendMail({ - template: "emailMfa.handlebars", - subjectLine: "Infisical MFA code", - recipients: [req.user.email], - substitutions: { - code - } - }); - - return res.status(200).send({ - message: "Successfully sent new MFA code" - }); -}; - -/** - * Verify MFA token [mfaToken] and issue JWT and refresh tokens if the - * MFA token [mfaToken] is valid - * @param req - * @param res - */ -export const verifyMfaToken = async (req: Request, res: Response) => { - const { - body: { mfaToken } - } = await validateRequest(reqValidator.VerifyMfaTokenV2, req); - - await TokenService.validateToken({ - type: TOKEN_EMAIL_MFA, - email: req.user.email, - token: mfaToken - }); - - const user = await User.findOne({ - email: req.user.email - }).select( - "+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices" - ); - - if (!user) throw new Error("Failed to find user"); - - await LoginSRPDetail.deleteOne({ userId: user.id }); - - await checkUserDevice({ - user, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - // issue tokens - const tokens = await issueAuthTokens({ - userId: user._id, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - // store (refresh) token in httpOnly cookie - res.cookie("jid", tokens.refreshToken, { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: await getHttpsEnabled() - }); - - interface VerifyMfaTokenRes { - encryptionVersion: number; - protectedKey?: string; - protectedKeyIV?: string; - protectedKeyTag?: string; - token: string; - publicKey: string; - encryptedPrivateKey: string; - iv: string; - tag: string; - } - - interface VerifyMfaTokenRes { - encryptionVersion: number; - protectedKey?: string; - protectedKeyIV?: string; - protectedKeyTag?: string; - token: string; - publicKey: string; - encryptedPrivateKey: string; - iv: string; - tag: string; - } - - const resObj: VerifyMfaTokenRes = { - encryptionVersion: user.encryptionVersion, - token: tokens.token, - publicKey: user.publicKey as string, - encryptedPrivateKey: user.encryptedPrivateKey as string, - iv: user.iv as string, - tag: user.tag as string - }; - - if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) { - resObj.protectedKey = user.protectedKey; - resObj.protectedKeyIV = user.protectedKeyIV; - resObj.protectedKeyTag = user.protectedKeyTag; - } - - return res.status(200).send(resObj); -}; diff --git a/backend-mongo/src/controllers/v2/environmentController.ts b/backend-mongo/src/controllers/v2/environmentController.ts deleted file mode 100644 index 3b412638b..000000000 --- a/backend-mongo/src/controllers/v2/environmentController.ts +++ /dev/null @@ -1,604 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { - Folder, - Integration, - Membership, - Secret, - ServiceToken, - ServiceTokenData, - Workspace -} from "../../models"; -import { EventType, SecretVersion } from "../../ee/models"; -import { EEAuditLogService, EELicenseService } from "../../ee/services"; -import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/environments"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; -import { SecretImport } from "../../models"; -import { Webhook } from "../../models"; - -/** - * Create new workspace environment named [environmentName] - * with slug [environmentSlug] under workspace with id - * @param req - * @param res - * @returns - */ -export const createWorkspaceEnvironment = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Create environment' - #swagger.description = 'Create environment' - - #swagger.security = [{ - "apiKeyAuth": [], - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of workspace where to create environment", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "environmentName": { - "type": "string", - "description": "Name of the environment to create", - "example": "development" - }, - "environmentSlug": { - "type": "string", - "description": "Slug of environment to create", - "example": "dev-environment" - } - }, - "required": ["environmentName", "environmentSlug"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Sucess message", - "example": "Successfully created environment" - }, - "workspace": { - "type": "string", - "description": "ID of workspace where environment was created", - "example": "abc123" - }, - "environment": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of created environment", - "example": "Staging" - }, - "slug": { - "type": "string", - "description": "Slug of created environment", - "example": "staging" - } - } - } - }, - "description": "Details of the created environment" - } - } - } - } - */ - const { - params: { workspaceId }, - body: { environmentName, environmentSlug } - } = await validateRequest(reqValidator.CreateWorkspaceEnvironmentV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Environments - ); - - const workspace = await Workspace.findById(workspaceId).exec(); - - if (!workspace) throw WorkspaceNotFoundError(); - - const plan = await EELicenseService.getPlan(workspace.organization); - - if (plan.environmentLimit !== null) { - // case: limit imposed on number of environments allowed - if (workspace.environments.length >= plan.environmentLimit) { - // case: number of environments used exceeds the number of environments allowed - - return res.status(400).send({ - message: - "Failed to create environment due to environment limit reached. Upgrade plan to create more environments." - }); - } - } - - if ( - !workspace || - workspace?.environments.find( - ({ name, slug }) => slug === environmentSlug || environmentName === name - ) - ) { - throw new Error("Failed to create workspace environment"); - } - - workspace?.environments.push({ - name: environmentName, - slug: environmentSlug.toLowerCase() - }); - await workspace.save(); - - await EELicenseService.refreshPlan(workspace.organization, new Types.ObjectId(workspaceId)); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_ENVIRONMENT, - metadata: { - name: environmentName, - slug: environmentSlug - } - }, - { - workspaceId: workspace._id - } - ); - - return res.status(200).send({ - message: "Successfully created new environment", - workspace: workspaceId, - environment: { - name: environmentName, - slug: environmentSlug - } - }); -}; - -/** - * Swaps the ordering of two environments in the database. This is purely for aesthetic purposes. - * @param req - * @param res - * @returns - */ -export const reorderWorkspaceEnvironments = async (req: Request, res: Response) => { - const { - params: { workspaceId }, - body: { environmentName, environmentSlug, otherEnvironmentSlug, otherEnvironmentName } - } = await validateRequest(reqValidator.ReorderWorkspaceEnvironmentsV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Environments - ); - - // atomic update the env to avoid conflict - const workspace = await Workspace.findById(workspaceId).exec(); - if (!workspace) { - throw BadRequestError({ message: "Couldn't load workspace" }); - } - - const environmentIndex = workspace.environments.findIndex( - (env) => env.name === environmentName && env.slug === environmentSlug - ); - const otherEnvironmentIndex = workspace.environments.findIndex( - (env) => env.name === otherEnvironmentName && env.slug === otherEnvironmentSlug - ); - - if (environmentIndex === -1 || otherEnvironmentIndex === -1) { - throw BadRequestError({ message: "environment or otherEnvironment couldn't be found" }); - } - - // swap the order of the environments - [workspace.environments[environmentIndex], workspace.environments[otherEnvironmentIndex]] = [ - workspace.environments[otherEnvironmentIndex], - workspace.environments[environmentIndex] - ]; - - await workspace.save(); - - return res.status(200).send({ - message: "Successfully reordered environments", - workspace: workspaceId - }); -}; - -/** - * Rename workspace environment with new name and slug of a workspace with [workspaceId] - * Old slug [oldEnvironmentSlug] must be provided - * @param req - * @param res - * @returns - */ -export const renameWorkspaceEnvironment = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update environment' - #swagger.description = 'Update environment' - - #swagger.security = [{ - "apiKeyAuth": [], - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of workspace where to update environment", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "environmentName": { - "type": "string", - "description": "Name of environment to update to", - "example": "Staging-Renamed" - }, - "environmentSlug": { - "type": "string", - "description": "Slug of environment to update to", - "example": "staging-renamed" - }, - "oldEnvironmentSlug": { - "type": "string", - "description": "Current slug of environment", - "example": "staging-old" - } - }, - "required": ["environmentName", "environmentSlug", "oldEnvironmentSlug"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Success message", - "example": "Successfully update environment" - }, - "workspace": { - "type": "string", - "description": "ID of workspace where environment was updated", - "example": "abc123" - }, - "environment": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of updated environment", - "example": "Staging-Renamed" - }, - "slug": { - "type": "string", - "description": "Slug of updated environment", - "example": "staging-renamed" - } - } - } - }, - "description": "Details of the renamed environment" - } - } - } - } - */ - const { - params: { workspaceId }, - body: { environmentName, environmentSlug, oldEnvironmentSlug } - } = await validateRequest(reqValidator.UpdateWorkspaceEnvironmentV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Environments - ); - - // user should pass both new slug and env name - if (!environmentSlug || !environmentName) { - throw new Error("Invalid environment given."); - } - - // atomic update the env to avoid conflict - const workspace = await Workspace.findById(workspaceId).exec(); - if (!workspace) { - throw new Error("Failed to create workspace environment"); - } - - const isEnvExist = workspace.environments.some( - ({ name, slug }) => - slug !== oldEnvironmentSlug && (name === environmentName || slug === environmentSlug) - ); - if (isEnvExist) { - throw new Error("Invalid environment given"); - } - - const envIndex = workspace?.environments.findIndex(({ slug }) => slug === oldEnvironmentSlug); - if (envIndex === -1) { - throw new Error("Invalid environment given"); - } - - const oldEnvironment = workspace.environments[envIndex]; - - workspace.environments[envIndex].name = environmentName; - workspace.environments[envIndex].slug = environmentSlug.toLowerCase(); - - await workspace.save(); - await Secret.updateMany( - { workspace: workspaceId, environment: oldEnvironmentSlug }, - { environment: environmentSlug } - ); - await SecretVersion.updateMany( - { workspace: workspaceId, environment: oldEnvironmentSlug }, - { environment: environmentSlug } - ); - await ServiceToken.updateMany( - { workspace: workspaceId, environment: oldEnvironmentSlug }, - { environment: environmentSlug } - ); - await ServiceTokenData.updateMany( - { - workspace: workspaceId, - "scopes.environment": oldEnvironmentSlug - }, - { $set: { "scopes.$[element].environment": environmentSlug } }, - { arrayFilters: [{ "element.environment": oldEnvironmentSlug }] } - ); - await Integration.updateMany( - { workspace: workspaceId, environment: oldEnvironmentSlug }, - { environment: environmentSlug } - ); - - await Folder.updateMany( - { workspace: workspaceId, environment: oldEnvironmentSlug }, - { environment: environmentSlug } - ); - - await SecretImport.updateMany( - { workspace: workspaceId, environment: oldEnvironmentSlug }, - { environment: environmentSlug } - ); - await SecretImport.updateMany( - { workspace: workspaceId, "imports.environment": oldEnvironmentSlug }, - { $set: { "imports.$[element].environment": environmentSlug } }, - { arrayFilters: [{ "element.environment": oldEnvironmentSlug }] }, - ); - - await Webhook.updateMany( - { workspace: workspaceId, environment: oldEnvironmentSlug }, - { environment: environmentSlug } - ); - - await Membership.updateMany( - { - workspace: workspaceId, - "deniedPermissions.environmentSlug": oldEnvironmentSlug - }, - { $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } }, - { arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] } - ); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_ENVIRONMENT, - metadata: { - oldName: oldEnvironment.name, - newName: environmentName, - oldSlug: oldEnvironment.slug, - newSlug: environmentSlug.toLowerCase() - } - }, - { - workspaceId: workspace._id - } - ); - - return res.status(200).send({ - message: "Successfully update environment", - workspace: workspaceId, - environment: { - name: environmentName, - slug: environmentSlug - } - }); -}; - -/** - * Delete workspace environment by [environmentSlug] of workspace [workspaceId] and do the clean up - * @param req - * @param res - * @returns - */ -export const deleteWorkspaceEnvironment = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Delete environment' - #swagger.description = 'Delete environment' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of workspace where to delete environment", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "environmentSlug": { - "type": "string", - "description": "Slug of environment to delete", - "example": "dev" - } - }, - "required": ["environmentSlug"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Success message", - "example": "Successfully deleted environment" - }, - "workspace": { - "type": "string", - "description": "ID of workspace where environment was deleted", - "example": "abc123" - }, - "environment": { - "type": "string", - "description": "Slug of deleted environment", - "example": "dev" - } - }, - "description": "Response after deleting an environment from a workspace" - } - } - } - } -*/ - const { - params: { workspaceId }, - body: { environmentSlug } - } = await validateRequest(reqValidator.DeleteWorkspaceEnvironmentV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Environments - ); - - // atomic update the env to avoid conflict - const workspace = await Workspace.findById(workspaceId).exec(); - if (!workspace) { - throw new Error("Failed to create workspace environment"); - } - - const envIndex = workspace?.environments.findIndex(({ slug }) => slug === environmentSlug); - if (envIndex === -1) { - throw new Error("Invalid environment given"); - } - - const oldEnvironment = workspace.environments[envIndex]; - - workspace.environments.splice(envIndex, 1); - await workspace.save(); - - // clean up - await Secret.deleteMany({ - workspace: workspaceId, - environment: environmentSlug - }); - await SecretVersion.deleteMany({ - workspace: workspaceId, - environment: environmentSlug - }); - - // await ServiceToken.deleteMany({ - // workspace: workspaceId, - // environment: environmentSlug, - // }); - - const result = await ServiceTokenData.updateMany( - { workspace: workspaceId }, - { $pull: { scopes: { environment: environmentSlug } } } - ); - - if (result.modifiedCount > 0) { - await ServiceTokenData.deleteMany({ workspace: workspaceId, scopes: { $size: 0 } }); - } - - await Integration.deleteMany({ - workspace: workspaceId, - environment: environmentSlug - }); - await Membership.updateMany( - { workspace: workspaceId }, - { $pull: { deniedPermissions: { environmentSlug: environmentSlug } } } - ); - - await EELicenseService.refreshPlan(workspace.organization, new Types.ObjectId(workspaceId)); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.DELETE_ENVIRONMENT, - metadata: { - name: oldEnvironment.name, - slug: oldEnvironment.slug - } - }, - { - workspaceId: workspace._id - } - ); - - return res.status(200).send({ - message: "Successfully deleted environment", - workspace: workspaceId, - environment: environmentSlug - }); -}; \ No newline at end of file diff --git a/backend-mongo/src/controllers/v2/index.ts b/backend-mongo/src/controllers/v2/index.ts deleted file mode 100644 index e06efe46c..000000000 --- a/backend-mongo/src/controllers/v2/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as authController from "./authController"; -import * as signupController from "./signupController"; -import * as usersController from "./usersController"; -import * as organizationsController from "./organizationsController"; -import * as workspaceController from "./workspaceController"; -import * as serviceTokenDataController from "./serviceTokenDataController"; -import * as secretController from "./secretController"; -import * as secretsController from "./secretsController"; -import * as environmentController from "./environmentController"; -import * as tagController from "./tagController"; -import * as membershipController from "./membershipController"; - -export { - authController, - signupController, - usersController, - organizationsController, - workspaceController, - serviceTokenDataController, - secretController, - secretsController, - environmentController, - tagController, - membershipController -}; diff --git a/backend-mongo/src/controllers/v2/membershipController.ts b/backend-mongo/src/controllers/v2/membershipController.ts deleted file mode 100644 index d6d25dad5..000000000 --- a/backend-mongo/src/controllers/v2/membershipController.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { ForbiddenError } from "@casl/ability"; -import { Request, Response } from "express"; -import { Types } from "mongoose"; - -import { getSiteURL } from "../../config"; -import { EventType } from "../../ee/models"; -import { EEAuditLogService } from "../../ee/services"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { sendMail } from "../../helpers"; -import { validateRequest } from "../../helpers/validation"; -import { IUser, Key, Membership, MembershipOrg, Workspace } from "../../models"; -import { BadRequestError } from "../../utils/errors"; -import * as reqValidator from "../../validation/membership"; -import { ACCEPTED, MEMBER } from "../../variables"; - -export const addUserToWorkspace = async (req: Request, res: Response) => { - const { - params: { workspaceId }, - body: { members } - } = await validateRequest(reqValidator.AddUserToWorkspaceV2, req); - // check workspace - const workspace = await Workspace.findById(workspaceId); - if (!workspace) throw new Error("Failed to find workspace"); - - // check permission - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Member - ); - - // validate members are part of the organization - const orgMembers = await MembershipOrg.find({ - status: ACCEPTED, - _id: { $in: members.map(({ orgMembershipId }) => orgMembershipId) }, - organization: workspace.organization - }) - .populate<{ user: IUser }>("user") - .select({ _id: 1, user: 1 }) - .lean(); - if (orgMembers.length !== members.length) - throw BadRequestError({ message: "Org member not found" }); - - const existingMember = await Membership.find({ - workspace: workspaceId, - user: { $in: orgMembers.map(({ user }) => user) } - }); - if (existingMember?.length) - throw BadRequestError({ message: "Some users are already part of workspace" }); - - await Membership.insertMany( - orgMembers.map(({ user }) => ({ user: user._id, workspace: workspaceId, role: MEMBER })) - ); - - const encKeyGroupedByOrgMemberId = members.reduce>( - (prev, curr) => ({ ...prev, [curr.orgMembershipId]: curr }), - {} - ); - await Key.insertMany( - orgMembers.map(({ user, _id: id }) => ({ - encryptedKey: encKeyGroupedByOrgMemberId[id.toString()].workspaceEncryptedKey, - nonce: encKeyGroupedByOrgMemberId[id.toString()].workspaceEncryptedNonce, - sender: req.user._id, - receiver: user._id, - workspace: workspaceId - })) - ); - - await sendMail({ - template: "workspaceInvitation.handlebars", - subjectLine: "Infisical workspace invitation", - recipients: orgMembers.map(({ user }) => user.email), - substitutions: { - inviterFirstName: req.user.firstName, - inviterEmail: req.user.email, - workspaceName: workspace.name, - callback_url: (await getSiteURL()) + "/login" - } - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.ADD_BATCH_WORKSPACE_MEMBER, - metadata: orgMembers.map(({ user }) => ({ - userId: user._id.toString(), - email: user.email - })) - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.status(200).send({ - success: true, - data: orgMembers - }); -}; diff --git a/backend-mongo/src/controllers/v2/organizationsController.ts b/backend-mongo/src/controllers/v2/organizationsController.ts deleted file mode 100644 index c6c49e23d..000000000 --- a/backend-mongo/src/controllers/v2/organizationsController.ts +++ /dev/null @@ -1,505 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { - IWorkspace, - Identity, - IdentityMembership, - IdentityMembershipOrg, - Membership, - MembershipOrg, - User, - Workspace -} from "../../models"; -import { Role } from "../../ee/models"; -import { deleteMembershipOrg } from "../../helpers/membershipOrg"; -import { - createOrganization as create, - deleteOrganization, - updateSubscriptionOrgQuantity -} from "../../helpers/organization"; -import { addMembershipsOrg } from "../../helpers/membershipOrg"; -import { BadRequestError, ResourceNotFoundError, UnauthorizedRequestError } from "../../utils/errors"; -import { ACCEPTED, ADMIN, CUSTOM, MEMBER, NO_ACCESS } from "../../variables"; -import * as reqValidator from "../../validation/organization"; -import { validateRequest } from "../../helpers/validation"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - getAuthDataOrgPermissions -} from "../../ee/services/RoleService"; -import { EELicenseService } from "../../ee/services"; -import { ForbiddenError } from "@casl/ability"; - -/** - * Return memberships for organization with id [organizationId] - * @param req - * @param res - */ -export const getOrganizationMemberships = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return organization user memberships' - #swagger.description = 'Return organization user memberships' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['organizationId'] = { - "description": "ID of organization", - "required": true, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "memberships": { - "type": "array", - "items": { - $ref: "#/components/schemas/MembershipOrg" - }, - "description": "Memberships of organization" - } - } - } - } - } - } - */ - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgMembersv2, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Member - ); - - const memberships = await MembershipOrg.find({ - organization: organizationId - }).populate("user", "+publicKey"); - - return res.status(200).send({ - memberships - }); -}; - -/** - * Update role of membership with id [membershipId] to role [role] - * @param req - * @param res - */ -export const updateOrganizationMembership = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update organization user membership' - #swagger.description = 'Update organization user membership' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['organizationId'] = { - "description": "ID of organization", - "required": true, - "type": "string" - } - - #swagger.parameters['membershipId'] = { - "description": "ID of organization membership to update", - "required": true, - "type": "string" - } - - #swagger.requestBody = { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "role": { - "type": "string", - "description": "Role of organization membership - either owner, admin, or member", - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "membership": { - $ref: "#/components/schemas/MembershipOrg", - "description": "Updated organization membership" - } - } - } - } - } - } - */ - const { - params: { organizationId, membershipId }, - body: { role } - } = await validateRequest(reqValidator.UpdateOrgMemberv2, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Member - ); - - const isCustomRole = ![ADMIN, MEMBER, NO_ACCESS].includes(role); - if (isCustomRole) { - const orgRole = await Role.findOne({ - slug: role, - isOrgRole: true, - organization: new Types.ObjectId(organizationId) - }); - - if (!orgRole) throw BadRequestError({ message: "Role not found" }); - - const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId)); - - if (!plan.rbac) return res.status(400).send({ - message: - "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." - }); - - const membership = await MembershipOrg.findByIdAndUpdate(membershipId, { - role: CUSTOM, - customRole: orgRole - }); - return res.status(200).send({ - membership - }); - } - - const membership = await MembershipOrg.findByIdAndUpdate( - membershipId, - { - $set: { - role - }, - $unset: { - customRole: 1 - } - }, - { - new: true - } - ); - - return res.status(200).send({ - membership - }); -}; - -/** - * Delete organization membership with id [membershipId] - * @param req - * @param res - * @returns - */ -export const deleteOrganizationMembership = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Delete organization user membership' - #swagger.description = 'Delete organization user membership' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['organizationId'] = { - "description": "ID of organization", - "required": true, - "type": "string" - } - - #swagger.parameters['membershipId'] = { - "description": "ID of organization membership to delete", - "required": true, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "membership": { - $ref: "#/components/schemas/MembershipOrg", - "description": "Deleted organization membership" - } - } - } - } - } - } - */ - const { - params: { organizationId, membershipId } - } = await validateRequest(reqValidator.DeleteOrgMemberv2, req); - - const membershipOrg = await MembershipOrg.findOne({ - _id: new Types.ObjectId(membershipId), - organization: new Types.ObjectId(organizationId) - }); - - if (!membershipOrg) throw ResourceNotFoundError(); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: membershipOrg.organization - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Member - ); - - // delete organization membership - const membership = await deleteMembershipOrg({ - membershipOrgId: membershipId - }); - - await updateSubscriptionOrgQuantity({ - organizationId: membership.organization.toString() - }); - - return res.status(200).send({ - membership - }); -}; - -/** - * Return workspaces for organization with id [organizationId] that user has - * access to - * @param req - * @param res - */ -export const getOrganizationWorkspaces = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return projects in organization that user is part of' - #swagger.description = 'Return projects in organization that user is part of' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['organizationId'] = { - "description": "ID of organization", - "required": true, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaces": { - "type": "array", - "items": { - $ref: "#/components/schemas/Project" - }, - "description": "Projects of organization" - } - } - } - } - } - } - */ - - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgWorkspacesv2, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Workspace - ); - - const workspacesSet = new Set( - ( - await Workspace.find( - { - organization: organizationId - }, - "_id" - ) - ).map((w) => w._id.toString()) - ); - - let workspaces: IWorkspace[] = []; - - if (req.authData.authPayload instanceof Identity) { - workspaces = ( - await IdentityMembership.find({ - identity: req.authData.authPayload._id - }).populate<{ workspace: IWorkspace }>("workspace") - ) - .filter((m) => workspacesSet.has(m.workspace._id.toString())) - .map((m) => m.workspace); - } - - if (req.authData.authPayload instanceof User) { - workspaces = ( - await Membership.find({ - user: req.authData.authPayload._id - }).populate<{ workspace: IWorkspace }>("workspace") - ) - .filter((m) => workspacesSet.has(m.workspace._id.toString())) - .map((m) => m.workspace); - } - - return res.status(200).send({ - workspaces - }); -}; - -/** - * Create new organization named [organizationName] - * and add user as owner - * @param req - * @param res - * @returns - */ -export const createOrganization = async (req: Request, res: Response) => { - const { - body: { name } - } = await validateRequest(reqValidator.CreateOrgv2, req); - - // create organization and add user as member - const organization = await create({ - email: req.user.email, - name - }); - - await addMembershipsOrg({ - userIds: [req.user._id.toString()], - organizationId: organization._id.toString(), - roles: [ADMIN], - statuses: [ACCEPTED] - }); - - return res.status(200).send({ - organization - }); -}; - -/** - * Delete organization with id [organizationId] - * @param req - * @param res - */ -export const deleteOrganizationById = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.DeleteOrgv2, req); - - const membershipOrg = await MembershipOrg.findOne({ - user: req.user._id, - organization: new Types.ObjectId(organizationId), - role: ADMIN - }); - - if (!membershipOrg) throw UnauthorizedRequestError(); - - const organization = await deleteOrganization({ - organizationId: new Types.ObjectId(organizationId) - }); - - return res.status(200).send({ - organization - }); -}; - -/** - * Return list of identity memberships for organization with id [organizationId] - * @param req - * @param res - * @returns - */ - export const getOrganizationIdentityMemberships = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return organization identity memberships' - #swagger.description = 'Return organization identity memberships' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['organizationId'] = { - "description": "ID of organization", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityMemberships": { - "type": "array", - "items": { - $ref: "#/components/schemas/IdentityMembershipOrg" - }, - "description": "Identity memberships of organization" - } - } - } - } - } - } - */ - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgIdentityMembershipsV2, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Identity - ); - - const identityMemberships = await IdentityMembershipOrg.find({ - organization: new Types.ObjectId(organizationId) - }).populate("identity customRole"); - - return res.status(200).send({ - identityMemberships - }); -} \ No newline at end of file diff --git a/backend-mongo/src/controllers/v2/secretController.ts b/backend-mongo/src/controllers/v2/secretController.ts deleted file mode 100644 index 28fab01a4..000000000 --- a/backend-mongo/src/controllers/v2/secretController.ts +++ /dev/null @@ -1,419 +0,0 @@ -import { Request, Response } from "express"; -import mongoose, { Types } from "mongoose"; -import { - CreateSecretRequestBody, - ModifySecretRequestBody, - SanitizedSecretForCreate, - SanitizedSecretModify -} from "../../types/secret"; -const { ValidationError } = mongoose.Error; -import { - ValidationError as RouteValidationError, - UnauthorizedRequestError -} from "../../utils/errors"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_UTF8, - SECRET_PERSONAL, - SECRET_SHARED -} from "../../variables"; -import { TelemetryService } from "../../services"; -import { Secret, User } from "../../models"; -import { AccountNotFoundError } from "../../utils/errors"; - -/** - * Create secret for workspace with id [workspaceId] and environment [environment] - * @param req - * @param res - */ -export const createSecret = async (req: Request, res: Response) => { - const postHogClient = await TelemetryService.getPostHogClient(); - const secretToCreate: CreateSecretRequestBody = req.body.secret; - const { workspaceId, environment } = req.params; - const sanitizedSecret: SanitizedSecretForCreate = { - secretKeyCiphertext: secretToCreate.secretKeyCiphertext, - secretKeyIV: secretToCreate.secretKeyIV, - secretKeyTag: secretToCreate.secretKeyTag, - secretKeyHash: secretToCreate.secretKeyHash, - secretValueCiphertext: secretToCreate.secretValueCiphertext, - secretValueIV: secretToCreate.secretValueIV, - secretValueTag: secretToCreate.secretValueTag, - secretValueHash: secretToCreate.secretValueHash, - secretCommentCiphertext: secretToCreate.secretCommentCiphertext, - secretCommentIV: secretToCreate.secretCommentIV, - secretCommentTag: secretToCreate.secretCommentTag, - secretCommentHash: secretToCreate.secretCommentHash, - workspace: new Types.ObjectId(workspaceId), - environment, - type: secretToCreate.type, - user: new Types.ObjectId(req.user._id), - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }; - - const secret = await new Secret(sanitizedSecret).save(); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets added", - distinctId: req.user.email, - properties: { - numberOfSecrets: 1, - workspaceId, - environment, - channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli", - userAgent: req.headers?.["user-agent"] - } - }); - } - - res.status(200).send({ - secret - }); -}; - -/** - * Create many secrets for workspace with id [workspaceId] and environment [environment] - * @param req - * @param res - */ -export const createSecrets = async (req: Request, res: Response) => { - const postHogClient = await TelemetryService.getPostHogClient(); - const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets; - const { workspaceId, environment } = req.params; - const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = []; - - secretsToCreate.forEach((rawSecret) => { - const safeUpdateFields: SanitizedSecretForCreate = { - secretKeyCiphertext: rawSecret.secretKeyCiphertext, - secretKeyIV: rawSecret.secretKeyIV, - secretKeyTag: rawSecret.secretKeyTag, - secretKeyHash: rawSecret.secretKeyHash, - secretValueCiphertext: rawSecret.secretValueCiphertext, - secretValueIV: rawSecret.secretValueIV, - secretValueTag: rawSecret.secretValueTag, - secretValueHash: rawSecret.secretValueHash, - secretCommentCiphertext: rawSecret.secretCommentCiphertext, - secretCommentIV: rawSecret.secretCommentIV, - secretCommentTag: rawSecret.secretCommentTag, - secretCommentHash: rawSecret.secretCommentHash, - workspace: new Types.ObjectId(workspaceId), - environment, - type: rawSecret.type, - user: new Types.ObjectId(req.user._id), - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }; - - sanitizedSecretesToCreate.push(safeUpdateFields); - }); - - const secrets = await Secret.insertMany(sanitizedSecretesToCreate); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets added", - distinctId: req.user.email, - properties: { - numberOfSecrets: (secretsToCreate ?? []).length, - workspaceId, - environment, - channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli", - userAgent: req.headers?.["user-agent"] - } - }); - } - - res.status(200).send({ - secrets - }); -}; - -/** - * Delete secrets in workspace with id [workspaceId] and environment [environment] - * @param req - * @param res - */ -export const deleteSecrets = async (req: Request, res: Response) => { - const postHogClient = await TelemetryService.getPostHogClient(); - const { workspaceId, environmentName } = req.params; - const secretIdsToDelete: string[] = req.body.secretIds; - - const secretIdsUserCanDelete = await Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }); - - const secretsUserCanDeleteSet: Set = new Set( - secretIdsUserCanDelete.map((objectId) => objectId._id.toString()) - ); - - // Filter out IDs that user can delete and then map them to delete operations - const deleteOperationsToPerform = secretIdsToDelete - .filter(secretIdToDelete => { - if (!secretsUserCanDeleteSet.has(secretIdToDelete)) { - throw RouteValidationError({ - message: "You cannot delete secrets that you do not have access to" - }); - } - return true; - }) - .map(secretIdToDelete => ({ - deleteOne: { filter: { _id: new Types.ObjectId(secretIdToDelete) } } - })); - - const numSecretsDeleted = deleteOperationsToPerform.length; - - await Secret.bulkWrite(deleteOperationsToPerform); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets deleted", - distinctId: req.user.email, - properties: { - numberOfSecrets: numSecretsDeleted, - environment: environmentName, - workspaceId, - channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli", - userAgent: req.headers?.["user-agent"] - } - }); - } - - res.status(200).send(); -}; - -/** - * Delete secret with id [secretId] - * @param req - * @param res - */ -export const deleteSecret = async (req: Request, res: Response) => { - const postHogClient = await TelemetryService.getPostHogClient(); - await Secret.findByIdAndDelete(req._secret._id); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets deleted", - distinctId: req.user.email, - properties: { - numberOfSecrets: 1, - workspaceId: req._secret.workspace.toString(), - environment: req._secret.environment, - channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli", - userAgent: req.headers?.["user-agent"] - } - }); - } - - res.status(200).send({ - secret: req._secret - }); -}; - -/** - * Update secrets for workspace with id [workspaceId] and environment [environment] - * @param req - * @param res - * @returns - */ -export const updateSecrets = async (req: Request, res: Response) => { - const postHogClient = await TelemetryService.getPostHogClient(); - const { workspaceId, environmentName } = req.params; - const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets; - const secretIdsUserCanModify = await Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }); - - const secretsUserCanModifySet: Set = new Set( - secretIdsUserCanModify.map((objectId) => objectId._id.toString()) - ); - const updateOperationsToPerform: any = []; - - secretsModificationsRequested.forEach((userModifiedSecret) => { - if (secretsUserCanModifySet.has(userModifiedSecret._id.toString())) { - const sanitizedSecret: SanitizedSecretModify = { - secretKeyCiphertext: userModifiedSecret.secretKeyCiphertext, - secretKeyIV: userModifiedSecret.secretKeyIV, - secretKeyTag: userModifiedSecret.secretKeyTag, - secretKeyHash: userModifiedSecret.secretKeyHash, - secretValueCiphertext: userModifiedSecret.secretValueCiphertext, - secretValueIV: userModifiedSecret.secretValueIV, - secretValueTag: userModifiedSecret.secretValueTag, - secretValueHash: userModifiedSecret.secretValueHash, - secretCommentCiphertext: userModifiedSecret.secretCommentCiphertext, - secretCommentIV: userModifiedSecret.secretCommentIV, - secretCommentTag: userModifiedSecret.secretCommentTag, - secretCommentHash: userModifiedSecret.secretCommentHash - }; - - const updateOperation = { - updateOne: { - filter: { _id: userModifiedSecret._id, workspace: workspaceId }, - update: { $inc: { version: 1 }, $set: sanitizedSecret } - } - }; - updateOperationsToPerform.push(updateOperation); - } else { - throw UnauthorizedRequestError({ - message: "You do not have permission to modify one or more of the requested secrets" - }); - } - }); - - await Secret.bulkWrite(updateOperationsToPerform); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets modified", - distinctId: req.user.email, - properties: { - numberOfSecrets: (secretsModificationsRequested ?? []).length, - environment: environmentName, - workspaceId, - channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli", - userAgent: req.headers?.["user-agent"] - } - }); - } - - return res.status(200).send(); -}; - -/** - * Update a secret within workspace with id [workspaceId] and environment [environment] - * @param req - * @param res - * @returns - */ -export const updateSecret = async (req: Request, res: Response) => { - const postHogClient = await TelemetryService.getPostHogClient(); - const { workspaceId, environmentName } = req.params; - const secretModificationsRequested: ModifySecretRequestBody = req.body.secret; - - await Secret.findOne({ workspace: workspaceId, environment: environmentName }, { _id: 1 }); - - const sanitizedSecret: SanitizedSecretModify = { - secretKeyCiphertext: secretModificationsRequested.secretKeyCiphertext, - secretKeyIV: secretModificationsRequested.secretKeyIV, - secretKeyTag: secretModificationsRequested.secretKeyTag, - secretKeyHash: secretModificationsRequested.secretKeyHash, - secretValueCiphertext: secretModificationsRequested.secretValueCiphertext, - secretValueIV: secretModificationsRequested.secretValueIV, - secretValueTag: secretModificationsRequested.secretValueTag, - secretValueHash: secretModificationsRequested.secretValueHash, - secretCommentCiphertext: secretModificationsRequested.secretCommentCiphertext, - secretCommentIV: secretModificationsRequested.secretCommentIV, - secretCommentTag: secretModificationsRequested.secretCommentTag, - secretCommentHash: secretModificationsRequested.secretCommentHash - }; - - const singleModificationUpdate = await Secret.updateOne( - { _id: secretModificationsRequested._id, workspace: workspaceId }, - { $inc: { version: 1 }, $set: sanitizedSecret } - ) - .catch((error) => { - if (error instanceof ValidationError) { - throw RouteValidationError({ - message: "Unable to apply modifications, please try again", - stack: error.stack - }); - } - - throw error; - }); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets modified", - distinctId: req.user.email, - properties: { - numberOfSecrets: 1, - environment: environmentName, - workspaceId, - channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli", - userAgent: req.headers?.["user-agent"] - } - }); - } - - return res.status(200).send(singleModificationUpdate); -}; - -/** - * Return secrets for workspace with id [workspaceId], environment [environment] and user - * with id [req.user._id] - * @param req - * @param res - * @returns - */ -export const getSecrets = async (req: Request, res: Response) => { - const postHogClient = await TelemetryService.getPostHogClient(); - const { environment } = req.query; - const { workspaceId } = req.params; - - let userId: Types.ObjectId | undefined = undefined; // used for getting personal secrets for user - let userEmail: string | undefined = undefined; // used for posthog - if (req.user) { - userId = req.user._id; - userEmail = req.user.email; - } - - if (req.serviceTokenData) { - userId = req.serviceTokenData.user; - - const user = await User.findById(req.serviceTokenData.user, "email"); - if (!user) throw AccountNotFoundError(); - userEmail = user.email; - } - - const secrets = await Secret.find({ - workspace: workspaceId, - environment, - $or: [{ user: userId }, { user: { $exists: false } }], - type: { $in: [SECRET_SHARED, SECRET_PERSONAL] } - }) - .catch((err) => { - throw RouteValidationError({ - message: "Failed to get secrets, please try again", - stack: err.stack - }); - }) - - if (postHogClient) { - postHogClient.capture({ - event: "secrets pulled", - distinctId: userEmail, - properties: { - numberOfSecrets: (secrets ?? []).length, - environment, - workspaceId, - channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli", - userAgent: req.headers?.["user-agent"] - } - }); - } - - return res.json(secrets); -}; - -/** - * Return secret with id [secretId] - * @param req - * @param res - * @returns - */ -export const getSecret = async (req: Request, res: Response) => { - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets pulled', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: 1, - // workspaceId: req._secret.workspace.toString(), - // environment: req._secret.environment, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } - - return res.status(200).send({ - secret: req._secret - }); -}; diff --git a/backend-mongo/src/controllers/v2/secretsController.ts b/backend-mongo/src/controllers/v2/secretsController.ts deleted file mode 100644 index 362221bf4..000000000 --- a/backend-mongo/src/controllers/v2/secretsController.ts +++ /dev/null @@ -1,1300 +0,0 @@ -import { Types } from "mongoose"; -import { Request, Response } from "express"; -import { Folder, ISecret, Secret, ServiceTokenData, Tag } from "../../models"; -import { AuditLog, EventType, SecretVersion } from "../../ee/models"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_UTF8, - K8_USER_AGENT_NAME, - SECRET_PERSONAL -} from "../../variables"; -import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; -import { EventService } from "../../services"; -import { eventPushSecrets } from "../../events"; -import { EEAuditLogService, EESecretService } from "../../ee/services"; -import { SecretService, TelemetryService } from "../../services"; -import { getUserAgentType } from "../../utils/posthog"; -import { PERMISSION_WRITE_SECRETS } from "../../variables"; -import { - userHasNoAbility, - userHasWorkspaceAccess, - userHasWriteOnlyAbility -} from "../../ee/helpers/checkMembershipPermissions"; -import _ from "lodash"; -import { - getFolderByPath, - getFolderIdFromServiceToken, - searchByFolderId, - searchByFolderIdWithDir -} from "../../services/FolderService"; -import { isValidScope } from "../../helpers/secrets"; -import path from "path"; -import { getAllImportedSecrets } from "../../services/SecretImportService"; -import { validateRequest } from "../../helpers/validation"; -import { - BatchSecretsV2, - GetSecretsV2, - validateServiceTokenDataClientForWorkspace -} from "../../validation"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError, subject } from "@casl/ability"; - -/** - * Peform a batch of any specified CUD secret operations - * (used by dashboard) - * @param req - * @param res - */ -export const batchSecrets = async (req: Request, res: Response) => { - const channel = getUserAgentType(req.headers["user-agent"]); - const postHogClient = await TelemetryService.getPostHogClient(); - - const validatedData = await validateRequest(BatchSecretsV2, req); - const { - body: { workspaceId, environment, requests } - } = validatedData; - let { - body: { secretPath, folderId } - } = validatedData; - - const secretIds = requests - .filter(({ method }) => method !== "POST") - // akhilmhdh: ts is dumb - .map((el) => new Types.ObjectId((el.secret as any)._id)); - - const oldSecrets = await Secret.find({ - _id: { - $in: secretIds - } - }); - if (oldSecrets.length != secretIds.length) { - throw BadRequestError({ message: "Failed to validate non-existent secrets" }); - } - - const createSecrets: any[] = []; - const updateSecrets: any[] = []; - const deleteSecrets: { _id: Types.ObjectId; secretName: string }[] = []; - - // get secret blind index salt - const salt = await SecretService.getSecretBlindIndexSalt({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - if (secretPath !== "/") { - folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath); - } - - if (folderId !== "root") { - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - if (!folders) throw BadRequestError({ message: "Folder not found" }); - - const folder = searchByFolderIdWithDir(folders.nodes, folderId as string); - if (!folder?.folder) throw BadRequestError({ message: "Folder not found" }); - - secretPath = path.join( - "/", - ...folder.dir.map(({ name }) => name).filter((name) => name !== "root") - ); - } - - if (req.authData.authPayload instanceof ServiceTokenData) { - await validateServiceTokenDataClientForWorkspace({ - serviceTokenData: req.authData.authPayload, - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath, - requiredPermissions: [PERMISSION_WRITE_SECRETS] - }); - } - - for await (const request of requests) { - // do a validation - - let secretBlindIndex = ""; - switch (request.method) { - case "POST": - secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({ - secretName: request.secret.secretName, - salt - }); - - createSecrets.push({ - ...request.secret, - version: 1, - user: request.secret.type === SECRET_PERSONAL ? req.user : undefined, - environment, - workspace: workspaceId, - folder: folderId, - secretBlindIndex, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }); - break; - case "PATCH": - secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({ - secretName: request.secret.secretName, - salt - }); - - updateSecrets.push({ - ...request.secret, - _id: request.secret._id, - secretBlindIndex, - folder: folderId, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }); - break; - case "DELETE": - deleteSecrets.push({ - _id: new Types.ObjectId(request.secret._id), - secretName: request.secret.secretName - }); - break; - } - } - // not using service token using auth - if (!(req.authData.authPayload instanceof ServiceTokenData)) { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - if (createSecrets.length) - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - subject(ProjectPermissionSub.Secrets, { environment, secretPath }) - ); - - if (updateSecrets.length) - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { environment, secretPath }) - ); - - if (deleteSecrets.length) - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - subject(ProjectPermissionSub.Secrets, { environment, secretPath }) - ); - } - - // handle create secrets - let createdSecrets: ISecret[] = []; - if (createSecrets.length > 0) { - createdSecrets = (await Secret.insertMany(createSecrets)) as any; - // (EE) add secret versions for new secrets - await EESecretService.addSecretVersions({ - secretVersions: createdSecrets.map((n: any) => { - return { - ...n._doc, - _id: new Types.ObjectId(), - secret: n._id, - isDeleted: false - }; - }) - }); - - const auditLogs = await Promise.all( - createdSecrets.map((secret, index) => { - return EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_SECRET, - metadata: { - environment: secret.environment, - secretPath: secretPath ?? "/", - secretId: secret._id.toString(), - secretKey: createSecrets[index].secretName, - secretVersion: secret.version - } - }, - { - workspaceId: secret.workspace - }, - false - ); - }) - ); - - await AuditLog.insertMany(auditLogs); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets added", - distinctId: req.user.email, - properties: { - numberOfSecrets: createdSecrets.length, - environment, - workspaceId, - folderId, - channel, - userAgent: req.headers?.["user-agent"] - } - }); - } - } - - // handle update secrets - let updatedSecrets: ISecret[] = []; - if (updateSecrets.length > 0 && oldSecrets) { - // construct object containing all secrets - let listedSecretsObj: { - [key: string]: { - version: number; - type: string; - }; - } = {}; - - listedSecretsObj = oldSecrets.reduce( - (obj: any, secret: ISecret) => ({ - ...obj, - [secret._id.toString()]: secret - }), - {} - ); - - const updateOperations = updateSecrets.map((u) => ({ - updateOne: { - filter: { - _id: new Types.ObjectId(u._id), - workspace: new Types.ObjectId(workspaceId), - environment - }, - update: { - $inc: { - version: 1 - }, - $unset: { - "metadata.source": true as const - }, - ...u, - _id: new Types.ObjectId(u._id) - } - } - })); - await Secret.bulkWrite(updateOperations); - - const secretVersions = updateSecrets.map( - (u) => - new SecretVersion({ - secret: new Types.ObjectId(u._id), - version: listedSecretsObj[u._id.toString()].version, - workspace: new Types.ObjectId(workspaceId), - type: listedSecretsObj[u._id.toString()].type, - environment, - isDeleted: false, - secretBlindIndex: u.secretBlindIndex, - secretKeyCiphertext: u.secretKeyCiphertext, - secretKeyIV: u.secretKeyIV, - secretKeyTag: u.secretKeyTag, - secretValueCiphertext: u.secretValueCiphertext, - secretValueIV: u.secretValueIV, - secretValueTag: u.secretValueTag, - secretCommentCiphertext: u.secretCommentCiphertext, - secretCommentIV: u.secretCommentIV, - secretCommentTag: u.secretCommentTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - tags: u.tags, - folder: u.folder - }) - ); - - await EESecretService.addSecretVersions({ - secretVersions - }); - - updatedSecrets = await Secret.find({ - _id: { - $in: updateSecrets.map((u) => new Types.ObjectId(u._id)) - } - }); - - const auditLogs = await Promise.all( - updateSecrets.map((secret) => { - return EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_SECRET, - metadata: { - environment, - secretPath: secretPath ?? "/", - secretId: secret._id.toString(), - secretKey: secret.secretName, - secretVersion: listedSecretsObj[secret._id.toString()].version - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - }, - false - ); - }) - ); - - await AuditLog.insertMany(auditLogs); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets modified", - distinctId: req.user.email, - properties: { - numberOfSecrets: updateSecrets.length, - environment, - workspaceId, - folderId, - channel, - userAgent: req.headers?.["user-agent"] - } - }); - } - } - - // handle delete secrets - if (deleteSecrets.length > 0) { - const deleteSecretIds: Types.ObjectId[] = deleteSecrets.map((s) => s._id); - - const deletedSecretsObj = ( - await Secret.find({ - _id: { - $in: deleteSecretIds - } - }) - ).reduce( - (obj: any, secret: ISecret) => ({ - ...obj, - [secret._id.toString()]: secret - }), - {} - ); - - await Secret.deleteMany({ - _id: { - $in: deleteSecretIds - }, - workspace: new Types.ObjectId(workspaceId), - environment - }); - - await EESecretService.markDeletedSecretVersions({ - secretIds: deleteSecretIds - }); - - const auditLogs = await Promise.all( - deleteSecrets.map((secret) => { - return EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.DELETE_SECRET, - metadata: { - environment, - secretPath: secretPath ?? "/", - secretId: secret._id.toString(), - secretKey: secret.secretName, - secretVersion: deletedSecretsObj[secret._id.toString()].version - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - }, - false - ); - }) - ); - - await AuditLog.insertMany(auditLogs); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets deleted", - distinctId: req.user.email, - properties: { - numberOfSecrets: deleteSecrets.length, - environment, - workspaceId, - channel: channel, - userAgent: req.headers?.["user-agent"] - } - }); - } - } - - // // trigger event - push secrets - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - // root condition else this will be filled according to the path or folderid - secretPath: secretPath || "/" - }) - }); - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - folderId - }); - - const resObj: { [key: string]: ISecret[] | string[] } = {}; - - if (createSecrets.length > 0) { - resObj["createdSecrets"] = createdSecrets; - } - - if (updateSecrets.length > 0) { - resObj["updatedSecrets"] = updatedSecrets; - } - - if (deleteSecrets.length > 0) { - resObj["deletedSecrets"] = deleteSecrets.map((d) => d._id.toString()); - } - - return res.status(200).send(resObj); -}; - -/** - * Create secret(s) for workspace with id [workspaceId] and environment [environment] - * @param req - * @param res - */ -export const createSecrets = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Create new secret(s)' - #swagger.description = 'Create one or many secrets for a given project and environment.' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.requestBody = { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of project", - }, - "environment": { - "type": "string", - "description": "Environment within project" - }, - "secrets": { - $ref: "#/components/schemas/CreateSecret", - "description": "Secret(s) to create - object or array of objects" - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - $ref: "#/components/schemas/Secret" - }, - "description": "Newly-created secrets for the given project and environment" - } - } - } - } - } - } - */ - - const channel = getUserAgentType(req.headers["user-agent"]); - const { - workspaceId, - environment, - secretPath - }: { - workspaceId: string; - environment: string; - secretPath?: string; - } = req.body; - let folderId = req.body.folderId; - - if (req.user) { - const hasAccess = await userHasWorkspaceAccess( - req.user, - new Types.ObjectId(workspaceId), - environment, - PERMISSION_WRITE_SECRETS - ); - if (!hasAccess) { - throw UnauthorizedRequestError({ - message: "You do not have the necessary permission(s) perform this action" - }); - } - } - - let listOfSecretsToCreate; - if (Array.isArray(req.body.secrets)) { - // case: create multiple secrets - listOfSecretsToCreate = req.body.secrets; - } else if (typeof req.body.secrets === "object") { - // case: create 1 secret - listOfSecretsToCreate = [req.body.secrets]; - } - - if (req.authData.authPayload instanceof ServiceTokenData) { - const isValidScopeAccess = isValidScope( - req.authData.authPayload, - environment, - secretPath || "/" - ); - - // in service token when not giving secretpath folderid must be root - // this is to avoid giving folderid when service tokens are used - if ((!secretPath && folderId !== "root") || (secretPath && !isValidScopeAccess)) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } - if (secretPath) { - folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath); - } - - // get secret blind index salt - const salt = await SecretService.getSecretBlindIndexSalt({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - type secretsToCreateType = { - type: string; - secretName?: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretCommentCiphertext: string; - secretCommentIV: string; - secretCommentTag: string; - tags: string[]; - }; - - const secretsToInsert: ISecret[] = await Promise.all( - listOfSecretsToCreate.map( - async ({ - type, - secretName, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - tags - }: secretsToCreateType) => { - let secretBlindIndex; - if (secretName) { - secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({ - secretName, - salt - }); - } - - return { - version: 1, - workspace: new Types.ObjectId(workspaceId), - type, - folderId, - ...(secretBlindIndex ? { secretBlindIndex } : {}), - user: req.user && type === SECRET_PERSONAL ? req.user : undefined, - environment, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - tags - }; - } - ) - ); - - const newlyCreatedSecrets: ISecret[] = (await Secret.insertMany(secretsToInsert)).map( - (insertedSecret) => insertedSecret.toObject() - ); - - setTimeout(async () => { - // trigger event - push secrets - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath: secretPath || "/" - }) - }); - }, 5000); - - // (EE) add secret versions for new secrets - await EESecretService.addSecretVersions({ - secretVersions: newlyCreatedSecrets.map( - ({ - _id, - version, - workspace, - type, - user, - environment, - secretBlindIndex, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag - }) => - new SecretVersion({ - secret: _id, - version, - workspace, - type, - user, - environment, - secretBlindIndex, - isDeleted: false, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - folder: folderId, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }) - ) - }); - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - folderId - }); - - const postHogClient = await TelemetryService.getPostHogClient(); - if (postHogClient) { - postHogClient.capture({ - event: "secrets added", - distinctId: await TelemetryService.getDistinctId({ - authData: req.authData - }), - properties: { - numberOfSecrets: listOfSecretsToCreate.length, - environment, - workspaceId, - channel: channel, - folderId, - userAgent: req.headers?.["user-agent"] - } - }); - } - - return res.status(200).send({ - secrets: newlyCreatedSecrets - }); -}; - -/** - * Return secret(s) for workspace with id [workspaceId], environment [environment] and user - * with id [req.user._id] - * @param req - * @param res - * @returns - */ -export const getSecrets = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Read secrets' - #swagger.description = 'Read secrets from a project and environment' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project", - "required": true, - "type": "string" - } - - #swagger.parameters['environment'] = { - "description": "Environment within project", - "required": true, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - $ref: "#/components/schemas/Secret" - }, - "description": "Secrets for the given project and environment" - } - } - } - } - } - } - */ - - const validatedData = await validateRequest(GetSecretsV2, req); - const { - query: { tagSlugs, secretPath, include_imports, workspaceId, environment } - } = validatedData; - let { - query: { folderId } - } = validatedData; - - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - - if ( - // if no folders and asking for a non root folder id or non root secret path - (!folders && folderId && folderId !== "root") || - (!folders && secretPath && secretPath !== "/") - ) { - res.send({ secrets: [] }); - return; - } - - if (folders && folderId !== "root") { - const folder = searchByFolderId(folders.nodes, folderId as string); - if (!folder) { - res.send({ secrets: [] }); - return; - } - } - - if (req.authData.authPayload instanceof ServiceTokenData) { - const isValidScopeAccess = isValidScope( - req.authData.authPayload, - environment, - (secretPath as string) || "/" - ); - - // in service token when not giving secretpath folderid must be root - // this is to avoid giving folderid when service tokens are used - if ((!secretPath && folderId !== "root") || (secretPath && !isValidScopeAccess)) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } - - if (folders && secretPath) { - // avoid throwing error and send empty list - const folder = getFolderByPath(folders.nodes, secretPath as string); - if (!folder) { - res.send({ secrets: [] }); - return; - } - folderId = folder.id; - } - - // secrets to return - let secrets: ISecret[] = []; - - // query tags table to get all tags ids for the tag names for the given workspace - let tagIds = []; - const tagNamesList = typeof tagSlugs === "string" && tagSlugs !== "" ? tagSlugs.split(",") : []; - if (tagNamesList != undefined && tagNamesList.length != 0) { - const workspaceFromDB = await Tag.find({ workspace: workspaceId }); - tagIds = _.map(tagNamesList, (tagName: string) => { - const tag = _.find(workspaceFromDB, { slug: tagName }); - return tag ? tag.id : null; - }); - } - - if (req.user) { - // case: client authorization is via JWT - const hasWriteOnlyAccess = await userHasWriteOnlyAbility( - req.user._id, - new Types.ObjectId(workspaceId), - environment - ); - const hasNoAccess = await userHasNoAbility( - req.user._id, - new Types.ObjectId(workspaceId), - environment - ); - if (hasNoAccess) { - throw UnauthorizedRequestError({ - message: "You do not have the necessary permission(s) perform this action" - }); - } - - const secretQuery: any = { - workspace: workspaceId, - environment, - folder: folderId, - $or: [ - { user: req.user._id }, // personal secrets for this user - { user: { $exists: false } } // shared secrets from workspace - ] - }; - - if (tagIds.length > 0) { - secretQuery.tags = { $in: tagIds }; - } - - if (hasWriteOnlyAccess) { - // only return the secret keys and not the values since user does not have right to see values - secrets = await Secret.find(secretQuery) - .select("secretKeyCiphertext secretKeyIV secretKeyTag") - .populate("tags"); - } else { - secrets = await Secret.find(secretQuery).populate("tags"); - } - } - - // case: client authorization is via service token - if (req.serviceTokenData) { - const userId = req.serviceTokenData.user; - - const secretQuery: any = { - workspace: workspaceId, - folder: folderId, - environment, - $or: [ - { user: userId }, // personal secrets for this user - { user: { $exists: false } } // shared secrets from workspace - ] - }; - - if (tagIds.length > 0) { - secretQuery.tags = { $in: tagIds }; - } - - // TODO check if service token has write only permission - - secrets = await Secret.find(secretQuery).populate("tags"); - } - - // TODO(akhilmhdh) - secret-imp change this to org type - let importedSecrets: any[] = []; - if (include_imports) { - // depreciated - importedSecrets = await getAllImportedSecrets( - workspaceId, - environment, - folderId as string, - () => false - ); - } - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.GET_SECRETS, - metadata: { - environment, - secretPath: (secretPath as string) ?? "/", - numberOfSecrets: secrets.length - } - }, - { - workspaceId: new Types.ObjectId(workspaceId as string) - } - ); - - const postHogClient = await TelemetryService.getPostHogClient(); - - // reduce the number of events captured - let shouldRecordK8Event = false; - if (req.authData.userAgent == K8_USER_AGENT_NAME) { - const randomNumber = Math.random(); - if (randomNumber > 0.9) { - shouldRecordK8Event = true; - } - } - - if (postHogClient) { - const shouldCapture = req.authData.userAgent !== K8_USER_AGENT_NAME || shouldRecordK8Event; - const approximateForNoneCapturedEvents = secrets.length * 10; - - if (shouldCapture) { - postHogClient.capture({ - event: "secrets pulled", - distinctId: await TelemetryService.getDistinctId({ - authData: req.authData - }), - properties: { - numberOfSecrets: shouldRecordK8Event ? approximateForNoneCapturedEvents : secrets.length, - environment, - workspaceId, - folderId, - channel: req.authData.userAgentType, - userAgent: req.authData.userAgent - } - }); - } - } - - return res.status(200).send({ - secrets, - ...(include_imports && { imports: importedSecrets }) - }); -}; - -/** - * Update secret(s) - * @param req - * @param res - */ -export const updateSecrets = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update secret(s)' - #swagger.description = 'Update secret(s)' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.requestBody = { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - $ref: "#/components/schemas/UpdateSecret", - "description": "Secret(s) to update - object or array of objects" - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - $ref: "#/components/schemas/Secret" - }, - "description": "Updated secrets" - } - } - } - } - } - } - */ - const channel = req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli"; - - interface PatchSecret { - id: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretCommentCiphertext: string; - secretCommentIV: string; - secretCommentTag: string; - tags: string[]; - } - - const updateOperationsToPerform = req.body.secrets.map((secret: PatchSecret) => { - const { - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - tags - } = secret; - - return { - updateOne: { - filter: { _id: new Types.ObjectId(secret.id) }, - update: { - $inc: { - version: 1 - }, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - tags, - ...(secretCommentCiphertext !== undefined && secretCommentIV && secretCommentTag - ? { - secretCommentCiphertext, - secretCommentIV, - secretCommentTag - } - : {}) - } - } - }; - }); - - await Secret.bulkWrite(updateOperationsToPerform); - - const secretModificationsBySecretId: { [key: string]: PatchSecret } = {}; - req.body.secrets.forEach((secret: PatchSecret) => { - secretModificationsBySecretId[secret.id] = secret; - }); - - const ListOfSecretsBeforeModifications = req.secrets; - const secretVersions = { - secretVersions: ListOfSecretsBeforeModifications.map((secret: ISecret) => { - const { - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - tags - } = secretModificationsBySecretId[secret._id.toString()]; - - return { - secret: secret._id, - version: secret.version + 1, - workspace: secret.workspace, - type: secret.type, - environment: secret.environment, - secretKeyCiphertext: secretKeyCiphertext ? secretKeyCiphertext : secret.secretKeyCiphertext, - secretKeyIV: secretKeyIV ? secretKeyIV : secret.secretKeyIV, - secretKeyTag: secretKeyTag ? secretKeyTag : secret.secretKeyTag, - secretValueCiphertext: secretValueCiphertext - ? secretValueCiphertext - : secret.secretValueCiphertext, - secretValueIV: secretValueIV ? secretValueIV : secret.secretValueIV, - secretValueTag: secretValueTag ? secretValueTag : secret.secretValueTag, - secretCommentCiphertext: secretCommentCiphertext - ? secretCommentCiphertext - : secret.secretCommentCiphertext, - secretCommentIV: secretCommentIV ? secretCommentIV : secret.secretCommentIV, - secretCommentTag: secretCommentTag ? secretCommentTag : secret.secretCommentTag, - tags: tags ? tags : secret.tags, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }; - }) - }; - - await EESecretService.addSecretVersions(secretVersions); - - // group secrets into workspaces so updated secrets can - // be logged and snapshotted separately for each workspace - const workspaceSecretObj: any = {}; - req.secrets.forEach((s: any) => { - if (s.workspace.toString() in workspaceSecretObj) { - workspaceSecretObj[s.workspace.toString()].push(s); - } else { - workspaceSecretObj[s.workspace.toString()] = [s]; - } - }); - - Object.keys(workspaceSecretObj).forEach(async (key) => { - // trigger event - push secrets - // This route is not used anymore thus keep it commented out as it does not expose environment - // it will end up creating a lot of requests from the server - // setTimeout(async () => { - // await EventService.handleEvent({ - // event: eventPushSecrets({ - // workspaceId: new Types.ObjectId(key), - // environment, - // }) - // }); - // }, 10000); - - // (EE) take a secret snapshot - // IMP(akhilmhdh): commented out due to unknown where the environment is - // await EESecretService.takeSecretSnapshot({ - // workspaceId: new Types.ObjectId(key), - // environment, - // folderId, - // }); - - const postHogClient = await TelemetryService.getPostHogClient(); - if (postHogClient) { - postHogClient.capture({ - event: "secrets modified", - distinctId: await TelemetryService.getDistinctId({ - authData: req.authData - }), - properties: { - numberOfSecrets: workspaceSecretObj[key].length, - environment: workspaceSecretObj[key][0].environment, - workspaceId: key, - channel: channel, - userAgent: req.headers?.["user-agent"] - } - }); - } - }); - - return res.status(200).send({ - secrets: await Secret.find({ - _id: { - $in: req.secrets.map((secret: ISecret) => secret._id) - } - }) - }); -}; - -/** - * Delete secret(s) - * @param req - * @param res - */ -export const deleteSecrets = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Delete secret(s)' - #swagger.description = 'Delete one or many secrets by their ID(s)' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.requestBody = { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secretIds": { - "type": "string", - "description": "ID(s) of secrets - string or array of strings" - }, - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - $ref: "#/components/schemas/Secret" - }, - "description": "Deleted secrets" - } - } - } - } - } - } - */ - - const channel = getUserAgentType(req.headers["user-agent"]); - const toDelete = req.secrets.map((s: any) => s._id); - - await Secret.deleteMany({ - _id: { - $in: toDelete - } - }); - - await EESecretService.markDeletedSecretVersions({ - secretIds: toDelete - }); - - // group secrets into workspaces so deleted secrets can - // be logged and snapshotted separately for each workspace - const workspaceSecretObj: any = {}; - req.secrets.forEach((s: any) => { - if (s.workspace.toString() in workspaceSecretObj) { - workspaceSecretObj[s.workspace.toString()].push(s); - } else { - workspaceSecretObj[s.workspace.toString()] = [s]; - } - }); - - Object.keys(workspaceSecretObj).forEach(async (key) => { - // trigger event - push secrets - // DEPRECIATED(akhilmhdh): as this would cause server to send so many request - // and this route is not used anymore thus like snapshot keeping it commented out - // await EventService.handleEvent({ - // event: eventPushSecrets({ - // workspaceId: new Types.ObjectId(key) - // }) - // }); - - // (EE) take a secret snapshot - // IMP(akhilmhdh): Not sure how to take secretSnapshot - // await EESecretService.takeSecretSnapshot({ - // workspaceId: new Types.ObjectId(key), - // }); - - const postHogClient = await TelemetryService.getPostHogClient(); - if (postHogClient) { - postHogClient.capture({ - event: "secrets deleted", - distinctId: await TelemetryService.getDistinctId({ - authData: req.authData - }), - properties: { - numberOfSecrets: workspaceSecretObj[key].length, - environment: workspaceSecretObj[key][0].environment, - workspaceId: key, - channel: channel, - userAgent: req.headers?.["user-agent"] - } - }); - } - }); - - return res.status(200).send({ - secrets: req.secrets - }); -}; diff --git a/backend-mongo/src/controllers/v2/serviceTokenDataController.ts b/backend-mongo/src/controllers/v2/serviceTokenDataController.ts deleted file mode 100644 index 19a412dce..000000000 --- a/backend-mongo/src/controllers/v2/serviceTokenDataController.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { Request, Response } from "express"; -import crypto from "crypto"; -import bcrypt from "bcrypt"; -import { ServiceTokenData } from "../../models"; -import { getSaltRounds } from "../../config"; -import { BadRequestError } from "../../utils/errors"; -import { ActorType, EventType } from "../../ee/models"; -import { EEAuditLogService } from "../../ee/services"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/serviceTokenData"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError, subject } from "@casl/ability"; -import { Types } from "mongoose"; - -/** - * Return service token data associated with service token on request - * @param req - * @param res - * @returns - */ -export const getServiceTokenData = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return Infisical Token data' - #swagger.description = 'Return Infisical Token data' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "serviceTokenData": { - "type": "object", - $ref: "#/components/schemas/ServiceTokenData", - "description": "Details of service token" - } - } - } - } - } - } - */ - - if (!(req.authData.authPayload instanceof ServiceTokenData)) - throw BadRequestError({ - message: "Failed accepted client validation for service token data" - }); - - const serviceTokenData = await ServiceTokenData.findById(req.authData.authPayload._id) - .select("+encryptedKey +iv +tag") - .populate("user") - .lean(); - - return res.status(200).json(serviceTokenData); -}; - -/** - * Create new service token data for workspace with id [workspaceId] and - * environment [environment]. - * @param req - * @param res - * @returns - */ -export const createServiceTokenData = async (req: Request, res: Response) => { - let serviceTokenData; - - const { - body: { workspaceId, permissions, tag, encryptedKey, scopes, name, expiresIn, iv } - } = await validateRequest(reqValidator.CreateServiceTokenV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.ServiceTokens - ); - - scopes.forEach(({ environment, secretPath }) => { - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: secretPath }) - ); - }) - - - const secret = crypto.randomBytes(16).toString("hex"); - const secretHash = await bcrypt.hash(secret, await getSaltRounds()); - - let expiresAt; - if (expiresIn) { - expiresAt = new Date(); - expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); - } - - let user; - - if (req.authData.actor.type === ActorType.USER) { - user = req.authData.authPayload._id; - } - - serviceTokenData = await new ServiceTokenData({ - name, - workspace: workspaceId, - user, - scopes, - lastUsed: new Date(), - expiresAt, - secretHash, - encryptedKey, - iv, - tag, - permissions - }).save(); - - // return service token data without sensitive data - serviceTokenData = await ServiceTokenData.findById(serviceTokenData._id); - - if (!serviceTokenData) throw new Error("Failed to find service token data"); - - const serviceToken = `st.${serviceTokenData._id.toString()}.${secret}`; - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_SERVICE_TOKEN, - metadata: { - name, - scopes - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.status(200).send({ - serviceToken, - serviceTokenData - }); -}; - -/** - * Delete service token data with id [serviceTokenDataId]. - * @param req - * @param res - * @returns - */ -export const deleteServiceTokenData = async (req: Request, res: Response) => { - const { - params: { serviceTokenDataId } - } = await validateRequest(reqValidator.DeleteServiceTokenV2, req); - - let serviceTokenData = await ServiceTokenData.findById(serviceTokenDataId); - if (!serviceTokenData) throw BadRequestError({ message: "Service token not found" }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: serviceTokenData.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.ServiceTokens - ); - - serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId); - - if (!serviceTokenData) - return res.status(200).send({ - message: "Failed to delete service token" - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.DELETE_SERVICE_TOKEN, - metadata: { - name: serviceTokenData.name, - scopes: serviceTokenData?.scopes - } - }, - { - workspaceId: serviceTokenData.workspace - } - ); - - return res.status(200).send({ - serviceTokenData - }); -}; diff --git a/backend-mongo/src/controllers/v2/signupController.ts b/backend-mongo/src/controllers/v2/signupController.ts deleted file mode 100644 index 66daf5701..000000000 --- a/backend-mongo/src/controllers/v2/signupController.ts +++ /dev/null @@ -1,262 +0,0 @@ -import { Request, Response } from "express"; -import { MembershipOrg, User } from "../../models"; -import { completeAccount } from "../../helpers/user"; -import { - initializeDefaultOrg, -} from "../../helpers/signup"; -import { issueAuthTokens } from "../../helpers/auth"; -import { ACCEPTED, INVITED } from "../../variables"; -import { standardRequest } from "../../config/request"; -import { getHttpsEnabled, getLoopsApiKey } from "../../config"; -import { updateSubscriptionOrgQuantity } from "../../helpers/organization"; - -/** - * Complete setting up user by adding their personal and auth information as part of the - * signup flow - * @param req - * @param res - * @returns - */ -export const completeAccountSignup = async (req: Request, res: Response) => { - let user; - const { - email, - firstName, - lastName, - protectedKey, - protectedKeyIV, - protectedKeyTag, - publicKey, - encryptedPrivateKey, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag, - salt, - verifier, - organizationName, - }: { - email: string; - firstName: string; - lastName: string; - protectedKey: string; - protectedKeyIV: string; - protectedKeyTag: string; - publicKey: string; - encryptedPrivateKey: string; - encryptedPrivateKeyIV: string; - encryptedPrivateKeyTag: string; - salt: string; - verifier: string; - organizationName: string; - } = req.body; - - // get user - user = await User.findOne({ email }); - - if (!user || (user && user?.publicKey)) { - // case 1: user doesn't exist. - // case 2: user has already completed account - return res.status(403).send({ - error: "Failed to complete account for complete user", - }); - } - - // complete setting up user's account - user = await completeAccount({ - userId: user._id.toString(), - firstName, - lastName, - encryptionVersion: 2, - protectedKey, - protectedKeyIV, - protectedKeyTag, - publicKey, - encryptedPrivateKey, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag, - salt, - verifier, - }); - - if (!user) - throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null - - // initialize default organization and workspace - await initializeDefaultOrg({ - organizationName, - user, - }); - - // update organization membership statuses that are - // invited to completed with user attached - const membershipsToUpdate = await MembershipOrg.find({ - inviteEmail: email, - status: INVITED, - }); - - membershipsToUpdate.forEach(async (membership) => { - await updateSubscriptionOrgQuantity({ - organizationId: membership.organization.toString(), - }); - }); - - // update organization membership statuses that are - // invited to completed with user attached - await MembershipOrg.updateMany( - { - inviteEmail: email, - status: INVITED, - }, - { - user, - status: ACCEPTED, - } - ); - - // issue tokens - const tokens = await issueAuthTokens({ - userId: user._id, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "", - }); - - const token = tokens.token; - - // sending a welcome email to new users - if (await getLoopsApiKey()) { - await standardRequest.post("https://app.loops.so/api/v1/events/send", { - "email": email, - "eventName": "Sign Up", - "firstName": firstName, - "lastName": lastName, - }, { - headers: { - "Accept": "application/json", - "Authorization": "Bearer " + (await getLoopsApiKey()), - }, - }); - } - - // store (refresh) token in httpOnly cookie - res.cookie("jid", tokens.refreshToken, { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: await getHttpsEnabled(), - }); - - return res.status(200).send({ - message: "Successfully set up account", - user, - token, - }); -}; - -/** - * Complete setting up user by adding their personal and auth information as part of the - * invite flow - * @param req - * @param res - * @returns - */ -export const completeAccountInvite = async (req: Request, res: Response) => { - let user; - const { - email, - firstName, - lastName, - protectedKey, - protectedKeyIV, - protectedKeyTag, - publicKey, - encryptedPrivateKey, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag, - salt, - verifier, - } = req.body; - - // get user - user = await User.findOne({ email }); - - if (!user || (user && user?.publicKey)) { - // case 1: user doesn't exist. - // case 2: user has already completed account - return res.status(403).send({ - error: "Failed to complete account for complete user", - }); - } - - const membershipOrg = await MembershipOrg.findOne({ - inviteEmail: email, - status: INVITED, - }); - - if (!membershipOrg) throw new Error("Failed to find invitations for email"); - - // complete setting up user's account - user = await completeAccount({ - userId: user._id.toString(), - firstName, - lastName, - encryptionVersion: 2, - protectedKey, - protectedKeyIV, - protectedKeyTag, - publicKey, - encryptedPrivateKey, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag, - salt, - verifier, - }); - - if (!user) - throw new Error("Failed to complete account for non-existent user"); - - // update organization membership statuses that are - // invited to completed with user attached - const membershipsToUpdate = await MembershipOrg.find({ - inviteEmail: email, - status: INVITED, - }); - - membershipsToUpdate.forEach(async (membership) => { - await updateSubscriptionOrgQuantity({ - organizationId: membership.organization.toString(), - }); - }); - - await MembershipOrg.updateMany( - { - inviteEmail: email, - status: INVITED, - }, - { - user, - status: ACCEPTED, - } - ); - - // issue tokens - const tokens = await issueAuthTokens({ - userId: user._id, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "", - }); - - const token = tokens.token; - - // store (refresh) token in httpOnly cookie - res.cookie("jid", tokens.refreshToken, { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: await getHttpsEnabled(), - }); - - return res.status(200).send({ - message: "Successfully set up account", - user, - token, - }); -}; diff --git a/backend-mongo/src/controllers/v2/tagController.ts b/backend-mongo/src/controllers/v2/tagController.ts deleted file mode 100644 index c803b0e18..000000000 --- a/backend-mongo/src/controllers/v2/tagController.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { ForbiddenError } from "@casl/ability"; -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { Secret, Tag } from "../../models"; -import { BadRequestError } from "../../utils/errors"; -import { validateRequest } from "../../helpers/validation"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import * as reqValidator from "../../validation/tags"; - -export const createWorkspaceTag = async (req: Request, res: Response) => { - const { - body: { name, slug }, - params: { workspaceId } - } = await validateRequest(reqValidator.CreateWorkspaceTagsV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Tags - ); - - const tagToCreate = { - name, - workspace: new Types.ObjectId(workspaceId), - slug, - user: new Types.ObjectId(req.user._id) - }; - - const createdTag = await new Tag(tagToCreate).save(); - - res.json(createdTag); -}; - -export const deleteWorkspaceTag = async (req: Request, res: Response) => { - const { - params: { tagId } - } = await validateRequest(reqValidator.DeleteWorkspaceTagsV2, req); - - const tagFromDB = await Tag.findById(tagId); - if (!tagFromDB) { - throw BadRequestError(); - } - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: tagFromDB.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Tags - ); - - const result = await Tag.findByIdAndDelete(tagId); - - // remove the tag from secrets - await Secret.updateMany({ tags: { $in: [tagId] } }, { $pull: { tags: tagId } }); - - res.json(result); -}; - -export const getWorkspaceTags = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceTagsV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Tags - ); - - const workspaceTags = await Tag.find({ - workspace: new Types.ObjectId(workspaceId) - }); - - return res.json({ - workspaceTags - }); -}; diff --git a/backend-mongo/src/controllers/v2/usersController.ts b/backend-mongo/src/controllers/v2/usersController.ts deleted file mode 100644 index 3998956f7..000000000 --- a/backend-mongo/src/controllers/v2/usersController.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import crypto from "crypto"; -import bcrypt from "bcrypt"; -import { APIKeyData, AuthMethod, MembershipOrg, TokenVersion, User } from "../../models"; -import { getSaltRounds } from "../../config"; -import { validateRequest } from "../../helpers/validation"; -import { deleteUser } from "../../helpers/user"; -import * as reqValidator from "../../validation"; - -/** - * Update the current user's MFA-enabled status [isMfaEnabled]. - * Note: Infisical currently only supports email-based 2FA only; this will expand to - * include SMS and authenticator app modes of authentication in the future. - * @param req - * @param res - * @returns - */ -export const updateMyMfaEnabled = async (req: Request, res: Response) => { - const { - body: { isMfaEnabled } - } = await validateRequest(reqValidator.UpdateMyMfaEnabledV2, req); - - req.user.isMfaEnabled = isMfaEnabled; - - if (isMfaEnabled) { - // TODO: adapt this route/controller - // to work for different forms of MFA - req.user.mfaMethods = ["email"]; - } else { - req.user.mfaMethods = []; - } - - await req.user.save(); - - const user = req.user; - - return res.status(200).send({ - user - }); -}; - -/** - * Update name of the current user to [firstName, lastName]. - * @param req - * @param res - * @returns - */ -export const updateName = async (req: Request, res: Response) => { - const { - body: { lastName, firstName } - } = await validateRequest(reqValidator.UpdateNameV2, req); - - const user = await User.findByIdAndUpdate( - req.user._id.toString(), - { - firstName, - lastName: lastName ?? "" - }, - { - new: true - } - ); - - return res.status(200).send({ - user - }); -}; - -/** - * Update auth method of the current user to [authMethods] - * @param req - * @param res - * @returns - */ -export const updateAuthMethods = async (req: Request, res: Response) => { - const { - body: { authMethods } - } = await validateRequest(reqValidator.UpdateAuthMethodsV2, req); - - const hasSamlEnabled = req.user.authMethods.some((authMethod: AuthMethod) => - [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(authMethod) - ); - - if (hasSamlEnabled) { - return res.status(400).send({ - message: "Failed to update user authentication method because SAML SSO is enforced" - }); - } - - const user = await User.findByIdAndUpdate( - req.user._id.toString(), - { - authMethods - }, - { - new: true - } - ); - - return res.status(200).send({ - user - }); -}; - -/** - * Return organizations that the current user is part of. - * @param req - * @param res - */ -export const getMyOrganizations = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return organizations that current user is part of' - #swagger.description = 'Return organizations that current user is part of' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "organizations": { - "type": "array", - "items": { - $ref: "#/components/schemas/Organization" - }, - "description": "Organizations that user is part of" - } - } - } - } - } - } - */ - const organizations = ( - await MembershipOrg.find({ - user: req.user._id - }).populate("organization") - ).map((m) => m.organization); - - return res.status(200).send({ - organizations - }); -}; - -/** - * Return API keys belonging to current user. - * @param req - * @param res - * @returns - */ -export const getMyAPIKeys = async (req: Request, res: Response) => { - const apiKeyData = await APIKeyData.find({ - user: req.user._id - }); - - return res.status(200).send(apiKeyData); -}; - -/** - * Create new API key for current user. - * @param req - * @param res - * @returns - */ -export const createAPIKey = async (req: Request, res: Response) => { - const { - body: { name, expiresIn } - } = await validateRequest(reqValidator.CreateApiKeyV2, req); - - const secret = crypto.randomBytes(16).toString("hex"); - const secretHash = await bcrypt.hash(secret, await getSaltRounds()); - - const expiresAt = new Date(); - expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); - - let apiKeyData = await new APIKeyData({ - name, - lastUsed: new Date(), - expiresAt, - user: req.user._id, - secretHash - }).save(); - - // return api key data without sensitive data - apiKeyData = (await APIKeyData.findById(apiKeyData._id)) as any; - - if (!apiKeyData) throw new Error("Failed to find API key data"); - - const apiKey = `ak.${apiKeyData._id.toString()}.${secret}`; - - return res.status(200).send({ - apiKey, - apiKeyData - }); -}; - -/** - * Delete API key with id [apiKeyDataId] belonging to current user - * @param req - * @param res - */ -export const deleteAPIKey = async (req: Request, res: Response) => { - const { - params: { apiKeyDataId } - } = await validateRequest(reqValidator.DeleteApiKeyV2, req); - - const apiKeyData = await APIKeyData.findOneAndDelete({ - _id: new Types.ObjectId(apiKeyDataId), - user: req.user._id - }); - - return res.status(200).send({ - apiKeyData - }); -}; - -/** - * Return active sessions (TokenVersion) belonging to user - * @param req - * @param res - * @returns - */ -export const getMySessions = async (req: Request, res: Response) => { - const tokenVersions = await TokenVersion.find({ - user: req.user._id - }); - - return res.status(200).send(tokenVersions); -}; - -/** - * Revoke all active sessions belong to user - * @param req - * @param res - * @returns - */ -export const deleteMySessions = async (req: Request, res: Response) => { - await TokenVersion.updateMany( - { - user: req.user._id - }, - { - $inc: { - refreshVersion: 1, - accessVersion: 1 - } - } - ); - - return res.status(200).send({ - message: "Successfully revoked all sessions" - }); -}; - -/** - * Return the current user. - * @param req - * @param res - * @returns - */ - export const getMe = async (req: Request, res: Response) => { - /* - #swagger.summary = "Retrieve the current user on the request" - #swagger.description = "Retrieve the current user on the request" - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "user": { - "type": "object", - $ref: "#/components/schemas/CurrentUser", - "description": "Current user on request" - } - } - } - } - } - } - */ - const user = await User.findById(req.user._id).select( - "+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag" - ); - - return res.status(200).send({ - user - }); -}; - -/** - * Delete the current user. - * @param req - * @param res - */ -export const deleteMe = async (req: Request, res: Response) => { - const user = await deleteUser({ - userId: req.user._id - }); - - return res.status(200).send({ - user - }); -} \ No newline at end of file diff --git a/backend-mongo/src/controllers/v2/workspaceController.ts b/backend-mongo/src/controllers/v2/workspaceController.ts deleted file mode 100644 index dca217a1b..000000000 --- a/backend-mongo/src/controllers/v2/workspaceController.ts +++ /dev/null @@ -1,883 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { - IIdentity, - IdentityMembership, - IdentityMembershipOrg, - Key, - Membership, - ServiceTokenData, - Workspace -} from "../../models"; -import { IRole, Role } from "../../ee/models"; -import { - pullSecrets as pull, - v2PushSecrets as push, - reformatPullSecrets -} from "../../helpers/secret"; -import { pushKeys } from "../../helpers/key"; -import { EventService, TelemetryService } from "../../services"; -import { eventPushSecrets } from "../../events"; -import { EEAuditLogService } from "../../ee/services"; -import { EventType } from "../../ee/models"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions, - getWorkspaceRolePermissions, - isAtLeastAsPrivilegedWorkspace -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; -import { BadRequestError, ForbiddenRequestError, ResourceNotFoundError } from "../../utils/errors"; -import { ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../../variables"; - -interface V2PushSecret { - type: string; // personal or shared - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretKeyHash: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretValueHash: string; - secretCommentCiphertext?: string; - secretCommentIV?: string; - secretCommentTag?: string; - secretCommentHash?: string; -} - -/** - * Upload (encrypted) secrets to workspace with id [workspaceId] - * for environment [environment] - * @param req - * @param res - * @returns - */ -export const pushWorkspaceSecrets = async (req: Request, res: Response) => { - // upload (encrypted) secrets to workspace with id [workspaceId] - const postHogClient = await TelemetryService.getPostHogClient(); - let { secrets }: { secrets: V2PushSecret[] } = req.body; - const { keys, environment, channel } = req.body; - const { workspaceId } = req.params; - - // validate environment - const workspaceEnvs = req.membership.workspace.environments; - if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { - throw new Error("Failed to validate environment"); - } - - // sanitize secrets - secrets = secrets.filter( - (s: V2PushSecret) => s.secretKeyCiphertext !== "" && s.secretValueCiphertext !== "" - ); - - await push({ - userId: req.user._id, - workspaceId, - environment, - secrets, - channel: channel ? channel : "cli", - ipAddress: req.realIP - }); - - await pushKeys({ - userId: req.user._id, - workspaceId, - keys - }); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets pushed", - distinctId: req.user.email, - properties: { - numberOfSecrets: secrets.length, - environment, - workspaceId, - channel: channel ? channel : "cli" - } - }); - } - - // trigger event - push secrets - EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath: "/" - }) - }); - - return res.status(200).send({ - message: "Successfully uploaded workspace secrets" - }); -}; - -/** - * Return (encrypted) secrets for workspace with id [workspaceId] - * for environment [environment] - * @param req - * @param res - * @returns - */ -export const pullSecrets = async (req: Request, res: Response) => { - let secrets; - const postHogClient = await TelemetryService.getPostHogClient(); - const environment: string = req.query.environment as string; - const channel: string = req.query.channel as string; - const { workspaceId } = req.params; - - let userId; - if (req.user) { - userId = req.user._id.toString(); - } else if (req.serviceTokenData) { - userId = req.serviceTokenData.user.toString(); - } - // validate environment - const workspaceEnvs = req.membership.workspace.environments; - if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { - throw new Error("Failed to validate environment"); - } - - secrets = await pull({ - userId, - workspaceId, - environment, - channel: channel ? channel : "cli", - ipAddress: req.realIP - }); - - if (channel !== "cli") { - secrets = reformatPullSecrets({ secrets }); - } - - if (postHogClient) { - // capture secrets pushed event in production - postHogClient.capture({ - distinctId: req.user.email, - event: "secrets pulled", - properties: { - numberOfSecrets: secrets.length, - environment, - workspaceId, - channel: channel ? channel : "cli" - } - }); - } - - return res.status(200).send({ - secrets - }); -}; - -export const getWorkspaceKey = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return encrypted project key' - #swagger.description = 'Return encrypted project key' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project", - "required": true, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "array", - "items": { - $ref: "#/components/schemas/ProjectKey" - }, - "description": "Encrypted project key for the given project" - } - } - } - } - */ - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceKeyV2, req); - - const key = await Key.findOne({ - workspace: workspaceId, - receiver: req.user._id - }).populate("sender", "+publicKey"); - - if (!key) throw new Error(`getWorkspaceKey: Failed to find workspace key [workspaceId=${workspaceId}] [receiver=${req.user._id}]`); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.GET_WORKSPACE_KEY, - metadata: { - keyId: key._id.toString() - } - }, - { - workspaceId: new Types.ObjectId(workspaceId) - } - ); - - return res.status(200).json(key); -}; - -export const getWorkspaceServiceTokenData = async (req: Request, res: Response) => { - const { workspaceId } = req.params; - - const serviceTokenData = await ServiceTokenData.find({ - workspace: workspaceId - }).select("+encryptedKey +iv +tag"); - - return res.status(200).send({ - serviceTokenData - }); -}; - -/** - * Return memberships for workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ -export const getWorkspaceMemberships = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return project user memberships' - #swagger.description = 'Return project user memberships' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project", - "required": true, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "memberships": { - "type": "array", - "items": { - $ref: "#/components/schemas/Membership" - }, - "description": "Memberships of project" - } - } - } - } - } - } - */ - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceMembershipsV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Member - ); - - const memberships = await Membership.find({ - workspace: workspaceId - }).populate("user", "+publicKey"); - - return res.status(200).send({ - memberships - }); -}; - -/** - * Update role of membership with id [membershipId] to role [role] - * @param req - * @param res - * @returns - */ -export const updateWorkspaceMembership = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update project user membership' - #swagger.description = 'Update project user membership' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project", - "required": true, - "type": "string" - } - - #swagger.parameters['membershipId'] = { - "description": "ID of project membership to update", - "required": true, - "type": "string" - } - - #swagger.requestBody = { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "role": { - "type": "string", - "description": "Role to update to for project membership", - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "membership": { - $ref: "#/components/schemas/Membership", - "description": "Updated membership" - } - } - } - } - } - } - */ - const { - params: { workspaceId, membershipId }, - body: { role } - } = await validateRequest(reqValidator.UpdateWorkspaceMembershipsV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Member - ); - - const membership = await Membership.findByIdAndUpdate( - membershipId, - { - role - }, - { - new: true - } - ); - - return res.status(200).send({ - membership - }); -}; - -/** - * Delete workspace membership with id [membershipId] - * @param req - * @param res - * @returns - */ -export const deleteWorkspaceMembership = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Delete project user membership' - #swagger.description = 'Delete project user membership' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project", - "required": true, - "type": "string" - } - - #swagger.parameters['membershipId'] = { - "description": "ID of project membership to delete", - "required": true, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "membership": { - $ref: "#/components/schemas/Membership", - "description": "Deleted membership" - } - } - } - } - } - } - */ - const { - params: { workspaceId, membershipId } - } = await validateRequest(reqValidator.DeleteWorkspaceMembershipsV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Member - ); - - const membership = await Membership.findByIdAndDelete(membershipId); - - if (!membership) throw new Error("Failed to delete workspace membership"); - - await Key.deleteMany({ - receiver: membership.user, - workspace: membership.workspace - }); - - return res.status(200).send({ - membership - }); -}; - -/** - * Change autoCapitilzation Rule of workspace - * @param req - * @param res - * @returns - */ -export const toggleAutoCapitalization = async (req: Request, res: Response) => { - const { - params: { workspaceId }, - body: { autoCapitalization } - } = await validateRequest(reqValidator.ToggleAutoCapitalizationV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Settings - ); - - const workspace = await Workspace.findOneAndUpdate( - { - _id: workspaceId - }, - { - autoCapitalization - }, - { - new: true - } - ); - - return res.status(200).send({ - message: "Successfully changed autoCapitalization setting", - workspace - }); -}; - -/** - * Add identity with id [identityId] to workspace - * with id [workspaceId] - * @param req - * @param res - */ -export const addIdentityToWorkspace = async (req: Request, res: Response) => { - const { - params: { workspaceId, identityId }, - body: { - role - } - } = await validateRequest(reqValidator.AddIdentityToWorkspaceV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Identity - ); - - let identityMembership = await IdentityMembership.findOne({ - identity: new Types.ObjectId(identityId), - workspace: new Types.ObjectId(workspaceId) - }); - - if (identityMembership) throw BadRequestError({ - message: `Identity with id ${identityId} already exists in project with id ${workspaceId}` - }); - - - const workspace = await Workspace.findById(workspaceId); - if (!workspace) throw ResourceNotFoundError(); - - const identityMembershipOrg = await IdentityMembershipOrg.findOne({ - identity: new Types.ObjectId(identityId), - organization: workspace.organization - }); - - if (!identityMembershipOrg) throw ResourceNotFoundError({ - message: `Failed to find identity with id ${identityId}` - }); - - if (!identityMembershipOrg.organization.equals(workspace.organization)) throw BadRequestError({ - message: "Failed to add identity to project in another organization" - }); - - const rolePermission = await getWorkspaceRolePermissions(role, workspaceId); - const isAsPrivilegedAsIntendedRole = isAtLeastAsPrivilegedWorkspace(permission, rolePermission); - - if (!isAsPrivilegedAsIntendedRole) throw ForbiddenRequestError({ - message: "Failed to add identity to project with more privileged role" - }); - - let customRole; - if (role) { - const isCustomRole = ![ADMIN, MEMBER, VIEWER, NO_ACCESS].includes(role); - if (isCustomRole) { - customRole = await Role.findOne({ - slug: role, - isOrgRole: false, - workspace: new Types.ObjectId(workspaceId) - }); - - if (!customRole) throw BadRequestError({ message: "Role not found" }); - } - } - - identityMembership = await new IdentityMembership({ - identity: identityMembershipOrg.identity, - workspace: new Types.ObjectId(workspaceId), - role: customRole ? CUSTOM : role, - customRole - }).save(); - - return res.status(200).send({ - identityMembership - }); -} - -/** - * Update role of identity with id [identityId] in workspace - * with id [workspaceId] to [role] - * @param req - * @param res - */ - export const updateIdentityWorkspaceRole = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update project identity membership' - #swagger.description = 'Update project identity membership' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project", - "required": true, - "type": "string" - } - - #swagger.parameters['identityId'] = { - "description": "ID of identity whose membership to update in project", - "required": true, - "type": "string" - } - - #swagger.requestBody = { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "role": { - "type": "string", - "description": "Role to update to for identity project membership", - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityMembership": { - $ref: "#/components/schemas/IdentityMembership", - "description": "Updated identity membership" - } - } - } - } - } - } - */ - const { - params: { workspaceId, identityId }, - body: { - role - } - } = await validateRequest(reqValidator.UpdateIdentityWorkspaceRoleV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Identity - ); - - let identityMembership = await IdentityMembership - .findOne({ - identity: new Types.ObjectId(identityId), - workspace: new Types.ObjectId(workspaceId) - }) - .populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembership) throw BadRequestError({ - message: `Identity with id ${identityId} does not exist in project with id ${workspaceId}` - }); - - const identityRolePermission = await getWorkspaceRolePermissions( - identityMembership?.customRole?.slug ?? identityMembership.role, - identityMembership.workspace.toString() - ); - const isAsPrivilegedAsIdentity = isAtLeastAsPrivilegedWorkspace(permission, identityRolePermission); - if (!isAsPrivilegedAsIdentity) throw ForbiddenRequestError({ - message: "Failed to update role of more privileged identity" - }); - - const rolePermission = await getWorkspaceRolePermissions(role, workspaceId); - const isAsPrivilegedAsIntendedRole = isAtLeastAsPrivilegedWorkspace(permission, rolePermission); - - if (!isAsPrivilegedAsIntendedRole) throw ForbiddenRequestError({ - message: "Failed to update identity to a more privileged role" - }); - - let customRole; - if (role) { - const isCustomRole = ![ADMIN, MEMBER, VIEWER, NO_ACCESS].includes(role); - if (isCustomRole) { - customRole = await Role.findOne({ - slug: role, - isOrgRole: false, - workspace: new Types.ObjectId(workspaceId) - }); - - if (!customRole) throw BadRequestError({ message: "Role not found" }); - } - } - - identityMembership = await IdentityMembership.findOneAndUpdate( - { - identity: identityMembership.identity._id, - workspace: new Types.ObjectId(workspaceId), - }, - { - role: customRole ? CUSTOM : role, - customRole - }, - { - new: true - } - ); - - return res.status(200).send({ - identityMembership - }); -} - -/** - * Delete identity with id [identityId] from workspace - * with id [workspaceId] - * @param req - * @param res - */ - export const deleteIdentityFromWorkspace = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Delete project identity membership' - #swagger.description = 'Delete project identity membership' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project", - "required": true, - "type": "string" - } - - #swagger.parameters['identityId'] = { - "description": "ID of identity whose membership to delete in project", - "required": true, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityMembership": { - $ref: "#/components/schemas/IdentityMembership", - "description": "Deleted identity membership" - } - } - } - } - } - } - */ - const { - params: { workspaceId, identityId } - } = await validateRequest(reqValidator.DeleteIdentityFromWorkspaceV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Identity - ); - - const identityMembership = await IdentityMembership - .findOne({ - identity: new Types.ObjectId(identityId), - workspace: new Types.ObjectId(workspaceId) - }) - .populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembership) throw ResourceNotFoundError({ - message: `Identity with id ${identityId} does not exist in project with id ${workspaceId}` - }); - - const identityRolePermission = await getWorkspaceRolePermissions( - identityMembership?.customRole?.slug ?? identityMembership.role, - identityMembership.workspace.toString() - ); - const isAsPrivilegedAsIdentity = isAtLeastAsPrivilegedWorkspace(permission, identityRolePermission); - if (!isAsPrivilegedAsIdentity) throw ForbiddenRequestError({ - message: "Failed to remove more privileged identity from project" - }); - - await IdentityMembership.findByIdAndDelete(identityMembership._id); - - return res.status(200).send({ - identityMembership - }); -} - -/** - * Return list of identity memberships for workspace with id [workspaceId] - * @param req - * @param res - * @returns - */ - export const getWorkspaceIdentityMemberships = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return project identity memberships' - #swagger.description = 'Return project identity memberships' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identityMemberships": { - "type": "array", - "items": { - $ref: "#/components/schemas/IdentityMembership" - }, - "description": "Identity memberships of project" - } - } - } - } - } - } - */ - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceIdentityMembersV2, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Identity - ); - - const identityMemberships = await IdentityMembership.find({ - workspace: new Types.ObjectId(workspaceId) - }).populate("identity customRole"); - - return res.status(200).send({ - identityMemberships - }); -} \ No newline at end of file diff --git a/backend-mongo/src/controllers/v3/authController.ts b/backend-mongo/src/controllers/v3/authController.ts deleted file mode 100644 index 4e3576c3b..000000000 --- a/backend-mongo/src/controllers/v3/authController.ts +++ /dev/null @@ -1,224 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -import { Request, Response } from "express"; -import jwt from "jsonwebtoken"; -import * as bigintConversion from "bigint-conversion"; -const jsrp = require("jsrp"); -import { LoginSRPDetail, User } from "../../models"; -import { createToken, issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth"; -import { checkUserDevice } from "../../helpers/user"; -import { sendMail } from "../../helpers/nodemailer"; -import { TokenService } from "../../services"; -import { BadRequestError, InternalServerError } from "../../utils/errors"; -import { AuthTokenType, TOKEN_EMAIL_MFA } from "../../variables"; -import { getAuthSecret, getHttpsEnabled, getJwtMfaLifetime } from "../../config"; -import { AuthMethod } from "../../models/user"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/auth"; - -declare module "jsonwebtoken" { - export interface ProviderAuthJwtPayload extends jwt.JwtPayload { - userId: string; - email: string; - authProvider: AuthMethod; - isUserCompleted: boolean; - } -} - -/** - * Log in user step 1: Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol - * @param req - * @param res - * @returns - */ -export const login1 = async (req: Request, res: Response) => { - const { - body: { email, clientPublicKey, providerAuthToken } - } = await validateRequest(reqValidator.Login1V3, req); - - const user = await User.findOne({ - email - }).select("+salt +verifier"); - - if (!user) throw new Error("Failed to find user"); - - if (!user.authMethods.includes(AuthMethod.EMAIL)) { - await validateProviderAuthToken({ - email, - providerAuthToken - }); - } - - const server = new jsrp.server(); - server.init( - { - salt: user.salt, - verifier: user.verifier - }, - async () => { - // generate server-side public key - const serverPublicKey = server.getPublicKey(); - await LoginSRPDetail.findOneAndReplace( - { - email: email - }, - { - email, - userId: user.id, - clientPublicKey: clientPublicKey, - serverBInt: bigintConversion.bigintToBuf(server.bInt) - }, - { upsert: true, returnNewDocument: false } - ); - - return res.status(200).send({ - serverPublicKey, - salt: user.salt - }); - } - ); -}; - -/** - * Log in user step 2: complete step 2 of SRP protocol and return token and their (encrypted) - * private key - * @param req - * @param res - * @returns - */ -export const login2 = async (req: Request, res: Response) => { - if (!req.headers["user-agent"]) - throw InternalServerError({ message: "User-Agent header is required" }); - - const { - body: { email, providerAuthToken, clientProof } - } = await validateRequest(reqValidator.Login2V3, req); - - const user = await User.findOne({ - email - }).select( - "+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices" - ); - - if (!user) throw new Error("Failed to find user"); - - if (!user.authMethods.includes(AuthMethod.EMAIL)) { - await validateProviderAuthToken({ - email, - providerAuthToken - }); - } - - const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email }); - - if (!loginSRPDetail) { - return BadRequestError(Error("Failed to find login details for SRP")); - } - - const server = new jsrp.server(); - server.init( - { - salt: user.salt, - verifier: user.verifier, - b: loginSRPDetail.serverBInt - }, - async () => { - server.setClientPublicKey(loginSRPDetail.clientPublicKey); - - // compare server and client shared keys - if (server.checkClientProof(clientProof)) { - if (user.isMfaEnabled) { - // case: user has MFA enabled - - // generate temporary MFA token - const token = createToken({ - payload: { - authTokenType: AuthTokenType.MFA_TOKEN, - userId: user._id.toString() - }, - expiresIn: await getJwtMfaLifetime(), - secret: await getAuthSecret() - }); - - const code = await TokenService.createToken({ - type: TOKEN_EMAIL_MFA, - email - }); - - // send MFA code [code] to [email] - await sendMail({ - template: "emailMfa.handlebars", - subjectLine: "Infisical MFA code", - recipients: [user.email], - substitutions: { - code - } - }); - - return res.status(200).send({ - mfaEnabled: true, - token - }); - } - - await checkUserDevice({ - user, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - // issue tokens - const tokens = await issueAuthTokens({ - userId: user._id, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - // store (refresh) token in httpOnly cookie - res.cookie("jid", tokens.refreshToken, { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: await getHttpsEnabled() - }); - - // case: user does not have MFA enablgged - // return (access) token in response - - interface ResponseData { - mfaEnabled: boolean; - encryptionVersion: any; - protectedKey?: string; - protectedKeyIV?: string; - protectedKeyTag?: string; - token: string; - publicKey?: string; - encryptedPrivateKey?: string; - iv?: string; - tag?: string; - } - - const response: ResponseData = { - mfaEnabled: false, - encryptionVersion: user.encryptionVersion, - token: tokens.token, - publicKey: user.publicKey, - encryptedPrivateKey: user.encryptedPrivateKey, - iv: user.iv, - tag: user.tag - }; - - if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) { - response.protectedKey = user.protectedKey; - response.protectedKeyIV = user.protectedKeyIV; - response.protectedKeyTag = user.protectedKeyTag; - } - - return res.status(200).send(response); - } - - return res.status(400).send({ - message: "Failed to authenticate. Try again?" - }); - } - ); -}; diff --git a/backend-mongo/src/controllers/v3/index.ts b/backend-mongo/src/controllers/v3/index.ts deleted file mode 100644 index b52e0aa41..000000000 --- a/backend-mongo/src/controllers/v3/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as usersController from "./usersController"; -import * as secretsController from "./secretsController"; -import * as workspacesController from "./workspacesController"; -import * as authController from "./authController"; -import * as signupController from "./signupController"; - -export { - usersController, - authController, - secretsController, - signupController, - workspacesController -} diff --git a/backend-mongo/src/controllers/v3/secretsController.ts b/backend-mongo/src/controllers/v3/secretsController.ts deleted file mode 100644 index b9edc431c..000000000 --- a/backend-mongo/src/controllers/v3/secretsController.ts +++ /dev/null @@ -1,1461 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { EventService, SecretService } from "../../services"; -import { eventPushSecrets } from "../../events"; -import { BotService } from "../../services"; -import { containsGlobPatterns, repackageSecretToRaw } from "../../helpers/secrets"; -import { encryptSymmetric128BitHexKeyUTF8 } from "../../utils/crypto"; -import { getAllImportedSecrets } from "../../services/SecretImportService"; -import { Folder, IServiceTokenData, Membership, ServiceTokenData, User } from "../../models"; -import { getFolderByPath } from "../../services/FolderService"; -import { BadRequestError } from "../../utils/errors"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/secrets"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { ForbiddenError, subject } from "@casl/ability"; -import { validateServiceTokenDataClientForWorkspace } from "../../validation"; -import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables"; -import { ActorType } from "../../ee/models"; -import { UnauthorizedRequestError } from "../../utils/errors"; -import { AuthData } from "../../interfaces/middleware"; -import { - generateSecretApprovalRequest, - getSecretPolicyOfBoard -} from "../../ee/services/SecretApprovalService"; -import { CommitType } from "../../ee/models/secretApprovalRequest"; -import { logger } from "../../utils/logging"; -import { createReminder, deleteReminder } from "../../helpers/reminder"; - -const checkSecretsPermission = async ({ - authData, - workspaceId, - environment, - secretPath, - secretAction -}: { - authData: AuthData; - workspaceId: string; - environment: string; - secretPath: string; - secretAction: ProjectPermissionActions; // CRUD -}): Promise<{ - authVerifier: (env: string, secPath: string) => boolean; -}> => { - let STV2RequiredPermissions = []; - - switch (secretAction) { - case ProjectPermissionActions.Create: - STV2RequiredPermissions = [PERMISSION_WRITE_SECRETS]; - break; - case ProjectPermissionActions.Read: - STV2RequiredPermissions = [PERMISSION_READ_SECRETS]; - break; - case ProjectPermissionActions.Edit: - STV2RequiredPermissions = [PERMISSION_WRITE_SECRETS]; - break; - case ProjectPermissionActions.Delete: - STV2RequiredPermissions = [PERMISSION_WRITE_SECRETS]; - break; - } - - switch (authData.actor.type) { - case ActorType.USER: { - const { permission } = await getAuthDataProjectPermissions({ - authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - secretAction, - subject(ProjectPermissionSub.Secrets, { environment, secretPath }) - ); - return { - authVerifier: (env: string, secPath: string) => - permission.can( - secretAction, - subject(ProjectPermissionSub.Secrets, { - environment: env, - secretPath: secPath - }) - ) - }; - } - case ActorType.SERVICE: { - await validateServiceTokenDataClientForWorkspace({ - serviceTokenData: authData.authPayload as IServiceTokenData, - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath, - requiredPermissions: STV2RequiredPermissions - }); - return { authVerifier: () => true }; - } - case ActorType.IDENTITY: { - const { permission } = await getAuthDataProjectPermissions({ - authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - secretAction, - subject(ProjectPermissionSub.Secrets, { environment, secretPath }) - ); - return { - authVerifier: (env: string, secPath: string) => - permission.can( - secretAction, - subject(ProjectPermissionSub.Secrets, { - environment: env, - secretPath: secPath - }) - ) - }; - } - default: { - throw UnauthorizedRequestError(); - } - } -}; - -/** - * Return secrets for workspace with id [workspaceId] and environment - * [environment] in plaintext - * @param req - * @param res - */ -export const getSecretsRaw = async (req: Request, res: Response) => { - /* - #swagger.summary = 'List secrets' - #swagger.description = 'List secrets' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of workspace where to get secrets from", - "required": true, - "type": "string", - "in": "query" - } - - #swagger.parameters['environment'] = { - "description": "Slug of environment where to get secrets from", - "required": true, - "type": "string", - "in": "query" - } - - #swagger.parameters['secretPath'] = { - "description": "Path where to update secret like / or /foo/bar. Default is /", - "required": false, - "type": "string", - "in": "query" - } - - #swagger.parameters['include_imports'] = { - "description": "Whether or not to include imported secrets. Default is false", - "required": false, - "type": "boolean", - "in": "query" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - $ref: '#/definitions/RawSecret' - }, - "description": "List of secrets" - } - } - } - } - } - } - */ - const validatedData = await validateRequest(reqValidator.GetSecretsRawV3, req); - let { - query: { secretPath, environment, workspaceId } - } = validatedData; - const { - query: { include_imports: includeImports } - } = validatedData; - - logger.info( - `getSecretsRaw: fetch raw secrets [environment=${environment}] [workspaceId=${workspaceId}] [secretPath=${secretPath}] [includeImports=${includeImports}]` - ); - - if (req.authData.authPayload instanceof ServiceTokenData) { - // if the service token has single scope, it will get all secrets for that scope by default - const serviceTokenDetails: IServiceTokenData = req?.serviceTokenData; - if ( - serviceTokenDetails && - serviceTokenDetails.scopes.length == 1 && - !containsGlobPatterns(serviceTokenDetails.scopes[0].secretPath) - ) { - const scope = serviceTokenDetails.scopes[0]; - secretPath = scope.secretPath; - environment = scope.environment; - workspaceId = serviceTokenDetails.workspace.toString(); - } - } - - if (!environment || !workspaceId) - throw BadRequestError({ message: "Missing environment or workspace id" }); - - const { authVerifier: permissionCheckFn } = await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Read - }); - - const secrets = await SecretService.getSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath, - authData: req.authData - }); - - const key = await BotService.getWorkspaceKeyWithBot({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - if (includeImports) { - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - let folderId = "root"; - // if folder exist get it and replace folderid with new one - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath as string); - if (!folder) { - throw BadRequestError({ message: "Folder not found" }); - } - folderId = folder.id; - } - const importedSecrets = await getAllImportedSecrets( - workspaceId, - environment, - folderId, - permissionCheckFn - ); - return res.status(200).send({ - secrets: secrets.map((secret) => - repackageSecretToRaw({ - secret, - key - }) - ), - imports: importedSecrets.map((el) => ({ - ...el, - secrets: el.secrets.map((secret) => repackageSecretToRaw({ secret, key })) - })) - }); - } - - return res.status(200).send({ - secrets: secrets.map((secret) => { - const rep = repackageSecretToRaw({ - secret, - key - }); - return rep; - }) - }); -}; - -/** - * Return secret with name [secretName] in plaintext - * @param req - * @param res - */ -export const getSecretByNameRaw = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Get secret' - #swagger.description = 'Get secret' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['secretName'] = { - "description": "Name of secret to get", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.parameters['workspaceId'] = { - "description": "ID of workspace where to get secret", - "required": true, - "type": "string", - "in": "query" - } - - #swagger.parameters['environment'] = { - "description": "Slug of environment where to get secret", - "required": true, - "type": "string", - "in": "query" - } - - #swagger.parameters['secretPath'] = { - "description": "Path where to update secret like / or /foo/bar. Default is /", - "required": false, - "type": "string", - "in": "query" - } - - #swagger.parameters['type'] = { - "description": "Type of secret to get; either shared or personal. Default is shared.", - "required": true, - "type": "string", - "in": "query" - } - - #swagger.parameters['include_imports'] = { - "description": "Whether or not to include imported secrets. Default is false", - "required": false, - "type": "boolean", - "in": "query" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secret": { - $ref: '#/definitions/RawSecret' - } - } - } - } - } - } - */ - const { - query: { secretPath, environment, workspaceId, type, include_imports, version }, - params: { secretName } - } = await validateRequest(reqValidator.GetSecretByNameRawV3, req); - - logger.info( - `getSecretByNameRaw: fetch raw secret by name [environment=${environment}] [workspaceId=${workspaceId}] [secretPath=${secretPath}] [type=${type}] [include_imports=${include_imports}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Read - }); - - const secret = await SecretService.getSecret({ - secretName, - workspaceId: new Types.ObjectId(workspaceId), - environment, - type, - secretPath, - authData: req.authData, - include_imports, - version - }); - - const key = await BotService.getWorkspaceKeyWithBot({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - return res.status(200).send({ - secret: repackageSecretToRaw({ - secret, - key - }) - }); -}; - -/** - * Create secret with name [secretName] in plaintext - * @param req - * @param res - */ -export const createSecretRaw = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Create secret' - #swagger.description = 'Create secret' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['secretName'] = { - "description": "Name of secret to create", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of the workspace where to create secret", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to create secret", - "example": "dev" - }, - "secretPath": { - "type": "string", - "description": "Path where to create secret. Default is /", - "example": "/foo/bar" - }, - "secretValue": { - "type": "string", - "description": "Value of secret to create", - "example": "Some value" - }, - "secretComment": { - "type": "string", - "description": "Comment for secret to create", - "example": "Some comment" - }, - "type": { - "type": "string", - "description": "Type of secret to create; either shared or personal. Default is shared.", - "example": "shared" - }, - "skipMultilineEncoding": { - "type": "boolean", - "description": "Convert multi line secrets into one line by wrapping", - "example": "true" - }, - }, - "required": ["workspaceId", "environment", "secretValue"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - $ref: '#/definitions/RawSecret' - } - } - } - } - */ - const { - params: { secretName }, - body: { - workspaceId, - environment, - secretPath, - type, - secretValue, - secretComment, - skipMultilineEncoding - } - } = await validateRequest(reqValidator.CreateSecretRawV3, req); - - logger.info( - `createSecretRaw: create a secret raw by name and value [environment=${environment}] [workspaceId=${workspaceId}] [secretPath=${secretPath}] [type=${type}] [skipMultilineEncoding=${skipMultilineEncoding}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Create - }); - - const key = await BotService.getWorkspaceKeyWithBot({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8({ - plaintext: secretName, - key - }); - - const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({ - plaintext: secretValue, - key - }); - - const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8({ - plaintext: secretComment, - key - }); - - const secret = await SecretService.createSecret({ - secretName, - workspaceId: new Types.ObjectId(workspaceId), - environment, - type, - authData: req.authData, - secretKeyCiphertext: secretKeyEncrypted.ciphertext, - secretKeyIV: secretKeyEncrypted.iv, - secretKeyTag: secretKeyEncrypted.tag, - secretValueCiphertext: secretValueEncrypted.ciphertext, - secretValueIV: secretValueEncrypted.iv, - secretValueTag: secretValueEncrypted.tag, - secretPath, - secretCommentCiphertext: secretCommentEncrypted.ciphertext, - secretCommentIV: secretCommentEncrypted.iv, - secretCommentTag: secretCommentEncrypted.tag, - skipMultilineEncoding - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath - }) - }); - - const secretWithoutBlindIndex = secret.toObject(); - delete secretWithoutBlindIndex.secretBlindIndex; - - return res.status(200).send({ - secret: repackageSecretToRaw({ - secret: secretWithoutBlindIndex, - key - }) - }); -}; - -/** - * Update secret with name [secretName] - * @param req - * @param res - */ -export const updateSecretByNameRaw = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update secret' - #swagger.description = 'Update secret' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['secretName'] = { - "description": "Name of secret to update", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of the workspace where to update secret", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of environment where to update secret", - "example": "dev" - }, - "secretPath": { - "type": "string", - "description": "Path where to update secret like / or /foo/bar. Default is /", - "example": "/foo/bar" - }, - "secretValue": { - "type": "string", - "description": "Value of secret to update to", - "example": "Some value" - }, - "type": { - "type": "string", - "description": "Type of secret to update; either shared or personal. Default is shared.", - "example": "shared" - }, - "skipMultilineEncoding": { - "type": "boolean", - "description": "Convert multi line secrets into one line by wrapping", - "example": "true" - }, - }, - "required": ["workspaceId", "environment", "secretValue"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - $ref: '#/definitions/RawSecret' - } - } - } - } - */ - const { - params: { secretName }, - body: { workspaceId, environment, secretValue, secretPath, type, skipMultilineEncoding } - } = await validateRequest(reqValidator.UpdateSecretByNameRawV3, req); - - logger.info( - `updateSecretByNameRaw: update raw secret by name [environment=${environment}] [workspaceId=${workspaceId}] [secretPath=${secretPath}] [type=${type}] [skipMultilineEncoding=${skipMultilineEncoding}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Edit - }); - - const key = await BotService.getWorkspaceKeyWithBot({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({ - plaintext: secretValue, - key - }); - - const secret = await SecretService.updateSecret({ - secretName, - workspaceId: new Types.ObjectId(workspaceId), - environment, - type, - authData: req.authData, - secretValueCiphertext: secretValueEncrypted.ciphertext, - secretValueIV: secretValueEncrypted.iv, - secretValueTag: secretValueEncrypted.tag, - secretPath, - skipMultilineEncoding - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath - }) - }); - - return res.status(200).send({ - secret: repackageSecretToRaw({ - secret, - key - }) - }); -}; - -/** - * Delete secret with name [secretName] - * @param req - * @param res - */ -export const deleteSecretByNameRaw = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Delete secret' - #swagger.description = 'Delete secret' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['secretName'] = { - "description": "Name of secret to delete", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "workspaceId": { - "type": "string", - "description": "ID of workspace where to delete secret", - "example": "someWorkspaceId" - }, - "environment": { - "type": "string", - "description": "Slug of Environment where to delete secret", - "example": "dev" - }, - "secretPath": { - "type": "string", - "description": "Path where to delete secret. Default is /", - "example": "/foo/bar" - }, - "type": { - "type": "string", - "description": "Type of secret to delete; either shared or personal. Default is shared", - "example": "shared" - } - }, - "required": ["workspaceId", "environment"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "secret": { - $ref: '#/definitions/RawSecret' - } - }, - "description": "The deleted secret" - } - } - } - } - */ - const { - params: { secretName }, - body: { environment, secretPath, type, workspaceId } - } = await validateRequest(reqValidator.DeleteSecretByNameRawV3, req); - - logger.info( - `deleteSecretByNameRaw: delete a secret by name [environment=${environment}] [workspaceId=${workspaceId}] [secretPath=${secretPath}] [type=${type}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Delete - }); - - const { secret } = await SecretService.deleteSecret({ - secretName, - workspaceId: new Types.ObjectId(workspaceId), - environment, - type, - authData: req.authData, - secretPath - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath - }) - }); - - const key = await BotService.getWorkspaceKeyWithBot({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - return res.status(200).send({ - secret: repackageSecretToRaw({ - secret, - key - }) - }); -}; - -/** - * Get secrets for workspace with id [workspaceId] and environment - * [environment] - * @param req - * @param res - */ -export const getSecrets = async (req: Request, res: Response) => { - const validatedData = await validateRequest(reqValidator.GetSecretsV3, req); - const { - query: { environment, workspaceId, include_imports: includeImports } - } = validatedData; - - const { - query: { secretPath } - } = validatedData; - - logger.info( - `getSecrets: fetch encrypted secrets [environment=${environment}] [workspaceId=${workspaceId}] [includeImports=${includeImports}]` - ); - - const { authVerifier: permissionCheckFn } = await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Read - }); - - const secrets = await SecretService.getSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath, - authData: req.authData - }); - - if (includeImports) { - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - let folderId = "root"; - // if folder exist get it and replace folderid with new one - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath as string); - if (!folder) { - throw BadRequestError({ message: "Folder not found" }); - } - folderId = folder.id; - } - const importedSecrets = await getAllImportedSecrets( - workspaceId, - environment, - folderId, - permissionCheckFn - ); - return res.status(200).send({ - secrets, - imports: importedSecrets - }); - } - - return res.status(200).send({ - secrets - }); -}; - -/** - * Return secret with name [secretName] - * @param req - * @param res - */ -export const getSecretByName = async (req: Request, res: Response) => { - const { - query: { secretPath, environment, workspaceId, type, include_imports, version }, - params: { secretName } - } = await validateRequest(reqValidator.GetSecretByNameV3, req); - - logger.info( - `getSecretByName: get a single secret by name [environment=${environment}] [workspaceId=${workspaceId}] [include_imports=${include_imports}] [type=${type}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Read - }); - - const secret = await SecretService.getSecret({ - secretName, - workspaceId: new Types.ObjectId(workspaceId), - environment, - type, - secretPath, - authData: req.authData, - include_imports, - version - }); - - return res.status(200).send({ - secret - }); -}; - -/** - * Create secret with name [secretName] - * @param req - * @param res - */ -export const createSecret = async (req: Request, res: Response) => { - const { - body: { - workspaceId, - secretPath, - environment, - metadata, - type, - secretKeyIV, - secretKeyTag, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretKeyCiphertext, - secretValueCiphertext, - secretCommentCiphertext, - skipMultilineEncoding - }, - params: { secretName } - } = await validateRequest(reqValidator.CreateSecretV3, req); - - logger.info( - `createSecret: create an encrypted secret [environment=${environment}] [workspaceId=${workspaceId}] [skipMultilineEncoding=${skipMultilineEncoding}] [type=${type}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Create - }); - - if (req.authData.authPayload instanceof User) { - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (membership && type !== "personal") { - const secretApprovalPolicy = await getSecretPolicyOfBoard( - workspaceId, - environment, - secretPath - ); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - authData: req.authData, - data: { - [CommitType.CREATE]: [ - { - secretName, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - skipMultilineEncoding, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV - } - ] - } - }); - return res.send({ approval: secretApprovalRequest }); - } - } - } - - const secret = await SecretService.createSecret({ - secretName, - workspaceId: new Types.ObjectId(workspaceId), - environment, - type, - authData: req.authData, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretPath, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - metadata, - skipMultilineEncoding - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath - }) - }); - - const secretWithoutBlindIndex = secret.toObject(); - delete secretWithoutBlindIndex.secretBlindIndex; - - return res.status(200).send({ - secret: secretWithoutBlindIndex - }); -}; - -/** - * Update secret with name [secretName] - * @param req - * @param res - */ -export const updateSecretByName = async (req: Request, res: Response) => { - const { - body: { - secretValueCiphertext, - secretValueTag, - secretValueIV, - secretId, - type, - environment, - secretPath, - workspaceId, - tags, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - secretName: newSecretName, - secretKeyIV, - secretKeyTag, - secretKeyCiphertext, - skipMultilineEncoding, - secretReminderRepeatDays, - secretReminderNote - }, - params: { secretName } - } = await validateRequest(reqValidator.UpdateSecretByNameV3, req); - - logger.info( - `updateSecretByName: update a encrypted secret by name [environment=${environment}] [workspaceId=${workspaceId}] [skipMultilineEncoding=${skipMultilineEncoding}] [type=${type}]` - ); - - if (newSecretName && (!secretKeyIV || !secretKeyTag || !secretKeyCiphertext)) { - throw BadRequestError({ message: "Missing encrypted key" }); - } - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Edit - }); - - if (req.authData.authPayload instanceof User) { - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (membership && type !== "personal") { - const secretApprovalPolicy = await getSecretPolicyOfBoard( - workspaceId, - environment, - secretPath - ); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - authData: req.authData, - data: { - [CommitType.UPDATE]: [ - { - secretName, - newSecretName, - secretValueCiphertext, - secretValueIV, - secretValueTag, - tags, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - skipMultilineEncoding, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV - } - ] - } - }); - return res.send({ approval: secretApprovalRequest }); - } - } - } - - if (type !== "personal") { - const existingSecret = await SecretService.getSecret({ - secretName, - workspaceId: new Types.ObjectId(workspaceId), - environment, - type, - secretPath, - authData: req.authData - }); - - if (secretReminderRepeatDays !== undefined) { - if ( - (secretReminderRepeatDays && - existingSecret.secretReminderRepeatDays !== secretReminderRepeatDays) || - (secretReminderNote && existingSecret.secretReminderNote !== secretReminderNote) - ) { - await createReminder(existingSecret, { - _id: existingSecret._id, - secretReminderRepeatDays, - secretReminderNote, - workspace: existingSecret.workspace - }); - } else if ( - secretReminderRepeatDays === null && - secretReminderNote === null && - existingSecret.secretReminderRepeatDays - ) { - await deleteReminder({ - _id: existingSecret._id, - secretReminderRepeatDays: existingSecret.secretReminderRepeatDays - }); - } - } - } - - const secret = await SecretService.updateSecret({ - secretName, - workspaceId: new Types.ObjectId(workspaceId), - environment, - type, - secretId, - authData: req.authData, - newSecretName, - secretValueCiphertext, - secretValueIV, - secretReminderRepeatDays, - secretReminderNote, - secretValueTag, - secretPath, - tags, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - skipMultilineEncoding, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath - }) - }); - - return res.status(200).send({ - secret - }); -}; - -/** - * Delete secret with name [secretName] - * @param req - * @param res - */ -export const deleteSecretByName = async (req: Request, res: Response) => { - const { - body: { type, environment, secretPath, workspaceId, secretId }, - params: { secretName } - } = await validateRequest(reqValidator.DeleteSecretByNameV3, req); - - logger.info( - `deleteSecretByName: delete a encrypted secret by name [environment=${environment}] [workspaceId=${workspaceId}] [type=${type}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Delete - }); - - if (req.authData.authPayload instanceof User) { - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (membership && type !== "personal") { - const secretApprovalPolicy = await getSecretPolicyOfBoard( - workspaceId, - environment, - secretPath - ); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - authData: req.authData, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - data: { - [CommitType.DELETE]: [ - { - secretName - } - ] - } - }); - return res.send({ approval: secretApprovalRequest }); - } - } - } - - const { secret } = await SecretService.deleteSecret({ - secretName, - secretId, - workspaceId: new Types.ObjectId(workspaceId), - environment, - type, - authData: req.authData, - secretPath - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath - }) - }); - - return res.status(200).send({ - secret - }); -}; - -export const createSecretByNameBatch = async (req: Request, res: Response) => { - const { - body: { secrets, secretPath, environment, workspaceId } - } = await validateRequest(reqValidator.CreateSecretByNameBatchV3, req); - - logger.info( - `createSecretByNameBatch: create a list of secrets by their names [environment=${environment}] [workspaceId=${workspaceId}] [secretsLength=${secrets?.length}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Create - }); - - if (req.authData.authPayload instanceof User) { - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (membership) { - const secretApprovalPolicy = await getSecretPolicyOfBoard( - workspaceId, - environment, - secretPath - ); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - authData: req.authData, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - data: { - [CommitType.CREATE]: secrets.filter(({ type }) => type === "shared") - } - }); - return res.send({ approval: secretApprovalRequest }); - } - } - } - - const createdSecrets = await SecretService.createSecretBatch({ - secretPath, - environment, - workspaceId: new Types.ObjectId(workspaceId), - secrets, - authData: req.authData - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath - }) - }); - - return res.status(200).send({ - secrets: createdSecrets - }); -}; - -export const updateSecretByNameBatch = async (req: Request, res: Response) => { - const { - body: { secrets, secretPath, environment, workspaceId } - } = await validateRequest(reqValidator.UpdateSecretByNameBatchV3, req); - - logger.info( - `updateSecretByNameBatch: update a list of secrets by their names [environment=${environment}] [workspaceId=${workspaceId}] [secretsLength=${secrets?.length}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Edit - }); - - if (req.authData.authPayload instanceof User) { - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (membership) { - const secretApprovalPolicy = await getSecretPolicyOfBoard( - workspaceId, - environment, - secretPath - ); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - data: { - [CommitType.UPDATE]: secrets.filter(({ type }) => type === "shared") - }, - authData: req.authData - }); - return res.send({ approval: secretApprovalRequest }); - } - } - } - - const updatedSecrets = await SecretService.updateSecretBatch({ - secretPath, - environment, - workspaceId: new Types.ObjectId(workspaceId), - secrets, - authData: req.authData - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath - }) - }); - - return res.status(200).send({ - secrets: updatedSecrets - }); -}; - -export const deleteSecretByNameBatch = async (req: Request, res: Response) => { - const { - body: { secrets, secretPath, environment, workspaceId } - } = await validateRequest(reqValidator.DeleteSecretByNameBatchV3, req); - - logger.info( - `deleteSecretByNameBatch: delete a list of secrets by their names [environment=${environment}] [workspaceId=${workspaceId}] [secretsLength=${secrets?.length}]` - ); - - await checkSecretsPermission({ - authData: req.authData, - workspaceId, - environment, - secretPath, - secretAction: ProjectPermissionActions.Delete - }); - - if (req.authData.authPayload instanceof User) { - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (membership) { - const secretApprovalPolicy = await getSecretPolicyOfBoard( - workspaceId, - environment, - secretPath - ); - if (secretApprovalPolicy) { - const secretApprovalRequest = await generateSecretApprovalRequest({ - workspaceId, - environment, - secretPath, - policy: secretApprovalPolicy, - commiterMembershipId: membership._id.toString(), - data: { - [CommitType.DELETE]: secrets.filter(({ type }) => type === "shared") - }, - authData: req.authData - }); - return res.send({ approval: secretApprovalRequest }); - } - } - } - - const deletedSecrets = await SecretService.deleteSecretBatch({ - secretPath, - environment, - workspaceId: new Types.ObjectId(workspaceId), - secrets, - authData: req.authData - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - secretPath - }) - }); - - return res.status(200).send({ - secrets: deletedSecrets - }); -}; diff --git a/backend-mongo/src/controllers/v3/signupController.ts b/backend-mongo/src/controllers/v3/signupController.ts deleted file mode 100644 index d16fbe9ca..000000000 --- a/backend-mongo/src/controllers/v3/signupController.ts +++ /dev/null @@ -1,193 +0,0 @@ -import jwt from "jsonwebtoken"; -import { Request, Response } from "express"; -import * as Sentry from "@sentry/node"; -import { MembershipOrg, User } from "../../models"; -import { completeAccount } from "../../helpers/user"; -import { initializeDefaultOrg } from "../../helpers/signup"; -import { issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth"; -import { ACCEPTED, AuthTokenType, INVITED } from "../../variables"; -import { standardRequest } from "../../config/request"; -import { getAuthSecret, getHttpsEnabled, getLoopsApiKey } from "../../config"; -import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; -import { TelemetryService } from "../../services"; -import { AuthMethod } from "../../models"; -import { validateRequest } from "../../helpers/validation"; -import * as reqValidator from "../../validation/auth"; - -/** - * Complete setting up user by adding their personal and auth information as part of the - * signup flow - * @param req - * @param res - * @returns - */ -export const completeAccountSignup = async (req: Request, res: Response) => { - let user, token; - try { - const { - body: { - email, - publicKey, - salt, - lastName, - verifier, - firstName, - protectedKey, - protectedKeyIV, - protectedKeyTag, - organizationName, - providerAuthToken, - attributionSource, - encryptedPrivateKey, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag - } - } = await validateRequest(reqValidator.CompletedAccountSignupV3, req); - - user = await User.findOne({ email }); - - if (!user || (user && user?.publicKey)) { - // case 1: user doesn't exist. - // case 2: user has already completed account - return res.status(403).send({ - error: "Failed to complete account for complete user" - }); - } - - if (providerAuthToken) { - await validateProviderAuthToken({ - email, - providerAuthToken - }); - } else { - const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>( - req.headers["authorization"]?.split(" ", 2) - ) ?? [null, null]; - if (AUTH_TOKEN_TYPE === null) { - throw BadRequestError({ message: "Missing Authorization Header in the request header." }); - } - if (AUTH_TOKEN_TYPE.toLowerCase() !== "bearer") { - throw BadRequestError({ - message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.` - }); - } - if (AUTH_TOKEN_VALUE === null) { - throw BadRequestError({ - message: "Missing Authorization Body in the request header" - }); - } - - const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, await getAuthSecret()) - ); - - if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw UnauthorizedRequestError(); - if (decodedToken.userId !== user.id) throw UnauthorizedRequestError(); - } - - // complete setting up user's account - user = await completeAccount({ - userId: user._id.toString(), - firstName, - lastName, - encryptionVersion: 2, - protectedKey, - protectedKeyIV, - protectedKeyTag, - publicKey, - encryptedPrivateKey, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag, - salt, - verifier - }); - - if (!user) throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null - - const hasSamlEnabled = user.authMethods.some((authMethod: AuthMethod) => - [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(authMethod) - ); - - if (!hasSamlEnabled) { - // TODO: modify this part - // initialize default organization and workspace - await initializeDefaultOrg({ - organizationName, - user - }); - } - - // update organization membership statuses that are - // invited to completed with user attached - await MembershipOrg.updateMany( - { - inviteEmail: email, - status: INVITED - }, - { - user, - status: ACCEPTED - } - ); - - // issue tokens - const tokens = await issueAuthTokens({ - userId: user._id, - ip: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - token = tokens.token; - - // sending a welcome email to new users - if (await getLoopsApiKey()) { - await standardRequest.post( - "https://app.loops.so/api/v1/events/send", - { - email: email, - eventName: "Sign Up", - firstName: firstName, - lastName: lastName - }, - { - headers: { - Accept: "application/json", - Authorization: "Bearer " + (await getLoopsApiKey()) - } - } - ); - } - - // store (refresh) token in httpOnly cookie - res.cookie("jid", tokens.refreshToken, { - httpOnly: true, - path: "/", - sameSite: "strict", - secure: await getHttpsEnabled() - }); - - const postHogClient = await TelemetryService.getPostHogClient(); - if (postHogClient) { - postHogClient.capture({ - event: "User Signed Up", - distinctId: email, - properties: { - email, - ...(attributionSource ? { attributionSource } : {}) - } - }); - } - } catch (err) { - Sentry.setUser(null); - Sentry.captureException(err); - return res.status(400).send({ - message: "Failed to complete account setup" - }); - } - - return res.status(200).send({ - message: "Successfully set up account", - user, - token - }); -}; diff --git a/backend-mongo/src/controllers/v3/usersController.ts b/backend-mongo/src/controllers/v3/usersController.ts deleted file mode 100644 index e94173540..000000000 --- a/backend-mongo/src/controllers/v3/usersController.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Request, Response } from "express"; -import { APIKeyDataV2 } from "../../models"; - -/** - * Return API keys belonging to current user. - * @param req - * @param res - * @returns - */ -export const getMyAPIKeys = async (req: Request, res: Response) => { - const apiKeyData = await APIKeyDataV2.find({ - user: req.user._id - }); - - return res.status(200).send({ - apiKeyData - }); -} \ No newline at end of file diff --git a/backend-mongo/src/controllers/v3/workspacesController.ts b/backend-mongo/src/controllers/v3/workspacesController.ts deleted file mode 100644 index 32f04074a..000000000 --- a/backend-mongo/src/controllers/v3/workspacesController.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { validateRequest } from "../../helpers/validation"; -import { Membership, Secret, User } from "../../models"; -import { SecretService } from "../../services"; -import { getAuthDataProjectPermissions } from "../../ee/services/ProjectRoleService"; -import { UnauthorizedRequestError } from "../../utils/errors"; -import * as reqValidator from "../../validation/workspace"; - -/** - * Return whether or not all secrets in workspace with id [workspaceId] - * are blind-indexed - * @param req - * @param res - * @returns - */ -export const getWorkspaceBlindIndexStatus = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceBlinkIndexStatusV3, req); - - await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - if (req.authData.authPayload instanceof User) { - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (!membership) throw UnauthorizedRequestError(); - - if (membership.role !== "admin") - throw UnauthorizedRequestError({ message: "User must be an admin" }); - } - - const secretsWithoutBlindIndex = await Secret.countDocuments({ - workspace: new Types.ObjectId(workspaceId), - secretBlindIndex: { - $exists: false - } - }); - - return res.status(200).send(secretsWithoutBlindIndex === 0); -}; - -/** - * Get all secrets for workspace with id [workspaceId] - */ -export const getWorkspaceSecrets = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.GetWorkspaceSecretsV3, req); - - await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - if (req.authData.authPayload instanceof User) { - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (!membership) throw UnauthorizedRequestError(); - - if (membership.role !== "admin") - throw UnauthorizedRequestError({ message: "User must be an admin" }); - } - - const secrets = await Secret.find({ - workspace: new Types.ObjectId(workspaceId) - }); - - return res.status(200).send({ - secrets - }); -}; - -/** - * Update blind indices for secrets in workspace with id [workspaceId] - * @param req - * @param res - */ -export const nameWorkspaceSecrets = async (req: Request, res: Response) => { - const { - params: { workspaceId }, - body: { secretsToUpdate } - } = await validateRequest(reqValidator.NameWorkspaceSecretsV3, req); - - await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - if (req.authData.authPayload instanceof User) { - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (!membership) throw UnauthorizedRequestError(); - - if (membership.role !== "admin") - throw UnauthorizedRequestError({ message: "User must be an admin" }); - } - - // get secret blind index salt - const salt = await SecretService.getSecretBlindIndexSalt({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - // update secret blind indices - const operations = await Promise.all( - secretsToUpdate.map(async (secretToUpdate) => { - const secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({ - secretName: secretToUpdate.secretName, - salt - }); - - return { - updateOne: { - filter: { - _id: new Types.ObjectId(secretToUpdate._id) - }, - update: { - secretBlindIndex - } - } - }; - }) - ); - - await Secret.bulkWrite(operations); - - return res.status(200).send({ - message: "Successfully named workspace secrets" - }); -}; diff --git a/backend-mongo/src/data/disposable_emails.txt b/backend-mongo/src/data/disposable_emails.txt deleted file mode 100644 index 70e24a460..000000000 --- a/backend-mongo/src/data/disposable_emails.txt +++ /dev/null @@ -1,3519 +0,0 @@ -0-mail.com -027168.com -0815.ru -0815.ry -0815.su -0845.ru -0box.eu -0clickemail.com -0n0ff.net -0nelce.com -0v.ro -0w.ro -0wnd.net -0wnd.org -0x207.info -1-8.biz -1-tm.com -10-minute-mail.com -1000rebates.stream -100likers.com -105kg.ru -10dk.email -10mail.com -10mail.org -10minut.com.pl -10minut.xyz -10minutemail.be -10minutemail.cf -10minutemail.co.uk -10minutemail.co.za -10minutemail.com -10minutemail.de -10minutemail.ga -10minutemail.gq -10minutemail.ml -10minutemail.net -10minutemail.nl -10minutemail.pro -10minutemail.us -10minutemailbox.com -10minutemails.in -10minutenemail.de -10minutesmail.com -10minutesmail.fr -10minutmail.pl -10x9.com -11163.com -123-m.com -12hosting.net -12houremail.com -12minutemail.com -12minutemail.net -12storage.com -140unichars.com -147.cl -14n.co.uk -15qm.com -1blackmoon.com -1ce.us -1chuan.com -1clck2.com -1fsdfdsfsdf.tk -1mail.ml -1pad.de -1s.fr -1secmail.com -1secmail.net -1secmail.org -1st-forms.com -1to1mail.org -1usemail.com -1webmail.info -1zhuan.com -2012-2016.ru -20email.eu -20email.it -20mail.eu -20mail.in -20mail.it -20minutemail.com -20minutemail.it -20mm.eu -2120001.net -21cn.com -247web.net -24hinbox.com -24hourmail.com -24hourmail.net -2anom.com -2chmail.net -2ether.net -2fdgdfgdfgdf.tk -2odem.com -2prong.com -2wc.info -300book.info -30mail.ir -30minutemail.com -30wave.com -3202.com -36ru.com -3d-painting.com -3l6.com -3mail.ga -3trtretgfrfe.tk -4-n.us -4057.com -418.dk -42o.org -4gfdsgfdgfd.tk -4k5.net -4mail.cf -4mail.ga -4nextmail.com -4nmv.ru -4tb.host -4warding.com -4warding.net -4warding.org -50set.ru -55hosting.net -5ghgfhfghfgh.tk -5gramos.com -5july.org -5mail.cf -5mail.ga -5minutemail.net -5oz.ru -5tb.in -5x25.com -5ymail.com -60minutemail.com -672643.net -675hosting.com -675hosting.net -675hosting.org -6hjgjhgkilkj.tk -6ip.us -6mail.cf -6mail.ga -6mail.ml -6paq.com -6somok.ru -6url.com -75hosting.com -75hosting.net -75hosting.org -7days-printing.com -7mail.ga -7mail.ml -7tags.com -80665.com -8127ep.com -8mail.cf -8mail.ga -8mail.ml -99.com -99cows.com -99experts.com -9mail.cf -9me.site -9mot.ru -9ox.net -9q.ro -a-bc.net -a45.in -a7996.com -aa5zy64.com -abacuswe.us -abakiss.com -abcmail.email -abilitywe.us -abovewe.us -absolutewe.us -abundantwe.us -abusemail.de -abuser.eu -abyssmail.com -ac20mail.in -academiccommunity.com -academywe.us -acceleratewe.us -accentwe.us -acceptwe.us -acclaimwe.us -accordwe.us -accreditedwe.us -acentri.com -achievementwe.us -achievewe.us -acornwe.us -acrossgracealley.com -acrylicwe.us -activatewe.us -activitywe.us -acucre.com -acuitywe.us -acumenwe.us -adaptivewe.us -adaptwe.us -add3000.pp.ua -addictingtrailers.com -adeptwe.us -adfskj.com -adios.email -adiq.eu -aditus.info -admiralwe.us -ado888.biz -adobeccepdm.com -adoniswe.us -adpugh.org -adroh.com -adsd.org -adubiz.info -advantagewe.us -advantimo.com -adventurewe.us -adventwe.us -advisorwe.us -advocatewe.us -adwaterandstir.com -aegde.com -aegia.net -aegiscorp.net -aegiswe.us -aelo.es -aeonpsi.com -afarek.com -affiliate-nebenjob.info -affiliatedwe.us -affilikingz.de -affinitywe.us -affluentwe.us -affordablewe.us -afia.pro -afrobacon.com -afterhourswe.us -agedmail.com -agendawe.us -agger.ro -agilewe.us -agorawe.us -agtx.net -aheadwe.us -ahem.email -ahk.jp -ahmedkhlef.com -air2token.com -airmailbox.website -airsi.de -ajaxapp.net -akapost.com -akerd.com -akgq701.com -akmail.in -al-qaeda.us -albionwe.us -alchemywe.us -alfaceti.com -aliaswe.us -alienware13.com -aligamel.com -alisongamel.com -alivance.com -alivewe.us -all-cats.ru -allaccesswe.us -allamericanwe.us -allaroundwe.us -alldirectbuy.com -allegiancewe.us -allegrowe.us -allemojikeyboard.com -allgoodwe.us -alliancewe.us -allinonewe.us -allofthem.net -alloutwe.us -allowed.org -alloywe.us -allprowe.us -allseasonswe.us -allstarwe.us -allthegoodnamesaretaken.org -allurewe.us -almondwe.us -alph.wtf -alpha-web.net -alphaomegawe.us -alpinewe.us -altairwe.us -altitudewe.us -altuswe.us -ama-trade.de -ama-trans.de -amadeuswe.us -amail.club -amail.com -amail1.com -amail4.me -amazon-aws.org -amberwe.us -ambiancewe.us -ambitiouswe.us -amelabs.com -americanawe.us -americasbestwe.us -americaswe.us -amicuswe.us -amilegit.com -amiri.net -amiriindustries.com -amplewe.us -amplifiedwe.us -amplifywe.us -ampsylike.com -analogwe.us -analysiswe.us -analyticalwe.us -analyticswe.us -analyticwe.us -anappfor.com -anappthat.com -andreihusanu.ro -andthen.us -animesos.com -anit.ro -ano-mail.net -anon-mail.de -anonbox.net -anonmail.top -anonmails.de -anonymail.dk -anonymbox.com -anonymized.org -anonymousness.com -anotherdomaincyka.tk -ansibleemail.com -anthony-junkmail.com -antireg.com -antireg.ru -antispam.de -antispam24.de -antispammail.de -anyalias.com -aoeuhtns.com -apfelkorps.de -aphlog.com -apkmd.com -appc.se -appinventor.nl -appixie.com -apps.dj -appzily.com -arduino.hk -ariaz.jetzt -armyspy.com -aron.us -arroisijewellery.com -art-en-ligne.pro -artman-conception.com -arur01.tk -arurgitu.gq -arvato-community.de -aschenbrandt.net -asdasd.nl -asdasd.ru -ashleyandrew.com -ask-mail.com -asorent.com -ass.pp.ua -astonut.tk -astroempires.info -asu.mx -asu.su -at.hm -at0mik.org -atnextmail.com -attnetwork.com -augmentationtechnology.com -ausgefallen.info -auti.st -autorobotica.com -autosouvenir39.ru -autotwollow.com -autowb.com -aver.com -averdov.com -avia-tonic.fr -avls.pt -awatum.de -awdrt.org -awiki.org -awsoo.com -axiz.org -axon7zte.com -axsup.net -ayakamail.cf -azazazatashkent.tk -azcomputerworks.com -azmeil.tk -b1of96u.com -b2bx.net -b2cmail.de -badgerland.eu -badoop.com -badpotato.tk -balaket.com -banit.club -banit.me -bank-opros1.ru -bareed.ws -barryogorman.com -bartdevos.be -basscode.org -bauwerke-online.com -bazaaboom.com -bbbbyyzz.info -bbhost.us -bbitf.com -bbitj.com -bbitq.com -bcaoo.com -bcast.ws -bcb.ro -bccto.me -bdmuzic.pw -beaconmessenger.com -bearsarefuzzy.com -beddly.com -beefmilk.com -belamail.org -belljonestax.com -beluckygame.com -benipaula.org -bepureme.com -beribase.ru -beribaza.ru -berirabotay.ru -best-john-boats.com -bestchoiceusedcar.com -bestlistbase.com -bestoption25.club -bestparadize.com -bestsoundeffects.com -besttempmail.com -betr.co -bgtmail.com -bgx.ro -bheps.com -bidourlnks.com -big1.us -bigprofessor.so -bigstring.com -bigwhoop.co.za -bij.pl -binka.me -binkmail.com -binnary.com -bio-muesli.info -bio-muesli.net -bione.co -bitwhites.top -bitymails.us -blackgoldagency.ru -blackmarket.to -bladesmail.net -blip.ch -blnkt.net -block521.com -blogmyway.org -blogos.net -blogspam.ro -blondemorkin.com -bluedumpling.info -bluewerks.com -bnote.com -boatmail.us -bobmail.info -bobmurchison.com -bofthew.com -bonobo.email -boofx.com -bookthemmore.com -bootybay.de -borged.com -borged.net -borged.org -bot.nu -boun.cr -bouncr.com -boxformail.in -boximail.com -boxmail.lol -boxomail.live -boxtemp.com.br -bptfp.net -brand-app.biz -brandallday.net -brasx.org -breakthru.com -brefmail.com -brennendesreich.de -briggsmarcus.com -broadbandninja.com -bsnow.net -bspamfree.org -bspooky.com -bst-72.com -btb-notes.com -btc.email -btcmail.pw -btcmod.com -btizet.pl -buccalmassage.ru -budaya-tionghoa.com -budayationghoa.com -buffemail.com -bugfoo.com -bugmenever.com -bugmenot.com -bukhariansiddur.com -bulrushpress.com -bum.net -bumpymail.com -bunchofidiots.com -bund.us -bundes-li.ga -bunsenhoneydew.com -burnthespam.info -burstmail.info -businessbackend.com -businesssuccessislifesuccess.com -buspad.org -bussitussi.com -buymoreplays.com -buyordie.info -buyusdomain.com -buyusedlibrarybooks.org -buzzcluby.com -byebyemail.com -byespm.com -byom.de -c51vsgq.com -cachedot.net -californiafitnessdeals.com -cam4you.cc -camping-grill.info -candymail.de -cane.pw -capitalistdilemma.com -car101.pro -carbtc.net -cars2.club -carsencyclopedia.com -cartelera.org -caseedu.tk -cashflow35.com -casualdx.com -cavi.mx -cbair.com -cbes.net -cc.liamria -ccmail.uk -cdfaq.com -cdpa.cc -ceed.se -cek.pm -cellurl.com -centermail.com -centermail.net -cetpass.com -cfo2go.ro -chacuo.net -chaichuang.com -chalupaurybnicku.cz -chammy.info -chasefreedomactivate.com -chatich.com -cheaphub.net -cheatmail.de -chenbot.email -chibakenma.ml -chickenkiller.com -chielo.com -childsavetrust.org -chilkat.com -chinamkm.com -chithinh.com -chitthi.in -choco.la -chogmail.com -choicemail1.com -chong-mail.com -chong-mail.net -chong-mail.org -chumpstakingdumps.com -cigar-auctions.com -civikli.com -civx.org -ckaazaza.tk -ckiso.com -cl-cl.org -cl0ne.net -claimab.com -clandest.in -classesmail.com -clearwatermail.info -click-email.com -clickdeal.co -clipmail.eu -clixser.com -clonemoi.tk -cloud-mail.top -cloudns.cx -clout.wiki -clrmail.com -cmail.club -cmail.com -cmail.net -cmail.org -cnamed.com -cndps.com -cnew.ir -cnmsg.net -cnsds.de -co.cc -cobarekyo1.ml -cocoro.uk -cocovpn.com -codeandscotch.com -codivide.com -coffeetimer24.com -coieo.com -coin-host.net -coinlink.club -coldemail.info -compareshippingrates.org -completegolfswing.com -comwest.de -conf.work -consumerriot.com -contbay.com -cooh-2.site -coolandwacky.us -coolimpool.org -coreclip.com -cosmorph.com -courrieltemporaire.com -coza.ro -crankhole.com -crapmail.org -crastination.de -crazespaces.pw -crazymailing.com -cream.pink -crepeau12.com -cringemonster.com -cross-law.ga -cross-law.gq -crossmailjet.com -crossroadsmail.com -crunchcompass.com -crusthost.com -cs.email -csh.ro -cszbl.com -ctmailing.us -ctos.ch -cu.cc -cubiclink.com -cuendita.com -cuirushi.org -cuoly.com -cupbest.com -curlhph.tk -curryworld.de -cust.in -cutout.club -cutradition.com -cuvox.de -cyber-innovation.club -cyber-phone.eu -cylab.org -d1yun.com -d3p.dk -daabox.com -dab.ro -dacoolest.com -daemsteam.com -daibond.info -daily-email.com -daintly.com -damai.webcam -dammexe.net -damnthespam.com -dandikmail.com -darkharvestfilms.com -daryxfox.net -dasdasdascyka.tk -dash-pads.com -dataarca.com -datarca.com -datazo.ca -datenschutz.ru -datum2.com -davidkoh.net -davidlcreative.com -dawin.com -daymail.life -daymailonline.com -dayrep.com -dbunker.com -dcctb.com -dcemail.com -ddcrew.com -de-a.org -dea-21olympic.com -deadaddress.com -deadchildren.org -deadfake.cf -deadfake.ga -deadfake.ml -deadfake.tk -deadspam.com -deagot.com -dealja.com -dealrek.com -deekayen.us -defomail.com -degradedfun.net -deinbox.com -delayload.com -delayload.net -delikkt.de -delivrmail.com -demen.ml -dengekibunko.ga -dengekibunko.gq -dengekibunko.ml -der-kombi.de -derkombi.de -derluxuswagen.de -desoz.com -despam.it -despammed.com -dev-null.cf -dev-null.ga -dev-null.gq -dev-null.ml -developermail.com -devnullmail.com -deyom.com -dharmatel.net -dhm.ro -dhy.cc -dialogus.com -diapaulpainting.com -dicopto.com -digdig.org -digital-message.com -digitalesbusiness.info -digitalmail.info -digitalmariachis.com -digitalsanctuary.com -dildosfromspace.com -dim-coin.com -dingbone.com -diolang.com -directmail24.net -disaq.com -disbox.net -disbox.org -discard.cf -discard.email -discard.ga -discard.gq -discard.ml -discard.tk -discardmail.com -discardmail.de -discos4.com -disign-concept.eu -disign-revelation.com -dispo.in -dispomail.eu -disposable-e.ml -disposable-email.ml -disposable.cf -disposable.ga -disposable.ml -disposable.site -disposableaddress.com -disposableemailaddresses.com -disposableinbox.com -disposablemails.com -dispose.it -disposeamail.com -disposemail.com -disposemymail.com -dispostable.com -divad.ga -divermail.com -divismail.ru -diwaq.com -dlemail.ru -dmarc.ro -dndent.com -dnses.ro -doanart.com -dob.jp -dodgeit.com -dodgemail.de -dodgit.com -dodgit.org -dodsi.com -doiea.com -dolphinnet.net -domforfb1.tk -domforfb18.tk -domforfb19.tk -domforfb2.tk -domforfb23.tk -domforfb27.tk -domforfb29.tk -domforfb3.tk -domforfb4.tk -domforfb5.tk -domforfb6.tk -domforfb7.tk -domforfb8.tk -domforfb9.tk -domozmail.com -donemail.ru -dongqing365.com -dontreg.com -dontsendmespam.de -doojazz.com -doquier.tk -dotman.de -dotmsg.com -dotslashrage.com -doublemail.de -douchelounge.com -dozvon-spb.ru -dp76.com -dr69.site -drdrb.com -drdrb.net -dred.ru -drevo.si -drivetagdev.com -drmail.in -droolingfanboy.de -dropcake.de -dropjar.com -droplar.com -dropmail.me -dropsin.net -dsgvo.ru -dsiay.com -dspwebservices.com -duam.net -duck2.club -dudmail.com -duk33.com -dukedish.com -dump-email.info -dumpandjunk.com -dumpmail.de -dumpyemail.com -durandinterstellar.com -duskmail.com -dwse.edu.pl -dyceroprojects.com -dz17.net -e-mail.com -e-mail.org -e-marketstore.ru -e-tomarigi.com -e3z.de -e4ward.com -eanok.com -easy-trash-mail.com -easynetwork.info -easytrashmail.com -eatmea2z.club -eay.jp -ebbob.com -ebeschlussbuch.de -ecallheandi.com -ecolo-online.fr -edgex.ru -edinburgh-airporthotels.com -edv.to -ee1.pl -ee2.pl -eeedv.de -eelmail.com -efxs.ca -egzones.com -einmalmail.de -einrot.com -einrot.de -eintagsmail.de -elearningjournal.org -electro.mn -elitevipatlantamodels.com -elki-mkzn.ru -email-fake.cf -email-fake.com -email-fake.ga -email-fake.gq -email-fake.ml -email-fake.tk -email-jetable.fr -email-lab.com -email-temp.com -email.edu.pl -email.net -email1.pro -email60.com -emailage.cf -emailage.ga -emailage.gq -emailage.ml -emailage.tk -emailate.com -emailcu.icu -emaildienst.de -emaildrop.io -emailfake.com -emailfake.ml -emailfreedom.ml -emailgenerator.de -emailgo.de -emailias.com -emailigo.de -emailinfive.com -emailisvalid.com -emaillime.com -emailmiser.com -emailna.co -emailnax.com -emailo.pro -emailondeck.com -emailportal.info -emailproxsy.com -emailresort.com -emails.ga -emailsecurer.com -emailsensei.com -emailsingularity.net -emailspam.cf -emailspam.ga -emailspam.gq -emailspam.ml -emailspam.tk -emailsy.info -emailtech.info -emailtemporanea.com -emailtemporanea.net -emailtemporar.ro -emailtemporario.com.br -emailthe.net -emailtmp.com -emailto.de -emailure.net -emailwarden.com -emailxfer.com -emailz.cf -emailz.ga -emailz.gq -emailz.ml -emeil.in -emeil.ir -emeraldwebmail.com -emil.com -emkei.cf -emkei.ga -emkei.gq -emkei.ml -emkei.tk -eml.pp.ua -emlhub.com -emlpro.com -emltmp.com -empireanime.ga -emstjzh.com -emz.net -enayu.com -enterto.com -envy17.com -eoffice.top -eoopy.com -epb.ro -ephemail.net -ephemeral.email -eposta.buzz -eposta.work -eqiluxspam.ga -ereplyzy.com -ericjohnson.ml -ero-tube.org -esadverse.com -esbano-ru.ru -esc.la -escapehatchapp.com -esemay.com -esgeneri.com -esiix.com -esprity.com -estate-invest.fr -eth2btc.info -ether123.net -ethereum1.top -ethersports.org -ethersportz.info -etotvibor.ru -etranquil.com -etranquil.net -etranquil.org -euaqa.com -evanfox.info -eveav.com -evilcomputer.com -evopo.com -evyush.com -exdonuts.com -existiert.net -exitstageleft.net -explodemail.com -express.net.ua -extracurricularsociety.com -extremail.ru -eyepaste.com -ez.lv -ezehe.com -ezfill.com -ezstest.com -f4k.es -f5.si -facebook-email.cf -facebook-email.ga -facebook-email.ml -facebookmail.gq -facebookmail.ml -fackme.gq -fadingemail.com -faecesmail.me -fag.wf -failbone.com -faithkills.com -fake-box.com -fake-email.pp.ua -fake-mail.cf -fake-mail.ga -fake-mail.ml -fakedemail.com -fakeinbox.cf -fakeinbox.com -fakeinbox.ga -fakeinbox.info -fakeinbox.ml -fakeinbox.tk -fakeinformation.com -fakemail.fr -fakemail.io -fakemailgenerator.com -fakemailz.com -fallinhay.com -fammix.com -fanclub.pm -fangoh.com -fansworldwide.de -fantasymail.de -farrse.co.uk -fast-email.info -fast-mail.fr -fastacura.com -fastchevy.com -fastchrysler.com -fasternet.biz -fastkawasaki.com -fastmazda.com -fastmitsubishi.com -fastnissan.com -fastsubaru.com -fastsuzuki.com -fasttoyota.com -fastyamaha.com -fatflap.com -fbma.tk -fddns.ml -fdfdsfds.com -femailtor.com -fer-gabon.org -fermaxxi.ru -fettometern.com -fexbox.org -fexbox.ru -fexpost.com -fextemp.com -ficken.de -fictionsite.com -fightallspam.com -figjs.com -figshot.com -figurescoin.com -fiifke.de -filbert4u.com -filberts4u.com -film-blog.biz -filzmail.com -findemail.info -findu.pl -finews.biz -fir.hk -firemailbox.club -fitnesrezink.ru -fivemail.de -fixmail.tk -fizmail.com -fleckens.hu -flemail.ru -flowu.com -flu.cc -fluidsoft.us -flurred.com -fly-ts.de -flyinggeek.net -flyspam.com -foobarbot.net -footard.com -foreastate.com -forecastertests.com -foreskin.cf -foreskin.ga -foreskin.gq -foreskin.ml -foreskin.tk -forgetmail.com -fornow.eu -forspam.net -forward.cat -fosil.pro -foxja.com -foxtrotter.info -fr.cr -fr.nf -fr33mail.info -fragolina2.tk -frapmail.com -frappina.tk -free-email.cf -free-email.ga -free-temp.net -freebabysittercam.com -freeblackbootytube.com -freecat.net -freedom4you.info -freedompop.us -freefattymovies.com -freehotmail.net -freeinbox.email -freelance-france.eu -freeletter.me -freemail.ms -freemails.cf -freemails.ga -freemails.ml -freemeil.ga -freemeil.gq -freemeil.ml -freeml.net -freeplumpervideos.com -freerubli.ru -freeschoolgirlvids.com -freesistercam.com -freeteenbums.com -freundin.ru -friendlymail.co.uk -front14.org -frwdmail.com -ftp.sh -ftpinc.ca -fuckedupload.com -fuckingduh.com -fuckme69.club -fucknloveme.top -fuckxxme.top -fudgerub.com -fuirio.com -fukaru.com -fukurou.ch -fullangle.org -fulvie.com -fun64.com -funnycodesnippets.com -funnymail.de -furzauflunge.de -futuramind.com -fuwamofu.com -fuwari.be -fux0ringduh.com -fxnxs.com -fyii.de -g14l71lb.com -g1xmail.top -g2xmail.top -g3xmail.top -g4hdrop.us -gafy.net -gage.ga -galaxy.tv -gally.jp -gamail.top -gamegregious.com -gamgling.com -garasikita.pw -garbagecollector.org -garbagemail.org -gardenscape.ca -garizo.com -garliclife.com -garrymccooey.com -gav0.com -gawab.com -gbcmail.win -gbmail.top -gcmail.top -gdmail.top -gedmail.win -geekforex.com -geew.ru -gehensiemirnichtaufdensack.de -geldwaschmaschine.de -gelitik.in -genderfuck.net -geronra.com -geschent.biz -get-mail.cf -get-mail.ga -get-mail.ml -get-mail.tk -get.pp.ua -get1mail.com -get2mail.fr -getairmail.cf -getairmail.com -getairmail.ga -getairmail.gq -getairmail.ml -getairmail.tk -geteit.com -getfun.men -getmails.eu -getnada.com -getnowtoday.cf -getonemail.com -getonemail.net -getover.de -getsimpleemail.com -gett.icu -gexik.com -ggmal.ml -ghosttexter.de -giacmosuaviet.info -giaiphapmuasam.com -giantmail.de -gifto12.com -ginzi.be -ginzi.co.uk -ginzi.es -ginzi.net -ginzy.co.uk -ginzy.eu -girlmail.win -girlsindetention.com -girlsundertheinfluence.com -gishpuppy.com -giveh2o.info -givememail.club -givmail.com -glitch.sx -globaltouron.com -glubex.com -glucosegrin.com -gmal.com -gmatch.org -gmial.com -gmx1mail.top -gmxmail.top -gmxmail.win -gnctr-calgary.com -go2usa.info -go2vpn.net -goemailgo.com -golemico.com -gomail.in -goonby.com -goplaygame.ru -gorillaswithdirtyarmpits.com -goround.info -gosuslugi-spravka.ru -gothere.biz -gotmail.com -gotmail.net -gotmail.org -gowikibooks.com -gowikicampus.com -gowikicars.com -gowikifilms.com -gowikigames.com -gowikimusic.com -gowikinetwork.com -gowikitravel.com -gowikitv.com -grandmamail.com -grandmasmail.com -great-host.in -greencafe24.com -greendike.com -greenhousemail.com -greensloth.com -greggamel.com -greggamel.net -gregorsky.zone -gregorygamel.com -gregorygamel.net -grish.de -griuc.schule -grn.cc -groupbuff.com -grr.la -grugrug.ru -gruz-m.ru -gs-arc.org -gsredcross.org -gsrv.co.uk -gsxstring.ga -gudanglowongan.com -guerillamail.biz -guerillamail.com -guerillamail.de -guerillamail.info -guerillamail.net -guerillamail.org -guerillamailblock.com -guerrillamail.biz -guerrillamail.com -guerrillamail.de -guerrillamail.info -guerrillamail.net -guerrillamail.org -guerrillamailblock.com -gufum.com -gustr.com -gxemail.men -gynzi.co.uk -gynzi.es -gynzy.at -gynzy.es -gynzy.eu -gynzy.gr -gynzy.info -gynzy.lt -gynzy.mobi -gynzy.pl -gynzy.ro -gynzy.sk -gzb.ro -h8s.org -habitue.net -hacccc.com -hackersquad.tk -hackthatbit.ch -hahawrong.com -haida-edu.cn -hairs24.ru -haltospam.com -hamham.uk -hangxomcuatoilatotoro.ml -happydomik.ru -harakirimail.com -haribu.com -hartbot.de -hasanmail.ml -hat-geld.de -hatespam.org -hawrong.com -haydoo.com -hazelnut4u.com -hazelnuts4u.com -hazmatshipping.org -hccmail.win -headstrong.de -heathenhammer.com -heathenhero.com -hecat.es -heisei.be -hellodream.mobi -helloricky.com -helpinghandtaxcenter.org -helpjobs.ru -heros3.com -herp.in -herpderp.nl -hezll.com -hi5.si -hiddentragedy.com -hidebox.org -hidebusiness.xyz -hidemail.de -hidemail.pro -hidemail.us -hidzz.com -highbros.org -hiltonvr.com -himail.online -hmail.us -hmamail.com -hmh.ro -hoanggiaanh.com -hoanglong.tech -hochsitze.com -hola.org -holl.ga -honeys.be -honor-8.com -hopemail.biz -hornyalwary.top -host1s.com -hostcalls.com -hostguru.top -hostingmail.me -hostlaba.com -hot-mail.cf -hot-mail.ga -hot-mail.gq -hot-mail.ml -hot-mail.tk -hotmai.com -hotmailproduct.com -hotmial.com -hotpop.com -hotprice.co -hotsoup.be -housat.com -hpc.tw -hs.vc -ht.cx -huangniu8.com -hukkmu.tk -hulapla.de -humaility.com -hungpackage.com -hushmail.cf -huskion.net -hvastudiesucces.nl -hwsye.net -i2pmail.org -i6.cloudns.cc -iaoss.com -ibnuh.bz -icantbelieveineedtoexplainthisshit.com -icemail.club -ichigo.me -icx.in -icx.ro -idx4.com -idxue.com -ieatspam.eu -ieatspam.info -ieh-mail.de -iencm.com -iffymedia.com -ige.es -igg.biz -ignoremail.com -ihateyoualot.info -ihazspam.ca -iheartspam.org -ikbenspamvrij.nl -illistnoise.com -ilovespam.com -imail1.net -imails.info -imailt.com -imgof.com -imgv.de -immo-gerance.info -imstations.com -imul.info -in-ulm.de -in2reach.com -inactivemachine.com -inbax.tk -inbound.plus -inbox.si -inbox2.info -inboxalias.com -inboxbear.com -inboxclean.com -inboxclean.org -inboxdesign.me -inboxed.im -inboxed.pw -inboxkitten.com -inboxproxy.com -inboxstore.me -inclusiveprogress.com -incognitomail.com -incognitomail.net -incognitomail.org -incq.com -ind.st -indieclad.com -indirect.ws -indomaed.pw -indomina.cf -indoserver.stream -indosukses.press -ineec.net -infocom.zp.ua -inggo.org -inkomail.com -inmynetwork.tk -inoutmail.de -inoutmail.eu -inoutmail.info -inoutmail.net -inpwa.com -insanumingeniumhomebrew.com -insorg-mail.info -instaddr.ch -instance-email.com -instant-mail.de -instantblingmail.info -instantemailaddress.com -instantmail.fr -internet-v-stavropole.ru -internetoftags.com -interstats.org -intersteller.com -intopwa.com -intopwa.net -intopwa.org -investore.co -iozak.com -ip4.pp.ua -ip6.li -ip6.pp.ua -ipoo.org -ippandansei.tk -ipsur.org -irabops.com -irc.so -irish2me.com -irishspringrealty.com -iroid.com -ironiebehindert.de -irssi.tv -is.af -isdaq.com -ishop2k.com -isosq.com -istii.ro -isukrainestillacountry.com -it7.ovh -italy-mail.com -itcompu.com -itfast.net -itunesgiftcodegenerator.com -iubridge.com -iuemail.men -iwi.net -ixaks.com -ixx.io -j-p.us -jafps.com -jajxz.com -janproz.com -jaqis.com -jdmadventures.com -jdz.ro -je-recycle.info -jellow.ml -jellyrolls.com -jeoce.com -jet-renovation.fr -jetable.com -jetable.net -jetable.org -jetable.pp.ua -jiooq.com -jmail.ovh -jmail.ro -jnxjn.com -jobbikszimpatizans.hu -jobbrett.com -jobposts.net -jobs-to-be-done.net -joelpet.com -joetestalot.com -jopho.com -joseihorumon.info -josse.ltd -jourrapide.com -jpco.org -jsrsolutions.com -jumonji.tk -jungkamushukum.com -junk.to -junk1e.com -junkmail.ga -junkmail.gq -just-email.com -justemail.ml -juyouxi.com -jwork.ru -kademen.com -kadokawa.cf -kadokawa.ga -kadokawa.gq -kadokawa.ml -kadokawa.tk -kaengu.ru -kagi.be -kakadua.net -kalapi.org -kamen-market.ru -kamsg.com -kaovo.com -kappala.info -kara-turk.net -karatraman.ml -kariplan.com -karta-kykyruza.ru -kartvelo.com -kasmail.com -kaspop.com -katztube.com -kazelink.ml -kbox.li -kcrw.de -keepmymail.com -keinhirn.de -keipino.de -kekita.com -kellychibale-researchgroup-uct.com -kemptvillebaseball.com -kennedy808.com -kiani.com -killmail.com -killmail.net -kimsdisk.com -kingsq.ga -kino-100.ru -kiois.com -kismail.ru -kisstwink.com -kitnastar.com -kjkszpjcompany.com -kkmail.be -kksm.be -klassmaster.com -klassmaster.net -klick-tipp.us -klipschx12.com -kloap.com -kludgemush.com -klzlk.com -kmail.li -kmhow.com -knol-power.nl -kobrandly.com -kommunity.biz -kon42.com -konultant-jurist.ru -kook.ml -kopagas.com -kopaka.net -korona-nedvizhimosti.ru -koshu.ru -kosmetik-obatkuat.com -kostenlosemailadresse.de -koszmail.pl -kpay.be -kpooa.com -kpost.be -krd.ag -krsw.tk -kruay.com -krypton.tk -ksmtrck.tk -kuhrap.com -kulmeo.com -kulturbetrieb.info -kurzepost.de -kutakbisajauhjauh.gq -kvhrr.com -kvhrs.com -kvhrw.com -kwift.net -kwilco.net -kyal.pl -kyois.com -kzccv.com -l-c-a.us -l33r.eu -l6factors.com -labetteraverouge.at -labworld.org -lacedmail.com -lackmail.net -lackmail.ru -lacto.info -lags.us -lain.ch -lak.pp.ua -lakelivingstonrealestate.com -lakqs.com -lamasticots.com -landmail.co -laoeq.com -larisia.com -larland.com -last-chance.pro -lastmail.co -lastmail.com -lawlita.com -lazyinbox.com -lazyinbox.us -ldaho.biz -ldop.com -ldtp.com -le-tim.ru -lee.mx -leeching.net -leetmail.co -legalrc.loan -lellno.gq -lenovog4.com -lerbhe.com -letmeinonthis.com -letthemeatspam.com -lez.se -lgxscreen.com -lhsdv.com -liamcyrus.com -lifebyfood.com -lifetimefriends.info -lifetotech.com -ligsb.com -lillemap.net -lilo.me -lindenbaumjapan.com -link2mail.net -linkedintuts2016.pw -linshiyouxiang.net -linuxmail.so -litedrop.com -liveradio.tk -lkgn.se -llogin.ru -loadby.us -loan101.pro -loaoa.com -loapq.com -locanto1.club -locantofuck.top -locantowsite.club -locomodev.net -login-email.cf -login-email.ga -login-email.ml -login-email.tk -logular.com -loh.pp.ua -loin.in -lolfreak.net -lolmail.biz -lookugly.com -lordsofts.com -lortemail.dk -losemymail.com -lovemeet.faith -lovemeleaveme.com -lpfmgmtltd.com -lr7.us -lr78.com -lroid.com -lru.me -ls-server.ru -lsyx24.com -luckymail.org -lukecarriere.com -lukemail.info -lukop.dk -luv2.us -lyfestylecreditsolutions.com -lyft.live -lyricspad.net -lzoaq.com -m21.cc -m4ilweb.info -maboard.com -mac-24.com -macr2.com -macromaid.com -macromice.info -magamail.com -maggotymeat.ga -magicbox.ro -magim.be -magspam.net -maidlow.info -mail-card.net -mail-easy.fr -mail-filter.com -mail-help.net -mail-hosting.co -mail-hub.info -mail-now.top -mail-owl.com -mail-share.com -mail-temporaire.com -mail-temporaire.fr -mail-tester.com -mail.by -mail.wtf -mail0.ga -mail1.top -mail114.net -mail1a.de -mail1web.org -mail21.cc -mail22.club -mail2rss.org -mail333.com -mail4trash.com -mail666.ru -mail7.io -mail707.com -mail72.com -mailapp.top -mailback.com -mailbidon.com -mailbiz.biz -mailblocks.com -mailbox.in.ua -mailbox52.ga -mailbox80.biz -mailbox82.biz -mailbox87.de -mailbox92.biz -mailboxy.fun -mailbucket.org -mailcat.biz -mailcatch.com -mailchop.com -mailcker.com -maildax.me -mailde.de -mailde.info -maildrop.cc -maildrop.cf -maildrop.ga -maildrop.gq -maildrop.ml -maildu.de -maildx.com -maileater.com -mailed.in -mailed.ro -maileimer.de -maileme101.com -mailexpire.com -mailf5.com -mailfa.tk -mailfall.com -mailfirst.icu -mailforspam.com -mailfree.ga -mailfree.gq -mailfree.ml -mailfreeonline.com -mailfs.com -mailguard.me -mailgutter.com -mailhazard.com -mailhazard.us -mailhex.com -mailhub.pro -mailhz.me -mailimate.com -mailin8r.com -mailinatar.com -mailinater.com -mailinator.co.uk -mailinator.com -mailinator.gq -mailinator.info -mailinator.net -mailinator.org -mailinator.us -mailinator0.com -mailinator1.com -mailinator2.com -mailinator2.net -mailinator3.com -mailinator4.com -mailinator5.com -mailinator6.com -mailinator7.com -mailinator8.com -mailinator9.com -mailincubator.com -mailismagic.com -mailita.tk -mailjunk.cf -mailjunk.ga -mailjunk.gq -mailjunk.ml -mailjunk.tk -mailmate.com -mailme.gq -mailme.ir -mailme.lv -mailme24.com -mailmetrash.com -mailmoat.com -mailmoth.com -mailms.com -mailna.biz -mailna.co -mailna.in -mailna.me -mailnator.com -mailnesia.com -mailnull.com -mailonaut.com -mailorc.com -mailorg.org -mailosaur.net -mailox.fun -mailpick.biz -mailpluss.com -mailpooch.com -mailpoof.com -mailpress.gq -mailproxsy.com -mailquack.com -mailrock.biz -mailsac.com -mailscrap.com -mailseal.de -mailshell.com -mailshiv.com -mailsiphon.com -mailslapping.com -mailslite.com -mailsucker.net -mailt.net -mailt.top -mailtechx.com -mailtemp.info -mailtemporaire.com -mailtemporaire.fr -mailto.plus -mailtome.de -mailtothis.com -mailtraps.com -mailtrash.net -mailtrix.net -mailtv.net -mailtv.tv -mailuniverse.co.uk -mailzi.ru -mailzilla.com -mailzilla.org -mainerfolg.info -makemenaughty.club -makemetheking.com -malahov.de -malayalamdtp.com -mama3.org -mamulenok.ru -mandraghen.cf -manifestgenerator.com -mannawo.com -mansiondev.com -manybrain.com -mark-compressoren.ru -marketlink.info -markmurfin.com -mask03.ru -masonline.info -maswae.world -matamuasu.ga -matchpol.net -matra.site -max-mail.org -mbox.re -mbx.cc -mcache.net -mciek.com -mdhc.tk -meantinc.com -mebelnu.info -mechanicalresumes.com -medkabinet-uzi.ru -meepsheep.eu -meidecn.com -meinspamschutz.de -meltedbrownies.com -meltmail.com -memsg.site -mentonit.net -mepost.pw -merry.pink -messagebeamer.de -messwiththebestdielikethe.rest -metadownload.org -metaintern.net -metalunits.com -mezimages.net -mfsa.info -mfsa.ru -mhzayt.online -miaferrari.com -miauj.com -midcoastcustoms.com -midcoastcustoms.net -midcoastsolutions.com -midcoastsolutions.net -midiharmonica.com -midlertidig.com -midlertidig.net -midlertidig.org -mierdamail.com -migmail.net -migmail.pl -migumail.com -mihep.com -mijnhva.nl -ministry-of-silly-walks.de -minsmail.com -mintemail.com -mirai.re -misterpinball.de -miucce.com -mji.ro -mjj.edu.ge -mjukglass.nu -mkpfilm.com -ml8.ca -mm.my -mm5.se -mnode.me -moakt.cc -moakt.co -moakt.com -moakt.ws -mobileninja.co.uk -mobilevpn.top -moburl.com -mockmyid.com -moeri.org -mofu.be -mohmal.com -mohmal.im -mohmal.in -mohmal.tech -moimoi.re -molms.com -momentics.ru -monachat.tk -monadi.ml -moneypipe.net -monumentmail.com -moonwake.com -moot.es -moreawesomethanyou.com -moreorcs.com -morriesworld.ml -morsin.com -moruzza.com -motique.de -mountainregionallibrary.net -mox.pp.ua -moy-elektrik.ru -moza.pl -mozej.com -mp-j.ga -mr24.co -mrvpm.net -mrvpt.com -msgos.com -mspeciosa.com -msrc.ml -mswork.ru -msxd.com -mt2009.com -mt2014.com -mt2015.com -mtmdev.com -muathegame.com -muchomail.com -mucincanon.com -muehlacker.tk -muell.icu -muell.monster -muell.xyz -muellemail.com -muellmail.com -munoubengoshi.gq -musiccode.me -mutant.me -mvrht.com -mvrht.net -mwarner.org -mxclip.com -mxfuel.com -my-pomsies.ru -my-teddyy.ru -my10minutemail.com -mybitti.de -mycleaninbox.net -mycorneroftheinter.net -myde.ml -mydefipet.live -mydemo.equipment -myecho.es -myemailboxy.com -mygeoweb.info -myindohome.services -myinterserver.ml -mykickassideas.com -mymail-in.net -mymail90.com -mymailoasis.com -mynetstore.de -myopang.com -mypacks.net -mypartyclip.de -myphantomemail.com -mysamp.de -myspaceinc.com -myspaceinc.net -myspaceinc.org -myspacepimpedup.com -myspamless.com -mystvpn.com -mysugartime.ru -mytemp.email -mytempemail.com -mytempmail.com -mytrashmail.com -mywarnernet.net -mywrld.site -mywrld.top -myzx.com -mzico.com -n1nja.org -na-cat.com -nabuma.com -nada.email -nada.ltd -nagi.be -nakedtruth.biz -nanonym.ch -naslazhdai.ru -nationalgardeningclub.com -nawmin.info -nbzmr.com -negated.com -neko2.net -nekochan.fr -neomailbox.com -neotlozhniy-zaim.ru -nepwk.com -nervmich.net -nervtmich.net -net1mail.com -netcom.ws -netmails.com -netmails.net -netricity.nl -netris.net -netviewer-france.com -netzidiot.de -nevermail.de -newbpotato.tk -newfilm24.ru -newideasfornewpeople.info -newmail.top -next.ovh -nextmail.info -nextstopvalhalla.com -nezdiro.org -nezid.com -nezumi.be -nezzart.com -nfast.net -nguyenusedcars.com -nh3.ro -nice-4u.com -nicknassar.com -nincsmail.com -nincsmail.hu -niseko.be -niwl.net -nm7.cc -nmail.cf -nnh.com -nnot.net -nnoway.ru -no-spam.ws -no-ux.com -noblepioneer.com -nobugmail.com -nobulk.com -nobuma.com -noclickemail.com -nodezine.com -nogmailspam.info -noicd.com -nokiamail.com -nolemail.ga -nomail.cf -nomail.ga -nomail.pw -nomail2me.com -nomorespamemails.com -nonspam.eu -nonspammer.de -nonze.ro -noref.in -norseforce.com -norwegischlernen.info -nospam4.us -nospamfor.us -nospamthanks.info -nothingtoseehere.ca -notif.me -notmailinator.com -notrnailinator.com -notsharingmy.info -now.im -nowhere.org -nowmymail.com -nowmymail.net -nproxi.com -nthrl.com -ntlhelp.net -nubescontrol.com -nullbox.info -nurfuerspam.de -nut.cc -nutpa.net -nuts2trade.com -nvhrw.com -nwldx.com -nwytg.com -nwytg.net -ny7.me -nypato.com -nyrmusic.com -o2stk.org -o7i.net -oalsp.com -obfusko.com -objectmail.com -obobbo.com -oborudovanieizturcii.ru -obxpestcontrol.com -octovie.com -odaymail.com -odem.com -odnorazovoe.ru -oepia.com -oerpub.org -offshore-proxies.net -ohaaa.de -ohi.tw -oida.icu -oing.cf -okclprojects.com -okinawa.li -okrent.us -okzk.com -olimp-case.ru -olypmall.ru -omail.pro -omnievents.org -omtecha.com -one-mail.top -one-time.email -one2mail.info -onekisspresave.com -onemail.host -oneoffemail.com -oneoffmail.com -onetm.jp -onewaymail.com -onlatedotcom.info -online.ms -onlineidea.info -onqin.com -ontyne.biz -oohioo.com -oolus.com -oonies-shoprus.ru -oopi.org -oosln.com -opayq.com -openavz.com -opendns.ro -opentrash.com -opmmedia.ga -opp24.com -optimaweb.me -opwebw.com -oranek.com -ordinaryamerican.net -oreidresume.com -orgmbx.cc -oroki.de -oshietechan.link -otherinbox.com -ourklips.com -ourpreviewdomain.com -outlawspam.com -outmail.win -ovomail.co -ovpn.to -owleyes.ch -owlpic.com -ownsyou.de -oxopoha.com -ozyl.de -p-banlis.ru -p33.org -p71ce1m.com -pa9e.com -pachilly.com -packiu.com -pagamenti.tk -paharpurmim.ga -pakadebu.ga -pamaweb.com -pancakemail.com -papierkorb.me -paplease.com -para2019.ru -parlimentpetitioner.tk -pastebitch.com -patonce.com -pavilionx2.com -payperex2.com -payspun.com -pe.hu -pecinan.com -pecinan.net -pecinan.org -penisgoes.in -penoto.tk -pepbot.com -peterdethier.com -petloca.com -petrzilka.net -pewpewpewpew.pw -pfui.ru -phone-elkey.ru -photo-impact.eu -photomark.net -pi.vu -piaa.me -pig.pp.ua -pii.at -piki.si -pimpedupmyspace.com -pinehill-seattle.org -pingir.com -pipemail.space -pisls.com -pitaniezdorovie.ru -pivo-bar.ru -pixiil.com -pjjkp.com -placebomail10.com -pleasenoham.org -plexfirm.com -plexolan.de -plhk.ru -ploae.com -plw.me -poehali-otdihat.ru -pojok.ml -pokemail.net -pokiemobile.com -polarkingxx.ml -politikerclub.de -polyfaust.net -pooae.com -poofy.org -pookmail.com -poopiebutt.club -popcornfarm7.com -popcornfly.com -popesodomy.com -popgx.com -porjoton.com -porsh.net -posdz.com -posta.store -postacin.com -postonline.me -poutineyourface.com -powered.name -powerencry.com -powlearn.com -pp7rvv.com -ppetw.com -pptrvv.com -pqoia.com -pratikmail.com -pratikmail.net -pratikmail.org -prazdnik-37.ru -predatorrat.cf -predatorrat.ga -predatorrat.gq -predatorrat.ml -predatorrat.tk -premium-mail.fr -primabananen.net -prin.be -privacy.net -privatdemail.net -privy-mail.com -privy-mail.de -privymail.de -pro-tag.org -pro5g.com -procrackers.com -profast.top -projectcl.com -promailt.com -proprietativalcea.ro -propscore.com -protempmail.com -proxymail.eu -proxyparking.com -prtnx.com -prtshr.com -prtz.eu -psh.me -psles.com -psnator.com -psoxs.com -puglieisi.com -puji.pro -punkass.com -puppetmail.de -purcell.email -purelogistics.org -put2.net -puttanamaiala.tk -putthisinyourspamdatabase.com -pwrby.com -qasti.com -qbfree.us -qc.to -qibl.at -qiott.com -qipmail.net -qiq.us -qisdo.com -qisoa.com -qmrbe.com -qoika.com -qopow.com -qq.my -qsl.ro -qtum-ico.com -quadrafit.com -quick-mail.cc -quickemail.info -quickinbox.com -quickmail.nl -quicksend.ch -ququb.com -qvy.me -qwickmail.com -r4nd0m.de -ra3.us -rabin.ca -rabiot.reisen -rackabzar.com -raetp9.com -rainbowly.ml -raketenmann.de -rancidhome.net -randomail.io -randomail.net -rapt.be -raqid.com -rax.la -raxtest.com -razemail.com -razuz.com -rbb.org -rcasd.com -rcpt.at -rdklcrv.xyz -re-gister.com -reality-concept.club -reallymymail.com -realtyalerts.ca -rebates.stream -receiveee.com -recipeforfailure.com -recode.me -reconmail.com -recyclemail.dk -redfeathercrow.com -reftoken.net -regbypass.com -regspaces.tk -reimondo.com -rejectmail.com -rejo.technology -reliable-mail.com -remail.cf -remail.ga -remarkable.rocks -remote.li -reptilegenetics.com -resgedvgfed.tk -revolvingdoorhoax.org -rfc822.org -rhyta.com -richfinances.pw -riddermark.de -rifkian.ga -rippb.com -risingsuntouch.com -riski.cf -rklips.com -rkomo.com -rm2rf.com -rma.ec -rmqkr.net -rnailinator.com -ro.lt -robertspcrepair.com -robot-mail.com -rollindo.agency -ronnierage.net -rootfest.net -rosebearmylove.ru -rotaniliam.com -rover.info -rowe-solutions.com -royal.net -royaldoodles.org -royalmarket.life -royandk.com -rppkn.com -rsvhr.com -rtrtr.com -rtskiya.xyz -rudymail.ml -rumgel.com -runi.ca -rupayamail.com -ruru.be -rustydoor.com -rvb.ro -ryteto.me -s0ny.net -s33db0x.com -sabrestlouis.com -sackboii.com -saeoil.com -safaat.cf -safermail.info -safersignup.de -safetymail.info -safetypost.de -saharanightstempe.com -salmeow.tk -samsclass.info -sandcars.net -sandelf.de -sandwhichvideo.com -sanfinder.com -sanim.net -sanstr.com -sast.ro -satisfyme.club -satukosong.com -sausen.com -saynotospams.com -scatmail.com -scay.net -schachrol.com -schafmail.de -schmeissweg.tk -schrott-email.de -scrsot.com -sd3.in -sdvft.com -sdvgeft.com -sdvrecft.com -secmail.pw -secretemail.de -secure-mail.biz -secure-mail.cc -secured-link.net -securehost.com.es -seekapps.com -seekjobs4u.com -sejaa.lv -selfdestructingmail.com -selfdestructingmail.org -send22u.info -sendfree.org -sendingspecialflyers.com -sendnow.win -sendspamhere.com -senseless-entertainment.com -server.ms -services391.com -sexforswingers.com -sexical.com -sexyalwasmi.top -shadap.org -shalar.net -sharedmailbox.org -sharklasers.com -sheryli.com -shhmail.com -shhuut.org -shieldedmail.com -shieldemail.com -shiftmail.com -shipfromto.com -shiphazmat.org -shipping-regulations.com -shippingterms.org -shitaway.tk -shitmail.de -shitmail.me -shitmail.org -shmeriously.com -shopxda.com -shortmail.net -shotmail.ru -showslow.de -shrib.com -shut.name -shut.ws -siberpay.com -sidelka-mytischi.ru -siftportal.ru -sify.com -sika3.com -sikux.com -siliwangi.ga -silvercoin.life -sim-simka.ru -simaenaga.com -simpleitsecurity.info -sin.cl -sinaite.net -sinema.ml -sinfiltro.cl -singlespride.com -sinnlos-mail.de -sino.tw -siteposter.net -sizzlemctwizzle.com -sjuaq.com -skeefmail.com -skrx.tk -sky-inbox.com -sky-ts.de -skyrt.de -slapsfromlastnight.com -slaskpost.se -slave-auctions.net -slippery.email -slipry.net -slopsbox.com -slothmail.net -slushmail.com -sluteen.com -sly.io -smallker.tk -smapfree24.com -smapfree24.de -smapfree24.eu -smapfree24.info -smapfree24.org -smartnator.com -smarttalent.pw -smashmail.de -smellfear.com -smellrear.com -smellypotato.tk -smtp99.com -smwg.info -snakemail.com -snapwet.com -sneakmail.de -snece.com -social-mailer.tk -socialfurry.org -sofia.re -sofimail.com -sofort-mail.de -sofortmail.de -sofrge.com -softkey-office.ru -softpls.asia -sogetthis.com -sohai.ml -sohus.cn -soioa.com -soisz.com -solar-impact.pro -solvemail.info -solventtrap.wiki -songsign.com -sonshi.cf -soodmail.com -soodomail.com -soodonims.com -soombo.com -soon.it -spacebazzar.ru -spam-be-gone.com -spam.care -spam.la -spam.org.es -spam.su -spam4.me -spamail.de -spamarrest.com -spamavert.com -spambob.com -spambob.net -spambob.org -spambog.com -spambog.de -spambog.net -spambog.ru -spambooger.com -spambox.info -spambox.me -spambox.org -spambox.us -spamcero.com -spamcon.org -spamcorptastic.com -spamcowboy.com -spamcowboy.net -spamcowboy.org -spamday.com -spamdecoy.net -spamex.com -spamfighter.cf -spamfighter.ga -spamfighter.gq -spamfighter.ml -spamfighter.tk -spamfree.eu -spamfree24.com -spamfree24.de -spamfree24.eu -spamfree24.info -spamfree24.net -spamfree24.org -spamgoes.in -spamherelots.com -spamhereplease.com -spamhole.com -spamify.com -spaminator.de -spamkill.info -spaml.com -spaml.de -spamlot.net -spammer.fail -spammotel.com -spammy.host -spamobox.com -spamoff.de -spamsalad.in -spamslicer.com -spamsphere.com -spamspot.com -spamstack.net -spamthis.co.uk -spamthis.network -spamthisplease.com -spamtrail.com -spamtrap.ro -spamtroll.net -spamwc.cf -spamwc.ga -spamwc.gq -spamwc.ml -speedgaus.net -sperma.cf -spikio.com -spindl-e.com -spoofmail.de -spr.io -spritzzone.de -spruzme.com -spybox.de -spymail.com -squizzy.de -squizzy.net -sroff.com -sry.li -ssoia.com -stanfordujjain.com -starlight-breaker.net -starpower.space -startfu.com -startkeys.com -statdvr.com -stathost.net -statiix.com -stayhome.li -steam-area.ru -steambot.net -stexsy.com -stinkefinger.net -stop-my-spam.cf -stop-my-spam.com -stop-my-spam.ga -stop-my-spam.ml -stop-my-spam.pp.ua -stop-my-spam.tk -stopspam.app -storiqax.top -storj99.com -storj99.top -streetwisemail.com -stromox.com -stuckmail.com -stuffmail.de -stumpfwerk.com -stylist-volos.ru -submic.com -suburbanthug.com -suckmyd.com -sueshaw.com -suexamplesb.com -suioe.com -super-auswahl.de -supergreatmail.com -supermailer.jp -superplatyna.com -superrito.com -supersave.net -superstachel.de -superyp.com -suremail.info -sute.jp -svip520.cn -svk.jp -svxr.org -sweetpotato.ml -sweetxxx.de -swift-mail.net -swift10minutemail.com -syinxun.com -sylvannet.com -symphonyresume.com -syosetu.gq -syujob.accountants -szerz.com -tafmail.com -tafoi.gr -taglead.com -tagmymedia.com -tagyourself.com -talkinator.com -tanukis.org -tapchicuoihoi.com -taphear.com -tapi.re -tarzanmail.cf -tastrg.com -taukah.com -tb-on-line.net -tcwlm.com -tcwlx.com -tdtda.com -tech69.com -techblast.ch -techemail.com -techgroup.me -technoproxy.ru -teerest.com -teewars.org -tefl.ro -telecomix.pl -teleg.eu -teleworm.com -teleworm.us -tellos.xyz -teml.net -temp-link.net -temp-mail.com -temp-mail.de -temp-mail.org -temp-mail.pp.ua -temp-mail.ru -temp-mails.com -tempail.com -tempalias.com -tempe-mail.com -tempemail.biz -tempemail.co.za -tempemail.com -tempemail.net -tempinbox.co.uk -tempinbox.com -tempmail.cn -tempmail.co -tempmail.de -tempmail.eu -tempmail.it -tempmail.pp.ua -tempmail.us -tempmail.ws -tempmail2.com -tempmaildemo.com -tempmailer.com -tempmailer.de -tempmailer.net -tempmailo.com -tempomail.fr -tempomail.org -temporarily.de -temporarioemail.com.br -temporary-mail.net -temporaryemail.net -temporaryemail.us -temporaryforwarding.com -temporaryinbox.com -temporarymailaddress.com -tempr.email -tempsky.com -tempthe.net -tempymail.com -tensi.org -ternaklele.ga -testore.co -testudine.com -thanksnospam.info -thankyou2010.com -thatim.info -thc.st -theaviors.com -thebearshark.com -thecarinformation.com -thechildrensfocus.com -thecity.biz -thecloudindex.com -thediamants.org -thedirhq.info -theeyeoftruth.com -thejoker5.com -thelightningmail.net -thelimestones.com -thembones.com.au -themegreview.com -themostemail.com -thereddoors.online -theroyalweb.club -thescrappermovie.com -theteastory.info -thex.ro -thichanthit.com -thietbivanphong.asia -thisisnotmyrealemail.com -thismail.net -thisurl.website -thnikka.com -thoas.ru -thraml.com -thrma.com -throam.com -thrott.com -throwam.com -throwawayemailaddress.com -throwawaymail.com -throwawaymail.pp.ua -throya.com -thrubay.com -thunderbolt.science -thunkinator.org -thxmate.com -tiapz.com -tic.ec -tilien.com -timgiarevn.com -timkassouf.com -tinoza.org -tinyurl24.com -tipsb.com -tittbit.in -tiv.cc -tizi.com -tkitc.de -tlpn.org -tmail.com -tmail.ws -tmailinator.com -tmails.net -tmmbt.net -tmpbox.net -tmpemails.com -tmpeml.com -tmpeml.info -tmpjr.me -tmpmail.net -tmpmail.org -tmpx.sa.com -toddsbighug.com -tofeat.com -toiea.com -tokem.co -tokenmail.de -tonaeto.com -tonne.to -tonymanso.com -toomail.biz -toon.ml -top-shop-tovar.ru -top101.de -top1mail.ru -top1post.ru -topinrock.cf -topmail2.com -topmail2.net -topofertasdehoy.com -topranklist.de -toprumours.com -tormail.org -toss.pw -tosunkaya.com -totallynotfake.net -totalvista.com -totesmail.com -totoan.info -tourcc.com -tp-qa-mail.com -tpwlb.com -tqoai.com -tqosi.com -tradermail.info -tranceversal.com -trash-amil.com -trash-mail.at -trash-mail.cf -trash-mail.com -trash-mail.de -trash-mail.ga -trash-mail.gq -trash-mail.ml -trash-mail.tk -trash-me.com -trash2009.com -trash2010.com -trash2011.com -trashcanmail.com -trashdevil.com -trashdevil.de -trashemail.de -trashemails.de -trashinbox.com -trashmail.at -trashmail.com -trashmail.de -trashmail.gq -trashmail.io -trashmail.me -trashmail.net -trashmail.org -trashmail.ws -trashmailer.com -trashmailgenerator.de -trashmails.com -trashymail.com -trashymail.net -trasz.com -trayna.com -trbvm.com -trbvn.com -trbvo.com -trend-maker.ru -trgfu.com -trgovinanaveliko.info -trialmail.de -trickmail.net -trillianpro.com -triots.com -trixtrux1.ru -trollproject.com -tropicalbass.info -trungtamtoeic.com -truthfinderlogin.com -tryalert.com -tryninja.io -tryzoe.com -ttirv.org -ttszuo.xyz -tualias.com -tuofs.com -turoid.com -turual.com -turuma.com -tutuapp.bid -tvchd.com -tverya.com -twinmail.de -twkly.ml -twocowmail.net -twoweirdtricks.com -twzhhq.online -txen.de -txtadvertise.com -tyhe.ro -tyldd.com -tympe.net -uacro.com -uber-mail.com -ubismail.net -ubm.md -ucche.us -ucupdong.ml -uemail99.com -ufacturing.com -uggsrock.com -uguuchantele.com -uhe2.com -uhhu.ru -uiu.us -ujijima1129.gq -uk.to -ultra.fyi -ultrada.ru -uma3.be -umail.net -undo.it -unicodeworld.com -unids.com -unimark.org -unit7lahaina.com -unmail.ru -uooos.com -upliftnow.com -uplipht.com -uploadnolimit.com -upozowac.info -urfunktion.se -urhen.com -uroid.com -us.af -us.to -usa.cc -usako.net -usbc.be -used-product.fr -ushijima1129.cf -ushijima1129.ga -ushijima1129.gq -ushijima1129.ml -ushijima1129.tk -utiket.us -uu.gl -uu2.ovh -uuf.me -uwork4.us -uyhip.com -vaasfc4.tk -vaati.org -valemail.net -valhalladev.com -vankin.de -vctel.com -vda.ro -vddaz.com -vdig.com -veanlo.com -vemomail.win -venompen.com -veo.kr -ver0.cf -ver0.ga -ver0.gq -ver0.ml -ver0.tk -vercelli.cf -vercelli.ga -vercelli.gq -vercelli.ml -verdejo.com -vermutlich.net -veryday.ch -veryday.eu -veryday.info -veryrealemail.com -vesa.pw -vevs.de -vfemail.net -via.tokyo.jp -vickaentb.tk -victime.ninja -victoriantwins.com -vidchart.com -viditag.com -viewcastmedia.com -viewcastmedia.net -viewcastmedia.org -vikingsonly.com -vinernet.com -vintomaper.com -vipepe.com -vipmail.name -vipmail.pw -vipxm.net -viralplays.com -virtualemail.info -visal007.tk -visal168.cf -visal168.ga -visal168.gq -visal168.ml -visal168.tk -vixletdev.com -vixtricks.com -vkcode.ru -vmailing.info -vmani.com -vmpanda.com -vnedu.me -voidbay.com -volaj.com -voltaer.com -vomoto.com -vorga.org -votiputox.org -voxelcore.com -vpn.st -vps30.com -vps911.net -vradportal.com -vremonte24-store.ru -vrmtr.com -vsimcard.com -vssms.com -vtxmail.us -vubby.com -vuiy.pw -vusra.com -vztc.com -w-asertun.ru -w3internet.co.uk -wakingupesther.com -walala.org -walkmail.net -walkmail.ru -wallm.com -wanko.be -watch-harry-potter.com -watchever.biz -watchfull.net -watchironman3onlinefreefullmovie.com -wazabi.club -wbdev.tech -wbml.net -web-contact.info -web-ideal.fr -web-inc.net -web-mail.pp.ua -web2mailco.com -webcontact-france.eu -webemail.me -webhook.site -webm4il.info -webmail24.top -webtrip.ch -webuser.in -wee.my -wef.gr -weg-werf-email.de -wegwerf-email-addressen.de -wegwerf-email-adressen.de -wegwerf-email.at -wegwerf-email.de -wegwerf-email.net -wegwerf-emails.de -wegwerfadresse.de -wegwerfemail.com -wegwerfemail.de -wegwerfemail.info -wegwerfemail.net -wegwerfemail.org -wegwerfemailadresse.com -wegwerfmail.de -wegwerfmail.info -wegwerfmail.net -wegwerfmail.org -wegwerpmailadres.nl -wegwrfmail.de -wegwrfmail.net -wegwrfmail.org -wekawa.com -welikecookies.com -wellsfargocomcardholders.com -wemel.top -wetrainbayarea.com -wetrainbayarea.org -wfgdfhj.tk -wg0.com -wh4f.org -whatiaas.com -whatifanalytics.com -whatpaas.com -whatsaas.com -whiffles.org -whopy.com -whyspam.me -wibblesmith.com -wickmail.net -widaryanto.info -widget.gg -wierie.tk -wifimaple.com -wifioak.com -wikidocuslava.ru -wilemail.com -willhackforfood.biz -willselfdestruct.com -wimsg.com -winemaven.info -wins.com.br -wlist.ro -wmail.cf -wmail.club -wokcy.com -wolfmail.ml -wolfsmail.tk -wollan.info -worldspace.link -wpdork.com -wpg.im -wralawfirm.com -writeme.us -wronghead.com -ws.gy -wsym.de -wudet.men -wuespdj.xyz -wupics.com -wuuvo.com -wuzup.net -wuzupmail.net -wwjmp.com -wwwnew.eu -wxnw.net -x24.com -xagloo.co -xagloo.com -xbaby69.top -xcode.ro -xcodes.net -xcompress.com -xcoxc.com -xcpy.com -xemaps.com -xemne.com -xents.com -xjoi.com -xkx.me -xl.cx -xmail.com -xmailer.be -xmaily.com -xn--9kq967o.com -xn--d-bga.net -xojxe.com -xost.us -xoxox.cc -xperiae5.com -xrap.de -xrho.com -xvx.us -xwaretech.com -xwaretech.info -xwaretech.net -xww.ro -xxhamsterxx.ga -xxi2.com -xxlocanto.us -xxolocanto.us -xxqx3802.com -xy9ce.tk -xyzfree.net -xzsok.com -yabai-oppai.tk -yahmail.top -yahooproduct.net -yamail.win -yanet.me -yannmail.win -yapped.net -yaqp.com -yarnpedia.ga -ycare.de -ycn.ro -ye.vc -yedi.org -yeezus.ru -yep.it -yermail.net -yhg.biz -ynmrealty.com -yodx.ro -yogamaven.com -yoggm.com -yomail.info -yoo.ro -yopmail.com -yopmail.fr -yopmail.gq -yopmail.net -yopmail.pp.ua -yordanmail.cf -you-spam.com -yougotgoated.com -youmail.ga -youmailr.com -youneedmore.info -youpymail.com -yourdomain.com -youremail.cf -yourewronghereswhy.com -yourlms.biz -yourspamgoesto.space -yourtube.ml -yroid.com -yspend.com -ytpayy.com -yugasandrika.com -yui.it -yuoia.com -yuurok.com -yxzx.net -yyolf.net -z-o-e-v-a.ru -z0d.eu -z1p.biz -z86.ru -zain.site -zainmax.net -zaktouni.fr -zarabotokdoma11.ru -zasod.com -zaym-zaym.ru -zcrcd.com -zdenka.net -ze.tc -zebins.com -zebins.eu -zehnminuten.de -zehnminutenmail.de -zepp.dk -zetmail.com -zfymail.com -zhaoqian.ninja -zhaoyuanedu.cn -zhcne.com -zhewei88.com -zhorachu.com -zik.dj -zipcad.com -zipo1.gq -zippymail.info -zipsendtest.com -zoaxe.com -zoemail.com -zoemail.net -zoemail.org -zoetropes.org -zombie-hive.com -zomg.info -zsero.com -zumpul.com -zv68.com -zxcv.com -zxcvbnm.com -zymuying.com -zzi.us -zzrgg.com -zzz.com \ No newline at end of file diff --git a/backend-mongo/src/ee/LICENSE b/backend-mongo/src/ee/LICENSE deleted file mode 100644 index a1c37bb93..000000000 --- a/backend-mongo/src/ee/LICENSE +++ /dev/null @@ -1,36 +0,0 @@ -The Infisical Enterprise license (the โ€œEnterprise Licenseโ€) -Copyright (c) 2022 Infisical Inc - -With regard to the Infisical Software: - -This software and associated documentation files (the "Software") may only be -used in production, if you (and any entity that you represent) have agreed to, -and are in compliance with, the Infisical Subscription Terms of Service, available -at https://infisical.com/terms (the โ€œEnterprise Termsโ€), or other -agreement governing the use of the Software, as agreed by you and Infisical, -and otherwise have a valid Infisical Enterprise License for the -correct number of user seats. Subject to the foregoing sentence, you are free to -modify this Software and publish patches to the Software. You agree that Infisical -and/or its licensors (as applicable) retain all right, title and interest in and -to all such modifications and/or patches, and all such modifications and/or -patches may only be used, copied, modified, displayed, distributed, or otherwise -exploited with a valid Infiscial Enterprise subscription for the correct -number of user seats. Notwithstanding the foregoing, you may copy and modify -the Software for development and testing purposes, without requiring a -subscription. You agree that Infisical and/or its licensors (as applicable) retain -all right, title and interest in and to all such modifications. You are not -granted any other rights beyond what is expressly stated herein. Subject to the -foregoing, it is forbidden to copy, merge, publish, distribute, sublicense, -and/or sell the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -For all third party components incorporated into the Infisical Software, those -components are licensed under the original license provided by the owner of the -applicable component. diff --git a/backend-mongo/src/ee/controllers/v1/cloudProductsController.ts b/backend-mongo/src/ee/controllers/v1/cloudProductsController.ts deleted file mode 100644 index 0cc6ab372..000000000 --- a/backend-mongo/src/ee/controllers/v1/cloudProductsController.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Request, Response } from "express"; -import { EELicenseService } from "../../services"; -import { getLicenseServerUrl } from "../../../config"; -import { licenseServerKeyRequest } from "../../../config/request"; -import { validateRequest } from "../../../helpers/validation"; -import * as reqValidator from "../../../validation/cloudProducts"; - -/** - * Return available cloud product information. - * Note: Nicely formatted to easily construct a table from - * @param req - * @param res - * @returns - */ -export const getCloudProducts = async (req: Request, res: Response) => { - const { - query: { "billing-cycle": billingCycle } - } = await validateRequest(reqValidator.GetCloudProductsV1, req); - - if (EELicenseService.instanceType === "cloud") { - const { data } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}` - ); - - return res.status(200).send(data); - } - - return res.status(200).send({ - head: [], - rows: [] - }); -}; diff --git a/backend-mongo/src/ee/controllers/v1/identitiesController.ts b/backend-mongo/src/ee/controllers/v1/identitiesController.ts deleted file mode 100644 index 179beeda3..000000000 --- a/backend-mongo/src/ee/controllers/v1/identitiesController.ts +++ /dev/null @@ -1,460 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { - IIdentity, - Identity, - IdentityAccessToken, - IdentityMembership, - IdentityMembershipOrg, - IdentityUniversalAuth, - IdentityUniversalAuthClientSecret, - Organization -} from "../../../models"; -import { - EventType, - IRole, - Role -} from "../../models"; -import { validateRequest } from "../../../helpers/validation"; -import * as reqValidator from "../../../validation/identities"; -import { - getAuthDataOrgPermissions, - getOrgRolePermissions, - isAtLeastAsPrivilegedOrg -} from "../../services/RoleService"; -import { - BadRequestError, - ForbiddenRequestError, - ResourceNotFoundError, -} from "../../../utils/errors"; -import { ADMIN, CUSTOM, MEMBER, NO_ACCESS } from "../../../variables"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "../../services/RoleService"; -import { EEAuditLogService } from "../../services"; -import { ForbiddenError } from "@casl/ability"; - -/** - * Create identity - * @param req - * @param res - * @returns - */ -export const createIdentity = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Create identity' - #swagger.description = 'Create identity' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of entity to create", - "example": "development" - }, - "organizationId": { - "type": "string", - "description": "ID of organization where to create identity", - "example": "dev-environment" - }, - "role": { - "type": "string", - "description": "Role to assume for organization membership", - "example": "no-access" - } - }, - "required": ["name", "organizationId", "role"] - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identity": { - $ref: '#/definitions/Identity' - } - }, - "description": "Details of the created identity" - } - } - } - } - */ - const { - body: { - name, - organizationId, - role - } - } = await validateRequest(reqValidator.CreateIdentityV1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Identity - ); - - const rolePermission = await getOrgRolePermissions(role, organizationId); - const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission); - - if (!hasRequiredPrivileges) throw ForbiddenRequestError({ - message: "Failed to create a more privileged identity" - }); - - const organization = await Organization.findById(organizationId); - if (!organization) throw BadRequestError({ message: `Organization with id ${organizationId} not found` }); - - const isCustomRole = ![ADMIN, MEMBER, NO_ACCESS].includes(role); - - let customRole; - if (isCustomRole) { - customRole = await Role.findOne({ - slug: role, - isOrgRole: true, - organization: new Types.ObjectId(organizationId) - }); - - if (!customRole) throw BadRequestError({ message: "Role not found" }); - } - - const identity = await new Identity({ - name - }).save(); - - await new IdentityMembershipOrg({ - identity: identity._id, - organization: new Types.ObjectId(organizationId), - role: isCustomRole ? CUSTOM : role, - customRole - }).save(); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.CREATE_IDENTITY, - metadata: { - identityId: identity._id.toString(), - name - } - }, - { - organizationId: new Types.ObjectId(organizationId) - } - ); - - return res.status(200).send({ - identity - }); -} - -/** - * Update identity with id [identityId] - * @param req - * @param res - * @returns - */ - export const updateIdentity = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Update identity' - #swagger.description = 'Update identity' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['identityId'] = { - "description": "ID of identity to update", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.requestBody = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of entity to update to", - "example": "development" - }, - "role": { - "type": "string", - "description": "Role to update to for organization membership", - "example": "no-access" - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identity": { - $ref: '#/definitions/Identity' - } - }, - "description": "Details of the updated identity" - } - } - } - } - */ - const { - params: { identityId }, - body: { - name, - role - } - } = await validateRequest(reqValidator.UpdateIdentityV1, req); - - const identityMembershipOrg = await IdentityMembershipOrg - .findOne({ - identity: new Types.ObjectId(identityId) - }) - .populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembershipOrg) throw ResourceNotFoundError({ - message: `Failed to find identity with id ${identityId}` - }); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: identityMembershipOrg.organization - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Identity - ); - - const identityRolePermission = await getOrgRolePermissions( - identityMembershipOrg?.customRole?.slug ?? identityMembershipOrg.role, - identityMembershipOrg.organization.toString() - ); - const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, identityRolePermission); - if (!hasRequiredPrivileges) throw ForbiddenRequestError({ - message: "Failed to update more privileged identity" - }); - - if (role) { - const rolePermission = await getOrgRolePermissions(role, identityMembershipOrg.organization.toString()); - const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission); - - if (!hasRequiredPrivileges) throw ForbiddenRequestError({ - message: "Failed to update identity to a more privileged role" - }); - } - - let customRole; - if (role) { - const isCustomRole = ![ADMIN, MEMBER, NO_ACCESS].includes(role); - if (isCustomRole) { - customRole = await Role.findOne({ - slug: role, - isOrgRole: true, - organization: identityMembershipOrg.organization - }); - - if (!customRole) throw BadRequestError({ message: "Role not found" }); - } - } - - const identity = await Identity.findByIdAndUpdate( - identityId, - { - name, - }, - { - new: true - } - ); - - if (!identity) throw BadRequestError({ - message: `Failed to update identity with id ${identityId}` - }); - - await IdentityMembershipOrg.findOneAndUpdate( - { - identity: identity._id - }, - { - role: customRole ? CUSTOM : role, - ...(customRole ? { - customRole - } : {}), - ...(role && !customRole ? { // non-custom role - $unset: { - customRole: 1 - } - } : {}) - }, - { - new: true - } - ); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_IDENTITY, - metadata: { - identityId: identity._id.toString(), - name: identity.name, - } - }, - { - organizationId: identityMembershipOrg.organization - } - ); - - return res.status(200).send({ - identity - }); -} - -/** - * Delete identity with id [identityId] - * @param req - * @param res - * @returns - */ - export const deleteIdentity = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Delete identity' - #swagger.description = 'Delete identity' - - #swagger.security = [{ - "bearerAuth": [] - }] - - #swagger.parameters['identityId'] = { - "description": "ID of identity", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.responses[200] = { - content: { - "application/json": { - "schema": { - "type": "object", - "properties": { - "identity": { - $ref: '#/definitions/Identity' - } - }, - "description": "Details of the deleted identity" - } - } - } - } - */ - const { - params: { identityId } - } = await validateRequest(reqValidator.DeleteIdentityV1, req); - - const identityMembershipOrg = await IdentityMembershipOrg - .findOne({ - identity: new Types.ObjectId(identityId) - }) - .populate<{ - identity: IIdentity, - customRole: IRole - }>("identity customRole"); - - if (!identityMembershipOrg) throw ResourceNotFoundError({ - message: `Failed to find identity with id ${identityId}` - }); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: identityMembershipOrg.organization - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Identity - ); - - const identityRolePermission = await getOrgRolePermissions( - identityMembershipOrg?.customRole?.slug ?? identityMembershipOrg.role, - identityMembershipOrg.organization.toString() - ); - const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, identityRolePermission); - if (!hasRequiredPrivileges) throw ForbiddenRequestError({ - message: "Failed to delete more privileged identity" - }); - - const identity = await Identity.findByIdAndDelete(identityMembershipOrg.identity); - if (!identity) throw ResourceNotFoundError({ - message: `Identity with id ${identityId} not found` - }); - - await IdentityMembershipOrg.findByIdAndDelete(identityMembershipOrg._id); - - await IdentityMembership.deleteMany({ - identity: identityMembershipOrg.identity - }); - - await IdentityUniversalAuth.deleteMany({ - identity: identityMembershipOrg.identity - }); - - await IdentityUniversalAuthClientSecret.deleteMany({ - identity: identityMembershipOrg.identity - }); - - await IdentityAccessToken.deleteMany({ - identity: identityMembershipOrg.identity - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.DELETE_IDENTITY, - metadata: { - identityId: identity._id.toString() - } - }, - { - organizationId: identityMembershipOrg.organization - } - ); - - return res.status(200).send({ - identity - }); -} - - - - - diff --git a/backend-mongo/src/ee/controllers/v1/index.ts b/backend-mongo/src/ee/controllers/v1/index.ts deleted file mode 100644 index 0d17e5a06..000000000 --- a/backend-mongo/src/ee/controllers/v1/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import * as identitiesController from "./identitiesController"; -import * as secretController from "./secretController"; -import * as secretSnapshotController from "./secretSnapshotController"; -import * as organizationsController from "./organizationsController"; -import * as ssoController from "./ssoController"; -import * as usersController from "./usersController"; -import * as workspaceController from "./workspaceController"; -import * as membershipController from "./membershipController"; -import * as cloudProductsController from "./cloudProductsController"; -import * as roleController from "./roleController"; -import * as secretApprovalPolicyController from "./secretApprovalPolicyController"; -import * as secretApprovalRequestController from "./secretApprovalRequestsController"; -import * as secretRotationProviderController from "./secretRotationProviderController"; -import * as secretRotationController from "./secretRotationController"; - -export { - identitiesController, - secretController, - secretSnapshotController, - organizationsController, - ssoController, - usersController, - workspaceController, - membershipController, - cloudProductsController, - roleController, - secretApprovalPolicyController, - secretApprovalRequestController, - secretRotationProviderController, - secretRotationController -}; diff --git a/backend-mongo/src/ee/controllers/v1/membershipController.ts b/backend-mongo/src/ee/controllers/v1/membershipController.ts deleted file mode 100644 index 4d2321a45..000000000 --- a/backend-mongo/src/ee/controllers/v1/membershipController.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Request, Response } from "express"; -import { IUser, Membership, Workspace } from "../../../models"; -import { EventType } from "../../../ee/models"; -import { IMembershipPermission } from "../../../models/membership"; -import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors"; -import { ADMIN, MEMBER } from "../../../variables/organization"; -import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../../variables"; -import _ from "lodash"; -import { EEAuditLogService } from "../../services"; - -export const denyMembershipPermissions = async (req: Request, res: Response) => { - const { membershipId } = req.params; - const { permissions } = req.body; - const sanitizedMembershipPermissions: IMembershipPermission[] = permissions.map((permission: IMembershipPermission) => { - if (!permission.ability || !permission.environmentSlug || ![PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS].includes(permission.ability)) { - throw BadRequestError({ message: "One or more required fields are missing from the request or have incorrect type" }) - } - - return { - environmentSlug: permission.environmentSlug, - ability: permission.ability - } - }) - - const sanitizedMembershipPermissionsUnique = _.uniqWith(sanitizedMembershipPermissions, _.isEqual) - - const membershipToModify = await Membership.findById(membershipId) - if (!membershipToModify) { - throw BadRequestError({ message: "Unable to locate resource" }) - } - - // check if the user making the request is a admin of this project - if (![ADMIN, MEMBER].includes(membershipToModify.role)) { - throw UnauthorizedRequestError() - } - - // check if the requested slugs are indeed a part of this related workspace - const relatedWorkspace = await Workspace.findById(membershipToModify.workspace) - if (!relatedWorkspace) { - throw BadRequestError({ message: "Something went wrong when locating the related workspace" }) - } - - const uniqueEnvironmentSlugs = new Set(_.uniq(_.map(relatedWorkspace.environments, "slug"))); - - sanitizedMembershipPermissionsUnique.forEach(permission => { - if (!uniqueEnvironmentSlugs.has(permission.environmentSlug)) { - throw BadRequestError({ message: "Unknown environment slug reference" }) - } - }) - - // update the permissions - const updatedMembershipWithPermissions = await Membership.findByIdAndUpdate( - { _id: membershipToModify._id }, - { $set: { deniedPermissions: sanitizedMembershipPermissionsUnique } }, - { new: true } - ).populate<{ user: IUser }>("user"); - - if (!updatedMembershipWithPermissions) { - throw BadRequestError({ message: "The resource has been removed before it can be modified" }) - } - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS, - metadata: { - userId: updatedMembershipWithPermissions.user._id.toString(), - email: updatedMembershipWithPermissions.user.email, - deniedPermissions: updatedMembershipWithPermissions.deniedPermissions.map(({ - environmentSlug, - ability - }) => ({ - environmentSlug, - ability - })) - } - }, - { - workspaceId: updatedMembershipWithPermissions.workspace - } - ); - - res.send({ - permissionsDenied: updatedMembershipWithPermissions.deniedPermissions, - }) -} diff --git a/backend-mongo/src/ee/controllers/v1/organizationsController.ts b/backend-mongo/src/ee/controllers/v1/organizationsController.ts deleted file mode 100644 index 2f0d5ec39..000000000 --- a/backend-mongo/src/ee/controllers/v1/organizationsController.ts +++ /dev/null @@ -1,550 +0,0 @@ -import { Types } from "mongoose"; -import { Request, Response } from "express"; -import { getLicenseServerUrl } from "../../../config"; -import { licenseServerKeyRequest } from "../../../config/request"; -import { EELicenseService } from "../../services"; -import { validateRequest } from "../../../helpers/validation"; -import * as reqValidator from "../../../validation/organization"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - getAuthDataOrgPermissions, -} from "../../services/RoleService"; -import { ForbiddenError } from "@casl/ability"; -import { Organization } from "../../../models"; -import { OrganizationNotFoundError } from "../../../utils/errors"; - -export const getOrganizationPlansTable = async (req: Request, res: Response) => { - const { - query: { billingCycle }, - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgPlansTablev1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); - - const { data } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}` - ); - - return res.status(200).send(data); -}; - -/** - * Return the organization current plan's feature set - */ -export const getOrganizationPlan = async (req: Request, res: Response) => { - const { - query: { workspaceId }, - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgPlanv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); - - const plan = await EELicenseService.getPlan( - new Types.ObjectId(organizationId), - new Types.ObjectId(workspaceId) - ); - - return res.status(200).send({ - plan - }); -}; - -/** - * Return checkout url for pro trial - * @param req - * @param res - * @returns - */ -export const startOrganizationTrial = async (req: Request, res: Response) => { - const { - params: { organizationId }, - body: { success_url } - } = await validateRequest(reqValidator.StartOrgTrailv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Billing - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { - data: { url } - } = await licenseServerKeyRequest.post( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/session/trial`, - { - success_url - } - ); - - EELicenseService.delPlan(new Types.ObjectId(organizationId)); - - return res.status(200).send({ - url - }); -}; - -/** - * Return the organization's current plan's billing info - * @param req - * @param res - * @returns - */ -export const getOrganizationPlanBillingInfo = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgPlanBillingInfov1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { data } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/cloud-plan/billing` - ); - - return res.status(200).send(data); -}; - -/** - * Return the organization's current plan's feature table - * @param req - * @param res - * @returns - */ -export const getOrganizationPlanTable = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgPlanTablev1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { data } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/cloud-plan/table` - ); - - return res.status(200).send(data); -}; - -export const getOrganizationBillingDetails = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgBillingDetailsv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { data } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details` - ); - - return res.status(200).send(data); -}; - -export const updateOrganizationBillingDetails = async (req: Request, res: Response) => { - const { - params: { organizationId }, - body: { name, email } - } = await validateRequest(reqValidator.UpdateOrgBillingDetailsv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { data } = await licenseServerKeyRequest.patch( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details`, - { - ...(name ? { name } : {}), - ...(email ? { email } : {}) - } - ); - - return res.status(200).send(data); -}; - -/** - * Return the organization's payment methods on file - */ -export const getOrganizationPmtMethods = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgPmtMethodsv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { - data: { pmtMethods } - } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details/payment-methods` - ); - - return res.status(200).send(pmtMethods); -}; - -/** - * Return URL to add payment method for organization - */ -export const addOrganizationPmtMethod = async (req: Request, res: Response) => { - const { - params: { organizationId }, - body: { success_url, cancel_url } - } = await validateRequest(reqValidator.CreateOrgPmtMethodv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { - data: { url } - } = await licenseServerKeyRequest.post( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details/payment-methods`, - { - success_url, - cancel_url - } - ); - - return res.status(200).send({ - url - }); -}; - -/** - * Delete payment method with id [pmtMethodId] for organization - * @param req - * @param res - * @returns - */ -export const deleteOrganizationPmtMethod = async (req: Request, res: Response) => { - const { - params: { organizationId, pmtMethodId } - } = await validateRequest(reqValidator.DelOrgPmtMethodv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { data } = await licenseServerKeyRequest.delete( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details/payment-methods/${pmtMethodId}` - ); - - return res.status(200).send(data); -}; - -/** - * Return the organization's tax ids on file - */ -export const getOrganizationTaxIds = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgTaxIdsv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { - data: { tax_ids } - } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details/tax-ids` - ); - - return res.status(200).send(tax_ids); -}; - -/** - * Add tax id to organization - */ -export const addOrganizationTaxId = async (req: Request, res: Response) => { - const { - params: { organizationId }, - body: { type, value } - } = await validateRequest(reqValidator.CreateOrgTaxId, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { data } = await licenseServerKeyRequest.post( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details/tax-ids`, - { - type, - value - } - ); - - return res.status(200).send(data); -}; - -/** - * Delete tax id with id [taxId] from organization tax ids on file - * @param req - * @param res - * @returns - */ -export const deleteOrganizationTaxId = async (req: Request, res: Response) => { - const { - params: { organizationId, taxId } - } = await validateRequest(reqValidator.DelOrgTaxIdv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { data } = await licenseServerKeyRequest.delete( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/billing-details/tax-ids/${taxId}` - ); - - return res.status(200).send(data); -}; - -/** - * Return organization's invoices on file - * @param req - * @param res - * @returns - */ -export const getOrganizationInvoices = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgInvoicesv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { - data: { invoices } - } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/invoices` - ); - - return res.status(200).send(invoices); -}; - -/** - * Return organization's licenses on file - * @param req - * @param res - * @returns - */ -export const getOrganizationLicenses = async (req: Request, res: Response) => { - const { - params: { organizationId } - } = await validateRequest(reqValidator.GetOrgLicencesv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); - - const organization = await Organization.findById(organizationId); - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - const { - data: { licenses } - } = await licenseServerKeyRequest.get( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${ - organization.customerId - }/licenses` - ); - - return res.status(200).send(licenses); -}; diff --git a/backend-mongo/src/ee/controllers/v1/roleController.ts b/backend-mongo/src/ee/controllers/v1/roleController.ts deleted file mode 100644 index a4b3035e6..000000000 --- a/backend-mongo/src/ee/controllers/v1/roleController.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { Membership, User } from "../../../models"; -import { - CreateRoleSchema, - DeleteRoleSchema, - GetRoleSchema, - GetUserPermission, - GetUserProjectPermission, - UpdateRoleSchema -} from "../../validation/role"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - adminProjectPermissions, - getAuthDataProjectPermissions, - memberProjectPermissions, - noAccessProjectPermissions, - viewerProjectPermission -} from "../../services/ProjectRoleService"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - adminPermissions, - getAuthDataOrgPermissions, - getUserOrgPermissions, - memberPermissions, - noAccessPermissions -} from "../../services/RoleService"; -import { BadRequestError } from "../../../utils/errors"; -import { Role } from "../../models"; -import { validateRequest } from "../../../helpers/validation"; -import { packRules } from "@casl/ability/extra"; - -export const createRole = async (req: Request, res: Response) => { - const { - body: { workspaceId, name, description, slug, permissions, orgId } - } = await validateRequest(CreateRoleSchema, req); - - const isOrgRole = !workspaceId; // if workspaceid is provided then its a workspace rule - if (isOrgRole) { - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(orgId) - }); - - if (permission.cannot(OrgPermissionActions.Create, OrgPermissionSubjects.Role)) { - throw BadRequestError({ message: "user doesn't have the permission." }); - } - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - if (permission.cannot(ProjectPermissionActions.Create, ProjectPermissionSub.Role)) { - throw BadRequestError({ message: "User doesn't have the permission." }); - } - } - - const existingRole = await Role.findOne({ organization: orgId, workspace: workspaceId, slug }); - if (existingRole) { - throw BadRequestError({ message: "Role already exist" }); - } - - const role = new Role({ - organization: orgId, - workspace: workspaceId, - isOrgRole, - name, - slug, - permissions, - description - }); - await role.save(); - - res.status(200).json({ - message: "Successfully created role", - data: { - role - } - }); -}; - -export const updateRole = async (req: Request, res: Response) => { - const { - params: { id }, - body: { name, description, slug, permissions, workspaceId, orgId } - } = await validateRequest(UpdateRoleSchema, req); - const isOrgRole = !workspaceId; // if workspaceid is provided then its a workspace rule - - if (isOrgRole) { - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(orgId) - }); - if (permission.cannot(OrgPermissionActions.Edit, OrgPermissionSubjects.Role)) { - throw BadRequestError({ message: "User doesn't have the org permission." }); - } - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - if (permission.cannot(ProjectPermissionActions.Edit, ProjectPermissionSub.Role)) { - throw BadRequestError({ message: "User doesn't have the workspace permission." }); - } - } - - if (slug) { - const existingRole = await Role.findOne({ - organization: orgId, - slug, - isOrgRole, - workspace: workspaceId - }); - if (existingRole && existingRole.id !== id) { - throw BadRequestError({ message: "Role already exist" }); - } - } - - const role = await Role.findByIdAndUpdate( - id, - { name, description, slug, permissions }, - { returnDocument: "after" } - ); - - if (!role) { - throw BadRequestError({ message: "Role not found" }); - } - res.status(200).json({ - message: "Successfully updated role", - data: { - role - } - }); -}; - -export const deleteRole = async (req: Request, res: Response) => { - const { - params: { id } - } = await validateRequest(DeleteRoleSchema, req); - - const role = await Role.findById(id); - if (!role) { - throw BadRequestError({ message: "Role not found" }); - } - - const isOrgRole = !role.workspace; - if (isOrgRole) { - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: role.organization - }); - if (permission.cannot(OrgPermissionActions.Delete, OrgPermissionSubjects.Role)) { - throw BadRequestError({ message: "User doesn't have the org permission." }); - } - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: role.workspace - }); - - if (permission.cannot(ProjectPermissionActions.Delete, ProjectPermissionSub.Role)) { - throw BadRequestError({ message: "User doesn't have the workspace permission." }); - } - } - - await Role.findByIdAndDelete(role.id); - - res.status(200).json({ - message: "Successfully deleted role", - data: { - role - } - }); -}; - -export const getRoles = async (req: Request, res: Response) => { - const { - query: { workspaceId, orgId } - } = await validateRequest(GetRoleSchema, req); - - const isOrgRole = !workspaceId; - if (isOrgRole) { - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(orgId) - }); - if (permission.cannot(OrgPermissionActions.Read, OrgPermissionSubjects.Role)) { - throw BadRequestError({ message: "User doesn't have the org permission." }); - } - } else { - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - if (permission.cannot(ProjectPermissionActions.Read, ProjectPermissionSub.Role)) { - throw BadRequestError({ message: "User doesn't have the workspace permission." }); - } - } - - const customRoles = await Role.find({ organization: orgId, isOrgRole, workspace: workspaceId }); - // as this is shared between org and workspace switch the rule set based on it - const roles = [ - { - _id: "admin", - name: "Admin", - slug: "admin", - description: "Complete administration access over the organization", - permissions: isOrgRole ? adminPermissions.rules : adminProjectPermissions.rules - }, - { - _id: "no-access", - name: "No Access", - slug: "no-access", - description: "No access to any resources in the organization", - permissions: isOrgRole ? noAccessPermissions.rules : noAccessProjectPermissions.rules - }, - { - _id: "member", - name: isOrgRole ? "Member" : "Developer", - slug: "member", - description: "Non-administrative role in an organization", - permissions: isOrgRole ? memberPermissions.rules : memberProjectPermissions.rules - }, - // viewer role only for project level - ...(isOrgRole - ? [] - : [ - { - _id: "viewer", - name: "Viewer", - slug: "viewer", - description: "Non-administrative role in an organization", - permissions: viewerProjectPermission.rules - } - ]), - ...customRoles - ]; - - res.status(200).json({ - message: "Successfully fetched role list", - data: { - roles - } - }); -}; - -export const getUserPermissions = async (req: Request, res: Response) => { - const { - params: { orgId } - } = await validateRequest(GetUserPermission, req); - - const { permission, membership } = await getUserOrgPermissions(req.user._id, orgId); - - res.status(200).json({ - data: { - permissions: packRules(permission.rules), - membership - } - }); -}; - -export const getUserWorkspacePermissions = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(GetUserProjectPermission, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - let membership; - if (req.authData.authPayload instanceof User) { - membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }) - } - - res.status(200).json({ - data: { - permissions: packRules(permission.rules), - membership - } - }); -}; diff --git a/backend-mongo/src/ee/controllers/v1/secretApprovalPolicyController.ts b/backend-mongo/src/ee/controllers/v1/secretApprovalPolicyController.ts deleted file mode 100644 index 11f1ed557..000000000 --- a/backend-mongo/src/ee/controllers/v1/secretApprovalPolicyController.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { Types } from "mongoose"; -import { ForbiddenError, subject } from "@casl/ability"; -import { Request, Response } from "express"; -import { nanoid } from "nanoid"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../services/ProjectRoleService"; -import { validateRequest } from "../../../helpers/validation"; -import { SecretApprovalPolicy } from "../../models/secretApprovalPolicy"; -import { getSecretPolicyOfBoard } from "../../services/SecretApprovalService"; -import { BadRequestError } from "../../../utils/errors"; -import * as reqValidator from "../../validation/secretApproval"; - -const ERR_SECRET_APPROVAL_NOT_FOUND = BadRequestError({ message: "secret approval not found" }); - -export const createSecretApprovalPolicy = async (req: Request, res: Response) => { - const { - body: { approvals, secretPath, approvers, environment, workspaceId, name } - } = await validateRequest(reqValidator.CreateSecretApprovalRule, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.SecretApproval - ); - - const secretApproval = new SecretApprovalPolicy({ - workspace: workspaceId, - name: name ?? `${environment}-${nanoid(3)}`, - secretPath, - environment, - approvals, - approvers - }); - await secretApproval.save(); - - return res.send({ - approval: secretApproval - }); -}; - -export const updateSecretApprovalPolicy = async (req: Request, res: Response) => { - const { - body: { approvals, approvers, secretPath, name }, - params: { id } - } = await validateRequest(reqValidator.UpdateSecretApprovalRule, req); - - const secretApproval = await SecretApprovalPolicy.findById(id); - if (!secretApproval) throw ERR_SECRET_APPROVAL_NOT_FOUND; - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: secretApproval.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.SecretApproval - ); - - const updatedDoc = await SecretApprovalPolicy.findByIdAndUpdate(id, { - approvals, - approvers, - name: (name || secretApproval?.name) ?? `${secretApproval.environment}-${nanoid(3)}`, - ...(secretPath === null ? { $unset: { secretPath: 1 } } : { secretPath }) - }); - - return res.send({ - approval: updatedDoc - }); -}; - -export const deleteSecretApprovalPolicy = async (req: Request, res: Response) => { - const { - params: { id } - } = await validateRequest(reqValidator.DeleteSecretApprovalRule, req); - - const secretApproval = await SecretApprovalPolicy.findById(id); - if (!secretApproval) throw ERR_SECRET_APPROVAL_NOT_FOUND; - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: secretApproval.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.SecretApproval - ); - - const deletedDoc = await SecretApprovalPolicy.findByIdAndDelete(id); - - return res.send({ - approval: deletedDoc - }); -}; - -export const getSecretApprovalPolicy = async (req: Request, res: Response) => { - const { - query: { workspaceId } - } = await validateRequest(reqValidator.GetSecretApprovalRuleList, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretApproval - ); - - const doc = await SecretApprovalPolicy.find({ workspace: workspaceId }); - - return res.send({ - approvals: doc - }); -}; - -export const getSecretApprovalPolicyOfBoard = async (req: Request, res: Response) => { - const { - query: { workspaceId, environment, secretPath } - } = await validateRequest(reqValidator.GetSecretApprovalPolicyOfABoard, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - subject(ProjectPermissionSub.Secrets, { secretPath, environment }) - ); - - const secretApprovalPolicy = await getSecretPolicyOfBoard(workspaceId, environment, secretPath); - return res.send({ policy: secretApprovalPolicy }); -}; diff --git a/backend-mongo/src/ee/controllers/v1/secretApprovalRequestsController.ts b/backend-mongo/src/ee/controllers/v1/secretApprovalRequestsController.ts deleted file mode 100644 index 93096e536..000000000 --- a/backend-mongo/src/ee/controllers/v1/secretApprovalRequestsController.ts +++ /dev/null @@ -1,366 +0,0 @@ -import { Request, Response } from "express"; -import { validateRequest } from "../../../helpers/validation"; -import { Folder, Membership, User } from "../../../models"; -import { ApprovalStatus, SecretApprovalRequest } from "../../models/secretApprovalRequest"; -import * as reqValidator from "../../validation/secretApprovalRequest"; -import { getFolderWithPathFromId } from "../../../services/FolderService"; -import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors"; -import { ISecretApprovalPolicy, SecretApprovalPolicy } from "../../models/secretApprovalPolicy"; -import { performSecretApprovalRequestMerge } from "../../services/SecretApprovalService"; -import { Types } from "mongoose"; -import { EEAuditLogService } from "../../services"; -import { EventType } from "../../models"; - -export const getSecretApprovalRequestCount = async (req: Request, res: Response) => { - const { - query: { workspaceId } - } = await validateRequest(reqValidator.getSecretApprovalRequestCount, req); - - if (!(req.authData.authPayload instanceof User)) return; - - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (!membership) throw UnauthorizedRequestError(); - - const approvalRequestCount = await SecretApprovalRequest.aggregate([ - { - $match: { - workspace: new Types.ObjectId(workspaceId) - } - }, - { - $lookup: { - from: SecretApprovalPolicy.collection.name, - localField: "policy", - foreignField: "_id", - as: "policy" - } - }, - { $unwind: "$policy" }, - ...(membership.role !== "admin" - ? [ - { - $match: { - $or: [ - { committer: new Types.ObjectId(membership.id) }, - { "policy.approvers": new Types.ObjectId(membership.id) } - ] - } - } - ] - : []), - { - $group: { - _id: "$status", - count: { $sum: 1 } - } - } - ]); - const openRequests = approvalRequestCount.find(({ _id }) => _id === "open"); - const closedRequests = approvalRequestCount.find(({ _id }) => _id === "close"); - - return res.send({ - approvals: { open: openRequests?.count || 0, closed: closedRequests?.count || 0 } - }); -}; - -export const getSecretApprovalRequests = async (req: Request, res: Response) => { - const { - query: { status, committer, workspaceId, environment, limit, offset } - } = await validateRequest(reqValidator.getSecretApprovalRequests, req); - - if (!(req.authData.authPayload instanceof User)) return; - - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: new Types.ObjectId(workspaceId) - }); - - if (!membership) throw UnauthorizedRequestError(); - - const query = { - workspace: new Types.ObjectId(workspaceId), - environment, - committer: committer ? new Types.ObjectId(committer) : undefined, - status - }; - // to strip of undefined in query we use es6 spread to ignore those fields - Object.entries(query).forEach( - ([key, value]) => value === undefined && delete query[key as keyof typeof query] - ); - const approvalRequests = await SecretApprovalRequest.aggregate([ - { - $match: query - }, - { $sort: { createdAt: -1 } }, - { - $lookup: { - from: SecretApprovalPolicy.collection.name, - localField: "policy", - foreignField: "_id", - as: "policy" - } - }, - { $unwind: "$policy" }, - ...(membership.role !== "admin" - ? [ - { - $match: { - $or: [ - { committer: new Types.ObjectId(membership.id) }, - { "policy.approvers": new Types.ObjectId(membership.id) } - ] - } - } - ] - : []), - { $skip: offset }, - { $limit: limit } - ]); - if (!approvalRequests.length) return res.send({ approvals: [] }); - - const unqiueEnvs = environment ?? { - $in: [...new Set(approvalRequests.map(({ environment }) => environment))] - }; - const approvalRootFolders = await Folder.find({ - workspace: workspaceId, - environment: unqiueEnvs - }).lean(); - - const formatedApprovals = approvalRequests.map((el) => { - let secretPath = "/"; - const folders = approvalRootFolders.find(({ environment }) => environment === el.environment); - if (folders) { - secretPath = getFolderWithPathFromId(folders?.nodes, el.folderId)?.folderPath || "/"; - } - return { ...el, secretPath }; - }); - - return res.send({ - approvals: formatedApprovals - }); -}; - -export const getSecretApprovalRequestDetails = async (req: Request, res: Response) => { - const { - params: { id } - } = await validateRequest(reqValidator.getSecretApprovalRequestDetails, req); - const secretApprovalRequest = await SecretApprovalRequest.findById(id) - .populate<{ policy: ISecretApprovalPolicy }>("policy") - .populate({ - path: "commits.secretVersion", - populate: { - path: "tags" - } - }) - .populate("commits.secret", "version") - .populate("commits.newVersion.tags") - .lean(); - if (!secretApprovalRequest) - throw BadRequestError({ message: "Secret approval request not found" }); - - if (!(req.authData.authPayload instanceof User)) return; - - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: secretApprovalRequest.workspace - }); - - if (!membership) throw UnauthorizedRequestError(); - - // allow to fetch only if its admin or is the committer or approver - if ( - membership.role !== "admin" && - !secretApprovalRequest.committer.equals(membership.id) && - !secretApprovalRequest.policy.approvers.find( - (approverId) => approverId.toString() === membership._id.toString() - ) - ) { - throw UnauthorizedRequestError({ message: "User has no access" }); - } - - let secretPath = "/"; - const approvalRootFolders = await Folder.findOne({ - workspace: secretApprovalRequest.workspace, - environment: secretApprovalRequest.environment - }).lean(); - if (approvalRootFolders) { - secretPath = - getFolderWithPathFromId(approvalRootFolders?.nodes, secretApprovalRequest.folderId) - ?.folderPath || "/"; - } - - return res.send({ - approval: { ...secretApprovalRequest, secretPath } - }); -}; - -export const updateSecretApprovalReviewStatus = async (req: Request, res: Response) => { - const { - body: { status }, - params: { id } - } = await validateRequest(reqValidator.updateSecretApprovalReviewStatus, req); - const secretApprovalRequest = await SecretApprovalRequest.findById(id).populate<{ - policy: ISecretApprovalPolicy; - }>("policy"); - if (!secretApprovalRequest) - throw BadRequestError({ message: "Secret approval request not found" }); - - if (!(req.authData.authPayload instanceof User)) return; - - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: secretApprovalRequest.workspace - }); - - if (!membership) throw UnauthorizedRequestError(); - - if ( - membership.role !== "admin" && - secretApprovalRequest.committer !== membership.id && - !secretApprovalRequest.policy.approvers.find((approverId) => approverId.equals(membership.id)) - ) { - throw UnauthorizedRequestError({ message: "User has no access" }); - } - - const reviewerPos = secretApprovalRequest.reviewers.findIndex( - ({ member }) => member.toString() === membership._id.toString() - ); - if (reviewerPos !== -1) { - secretApprovalRequest.reviewers[reviewerPos].status = status; - } else { - secretApprovalRequest.reviewers.push({ member: membership._id, status }); - } - await secretApprovalRequest.save(); - - return res.send({ status }); -}; - -export const mergeSecretApprovalRequest = async (req: Request, res: Response) => { - const { - params: { id } - } = await validateRequest(reqValidator.mergeSecretApprovalRequest, req); - - const secretApprovalRequest = await SecretApprovalRequest.findById(id).populate<{ - policy: ISecretApprovalPolicy; - }>("policy"); - - if (!secretApprovalRequest) - throw BadRequestError({ message: "Secret approval request not found" }); - - if (!(req.authData.authPayload instanceof User)) return; - - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: secretApprovalRequest.workspace - }); - - if (!membership) throw UnauthorizedRequestError(); - - if ( - membership.role !== "admin" && - secretApprovalRequest.committer !== membership.id && - !secretApprovalRequest.policy.approvers.find((approverId) => approverId.equals(membership.id)) - ) { - throw UnauthorizedRequestError({ message: "User has no access" }); - } - - const reviewers = secretApprovalRequest.reviewers.reduce>( - (prev, curr) => ({ ...prev, [curr.member.toString()]: curr.status }), - {} - ); - const hasMinApproval = - secretApprovalRequest.policy.approvals <= - secretApprovalRequest.policy.approvers.filter( - (approverId) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED - ).length; - - if (!hasMinApproval) throw BadRequestError({ message: "Doesn't have minimum approvals needed" }); - - const approval = await performSecretApprovalRequestMerge( - id, - req.authData, - membership._id.toString() - ); - return res.send({ approval }); -}; - -export const updateSecretApprovalRequestStatus = async (req: Request, res: Response) => { - const { - body: { status }, - params: { id } - } = await validateRequest(reqValidator.updateSecretApprovalRequestStatus, req); - - const secretApprovalRequest = await SecretApprovalRequest.findById(id).populate<{ - policy: ISecretApprovalPolicy; - }>("policy"); - - if (!secretApprovalRequest) - throw BadRequestError({ message: "Secret approval request not found" }); - - if (!(req.authData.authPayload instanceof User)) return; - - const membership = await Membership.findOne({ - user: req.authData.authPayload._id, - workspace: secretApprovalRequest.workspace - }); - - if (!membership) throw UnauthorizedRequestError(); - - if ( - membership.role !== "admin" && - secretApprovalRequest.committer !== membership.id && - !secretApprovalRequest.policy.approvers.find((approverId) => approverId.equals(membership._id)) - ) { - throw UnauthorizedRequestError({ message: "User has no access" }); - } - - if (secretApprovalRequest.hasMerged) - throw BadRequestError({ message: "Approval request has been merged" }); - if (secretApprovalRequest.status === "close" && status === "close") - throw BadRequestError({ message: "Approval request is already closed" }); - if (secretApprovalRequest.status === "open" && status === "open") - throw BadRequestError({ message: "Approval request is already open" }); - - const updatedRequest = await SecretApprovalRequest.findByIdAndUpdate( - id, - { status, statusChangeBy: membership._id }, - { new: true } - ); - - if (status === "close") { - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.SECRET_APPROVAL_CLOSED, - metadata: { - closedBy: membership._id.toString(), - secretApprovalRequestId: id, - secretApprovalRequestSlug: secretApprovalRequest.slug - } - }, - { - workspaceId: secretApprovalRequest.workspace - } - ); - } else { - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.SECRET_APPROVAL_REOPENED, - metadata: { - reopenedBy: membership._id.toString(), - secretApprovalRequestId: id, - secretApprovalRequestSlug: secretApprovalRequest.slug - } - }, - { - workspaceId: secretApprovalRequest.workspace - } - ); - } - return res.send({ approval: updatedRequest }); -}; diff --git a/backend-mongo/src/ee/controllers/v1/secretController.ts b/backend-mongo/src/ee/controllers/v1/secretController.ts deleted file mode 100644 index ec6018929..000000000 --- a/backend-mongo/src/ee/controllers/v1/secretController.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { ForbiddenError, subject } from "@casl/ability"; -import { Request, Response } from "express"; -import { validateRequest } from "../../../helpers/validation"; -import { Folder, Secret } from "../../../models"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../services/ProjectRoleService"; -import { BadRequestError } from "../../../utils/errors"; -import * as reqValidator from "../../../validation"; -import { SecretVersion } from "../../models"; -import { EESecretService } from "../../services"; -import { getFolderWithPathFromId } from "../../../services/FolderService"; - -/** - * Return secret versions for secret with id [secretId] - * @param req - * @param res - */ -export const getSecretVersions = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return secret versions' - #swagger.description = 'Return secret versions' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.parameters['secretId'] = { - "description": "ID of secret", - "required": true, - "type": "string" - } - - #swagger.parameters['offset'] = { - "description": "Number of versions to skip", - "required": false, - "type": "string" - } - - #swagger.parameters['limit'] = { - "description": "Maximum number of versions to return", - "required": false, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - schema: { - "type": "object", - "properties": { - "secretVersions": { - "type": "array", - "items": { - $ref: "#/components/schemas/SecretVersion" - }, - "description": "Secret versions" - } - } - } - } - } - } - */ - const { - params: { secretId }, - query: { offset, limit } - } = await validateRequest(reqValidator.GetSecretVersionsV1, req); - - const secret = await Secret.findById(secretId); - if (!secret) { - throw BadRequestError({ message: "Failed to find secret" }); - } - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: secret.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); - - const secretVersions = await SecretVersion.find({ - secret: secretId - }) - .sort({ createdAt: -1 }) - .skip(offset) - .limit(limit); - - return res.status(200).send({ - secretVersions - }); -}; - -/** - * Roll back secret with id [secretId] to version [version] - * @param req - * @param res - * @returns - */ -export const rollbackSecretVersion = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Roll back secret to a version.' - #swagger.description = 'Roll back secret to a version.' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.parameters['secretId'] = { - "description": "ID of secret", - "required": true, - "type": "string" - } - - #swagger.requestBody = { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "version": { - "type": "integer", - "description": "Version of secret to roll back to" - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - schema: { - "type": "object", - "properties": { - "secret": { - "type": "object", - $ref: "#/components/schemas/Secret", - "description": "Secret rolled back to" - } - } - } - } - } - } - */ - - const { - params: { secretId }, - body: { version } - } = await validateRequest(reqValidator.RollbackSecretVersionV1, req); - - const toBeUpdatedSec = await Secret.findById(secretId); - if (!toBeUpdatedSec) { - throw BadRequestError({ message: "Failed to find secret" }); - } - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: toBeUpdatedSec.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.SecretRollback - ); - - // validate secret version - const oldSecretVersion = await SecretVersion.findOne({ - secret: secretId, - version - }).select("+secretBlindIndex"); - - if (!oldSecretVersion) throw new Error("Failed to find secret version"); - - const { - workspace, - type, - user, - environment, - secretBlindIndex, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - algorithm, - folder, - keyEncoding - } = oldSecretVersion; - - let secretPath = "/"; - const folders = await Folder.findOne({ workspace, environment }); - if (folders) - secretPath = getFolderWithPathFromId(folders.nodes, folder || "root")?.folderPath || "/"; - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - subject(ProjectPermissionSub.Secrets, { environment: toBeUpdatedSec.environment, secretPath }) - ); - - // update secret - const secret = await Secret.findByIdAndUpdate( - secretId, - { - $inc: { - version: 1 - }, - workspace, - type, - user, - environment, - ...(secretBlindIndex ? { secretBlindIndex } : {}), - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - folderId: folder, - algorithm, - keyEncoding - }, - { - new: true - } - ); - - if (!secret) throw new Error("Failed to find and update secret"); - - // add new secret version - await new SecretVersion({ - secret: secretId, - version: secret.version, - workspace, - type, - user, - environment, - isDeleted: false, - ...(secretBlindIndex ? { secretBlindIndex } : {}), - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - folder, - algorithm, - keyEncoding - }).save(); - - // take secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: secret.workspace, - environment, - folderId: folder - }); - - return res.status(200).send({ - secret - }); -}; diff --git a/backend-mongo/src/ee/controllers/v1/secretRotationController.ts b/backend-mongo/src/ee/controllers/v1/secretRotationController.ts deleted file mode 100644 index f2f80ffe6..000000000 --- a/backend-mongo/src/ee/controllers/v1/secretRotationController.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { validateRequest } from "../../../helpers/validation"; -import * as reqValidator from "../../validation/secretRotation"; -import * as secretRotationService from "../../secretRotation/service"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; - -export const createSecretRotation = async (req: Request, res: Response) => { - const { - body: { - provider, - customProvider, - interval, - outputs, - secretPath, - environment, - workspaceId, - inputs - } - } = await validateRequest(reqValidator.createSecretRotationV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.SecretRotation - ); - - const secretRotation = await secretRotationService.createSecretRotation({ - workspaceId, - inputs, - environment, - secretPath, - outputs, - interval, - customProvider, - provider - }); - - return res.send({ secretRotation }); -}; - -export const restartSecretRotations = async (req: Request, res: Response) => { - const { - body: { id } - } = await validateRequest(reqValidator.restartSecretRotationV1, req); - - const doc = await secretRotationService.getSecretRotationById({ id }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: doc.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.SecretRotation - ); - - const secretRotation = await secretRotationService.restartSecretRotation({ id }); - return res.send({ secretRotation }); -}; - -export const deleteSecretRotations = async (req: Request, res: Response) => { - const { - params: { id } - } = await validateRequest(reqValidator.removeSecretRotationV1, req); - - const doc = await secretRotationService.getSecretRotationById({ id }); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: doc.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.SecretRotation - ); - - const secretRotations = await secretRotationService.deleteSecretRotation({ id }); - return res.send({ secretRotations }); -}; - -export const getSecretRotations = async (req: Request, res: Response) => { - const { - query: { workspaceId } - } = await validateRequest(reqValidator.getSecretRotationV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRotation - ); - - const secretRotations = await secretRotationService.getSecretRotationOfWorkspace(workspaceId); - return res.send({ secretRotations }); -}; diff --git a/backend-mongo/src/ee/controllers/v1/secretRotationProviderController.ts b/backend-mongo/src/ee/controllers/v1/secretRotationProviderController.ts deleted file mode 100644 index 5e66c40b2..000000000 --- a/backend-mongo/src/ee/controllers/v1/secretRotationProviderController.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { validateRequest } from "../../../helpers/validation"; -import * as reqValidator from "../../validation/secretRotationProvider"; -import * as secretRotationProviderService from "../../secretRotation/service"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; - -export const getProviderTemplates = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(reqValidator.getSecretRotationProvidersV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRotation - ); - - const rotationProviderList = await secretRotationProviderService.getProviderTemplate({ - workspaceId - }); - - return res.send(rotationProviderList); -}; diff --git a/backend-mongo/src/ee/controllers/v1/secretSnapshotController.ts b/backend-mongo/src/ee/controllers/v1/secretSnapshotController.ts deleted file mode 100644 index 34a1a6ef2..000000000 --- a/backend-mongo/src/ee/controllers/v1/secretSnapshotController.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { ForbiddenError } from "@casl/ability"; -import { Request, Response } from "express"; -import { validateRequest } from "../../../helpers/validation"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../services/ProjectRoleService"; -import * as reqValidator from "../../../validation/secretSnapshot"; -import { ISecretVersion, SecretSnapshot, TFolderRootVersionSchema } from "../../models"; - -/** - * Return secret snapshot with id [secretSnapshotId] - * @param req - * @param res - * @returns - */ -export const getSecretSnapshot = async (req: Request, res: Response) => { - const { - params: { secretSnapshotId } - } = await validateRequest(reqValidator.GetSecretSnapshotV1, req); - - const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId) - .lean() - .populate<{ secretVersions: ISecretVersion[] }>({ - path: "secretVersions", - populate: { - path: "tags", - model: "Tag" - } - }) - .populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion"); - - if (!secretSnapshot) throw new Error("Failed to find secret snapshot"); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: secretSnapshot.workspace - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); - - const folderId = secretSnapshot.folderId; - // to show only the folder required secrets - secretSnapshot.secretVersions = secretSnapshot.secretVersions.filter( - ({ folder }) => folder === folderId - ); - - secretSnapshot.folderVersion = secretSnapshot?.folderVersion?.nodes?.children?.map( - ({ id, name }) => ({ - id, - name - }) - ) as any; - - return res.status(200).send({ - secretSnapshot - }); -}; diff --git a/backend-mongo/src/ee/controllers/v1/ssoController.ts b/backend-mongo/src/ee/controllers/v1/ssoController.ts deleted file mode 100644 index 29ed9c18e..000000000 --- a/backend-mongo/src/ee/controllers/v1/ssoController.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { BotOrgService } from "../../../services"; -import { SSOConfig } from "../../models"; -import { AuthMethod, MembershipOrg, User } from "../../../models"; -import { getSSOConfigHelper } from "../../helpers/organizations"; -import { client } from "../../../config"; -import { ResourceNotFoundError } from "../../../utils/errors"; -import { getSiteURL } from "../../../config"; -import { EELicenseService } from "../../services"; -import * as reqValidator from "../../../validation/sso"; -import { validateRequest } from "../../../helpers/validation"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - getAuthDataOrgPermissions -} from "../../services/RoleService"; -import { ForbiddenError } from "@casl/ability"; - -/** - * Redirect user to appropriate SSO endpoint after successful authentication - * to finish inputting their master key for logging in or signing up - * @param req - * @param res - * @returns - */ -export const redirectSSO = async (req: Request, res: Response) => { - if (req.isUserCompleted) { - return res.redirect( - `${await getSiteURL()}/login/sso?token=${encodeURIComponent(req.providerAuthToken)}` - ); - } - - return res.redirect( - `${await getSiteURL()}/signup/sso?token=${encodeURIComponent(req.providerAuthToken)}` - ); -}; - -/** - * Return organization SAML SSO configuration - * @param req - * @param res - * @returns - */ -export const getSSOConfig = async (req: Request, res: Response) => { - const { - query: { organizationId } - } = await validateRequest(reqValidator.GetSsoConfigv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Sso - ); - - const data = await getSSOConfigHelper({ - organizationId: new Types.ObjectId(organizationId) - }); - - return res.status(200).send(data); -}; - -/** - * Update organization SAML SSO configuration - * @param req - * @param res - * @returns - */ -export const updateSSOConfig = async (req: Request, res: Response) => { - const { - body: { organizationId, authProvider, isActive, entryPoint, issuer, cert } - } = await validateRequest(reqValidator.UpdateSsoConfigv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Sso - ); - - const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId)); - - if (!plan.samlSSO) - return res.status(400).send({ - message: - "Failed to update SAML SSO configuration due to plan restriction. Upgrade plan to update SSO configuration." - }); - - interface PatchUpdate { - authProvider?: string; - isActive?: boolean; - encryptedEntryPoint?: string; - entryPointIV?: string; - entryPointTag?: string; - encryptedIssuer?: string; - issuerIV?: string; - issuerTag?: string; - encryptedCert?: string; - certIV?: string; - certTag?: string; - } - - const update: PatchUpdate = {}; - - if (authProvider) { - update.authProvider = authProvider; - } - - if (isActive !== undefined) { - update.isActive = isActive; - } - - const key = await BotOrgService.getSymmetricKey(new Types.ObjectId(organizationId)); - - if (entryPoint) { - const { - ciphertext: encryptedEntryPoint, - iv: entryPointIV, - tag: entryPointTag - } = client.encryptSymmetric(entryPoint, key); - - update.encryptedEntryPoint = encryptedEntryPoint; - update.entryPointIV = entryPointIV; - update.entryPointTag = entryPointTag; - } - - if (issuer) { - const { - ciphertext: encryptedIssuer, - iv: issuerIV, - tag: issuerTag - } = client.encryptSymmetric(issuer, key); - - update.encryptedIssuer = encryptedIssuer; - update.issuerIV = issuerIV; - update.issuerTag = issuerTag; - } - - if (cert) { - const { - ciphertext: encryptedCert, - iv: certIV, - tag: certTag - } = client.encryptSymmetric(cert, key); - - update.encryptedCert = encryptedCert; - update.certIV = certIV; - update.certTag = certTag; - } - - const ssoConfig = await SSOConfig.findOneAndUpdate( - { - organization: new Types.ObjectId(organizationId) - }, - update, - { - new: true - } - ); - - if (!ssoConfig) - throw ResourceNotFoundError({ - message: "Failed to find SSO config to update" - }); - - if (update.isActive !== undefined) { - const membershipOrgs = await MembershipOrg.find({ - organization: new Types.ObjectId(organizationId) - }).select("user"); - - if (update.isActive) { - await User.updateMany( - { - _id: { - $in: membershipOrgs.map((membershipOrg) => membershipOrg.user) - } - }, - { - authMethods: [ssoConfig.authProvider] - } - ); - } else { - await User.updateMany( - { - _id: { - $in: membershipOrgs.map((membershipOrg) => membershipOrg.user) - } - }, - { - authMethods: [AuthMethod.EMAIL] - } - ); - } - } - - return res.status(200).send(ssoConfig); -}; - -/** - * Create organization SAML SSO configuration - * @param req - * @param res - * @returns - */ -export const createSSOConfig = async (req: Request, res: Response) => { - const { - body: { organizationId, authProvider, isActive, entryPoint, issuer, cert } - } = await validateRequest(reqValidator.CreateSsoConfigv1, req); - - const { permission } = await getAuthDataOrgPermissions({ - authData: req.authData, - organizationId: new Types.ObjectId(organizationId) - }); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Sso - ); - - const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId)); - - if (!plan.samlSSO) - return res.status(400).send({ - message: - "Failed to create SAML SSO configuration due to plan restriction. Upgrade plan to add SSO configuration." - }); - - const key = await BotOrgService.getSymmetricKey(new Types.ObjectId(organizationId)); - - const { - ciphertext: encryptedEntryPoint, - iv: entryPointIV, - tag: entryPointTag - } = client.encryptSymmetric(entryPoint, key); - - const { - ciphertext: encryptedIssuer, - iv: issuerIV, - tag: issuerTag - } = client.encryptSymmetric(issuer, key); - - const { - ciphertext: encryptedCert, - iv: certIV, - tag: certTag - } = client.encryptSymmetric(cert, key); - - const ssoConfig = await new SSOConfig({ - organization: new Types.ObjectId(organizationId), - authProvider, - isActive, - encryptedEntryPoint, - entryPointIV, - entryPointTag, - encryptedIssuer, - issuerIV, - issuerTag, - encryptedCert, - certIV, - certTag - }).save(); - - return res.status(200).send(ssoConfig); -}; diff --git a/backend-mongo/src/ee/controllers/v1/usersController.ts b/backend-mongo/src/ee/controllers/v1/usersController.ts deleted file mode 100644 index a492404f5..000000000 --- a/backend-mongo/src/ee/controllers/v1/usersController.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Request, Response } from "express"; - -/** - * Return the ip address of the current user - * @param req - * @param res - * @returns - */ -export const getMyIp = (req: Request, res: Response) => { - return res.status(200).send({ - ip: req.authData.ipAddress - }); -} \ No newline at end of file diff --git a/backend-mongo/src/ee/controllers/v1/workspaceController.ts b/backend-mongo/src/ee/controllers/v1/workspaceController.ts deleted file mode 100644 index 70d612792..000000000 --- a/backend-mongo/src/ee/controllers/v1/workspaceController.ts +++ /dev/null @@ -1,1079 +0,0 @@ -import { Request, Response } from "express"; -import { PipelineStage, Types } from "mongoose"; -import { - Folder, - Identity, - IdentityMembership, - Membership, - Secret, - ServiceTokenData, - TFolderSchema, - User, - Workspace -} from "../../../models"; -import { - ActorType, - AuditLog, - EventType, - FolderVersion, - IPType, - ISecretVersion, - IdentityActor, - SecretSnapshot, - SecretVersion, - ServiceActor, - TFolderRootVersionSchema, - TrustedIP, - UserActor -} from "../../models"; -import { EESecretService } from "../../services"; -import { getLatestSecretVersionIds } from "../../helpers/secretVersion"; -import { getFolderByPath, searchByFolderId } from "../../../services/FolderService"; -import { EEAuditLogService, EELicenseService } from "../../services"; -import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip"; -import { validateRequest } from "../../../helpers/validation"; -import { - AddWorkspaceTrustedIpV1, - DeleteWorkspaceTrustedIpV1, - GetWorkspaceAuditLogActorFilterOptsV1, - GetWorkspaceAuditLogsV1, - GetWorkspaceSecretSnapshotsCountV1, - GetWorkspaceSecretSnapshotsV1, - GetWorkspaceTrustedIpsV1, - RollbackWorkspaceSecretSnapshotV1, - UpdateWorkspaceTrustedIpV1 -} from "../../../validation"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - getAuthDataProjectPermissions -} from "../../services/ProjectRoleService"; -import { ForbiddenError } from "@casl/ability"; -import { BadRequestError } from "../../../utils/errors"; - -/** - * Return secret snapshots for workspace with id [workspaceId] - * @param req - * @param res - */ -export const getWorkspaceSecretSnapshots = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return project secret snapshot ids' - #swagger.description = 'Return project secret snapshots ids' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project where to get secret snapshots for", - "required": true, - "type": "string" - } - - #swagger.parameters['environment'] = { - "description": "Slug of environment where to get secret snapshots for", - "required": true, - "type": "string", - "in": "query" - } - - #swagger.parameters['directory'] = { - "description": "Path where to get secret snapshots for like / or /foo/bar. Default is /", - "required": false, - "type": "string", - "in": "query" - } - - #swagger.parameters['offset'] = { - "description": "Number of secret snapshots to skip", - "required": false, - "type": "string" - } - - #swagger.parameters['limit'] = { - "description": "Maximum number of secret snapshots to return", - "required": false, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - schema: { - "type": "object", - "properties": { - "secretSnapshots": { - "type": "array", - "items": { - $ref: "#/components/schemas/SecretSnapshot" - }, - "description": "Project secret snapshots" - } - } - } - } - } - } - */ - const { - params: { workspaceId }, - query: { environment, directory, offset, limit } - } = await validateRequest(GetWorkspaceSecretSnapshotsV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); - - let folderId = "root"; - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - if (!folders && directory !== "/") throw BadRequestError({ message: "Folder not found" }); - - if (folders) { - const folder = getFolderByPath(folders?.nodes, directory); - if (!folder) throw BadRequestError({ message: "Invalid folder id" }); - folderId = folder.id; - } - - const secretSnapshots = await SecretSnapshot.find({ - workspace: workspaceId, - environment, - folderId - }) - .sort({ createdAt: -1 }) - .skip(offset) - .limit(limit); - - return res.status(200).send({ - secretSnapshots - }); -}; - -/** - * Return count of secret snapshots for workspace with id [workspaceId] - * @param req - * @param res - */ -export const getWorkspaceSecretSnapshotsCount = async (req: Request, res: Response) => { - const { - params: { workspaceId }, - query: { environment, directory } - } = await validateRequest(GetWorkspaceSecretSnapshotsCountV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); - - let folderId = "root"; - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - if (!folders && directory !== "/") throw BadRequestError({ message: "Folder not found" }); - - if (folders) { - const folder = getFolderByPath(folders?.nodes, directory); - if (!folder) throw BadRequestError({ message: "Invalid folder id" }); - folderId = folder.id; - } - - const count = await SecretSnapshot.countDocuments({ - workspace: workspaceId, - environment, - folderId - }); - - return res.status(200).send({ - count - }); -}; - -/** - * Rollback secret snapshot with id [secretSnapshotId] to version [version] - * @param req - * @param res - * @returns - */ -export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Roll back project secrets to those captured in a secret snapshot version.' - #swagger.description = 'Roll back project secrets to those captured in a secret snapshot version.' - - #swagger.security = [{ - "apiKeyAuth": [], - "bearerAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of project where to roll back", - "required": true, - "type": "string" - } - - #swagger.requestBody = { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "environment": { - "type": "string", - "description": "Slug of environment where to roll back" - }, - "directory": { - "type": "string", - "description": "Path where to roll back for like / or /foo/bar. Default is /" - }, - "version": { - "type": "integer", - "description": "Version of secret snapshot to roll back to", - } - } - } - } - } - } - - #swagger.responses[200] = { - content: { - "application/json": { - schema: { - "type": "object", - "properties": { - "secrets": { - "type": "array", - "items": { - $ref: "#/components/schemas/Secret" - }, - "description": "Secrets rolled back to" - } - } - } - } - } - } - */ - - const { - params: { workspaceId }, - body: { directory, environment, version } - } = await validateRequest(RollbackWorkspaceSecretSnapshotV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.SecretRollback - ); - - let folderId = "root"; - const folders = await Folder.findOne({ workspace: workspaceId, environment }); - if (!folders && directory !== "/") throw BadRequestError({ message: "Folder not found" }); - - if (folders) { - const folder = getFolderByPath(folders?.nodes, directory); - if (!folder) throw BadRequestError({ message: "Invalid folder id" }); - folderId = folder.id; - } - - // validate secret snapshot - const secretSnapshot = await SecretSnapshot.findOne({ - workspace: workspaceId, - version, - environment, - folderId: folderId - }) - .populate<{ secretVersions: ISecretVersion[] }>({ - path: "secretVersions", - select: "+secretBlindIndex" - }) - .populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion"); - - if (!secretSnapshot) throw new Error("Failed to find secret snapshot"); - - const snapshotFolderTree = secretSnapshot.folderVersion; - const latestFolderTree = await Folder.findOne({ - workspace: workspaceId, - environment - }); - - const latestFolderVersion = await FolderVersion.findOne({ - environment, - workspace: workspaceId, - "nodes.id": folderId - }).sort({ "nodes.version": -1 }); - - const oldSecretVersionsObj: Record = {}; - const secretIds: Types.ObjectId[] = []; - const folderIds: string[] = [folderId]; - - secretSnapshot.secretVersions.forEach((snapSecVer) => { - oldSecretVersionsObj[snapSecVer.secret.toString()] = snapSecVer; - secretIds.push(snapSecVer.secret); - }); - - // the parent node from current latest one - // this will be modified according to the snapshot and latest snapshots - const newFolderTree = latestFolderTree && searchByFolderId(latestFolderTree.nodes, folderId); - - if (newFolderTree) { - newFolderTree.children = snapshotFolderTree?.nodes?.children || []; - const queue = [newFolderTree]; - // a bfs algorithm in which we take the latest snapshots of all the folders in a level - while (queue.length) { - const groupByFolderId: Record = {}; - // the original queue is popped out completely to get what ever in a level - // subqueue is filled with all the children thus next level folders - // subQueue will then be transfered to the oriinal queue - const subQueue: TFolderSchema[] = []; - // get everything inside a level - while (queue.length) { - const folder = queue.pop() as TFolderSchema; - folder.children.forEach((el) => { - folderIds.push(el.id); // push ids and data into queu - subQueue.push(el); - // to modify the original tree very fast we keep a reference object - // key with folder id and pointing to the various nodes - groupByFolderId[el.id] = el; - }); - } - // get latest snapshots of all the folder - const matchWsFoldersPipeline = { - $match: { - workspace: new Types.ObjectId(workspaceId), - environment, - folderId: { - $in: Object.keys(groupByFolderId) - } - } - }; - const sortByFolderIdAndVersion: PipelineStage = { - $sort: { folderId: 1, version: -1 } - }; - const pickLatestVersionOfEachFolder = { - $group: { - _id: "$folderId", - latestVersion: { $first: "$version" }, - doc: { - $first: "$$ROOT" - } - } - }; - const populateSecVersion = { - $lookup: { - from: SecretVersion.collection.name, - localField: "doc.secretVersions", - foreignField: "_id", - as: "doc.secretVersions" - } - }; - const populateFolderVersion = { - $lookup: { - from: FolderVersion.collection.name, - localField: "doc.folderVersion", - foreignField: "_id", - as: "doc.folderVersion" - } - }; - const unwindFolderVerField = { - $unwind: { - path: "$doc.folderVersion", - preserveNullAndEmptyArrays: true - } - }; - const latestSnapshotsByFolders: Array<{ doc: typeof secretSnapshot }> = - await SecretSnapshot.aggregate([ - matchWsFoldersPipeline, - sortByFolderIdAndVersion, - pickLatestVersionOfEachFolder, - populateSecVersion, - populateFolderVersion, - unwindFolderVerField - ]); - - // recursive snapshotting each level - latestSnapshotsByFolders.forEach((snap) => { - // mutate the folder tree to update the nodes to the latest version tree - // we are reconstructing the folder tree by latest snapshots here - if (groupByFolderId[snap.doc.folderId]) { - groupByFolderId[snap.doc.folderId].children = - snap.doc?.folderVersion?.nodes?.children || []; - } - - // push all children of next level snapshots - if (snap.doc.folderVersion?.nodes?.children) { - queue.push(...snap.doc.folderVersion.nodes.children); - } - - snap.doc.secretVersions.forEach((snapSecVer) => { - // record all the secrets - oldSecretVersionsObj[snapSecVer.secret.toString()] = snapSecVer; - secretIds.push(snapSecVer.secret); - }); - }); - - queue.push(...subQueue); - } - } - - // TODO: fix any - const latestSecretVersionIds = await getLatestSecretVersionIds({ - secretIds - }); - - // TODO: fix any - const latestSecretVersions: any = ( - await SecretVersion.find( - { - _id: { - $in: latestSecretVersionIds.map((s) => s.versionId) - } - }, - "secret version" - ) - ).reduce( - (accumulator, s) => ({ - ...accumulator, - [`${s.secret.toString()}`]: s - }), - {} - ); - - const secDelQuery: Record = { - workspace: workspaceId, - environment - // undefined means root thus collect all secrets - }; - if (folderId !== "root" && folderIds.length) secDelQuery.folder = { $in: folderIds }; - - // delete existing secrets - await Secret.deleteMany(secDelQuery); - await Folder.deleteOne({ - workspace: workspaceId, - environment - }); - - // add secrets - const secrets = await Secret.insertMany( - Object.keys(oldSecretVersionsObj).map((sv) => { - const { - secret: secretId, - workspace, - type, - user, - environment, - secretBlindIndex, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - createdAt, - algorithm, - keyEncoding, - folder: secFolderId - } = oldSecretVersionsObj[sv]; - - return { - _id: secretId, - version: latestSecretVersions[secretId.toString()].version + 1, - workspace, - type, - user, - environment, - secretBlindIndex: secretBlindIndex ?? undefined, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext: "", - secretCommentIV: "", - secretCommentTag: "", - createdAt, - algorithm, - keyEncoding, - folder: secFolderId - }; - }) - ); - - // add secret versions - const secretV = await SecretVersion.insertMany( - secrets.map( - ({ - _id, - version, - workspace, - type, - user, - environment, - secretBlindIndex, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - algorithm, - keyEncoding, - folder: secFolderId - }) => ({ - _id: new Types.ObjectId(), - secret: _id, - version, - workspace, - type, - user, - environment, - isDeleted: false, - secretBlindIndex: secretBlindIndex ?? undefined, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - algorithm, - keyEncoding, - folder: secFolderId - }) - ) - ); - - if (newFolderTree && latestFolderTree) { - // save the updated folder tree to the present one - newFolderTree.version = (latestFolderVersion?.nodes?.version || 0) + 1; - latestFolderTree._id = new Types.ObjectId(); - latestFolderTree.isNew = true; - await latestFolderTree.save(); - - // create new folder version - const newFolderVersion = new FolderVersion({ - workspace: workspaceId, - environment, - nodes: newFolderTree - }); - await newFolderVersion.save(); - } - - // update secret versions of restored secrets as not deleted - await SecretVersion.updateMany( - { - secret: { - $in: Object.keys(oldSecretVersionsObj).map((sv) => oldSecretVersionsObj[sv].secret) - } - }, - { - isDeleted: false - } - ); - - // take secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - folderId - }); - - return res.status(200).send({ - secrets - }); -}; - -/** - * Return audit logs for workspace with id [workspaceId] - * @param req - * @param res - */ -export const getWorkspaceAuditLogs = async (req: Request, res: Response) => { - /* - #swagger.summary = 'Return audit logs' - #swagger.description = 'Return audit logs' - - #swagger.security = [{ - "apiKeyAuth": [] - }] - - #swagger.parameters['workspaceId'] = { - "description": "ID of the workspace where to get folders from", - "required": true, - "type": "string", - "in": "path" - } - - #swagger.parameters['offset'] = { - "description": "Number of logs to skip before starting to return logs for pagination", - "required": false, - "type": "string" - } - - #swagger.parameters['limit'] = { - "description": "Maximum number of logs to return for pagination", - "required": false, - "type": "string" - } - - #swagger.parameters['startDate'] = { - "description": "Filter logs from this date in ISO-8601 format", - "required": false, - "type": "string" - } - - #swagger.parameters['endDate'] = { - "description": "Filter logs till this date in ISO-8601 format", - "required": false, - "type": "string" - } - - #swagger.parameters['eventType'] = { - "description": "Filter by type of event such as get-secrets, get-secret, create-secret, update-secret, delete-secret, etc.", - "required": false, - "type": "string", - } - - #swagger.parameters['userAgentType'] = { - "description": "Filter by type of user agent such as web, cli, k8-operator, or other", - "required": false, - "type": "string", - } - - #swagger.parameters['actor'] = { - "description": "Filter by actor such as user or service", - "required": false, - "type": "string" - } - - #swagger.responses[200] = { - content: { - "application/json": { - schema: { - "type": "object", - "properties": { - "auditLogs": { - "type": "array", - "items": { - $ref: "#/components/schemas/AuditLog", - }, - "description": "List of audit log" - }, - } - } - } - } - } - */ - const { - query: { limit, offset, endDate, eventType, startDate, userAgentType, actor }, - params: { workspaceId } - } = await validateRequest(GetWorkspaceAuditLogsV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.AuditLogs - ); - - let actorMetadataQuery = ""; - if (actor) { - switch (actor?.split("-", 2)[0]) { - case ActorType.USER: - actorMetadataQuery = "actor.metadata.userId"; - break; - case ActorType.SERVICE: - actorMetadataQuery = "actor.metadata.serviceId"; - break; - case ActorType.IDENTITY: - actorMetadataQuery = "actor.metadata.identityId"; - break; - } - } - - const query = { - workspace: new Types.ObjectId(workspaceId), - ...(eventType - ? { - "event.type": eventType - } - : {}), - ...(userAgentType - ? { - userAgentType - } - : {}), - ...(actor - ? { - "actor.type": actor.substring(0, actor.lastIndexOf("-")), - ...({ - [actorMetadataQuery]: actor.substring(actor.lastIndexOf("-") + 1) - }) - } - : {}), - ...(startDate || endDate - ? { - createdAt: { - ...(startDate && { $gte: new Date(startDate) }), - ...(endDate && { $lte: new Date(endDate) }) - } - } - : {}) - }; - - const auditLogs = await AuditLog.find(query).sort({ createdAt: -1 }).skip(offset).limit(limit); - - return res.status(200).send({ - auditLogs - }); -}; - -/** - * Return audit log actor filter options for workspace with id [workspaceId] - * @param req - * @param res - */ -export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(GetWorkspaceAuditLogActorFilterOptsV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.AuditLogs - ); - - const userIds = await Membership.distinct("user", { - workspace: new Types.ObjectId(workspaceId) - }); - - const userActors: UserActor[] = ( - await User.find({ - _id: { - $in: userIds - } - }).select("email") - ).map((user) => ({ - type: ActorType.USER, - metadata: { - userId: user._id.toString(), - email: user.email - } - })); - - const serviceActors: ServiceActor[] = ( - await ServiceTokenData.find({ - workspace: new Types.ObjectId(workspaceId) - }).select("name") - ).map((serviceTokenData) => ({ - type: ActorType.SERVICE, - metadata: { - serviceId: serviceTokenData._id.toString(), - name: serviceTokenData.name - } - })); - - const identityIds = await IdentityMembership.distinct("identity", { - workspace: new Types.ObjectId(workspaceId) - }); - - const identityActors: IdentityActor[] = ( - await Identity.find({ - _id: { - $in: identityIds - } - }) - ).map((identity) => ({ - type: ActorType.IDENTITY, - metadata: { - identityId: identity._id.toString(), - name: identity.name - } - })); - - const actors = [...userActors, ...serviceActors, ...identityActors]; - - return res.status(200).send({ - actors - }); -}; - -/** - * Return trusted ips for workspace with id [workspaceId] - * @param req - * @param res - */ -export const getWorkspaceTrustedIps = async (req: Request, res: Response) => { - const { - params: { workspaceId } - } = await validateRequest(GetWorkspaceTrustedIpsV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.IpAllowList - ); - - const trustedIps = await TrustedIP.find({ - workspace: new Types.ObjectId(workspaceId) - }); - - return res.status(200).send({ - trustedIps - }); -}; - -/** - * Add a trusted ip to workspace with id [workspaceId] - * @param req - * @param res - */ -export const addWorkspaceTrustedIp = async (req: Request, res: Response) => { - const { - params: { workspaceId }, - body: { comment, isActive, ipAddress: ip } - } = await validateRequest(AddWorkspaceTrustedIpV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.IpAllowList - ); - - const workspace = await Workspace.findById(workspaceId); - if (!workspace) throw BadRequestError({ message: "Workspace not found" }); - - const plan = await EELicenseService.getPlan(workspace.organization); - - if (!plan.ipAllowlisting) - return res.status(400).send({ - message: - "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." - }); - - const isValidIPOrCidr = isValidIpOrCidr(ip); - - if (!isValidIPOrCidr) - return res.status(400).send({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - - const { ipAddress, type, prefix } = extractIPDetails(ip); - - const trustedIp = await new TrustedIP({ - workspace: new Types.ObjectId(workspaceId), - ipAddress, - type, - prefix, - isActive, - comment - }).save(); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.ADD_TRUSTED_IP, - metadata: { - trustedIpId: trustedIp._id.toString(), - ipAddress: trustedIp.ipAddress, - prefix: trustedIp.prefix - } - }, - { - workspaceId: trustedIp.workspace - } - ); - - return res.status(200).send({ - trustedIp - }); -}; - -/** - * Update trusted ip with id [trustedIpId] workspace with id [workspaceId] - * @param req - * @param res - */ -export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => { - const { - params: { workspaceId, trustedIpId }, - body: { ipAddress: ip, comment } - } = await validateRequest(UpdateWorkspaceTrustedIpV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.IpAllowList - ); - - const workspace = await Workspace.findById(workspaceId); - if (!workspace) throw BadRequestError({ message: "Workspace not found" }); - - const plan = await EELicenseService.getPlan(workspace.organization); - - if (!plan.ipAllowlisting) - return res.status(400).send({ - message: - "Failed to update IP access range due to plan restriction. Upgrade plan to update IP access range." - }); - - const isValidIPOrCidr = isValidIpOrCidr(ip); - - if (!isValidIPOrCidr) - return res.status(400).send({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - - const { ipAddress, type, prefix } = extractIPDetails(ip); - - const updateObject: { - ipAddress: string; - type: IPType; - comment: string; - prefix?: number; - $unset?: { - prefix: number; - }; - } = { - ipAddress, - type, - comment - }; - - if (prefix !== undefined) { - updateObject.prefix = prefix; - } else { - updateObject.$unset = { prefix: 1 }; - } - - const trustedIp = await TrustedIP.findOneAndUpdate( - { - _id: new Types.ObjectId(trustedIpId), - workspace: new Types.ObjectId(workspaceId) - }, - updateObject, - { - new: true - } - ); - - if (!trustedIp) - return res.status(400).send({ - message: "Failed to update trusted IP" - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.UPDATE_TRUSTED_IP, - metadata: { - trustedIpId: trustedIp._id.toString(), - ipAddress: trustedIp.ipAddress, - prefix: trustedIp.prefix - } - }, - { - workspaceId: trustedIp.workspace - } - ); - - return res.status(200).send({ - trustedIp - }); -}; - -/** - * Delete IP access range from workspace with id [workspaceId] - * @param req - * @param res - */ -export const deleteWorkspaceTrustedIp = async (req: Request, res: Response) => { - const { - params: { workspaceId, trustedIpId } - } = await validateRequest(DeleteWorkspaceTrustedIpV1, req); - - const { permission } = await getAuthDataProjectPermissions({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId) - }); - - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.IpAllowList - ); - - const workspace = await Workspace.findById(workspaceId); - if (!workspace) throw BadRequestError({ message: "Workspace not found" }); - - const plan = await EELicenseService.getPlan(workspace.organization); - - if (!plan.ipAllowlisting) - return res.status(400).send({ - message: - "Failed to delete IP access range due to plan restriction. Upgrade plan to delete IP access range." - }); - - const trustedIp = await TrustedIP.findOneAndDelete({ - _id: new Types.ObjectId(trustedIpId), - workspace: new Types.ObjectId(workspaceId) - }); - - if (!trustedIp) - return res.status(400).send({ - message: "Failed to delete trusted IP" - }); - - await EEAuditLogService.createAuditLog( - req.authData, - { - type: EventType.DELETE_TRUSTED_IP, - metadata: { - trustedIpId: trustedIp._id.toString(), - ipAddress: trustedIp.ipAddress, - prefix: trustedIp.prefix - } - }, - { - workspaceId: trustedIp.workspace - } - ); - - return res.status(200).send({ - trustedIp - }); -}; diff --git a/backend-mongo/src/ee/controllers/v3/apiKeyDataController.ts b/backend-mongo/src/ee/controllers/v3/apiKeyDataController.ts deleted file mode 100644 index 1fe7d6d3b..000000000 --- a/backend-mongo/src/ee/controllers/v3/apiKeyDataController.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Request, Response } from "express"; -import { Types } from "mongoose"; -import { APIKeyDataV2 } from "../../../models/apiKeyDataV2"; -import { validateRequest } from "../../../helpers/validation"; -import { BadRequestError } from "../../../utils/errors"; -import * as reqValidator from "../../../validation"; -import { createToken } from "../../../helpers"; -import { AuthTokenType } from "../../../variables"; -import { getAuthSecret } from "../../../config"; - -/** - * Create API key data v2 - * @param req - * @param res - */ -export const createAPIKeyData = async (req: Request, res: Response) => { - const { - body: { - name - } - } = await validateRequest(reqValidator.CreateAPIKeyV3, req); - - const apiKeyData = await new APIKeyDataV2({ - name, - user: req.user._id, - usageCount: 0, - }).save(); - - const apiKey = createToken({ - payload: { - authTokenType: AuthTokenType.API_KEY, - apiKeyDataId: apiKeyData._id.toString(), - userId: req.user._id.toString() - }, - secret: await getAuthSecret() - }); - - return res.status(200).send({ - apiKeyData, - apiKey - }); -} - -/** - * Update API key data v2 with id [apiKeyDataId] - * @param req - * @param res - */ - export const updateAPIKeyData = async (req: Request, res: Response) => { - const { - params: { apiKeyDataId }, - body: { - name, - } - } = await validateRequest(reqValidator.UpdateAPIKeyV3, req); - - const apiKeyData = await APIKeyDataV2.findOneAndUpdate( - { - _id: new Types.ObjectId(apiKeyDataId), - user: req.user._id - }, - { - name - }, - { - new: true - } - ); - - if (!apiKeyData) throw BadRequestError({ - message: "Failed to update API key" - }); - - return res.status(200).send({ - apiKeyData - }); -} - -/** - * Delete API key data v2 with id [apiKeyDataId] - * @param req - * @param res - */ - export const deleteAPIKeyData = async (req: Request, res: Response) => { - const { - params: { apiKeyDataId } - } = await validateRequest(reqValidator.DeleteAPIKeyV3, req); - - const apiKeyData = await APIKeyDataV2.findOneAndDelete({ - _id: new Types.ObjectId(apiKeyDataId), - user: req.user._id - }); - - if (!apiKeyData) throw BadRequestError({ - message: "Failed to delete API key" - }); - - return res.status(200).send({ - apiKeyData - }); -} \ No newline at end of file diff --git a/backend-mongo/src/ee/controllers/v3/index.ts b/backend-mongo/src/ee/controllers/v3/index.ts deleted file mode 100644 index 2a8f130dd..000000000 --- a/backend-mongo/src/ee/controllers/v3/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as apiKeyDataController from "./apiKeyDataController"; - -export { - apiKeyDataController -} \ No newline at end of file diff --git a/backend-mongo/src/ee/helpers/checkMembershipPermissions.ts b/backend-mongo/src/ee/helpers/checkMembershipPermissions.ts deleted file mode 100644 index 4f6ddc771..000000000 --- a/backend-mongo/src/ee/helpers/checkMembershipPermissions.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Types } from "mongoose"; -import _ from "lodash"; -import { Membership } from "../../models"; -import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables"; - -export const userHasWorkspaceAccess = async (userId: Types.ObjectId, workspaceId: Types.ObjectId, environment: string, action: any) => { - const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId }) - if (!membershipForWorkspace) { - return false - } - - const deniedMembershipPermissions = membershipForWorkspace.deniedPermissions; - const isDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: action }); - - if (isDisallowed) { - return false - } - - return true -} - -export const userHasWriteOnlyAbility = async (userId: Types.ObjectId, workspaceId: Types.ObjectId, environment: string) => { - const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId }) - if (!membershipForWorkspace) { - return false - } - - const deniedMembershipPermissions = membershipForWorkspace.deniedPermissions; - const isWriteDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: PERMISSION_WRITE_SECRETS }); - const isReadDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: PERMISSION_READ_SECRETS }); - - // case: you have write only if read is blocked and write is not - if (isReadDisallowed && !isWriteDisallowed) { - return true - } - - return false -} - -export const userHasNoAbility = async (userId: Types.ObjectId, workspaceId: Types.ObjectId, environment: string) => { - const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId }) - if (!membershipForWorkspace) { - return true - } - - const deniedMembershipPermissions = membershipForWorkspace.deniedPermissions; - const isWriteDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: PERMISSION_WRITE_SECRETS }); - const isReadBlocked = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: PERMISSION_READ_SECRETS }); - - if (isReadBlocked && isWriteDisallowed) { - return true - } - - return false -} \ No newline at end of file diff --git a/backend-mongo/src/ee/helpers/organizations.ts b/backend-mongo/src/ee/helpers/organizations.ts deleted file mode 100644 index f9f125b39..000000000 --- a/backend-mongo/src/ee/helpers/organizations.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Types } from "mongoose"; -import { - SSOConfig -} from "../models"; -import { - BotOrgService -} from "../../services"; -import { client } from "../../config"; -import { ValidationError } from "../../utils/errors"; - -export const getSSOConfigHelper = async ({ - organizationId, - ssoConfigId -}: { - organizationId?: Types.ObjectId; - ssoConfigId?: Types.ObjectId; -}) => { - - if (!organizationId && !ssoConfigId) throw ValidationError({ - message: "Getting SSO data requires either id of organization or SSO data" - }); - - const ssoConfig = await SSOConfig.findOne({ - ...(organizationId ? { organization: organizationId } : {}), - ...(ssoConfigId ? { _id: ssoConfigId } : {}) - }); - - if (!ssoConfig) throw new Error("Failed to find organization SSO data"); - - const key = await BotOrgService.getSymmetricKey( - ssoConfig.organization - ); - - const entryPoint = client.decryptSymmetric( - ssoConfig.encryptedEntryPoint, - key, - ssoConfig.entryPointIV, - ssoConfig.entryPointTag - ); - - const issuer = client.decryptSymmetric( - ssoConfig.encryptedIssuer, - key, - ssoConfig.issuerIV, - ssoConfig.issuerTag - ); - - const cert = client.decryptSymmetric( - ssoConfig.encryptedCert, - key, - ssoConfig.certIV, - ssoConfig.certTag - ); - - return ({ - _id: ssoConfig._id, - organization: ssoConfig.organization, - authProvider: ssoConfig.authProvider, - isActive: ssoConfig.isActive, - entryPoint, - issuer, - cert - }); -} \ No newline at end of file diff --git a/backend-mongo/src/ee/helpers/secret.ts b/backend-mongo/src/ee/helpers/secret.ts deleted file mode 100644 index 54b26f56e..000000000 --- a/backend-mongo/src/ee/helpers/secret.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Types } from "mongoose"; -import { Secret } from "../../models"; -import { - FolderVersion, - ISecretVersion, - SecretSnapshot, - SecretVersion, -} from "../models"; - -/** - * Save a secret snapshot that is a copy of the current state of secrets in workspace with id - * [workspaceId] under a new snapshot with incremented version under the - * secretsnapshots collection. - * @param {Object} obj - * @param {String} obj.workspaceId - * @returns {SecretSnapshot} secretSnapshot - new secret snapshot - */ -const takeSecretSnapshotHelper = async ({ - workspaceId, - environment, - folderId = "root", -}: { - workspaceId: Types.ObjectId; - environment: string; - folderId?: string; -}) => { - // get all folder ids - const secretIds = ( - await Secret.find( - { - workspace: workspaceId, - environment, - folder: folderId, - }, - "_id" - ).lean() - ).map((s) => s._id); - - const latestSecretVersions = ( - await SecretVersion.aggregate([ - { - $match: { - environment, - workspace: new Types.ObjectId(workspaceId), - secret: { - $in: secretIds, - }, - }, - }, - { - $group: { - _id: "$secret", - version: { $max: "$version" }, - versionId: { $max: "$_id" }, // secret version id - }, - }, - { - $sort: { version: -1 }, - }, - ]).exec() - ).map((s) => s.versionId); - const latestFolderVersion = await FolderVersion.findOne({ - environment, - workspace: workspaceId, - "nodes.id": folderId, - }).sort({ "nodes.version": -1 }); - - const latestSecretSnapshot = await SecretSnapshot.findOne({ - workspace: workspaceId, - }).sort({ version: -1 }); - - const secretSnapshot = await new SecretSnapshot({ - workspace: workspaceId, - environment, - version: latestSecretSnapshot ? latestSecretSnapshot.version + 1 : 1, - secretVersions: latestSecretVersions, - folderId, - folderVersion: latestFolderVersion, - }).save(); - - return secretSnapshot; -}; - -/** - * Add secret versions [secretVersions] to the SecretVersion collection. - * @param {Object} obj - * @param {Object[]} obj.secretVersions - * @returns {SecretVersion[]} newSecretVersions - new secret versions - */ -const addSecretVersionsHelper = async ({ - secretVersions, -}: { - secretVersions: ISecretVersion[]; -}) => { - const newSecretVersions = await SecretVersion.insertMany(secretVersions); - - return newSecretVersions; -}; - -const markDeletedSecretVersionsHelper = async ({ - secretIds, -}: { - secretIds: Types.ObjectId[]; -}) => { - await SecretVersion.updateMany( - { - secret: { $in: secretIds }, - }, - { - isDeleted: true, - }, - { - new: true, - } - ); -}; - -export { - takeSecretSnapshotHelper, - addSecretVersionsHelper, - markDeletedSecretVersionsHelper, -}; diff --git a/backend-mongo/src/ee/helpers/secretVersion.ts b/backend-mongo/src/ee/helpers/secretVersion.ts deleted file mode 100644 index 4b173f9ef..000000000 --- a/backend-mongo/src/ee/helpers/secretVersion.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Types } from "mongoose"; -import { SecretVersion } from "../models"; - -/** - * Return latest secret versions for secrets with ids [secretIds] - * @param {Object} obj - * @param {Object} obj.secretIds = ids of secrets to get latest versions for - * @returns - */ -const getLatestSecretVersionIds = async ({ - secretIds, -}: { - secretIds: Types.ObjectId[]; -}) => { - const latestSecretVersionIds = await SecretVersion.aggregate([ - { - $match: { - secret: { - $in: secretIds, - }, - }, - }, - { - $group: { - _id: "$secret", - version: { $max: "$version" }, - versionId: { $max: "$_id" }, // id of latest secret version - }, - }, - { - $sort: { version: -1 }, - }, - ]).exec(); - - return latestSecretVersionIds; -}; - -/** - * Return latest [n] secret versions for secrets with ids [secretIds] - * @param {Object} obj - * @param {Object} obj.secretIds = ids of secrets to get latest versions for - * @param {Number} obj.n - number of latest secret versions to return for each secret - * @returns - */ -const getLatestNSecretSecretVersionIds = async ({ - secretIds, - n, -}: { - secretIds: Types.ObjectId[]; - n: number; -}) => { - // TODO: optimize query - const latestNSecretVersions = await SecretVersion.aggregate([ - { - $match: { - secret: { - $in: secretIds, - }, - }, - }, - { - $sort: { version: -1 }, - }, - { - $group: { - _id: "$secret", - versions: { $push: "$$ROOT" }, - }, - }, - { - $project: { - _id: 0, - secret: "$_id", - versions: { $slice: ["$versions", n] }, - }, - }, - ]); - - return latestNSecretVersions; -}; - -export { getLatestSecretVersionIds, getLatestNSecretSecretVersionIds }; diff --git a/backend-mongo/src/ee/models/auditLog/auditLog.ts b/backend-mongo/src/ee/models/auditLog/auditLog.ts deleted file mode 100644 index 824e2f89f..000000000 --- a/backend-mongo/src/ee/models/auditLog/auditLog.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { ActorType, EventType, UserAgentType } from "./enums"; -import { Actor, Event } from "./types"; - -export interface IAuditLog { - actor: Actor; - organization: Types.ObjectId; - workspace: Types.ObjectId; - ipAddress: string; - event: Event; - userAgent: string; - userAgentType: UserAgentType; - expiresAt?: Date; -} - -const auditLogSchema = new Schema( - { - actor: { - type: { - type: String, - enum: ActorType, - required: true - }, - metadata: { - type: Schema.Types.Mixed - } - }, - organization: { - type: Schema.Types.ObjectId, - required: false - }, - workspace: { - type: Schema.Types.ObjectId, - required: false, - index: true - }, - ipAddress: { - type: String, - required: true - }, - event: { - type: { - type: String, - enum: EventType, - required: true - }, - metadata: { - type: Schema.Types.Mixed - } - }, - userAgent: { - type: String, - required: true - }, - userAgentType: { - type: String, - enum: UserAgentType, - required: true - }, - expiresAt: { - type: Date, - expires: 0 - } - }, - { - timestamps: true - } -); - -export const AuditLog = model("AuditLog", auditLogSchema); diff --git a/backend-mongo/src/ee/models/auditLog/enums.ts b/backend-mongo/src/ee/models/auditLog/enums.ts deleted file mode 100644 index ad0051bbc..000000000 --- a/backend-mongo/src/ee/models/auditLog/enums.ts +++ /dev/null @@ -1,69 +0,0 @@ -export enum ActorType { // would extend to AWS, Azure, ... - USER = "user", // userIdentity - SERVICE = "service", - IDENTITY = "identity" -} - -export enum UserAgentType { - WEB = "web", - CLI = "cli", - K8_OPERATOR = "k8-operator", - TERRAFORM = "terraform", - OTHER = "other", - PYTHON_SDK = "InfisicalPythonSDK", - NODE_SDK = "InfisicalNodeSDK" -} - -export enum EventType { - GET_SECRETS = "get-secrets", - GET_SECRET = "get-secret", - REVEAL_SECRET = "reveal-secret", - CREATE_SECRET = "create-secret", - CREATE_SECRETS = "create-secrets", - UPDATE_SECRET = "update-secret", - UPDATE_SECRETS = "update-secrets", - DELETE_SECRET = "delete-secret", - DELETE_SECRETS = "delete-secrets", - GET_WORKSPACE_KEY = "get-workspace-key", - AUTHORIZE_INTEGRATION = "authorize-integration", - UNAUTHORIZE_INTEGRATION = "unauthorize-integration", - CREATE_INTEGRATION = "create-integration", - DELETE_INTEGRATION = "delete-integration", - ADD_TRUSTED_IP = "add-trusted-ip", - UPDATE_TRUSTED_IP = "update-trusted-ip", - DELETE_TRUSTED_IP = "delete-trusted-ip", - CREATE_SERVICE_TOKEN = "create-service-token", // v2 - DELETE_SERVICE_TOKEN = "delete-service-token", // v2 - CREATE_IDENTITY = "create-identity", - UPDATE_IDENTITY = "update-identity", - DELETE_IDENTITY = "delete-identity", - LOGIN_IDENTITY_UNIVERSAL_AUTH = "login-identity-universal-auth", - ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth", - UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth", - GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth", - CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret", - REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret", - GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret", - CREATE_ENVIRONMENT = "create-environment", - UPDATE_ENVIRONMENT = "update-environment", - DELETE_ENVIRONMENT = "delete-environment", - ADD_WORKSPACE_MEMBER = "add-workspace-member", - ADD_BATCH_WORKSPACE_MEMBER = "add-workspace-members", - REMOVE_WORKSPACE_MEMBER = "remove-workspace-member", - CREATE_FOLDER = "create-folder", - UPDATE_FOLDER = "update-folder", - DELETE_FOLDER = "delete-folder", - CREATE_WEBHOOK = "create-webhook", - UPDATE_WEBHOOK_STATUS = "update-webhook-status", - DELETE_WEBHOOK = "delete-webhook", - GET_SECRET_IMPORTS = "get-secret-imports", - CREATE_SECRET_IMPORT = "create-secret-import", - UPDATE_SECRET_IMPORT = "update-secret-import", - DELETE_SECRET_IMPORT = "delete-secret-import", - UPDATE_USER_WORKSPACE_ROLE = "update-user-workspace-role", - UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS = "update-user-workspace-denied-permissions", - SECRET_APPROVAL_MERGED = "secret-approval-merged", - SECRET_APPROVAL_REQUEST = "secret-approval-request", - SECRET_APPROVAL_CLOSED = "secret-approval-closed", - SECRET_APPROVAL_REOPENED = "secret-approval-reopened" -} diff --git a/backend-mongo/src/ee/models/auditLog/index.ts b/backend-mongo/src/ee/models/auditLog/index.ts deleted file mode 100644 index 37b86b5d1..000000000 --- a/backend-mongo/src/ee/models/auditLog/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./auditLog"; -export * from "./enums"; -export * from "./types"; \ No newline at end of file diff --git a/backend-mongo/src/ee/models/auditLog/types.ts b/backend-mongo/src/ee/models/auditLog/types.ts deleted file mode 100644 index a4e470414..000000000 --- a/backend-mongo/src/ee/models/auditLog/types.ts +++ /dev/null @@ -1,585 +0,0 @@ -import { ActorType, EventType } from "./enums"; -import { IIdentityTrustedIp } from "../../../models"; - -interface UserActorMetadata { - userId: string; - email: string; -} - -interface ServiceActorMetadata { - serviceId: string; - name: string; -} - -interface IdentityActorMetadata { - identityId: string; - name: string; -} - -export interface UserActor { - type: ActorType.USER; - metadata: UserActorMetadata; -} - -export interface ServiceActor { - type: ActorType.SERVICE; - metadata: ServiceActorMetadata; -} - -export interface IdentityActor { - type: ActorType.IDENTITY; - metadata: IdentityActorMetadata; -} - -export type Actor = UserActor | ServiceActor | IdentityActor; - -interface GetSecretsEvent { - type: EventType.GET_SECRETS; - metadata: { - environment: string; - secretPath: string; - numberOfSecrets: number; - }; -} - -interface GetSecretEvent { - type: EventType.GET_SECRET; - metadata: { - environment: string; - secretPath: string; - secretId: string; - secretKey: string; - secretVersion: number; - }; -} - -interface CreateSecretEvent { - type: EventType.CREATE_SECRET; - metadata: { - environment: string; - secretPath: string; - secretId: string; - secretKey: string; - secretVersion: number; - }; -} - -interface CreateSecretBatchEvent { - type: EventType.CREATE_SECRETS; - metadata: { - environment: string; - secretPath: string; - secrets: Array<{ secretId: string; secretKey: string; secretVersion: number }>; - }; -} - -interface UpdateSecretEvent { - type: EventType.UPDATE_SECRET; - metadata: { - environment: string; - secretPath: string; - secretId: string; - secretKey: string; - secretVersion: number; - }; -} - -interface UpdateSecretBatchEvent { - type: EventType.UPDATE_SECRETS; - metadata: { - environment: string; - secretPath: string; - secrets: Array<{ secretId: string; secretKey: string; secretVersion: number }>; - }; -} - -interface DeleteSecretEvent { - type: EventType.DELETE_SECRET; - metadata: { - environment: string; - secretPath: string; - secretId: string; - secretKey: string; - secretVersion: number; - }; -} - -interface DeleteSecretBatchEvent { - type: EventType.DELETE_SECRETS; - metadata: { - environment: string; - secretPath: string; - secrets: Array<{ secretId: string; secretKey: string; secretVersion: number }>; - }; -} - -interface GetWorkspaceKeyEvent { - type: EventType.GET_WORKSPACE_KEY; - metadata: { - keyId: string; - }; -} - -interface AuthorizeIntegrationEvent { - type: EventType.AUTHORIZE_INTEGRATION; - metadata: { - integration: string; - }; -} - -interface UnauthorizeIntegrationEvent { - type: EventType.UNAUTHORIZE_INTEGRATION; - metadata: { - integration: string; - }; -} - -interface CreateIntegrationEvent { - type: EventType.CREATE_INTEGRATION; - metadata: { - integrationId: string; - integration: string; // TODO: fix type - environment: string; - secretPath: string; - url?: string; - app?: string; - appId?: string; - targetEnvironment?: string; - targetEnvironmentId?: string; - targetService?: string; - targetServiceId?: string; - path?: string; - region?: string; - }; -} - -interface DeleteIntegrationEvent { - type: EventType.DELETE_INTEGRATION; - metadata: { - integrationId: string; - integration: string; // TODO: fix type - environment: string; - secretPath: string; - url?: string; - app?: string; - appId?: string; - targetEnvironment?: string; - targetEnvironmentId?: string; - targetService?: string; - targetServiceId?: string; - path?: string; - region?: string; - }; -} - -interface AddTrustedIPEvent { - type: EventType.ADD_TRUSTED_IP; - metadata: { - trustedIpId: string; - ipAddress: string; - prefix?: number; - }; -} - -interface UpdateTrustedIPEvent { - type: EventType.UPDATE_TRUSTED_IP; - metadata: { - trustedIpId: string; - ipAddress: string; - prefix?: number; - }; -} - -interface DeleteTrustedIPEvent { - type: EventType.DELETE_TRUSTED_IP; - metadata: { - trustedIpId: string; - ipAddress: string; - prefix?: number; - }; -} - -interface CreateServiceTokenEvent { - type: EventType.CREATE_SERVICE_TOKEN; - metadata: { - name: string; - scopes: Array<{ - environment: string; - secretPath: string; - }>; - }; -} - -interface DeleteServiceTokenEvent { - type: EventType.DELETE_SERVICE_TOKEN; - metadata: { - name: string; - scopes: Array<{ - environment: string; - secretPath: string; - }>; - }; -} - -interface CreateIdentityEvent { // note: currently not logging org-role - type: EventType.CREATE_IDENTITY; - metadata: { - identityId: string; - name: string; - }; -} - -interface UpdateIdentityEvent { - type: EventType.UPDATE_IDENTITY; - metadata: { - identityId: string; - name?: string; - }; -} - -interface DeleteIdentityEvent { - type: EventType.DELETE_IDENTITY; - metadata: { - identityId: string; - }; -} - -interface LoginIdentityUniversalAuthEvent { - type: EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH ; - metadata: { - identityId: string; - identityUniversalAuthId: string; - clientSecretId: string; - identityAccessTokenId: string; - }; -} - -interface AddIdentityUniversalAuthEvent { - type: EventType.ADD_IDENTITY_UNIVERSAL_AUTH; - metadata: { - identityId: string; - clientSecretTrustedIps: Array; - accessTokenTTL: number; - accessTokenMaxTTL: number; - accessTokenNumUsesLimit: number; - accessTokenTrustedIps: Array; - }; -} - -interface UpdateIdentityUniversalAuthEvent { - type: EventType.UPDATE_IDENTITY_UNIVERSAL_AUTH; - metadata: { - identityId: string; - clientSecretTrustedIps?: Array; - accessTokenTTL?: number; - accessTokenMaxTTL?: number; - accessTokenNumUsesLimit?: number; - accessTokenTrustedIps?: Array; - }; -} - -interface GetIdentityUniversalAuthEvent { - type: EventType.GET_IDENTITY_UNIVERSAL_AUTH; - metadata: { - identityId: string; - }; -} - -interface CreateIdentityUniversalAuthClientSecretEvent { - type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET ; - metadata: { - identityId: string; - clientSecretId: string; - }; -} - -interface GetIdentityUniversalAuthClientSecretsEvent { - type: EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS; - metadata: { - identityId: string; - }; -} - - -interface RevokeIdentityUniversalAuthClientSecretEvent { - type: EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET ; - metadata: { - identityId: string; - clientSecretId: string; - }; -} - -interface CreateEnvironmentEvent { - type: EventType.CREATE_ENVIRONMENT; - metadata: { - name: string; - slug: string; - }; -} - -interface UpdateEnvironmentEvent { - type: EventType.UPDATE_ENVIRONMENT; - metadata: { - oldName: string; - newName: string; - oldSlug: string; - newSlug: string; - }; -} - -interface DeleteEnvironmentEvent { - type: EventType.DELETE_ENVIRONMENT; - metadata: { - name: string; - slug: string; - }; -} - -interface AddWorkspaceMemberEvent { - type: EventType.ADD_WORKSPACE_MEMBER; - metadata: { - userId: string; - email: string; - }; -} - -interface AddBatchWorkspaceMemberEvent { - type: EventType.ADD_BATCH_WORKSPACE_MEMBER; - metadata: Array<{ - userId: string; - email: string; - }>; -} - -interface RemoveWorkspaceMemberEvent { - type: EventType.REMOVE_WORKSPACE_MEMBER; - metadata: { - userId: string; - email: string; - }; -} - -interface CreateFolderEvent { - type: EventType.CREATE_FOLDER; - metadata: { - environment: string; - folderId: string; - folderName: string; - folderPath: string; - }; -} - -interface UpdateFolderEvent { - type: EventType.UPDATE_FOLDER; - metadata: { - environment: string; - folderId: string; - oldFolderName: string; - newFolderName: string; - folderPath: string; - }; -} - -interface DeleteFolderEvent { - type: EventType.DELETE_FOLDER; - metadata: { - environment: string; - folderId: string; - folderName: string; - folderPath: string; - }; -} - -interface CreateWebhookEvent { - type: EventType.CREATE_WEBHOOK; - metadata: { - webhookId: string; - environment: string; - secretPath: string; - webhookUrl: string; - isDisabled: boolean; - }; -} - -interface UpdateWebhookStatusEvent { - type: EventType.UPDATE_WEBHOOK_STATUS; - metadata: { - webhookId: string; - environment: string; - secretPath: string; - webhookUrl: string; - isDisabled: boolean; - }; -} - -interface DeleteWebhookEvent { - type: EventType.DELETE_WEBHOOK; - metadata: { - webhookId: string; - environment: string; - secretPath: string; - webhookUrl: string; - isDisabled: boolean; - }; -} - -interface GetSecretImportsEvent { - type: EventType.GET_SECRET_IMPORTS; - metadata: { - environment: string; - secretImportId: string; - folderId: string; - numberOfImports: number; - }; -} - -interface CreateSecretImportEvent { - type: EventType.CREATE_SECRET_IMPORT; - metadata: { - secretImportId: string; - folderId: string; - importFromEnvironment: string; - importFromSecretPath: string; - importToEnvironment: string; - importToSecretPath: string; - }; -} - -interface UpdateSecretImportEvent { - type: EventType.UPDATE_SECRET_IMPORT; - metadata: { - secretImportId: string; - folderId: string; - importToEnvironment: string; - importToSecretPath: string; - orderBefore: { - environment: string; - secretPath: string; - }[]; - orderAfter: { - environment: string; - secretPath: string; - }[]; - }; -} - -interface DeleteSecretImportEvent { - type: EventType.DELETE_SECRET_IMPORT; - metadata: { - secretImportId: string; - folderId: string; - importFromEnvironment: string; - importFromSecretPath: string; - importToEnvironment: string; - importToSecretPath: string; - }; -} - -interface UpdateUserRole { - type: EventType.UPDATE_USER_WORKSPACE_ROLE; - metadata: { - userId: string; - email: string; - oldRole: string; - newRole: string; - }; -} - -interface UpdateUserDeniedPermissions { - type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS; - metadata: { - userId: string; - email: string; - deniedPermissions: { - environmentSlug: string; - ability: string; - }[]; - }; -} -interface SecretApprovalMerge { - type: EventType.SECRET_APPROVAL_MERGED; - metadata: { - mergedBy: string; - secretApprovalRequestSlug: string; - secretApprovalRequestId: string; - }; -} - -interface SecretApprovalClosed { - type: EventType.SECRET_APPROVAL_CLOSED; - metadata: { - closedBy: string; - secretApprovalRequestSlug: string; - secretApprovalRequestId: string; - }; -} - -interface SecretApprovalReopened { - type: EventType.SECRET_APPROVAL_REOPENED; - metadata: { - reopenedBy: string; - secretApprovalRequestSlug: string; - secretApprovalRequestId: string; - }; -} - -interface SecretApprovalRequest { - type: EventType.SECRET_APPROVAL_REQUEST; - metadata: { - committedBy: string; - secretApprovalRequestSlug: string; - secretApprovalRequestId: string; - }; -} - -export type Event = - | GetSecretsEvent - | GetSecretEvent - | CreateSecretEvent - | CreateSecretBatchEvent - | UpdateSecretEvent - | UpdateSecretBatchEvent - | DeleteSecretEvent - | DeleteSecretBatchEvent - | GetWorkspaceKeyEvent - | AuthorizeIntegrationEvent - | UnauthorizeIntegrationEvent - | CreateIntegrationEvent - | DeleteIntegrationEvent - | AddTrustedIPEvent - | UpdateTrustedIPEvent - | DeleteTrustedIPEvent - | CreateServiceTokenEvent - | DeleteServiceTokenEvent - | CreateIdentityEvent - | UpdateIdentityEvent - | DeleteIdentityEvent - | LoginIdentityUniversalAuthEvent - | AddIdentityUniversalAuthEvent - | UpdateIdentityUniversalAuthEvent - | GetIdentityUniversalAuthEvent - | CreateIdentityUniversalAuthClientSecretEvent - | GetIdentityUniversalAuthClientSecretsEvent - | RevokeIdentityUniversalAuthClientSecretEvent - | CreateEnvironmentEvent - | UpdateEnvironmentEvent - | DeleteEnvironmentEvent - | AddWorkspaceMemberEvent - | AddBatchWorkspaceMemberEvent - | RemoveWorkspaceMemberEvent - | CreateFolderEvent - | UpdateFolderEvent - | DeleteFolderEvent - | CreateWebhookEvent - | UpdateWebhookStatusEvent - | DeleteWebhookEvent - | GetSecretImportsEvent - | CreateSecretImportEvent - | UpdateSecretImportEvent - | DeleteSecretImportEvent - | UpdateUserRole - | UpdateUserDeniedPermissions - | SecretApprovalMerge - | SecretApprovalClosed - | SecretApprovalRequest - | SecretApprovalReopened; diff --git a/backend-mongo/src/ee/models/folderVersion.ts b/backend-mongo/src/ee/models/folderVersion.ts deleted file mode 100644 index dbcebcb92..000000000 --- a/backend-mongo/src/ee/models/folderVersion.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export type TFolderRootVersionSchema = { - _id: Types.ObjectId; - workspace: Types.ObjectId; - environment: string; - nodes: TFolderVersionSchema; -}; - -export type TFolderVersionSchema = { - id: string; - name: string; - version: number; - children: TFolderVersionSchema[]; -}; - -const folderVersionSchema = new Schema({ - id: { - required: true, - type: String, - default: "root", - }, - name: { - required: true, - type: String, - default: "root", - }, - version: { - required: true, - type: Number, - default: 1, - }, -}); - -folderVersionSchema.add({ children: [folderVersionSchema] }); - -const folderRootVersionSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - }, - environment: { - type: String, - required: true, - }, - nodes: folderVersionSchema, - }, - { - timestamps: true, - } -); - -export const FolderVersion = model( - "FolderVersion", - folderRootVersionSchema -); \ No newline at end of file diff --git a/backend-mongo/src/ee/models/gitAppInstallationSession.ts b/backend-mongo/src/ee/models/gitAppInstallationSession.ts deleted file mode 100644 index 0cdf8df3c..000000000 --- a/backend-mongo/src/ee/models/gitAppInstallationSession.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -type GitAppInstallationSession = { - id: string; - sessionId: string; - organization: Types.ObjectId; - user: Types.ObjectId; -} - -const gitAppInstallationSession = new Schema({ - id: { - required: true, - type: String, - }, - sessionId: { - type: String, - required: true, - unique: true - }, - organization: { - type: Schema.Types.ObjectId, - required: true, - unique: true - }, - user: { - type: Schema.Types.ObjectId, - ref: "User" - } -}); - - -export const GitAppInstallationSession = model("git_app_installation_session", gitAppInstallationSession); \ No newline at end of file diff --git a/backend-mongo/src/ee/models/gitAppOrganizationInstallation.ts b/backend-mongo/src/ee/models/gitAppOrganizationInstallation.ts deleted file mode 100644 index 4ce55b0cb..000000000 --- a/backend-mongo/src/ee/models/gitAppOrganizationInstallation.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Schema, model } from "mongoose"; - -type Installation = { - installationId: string - organizationId: string - user: Schema.Types.ObjectId -}; - - -const gitAppOrganizationInstallation = new Schema({ - installationId: { - type: String, - required: true, - unique: true - }, - organizationId: { - type: String, - required: true, - unique: true - }, - user: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - } -}); - - -export const GitAppOrganizationInstallation = model("git_app_organization_installation", gitAppOrganizationInstallation); \ No newline at end of file diff --git a/backend-mongo/src/ee/models/gitRisks.ts b/backend-mongo/src/ee/models/gitRisks.ts deleted file mode 100644 index 8d3f59208..000000000 --- a/backend-mongo/src/ee/models/gitRisks.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { Schema, model } from "mongoose"; - -export const STATUS_RESOLVED_FALSE_POSITIVE = "RESOLVED_FALSE_POSITIVE"; -export const STATUS_RESOLVED_REVOKED = "RESOLVED_REVOKED"; -export const STATUS_RESOLVED_NOT_REVOKED = "RESOLVED_NOT_REVOKED"; -export const STATUS_UNRESOLVED = "UNRESOLVED"; - -export type IGitRisks = { - id: string; - description: string; - startLine: string; - endLine: string; - startColumn: string; - endColumn: string; - match: string; - secret: string; - file: string; - symlinkFile: string; - commit: string; - entropy: string; - author: string; - email: string; - date: string; - message: string; - tags: string[]; - ruleID: string; - fingerprint: string; - fingerPrintWithoutCommitId: string - - isFalsePositive: boolean; // New field for marking risks as false positives - isResolved: boolean; // New field for marking risks as resolved - riskOwner: string | null; // New field for setting a risk owner (nullable string) - installationId: string, - repositoryId: string, - repositoryLink: string - repositoryFullName: string - status: string - pusher: { - name: string, - email: string - }, - organization: Schema.Types.ObjectId, -} - -const gitRisks = new Schema({ - id: { - type: String, - }, - description: { - type: String, - }, - startLine: { - type: String, - }, - endLine: { - type: String, - }, - startColumn: { - type: String, - }, - endColumn: { - type: String, - }, - file: { - type: String, - }, - symlinkFile: { - type: String, - }, - commit: { - type: String, - }, - entropy: { - type: String, - }, - author: { - type: String, - }, - email: { - type: String, - }, - date: { - type: String, - }, - message: { - type: String, - }, - tags: { - type: [String], - }, - ruleID: { - type: String, - }, - fingerprint: { - type: String, - unique: true - }, - fingerPrintWithoutCommitId: { - type: String, - }, - isFalsePositive: { - type: Boolean, - default: false - }, - isResolved: { - type: Boolean, - default: false - }, - riskOwner: { - type: String, - default: null - }, - installationId: { - type: String, - require: true - }, - repositoryId: { - type: String - }, - repositoryLink: { - type: String - }, - repositoryFullName: { - type: String - }, - pusher: { - name: { - type: String - }, - email: { - type: String - }, - }, - organization: { - type: Schema.Types.ObjectId, - ref: "Organization", - }, - status: { - type: String, - enum: [ - STATUS_RESOLVED_FALSE_POSITIVE, - STATUS_RESOLVED_REVOKED, - STATUS_RESOLVED_NOT_REVOKED, - STATUS_UNRESOLVED - ], - default: STATUS_UNRESOLVED - } -}, { timestamps: true }); - -export const GitRisks = model("GitRisks", gitRisks); \ No newline at end of file diff --git a/backend-mongo/src/ee/models/index.ts b/backend-mongo/src/ee/models/index.ts deleted file mode 100644 index b2a9556ac..000000000 --- a/backend-mongo/src/ee/models/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -export * from "./secretSnapshot"; -export * from "./secretVersion"; -export * from "./folderVersion"; -export * from "./role"; -export * from "./ssoConfig"; -export * from "./trustedIp"; -export * from "./auditLog"; -export * from "./gitRisks"; -export * from "./gitAppOrganizationInstallation"; -export * from "./gitAppInstallationSession"; -export * from "./secretApprovalPolicy"; -export * from "./secretApprovalRequest"; diff --git a/backend-mongo/src/ee/models/role.ts b/backend-mongo/src/ee/models/role.ts deleted file mode 100644 index d3de1d3ae..000000000 --- a/backend-mongo/src/ee/models/role.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface IRole { - _id: Types.ObjectId; - name: string; - description: string; - slug: string; - permissions: Array; - workspace: Types.ObjectId; - organization: Types.ObjectId; - isOrgRole: boolean; -} - -const roleSchema = new Schema( - { - name: { - type: String, - required: true - }, - organization: { - type: Schema.Types.ObjectId, - ref: "Organization", - required: true - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace" - }, - isOrgRole: { - type: Boolean, - required: true, - select: false - }, - description: { - type: String - }, - slug: { - type: String, - required: true - }, - permissions: { - type: Array, - required: true - } - }, - { - timestamps: true - } -); - -roleSchema.index({ organization: 1, workspace: 1 }); - -export const Role = model("Role", roleSchema); \ No newline at end of file diff --git a/backend-mongo/src/ee/models/secretApprovalPolicy.ts b/backend-mongo/src/ee/models/secretApprovalPolicy.ts deleted file mode 100644 index 376b541c7..000000000 --- a/backend-mongo/src/ee/models/secretApprovalPolicy.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface ISecretApprovalPolicy { - _id: Types.ObjectId; - workspace: Types.ObjectId; - name: string; - environment: string; - secretPath?: string; - approvers: Types.ObjectId[]; - approvals: number; -} - -const secretApprovalPolicySchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - approvers: [ - { - // user associated with the personal secret - type: Schema.Types.ObjectId, - ref: "Membership" - } - ], - name: { - type: String - }, - environment: { - type: String, - required: true - }, - secretPath: { - type: String, - required: false - }, - approvals: { - type: Number, - default: 1 - } - }, - { - timestamps: true - } -); - -export const SecretApprovalPolicy = model( - "SecretApprovalPolicy", - secretApprovalPolicySchema -); diff --git a/backend-mongo/src/ee/models/secretApprovalRequest.ts b/backend-mongo/src/ee/models/secretApprovalRequest.ts deleted file mode 100644 index 24e8af39a..000000000 --- a/backend-mongo/src/ee/models/secretApprovalRequest.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { customAlphabet } from "nanoid"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8 -} from "../../variables"; - -export enum ApprovalStatus { - PENDING = "pending", - APPROVED = "approved", - REJECTED = "rejected" -} - -export enum CommitType { - DELETE = "delete", - UPDATE = "update", - CREATE = "create" -} - -const SLUG_ALPHABETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; -const nanoId = customAlphabet(SLUG_ALPHABETS, 10); - -export interface ISecretApprovalSecChange { - _id: Types.ObjectId; - version: number; - secretBlindIndex?: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretCommentIV?: string; - secretCommentTag?: string; - secretCommentCiphertext?: string; - skipMultilineEncoding?: boolean; - algorithm?: "aes-256-gcm"; - keyEncoding?: "utf8" | "base64"; - tags?: string[]; -} - -export type ISecretCommits = Array< - | { - newVersion: ISecretApprovalSecChange; - op: CommitType.CREATE; - } - | { - // secret is recorded to get the latest version, we can keep ref to secret for pulling change as it will also get changed - // on merge - secretVersion: J; - secret: T; - newVersion: Partial> & { _id: Types.ObjectId }; - op: CommitType.UPDATE; - } - | { - secret: T; - secretVersion: J; - op: CommitType.DELETE; - } ->; -export interface ISecretApprovalRequest { - _id: Types.ObjectId; - committer: Types.ObjectId; - slug: string; - statusChangeBy: Types.ObjectId; - reviewers: { - member: Types.ObjectId; - status: ApprovalStatus; - }[]; - workspace: Types.ObjectId; - environment: string; - folderId: string; - hasMerged: boolean; - status: "open" | "close"; - policy: Types.ObjectId; - commits: ISecretCommits; - conflicts: Array<{ secretId: string; op: CommitType }>; -} - -const secretApprovalSecretChangeSchema = new Schema({ - version: { - type: Number, - default: 1, - required: true - }, - secretBlindIndex: { - type: String, - select: false - }, - secretKeyCiphertext: { - type: String, - required: true - }, - secretKeyIV: { - type: String, // symmetric - required: true - }, - secretKeyTag: { - type: String, // symmetric - required: true - }, - secretValueCiphertext: { - type: String, - required: true - }, - secretValueIV: { - type: String, // symmetric - required: true - }, - secretValueTag: { - type: String, // symmetric - required: true - }, - skipMultilineEncoding: { - type: Boolean, - required: false - }, - algorithm: { - // the encryption algorithm used - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true, - default: ALGORITHM_AES_256_GCM - }, - keyEncoding: { - type: String, - enum: [ENCODING_SCHEME_UTF8, ENCODING_SCHEME_BASE64], - required: true, - default: ENCODING_SCHEME_UTF8 - }, - tags: { - ref: "Tag", - type: [Schema.Types.ObjectId], - default: [] - } -}); - -const secretApprovalRequestSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - environment: { - type: String, - required: true - }, - folderId: { - type: String, - required: true, - default: "root" - }, - slug: { - type: String, - default: () => nanoId() - }, - reviewers: { - type: [ - { - member: { - // user associated with the personal secret - type: Schema.Types.ObjectId, - ref: "Membership" - }, - status: { type: String, enum: ApprovalStatus, default: ApprovalStatus.PENDING } - } - ], - default: [] - }, - policy: { type: Schema.Types.ObjectId, ref: "SecretApprovalPolicy" }, - hasMerged: { type: Boolean, default: false }, - status: { type: String, enum: ["close", "open"], default: "open" }, - committer: { type: Schema.Types.ObjectId, ref: "Membership" }, - statusChangeBy: { type: Schema.Types.ObjectId, ref: "Membership" }, - commits: [ - { - secret: { type: Types.ObjectId, ref: "Secret" }, - newVersion: secretApprovalSecretChangeSchema, - secretVersion: { type: Types.ObjectId, ref: "SecretVersion" }, - op: { type: String, enum: [CommitType], required: true } - } - ], - conflicts: { - type: [ - { - secretId: { type: String, required: true }, - op: { type: String, enum: [CommitType], required: true } - } - ], - default: [] - } - }, - { - timestamps: true - } -); - -export const SecretApprovalRequest = model( - "SecretApprovalRequest", - secretApprovalRequestSchema -); diff --git a/backend-mongo/src/ee/models/secretSnapshot.ts b/backend-mongo/src/ee/models/secretSnapshot.ts deleted file mode 100644 index 71d1b27e6..000000000 --- a/backend-mongo/src/ee/models/secretSnapshot.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface ISecretSnapshot { - workspace: Types.ObjectId; - environment: string; - folderId: string | "root"; - version: number; - secretVersions: Types.ObjectId[]; - folderVersion: Types.ObjectId; -} - -const secretSnapshotSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - }, - environment: { - type: String, - required: true, - }, - folderId: { - type: String, - default: "root", - }, - version: { - type: Number, - default: 1, - required: true, - }, - secretVersions: [ - { - type: Schema.Types.ObjectId, - ref: "SecretVersion", - required: true, - }, - ], - folderVersion: { - type: Schema.Types.ObjectId, - ref: "FolderVersion", - }, - }, - { - timestamps: true, - } -); - -export const SecretSnapshot = model( - "SecretSnapshot", - secretSnapshotSchema -); \ No newline at end of file diff --git a/backend-mongo/src/ee/models/secretVersion.ts b/backend-mongo/src/ee/models/secretVersion.ts deleted file mode 100644 index 11ffa79ab..000000000 --- a/backend-mongo/src/ee/models/secretVersion.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, - SECRET_PERSONAL, - SECRET_SHARED -} from "../../variables"; - -export interface ISecretVersion { - _id: Types.ObjectId; - secret: Types.ObjectId; - version: number; - workspace: Types.ObjectId; // new - type: string; // new - user?: Types.ObjectId; // new - environment: string; // new - isDeleted: boolean; - secretBlindIndex?: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - skipMultilineEncoding?: boolean; - algorithm: "aes-256-gcm"; - keyEncoding: "utf8" | "base64"; - createdAt: string; - folder?: string; - tags?: string[]; -} - -const secretVersionSchema = new Schema( - { - secret: { - // could be deleted - type: Schema.Types.ObjectId, - ref: "Secret", - required: true - }, - version: { - type: Number, - default: 1, - required: true - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - type: { - type: String, - enum: [SECRET_SHARED, SECRET_PERSONAL], - required: true - }, - user: { - // user associated with the personal secret - type: Schema.Types.ObjectId, - ref: "User" - }, - environment: { - type: String, - required: true - }, - isDeleted: { - // consider removing field - type: Boolean, - default: false, - required: true - }, - secretBlindIndex: { - type: String, - select: false - }, - secretKeyCiphertext: { - type: String, - required: true - }, - secretKeyIV: { - type: String, // symmetric - required: true - }, - secretKeyTag: { - type: String, // symmetric - required: true - }, - secretValueCiphertext: { - type: String, - required: true - }, - secretValueIV: { - type: String, // symmetric - required: true - }, - secretValueTag: { - type: String, // symmetric - required: true - }, - skipMultilineEncoding: { - type: Boolean, - required: false - }, - algorithm: { - // the encryption algorithm used - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true, - default: ALGORITHM_AES_256_GCM - }, - keyEncoding: { - type: String, - enum: [ENCODING_SCHEME_UTF8, ENCODING_SCHEME_BASE64], - required: true, - default: ENCODING_SCHEME_UTF8 - }, - folder: { - type: String, - required: true - }, - tags: { - ref: "Tag", - type: [Schema.Types.ObjectId], - default: [] - } - }, - { - timestamps: true - } -); - -export const SecretVersion = model("SecretVersion", secretVersionSchema); diff --git a/backend-mongo/src/ee/models/ssoConfig.ts b/backend-mongo/src/ee/models/ssoConfig.ts deleted file mode 100644 index b591b8817..000000000 --- a/backend-mongo/src/ee/models/ssoConfig.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export enum AuthProvider { - OKTA_SAML = "okta-saml", - AZURE_SAML = "azure-saml", - JUMPCLOUD_SAML = "jumpcloud-saml" -} - -export interface ISSOConfig { - organization: Types.ObjectId; - authProvider: AuthProvider; - isActive: boolean; - encryptedEntryPoint: string; - entryPointIV: string; - entryPointTag: string; - encryptedIssuer: string; - issuerIV: string; - issuerTag: string; - encryptedCert: string; - certIV: string; - certTag: string; -} - -const ssoConfigSchema = new Schema( - { - organization: { - type: Schema.Types.ObjectId, - ref: "Organization" - }, - authProvider: { - type: String, - enum: AuthProvider, - required: true - }, - isActive: { - type: Boolean, - required: true - }, - encryptedEntryPoint: { - type: String - }, - entryPointIV: { - type: String - }, - entryPointTag: { - type: String - }, - encryptedIssuer: { - type: String - }, - issuerIV: { - type: String - }, - issuerTag: { - type: String - }, - encryptedCert: { - type: String - }, - certIV: { - type: String - }, - certTag: { - type: String - } - }, - { - timestamps: true - } -); - -export const SSOConfig = model("SSOConfig", ssoConfigSchema); \ No newline at end of file diff --git a/backend-mongo/src/ee/models/trustedIp.ts b/backend-mongo/src/ee/models/trustedIp.ts deleted file mode 100644 index 85616be11..000000000 --- a/backend-mongo/src/ee/models/trustedIp.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export enum IPType { - IPV4 = "ipv4", - IPV6 = "ipv6" -} - -export interface ITrustedIP { - _id: Types.ObjectId; - workspace: Types.ObjectId; - ipAddress: string; - type: "ipv4" | "ipv6", // either IPv4/IPv6 address or network IPv4/IPv6 address - isActive: boolean; - comment: string; - prefix?: number; // CIDR -} - -const trustedIpSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - ipAddress: { - type: String, - required: true - }, - type: { - type: String, - enum: [ - IPType.IPV4, - IPType.IPV6 - ], - required: true - }, - prefix: { - type: Number, - required: false - }, - isActive: { - type: Boolean, - required: true - }, - comment: { - type: String - } - }, - { - timestamps: true - } -); - -export const TrustedIP = model("TrustedIP", trustedIpSchema); \ No newline at end of file diff --git a/backend-mongo/src/ee/routes/v1/cloudProducts.ts b/backend-mongo/src/ee/routes/v1/cloudProducts.ts deleted file mode 100644 index 23912222b..000000000 --- a/backend-mongo/src/ee/routes/v1/cloudProducts.ts +++ /dev/null @@ -1,16 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth, validateRequest } from "../../../middleware"; -import { cloudProductsController } from "../../controllers/v1"; -import { AuthMode } from "../../../variables"; - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - validateRequest, - cloudProductsController.getCloudProducts -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/identities.ts b/backend-mongo/src/ee/routes/v1/identities.ts deleted file mode 100644 index c78a7d8c8..000000000 --- a/backend-mongo/src/ee/routes/v1/identities.ts +++ /dev/null @@ -1,31 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { AuthMode } from "../../../variables"; -import { identitiesController } from "../../controllers/v1"; - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - identitiesController.createIdentity -); - -router.patch( - "/:identityId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - identitiesController.updateIdentity -); - -router.delete( - "/:identityId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - identitiesController.deleteIdentity -); - -export default router; \ No newline at end of file diff --git a/backend-mongo/src/ee/routes/v1/index.ts b/backend-mongo/src/ee/routes/v1/index.ts deleted file mode 100644 index b22c61629..000000000 --- a/backend-mongo/src/ee/routes/v1/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import identities from "./identities"; -import secret from "./secret"; -import secretSnapshot from "./secretSnapshot"; -import organizations from "./organizations"; -import sso from "./sso"; -import users from "./users"; -import workspace from "./workspace"; -import cloudProducts from "./cloudProducts"; -import secretScanning from "./secretScanning"; -import roles from "./role"; -import secretApprovalPolicy from "./secretApprovalPolicy"; -import secretApprovalRequest from "./secretApprovalRequest"; -import secretRotationProvider from "./secretRotationProvider"; -import secretRotation from "./secretRotation"; - -export { - identities, - secret, - secretSnapshot, - organizations, - sso, - users, - workspace, - cloudProducts, - secretScanning, - roles, - secretApprovalPolicy, - secretApprovalRequest, - secretRotationProvider, - secretRotation -}; diff --git a/backend-mongo/src/ee/routes/v1/organizations.ts b/backend-mongo/src/ee/routes/v1/organizations.ts deleted file mode 100644 index bbe5019b4..000000000 --- a/backend-mongo/src/ee/routes/v1/organizations.ts +++ /dev/null @@ -1,127 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { organizationsController } from "../../controllers/v1"; -import { AuthMode } from "../../../variables"; - -router.get( - "/:organizationId/plans/table", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationPlansTable -); - -router.get( - "/:organizationId/plan", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationPlan -); - -router.post( - "/:organizationId/session/trial", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.startOrganizationTrial -); - -router.get( - "/:organizationId/plan/billing", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationPlanBillingInfo -); - -router.get( - "/:organizationId/plan/table", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationPlanTable -); - -router.get( - "/:organizationId/billing-details", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationBillingDetails -); - -router.patch( - "/:organizationId/billing-details", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.updateOrganizationBillingDetails -); - -router.get( - "/:organizationId/billing-details/payment-methods", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationPmtMethods -); - -router.post( - "/:organizationId/billing-details/payment-methods", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.addOrganizationPmtMethod -); - -router.delete( - "/:organizationId/billing-details/payment-methods/:pmtMethodId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.deleteOrganizationPmtMethod -); - -router.get( - "/:organizationId/billing-details/tax-ids", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationTaxIds -); - -router.post( - "/:organizationId/billing-details/tax-ids", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.addOrganizationTaxId -); - -router.delete( - "/:organizationId/billing-details/tax-ids/:taxId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.deleteOrganizationTaxId -); - -router.get( - "/:organizationId/invoices", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationInvoices -); - -router.get( - "/:organizationId/licenses", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationLicenses -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/role.ts b/backend-mongo/src/ee/routes/v1/role.ts deleted file mode 100644 index 0794b6b19..000000000 --- a/backend-mongo/src/ee/routes/v1/role.ts +++ /dev/null @@ -1,33 +0,0 @@ -import express from "express"; -import { roleController } from "../../controllers/v1"; -import { requireAuth } from "../../../middleware"; -import { AuthMode } from "../../../variables"; - -const router = express.Router(); - -router.post("/", requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), roleController.createRole); - -router.patch("/:id", requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), roleController.updateRole); - -router.delete( - "/:id", - requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), - roleController.deleteRole -); - -router.get("/", requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), roleController.getRoles); - -// get a user permissions in an org -router.get( - "/organization/:orgId/permissions", - requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), - roleController.getUserPermissions -); - -router.get( - "/workspace/:workspaceId/permissions", - requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), - roleController.getUserWorkspacePermissions -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/secret.ts b/backend-mongo/src/ee/routes/v1/secret.ts deleted file mode 100644 index bdbdec965..000000000 --- a/backend-mongo/src/ee/routes/v1/secret.ts +++ /dev/null @@ -1,25 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { secretController } from "../../controllers/v1"; -import { - AuthMode -} from "../../../variables"; - -router.get( - "/:secretId/secret-versions", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - secretController.getSecretVersions -); - -router.post( - "/:secretId/secret-versions/rollback", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - secretController.rollbackSecretVersion -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/secretApprovalPolicy.ts b/backend-mongo/src/ee/routes/v1/secretApprovalPolicy.ts deleted file mode 100644 index c4b082286..000000000 --- a/backend-mongo/src/ee/routes/v1/secretApprovalPolicy.ts +++ /dev/null @@ -1,47 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { secretApprovalPolicyController } from "../../controllers/v1"; -import { AuthMode } from "../../../variables"; - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalPolicyController.getSecretApprovalPolicy -); - -router.get( - "/board", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalPolicyController.getSecretApprovalPolicyOfBoard -); - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalPolicyController.createSecretApprovalPolicy -); - -router.patch( - "/:id", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalPolicyController.updateSecretApprovalPolicy -); - -router.delete( - "/:id", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalPolicyController.deleteSecretApprovalPolicy -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/secretApprovalRequest.ts b/backend-mongo/src/ee/routes/v1/secretApprovalRequest.ts deleted file mode 100644 index e78c67c96..000000000 --- a/backend-mongo/src/ee/routes/v1/secretApprovalRequest.ts +++ /dev/null @@ -1,55 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { secretApprovalRequestController } from "../../controllers/v1"; -import { AuthMode } from "../../../variables"; - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalRequestController.getSecretApprovalRequests -); - -router.get( - "/count", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalRequestController.getSecretApprovalRequestCount -); - -router.get( - "/:id", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalRequestController.getSecretApprovalRequestDetails -); - -router.post( - "/:id/merge", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalRequestController.mergeSecretApprovalRequest -); - -router.post( - "/:id/review", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalRequestController.updateSecretApprovalReviewStatus -); - -router.post( - "/:id/status", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretApprovalRequestController.updateSecretApprovalRequestStatus -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/secretRotation.ts b/backend-mongo/src/ee/routes/v1/secretRotation.ts deleted file mode 100644 index a4da8a72f..000000000 --- a/backend-mongo/src/ee/routes/v1/secretRotation.ts +++ /dev/null @@ -1,41 +0,0 @@ -import express from "express"; - -import { AuthMode } from "../../../variables"; -import { requireAuth } from "../../../middleware"; -import { secretRotationController } from "../../controllers/v1"; - -const router = express.Router(); - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretRotationController.createSecretRotation -); - -router.post( - "/restart", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretRotationController.restartSecretRotations -); - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretRotationController.getSecretRotations -); - -router.delete( - "/:id", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretRotationController.deleteSecretRotations -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/secretRotationProvider.ts b/backend-mongo/src/ee/routes/v1/secretRotationProvider.ts deleted file mode 100644 index 16ab17184..000000000 --- a/backend-mongo/src/ee/routes/v1/secretRotationProvider.ts +++ /dev/null @@ -1,17 +0,0 @@ -import express from "express"; - -import { AuthMode } from "../../../variables"; -import { requireAuth } from "../../../middleware"; -import { secretRotationProviderController } from "../../controllers/v1"; - -const router = express.Router(); - -router.get( - "/:workspaceId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretRotationProviderController.getProviderTemplates -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/secretScanning.ts b/backend-mongo/src/ee/routes/v1/secretScanning.ts deleted file mode 100644 index 0afdf0545..000000000 --- a/backend-mongo/src/ee/routes/v1/secretScanning.ts +++ /dev/null @@ -1,53 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { - createInstallationSession, - getCurrentOrganizationInstallationStatus, - getRisksForOrganization, - linkInstallationToOrganization, - updateRisksStatus -} from "../../../controllers/v1/secretScanningController"; -import { AuthMode } from "../../../variables"; - -router.post( - "/create-installation-session/organization/:organizationId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - createInstallationSession -); - -router.post( - "/link-installation", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - linkInstallationToOrganization -); - -router.get( - "/installation-status/organization/:organizationId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - getCurrentOrganizationInstallationStatus -); - -router.get( - "/organization/:organizationId/risks", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - getRisksForOrganization -); - -router.post( - "/organization/:organizationId/risks/:riskId/status", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - updateRisksStatus -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/secretSnapshot.ts b/backend-mongo/src/ee/routes/v1/secretSnapshot.ts deleted file mode 100644 index f8c643e60..000000000 --- a/backend-mongo/src/ee/routes/v1/secretSnapshot.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { AuthMode } from "../../../variables"; -import { secretSnapshotController } from "../../controllers/v1"; - -router.get( - "/:secretSnapshotId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - secretSnapshotController.getSecretSnapshot -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/sso.ts b/backend-mongo/src/ee/routes/v1/sso.ts deleted file mode 100644 index 24f0d36a1..000000000 --- a/backend-mongo/src/ee/routes/v1/sso.ts +++ /dev/null @@ -1,60 +0,0 @@ -import express from "express"; -const router = express.Router(); -import passport from "passport"; -import { requireAuth } from "../../../middleware"; -import { ssoController } from "../../controllers/v1"; -import { authLimiter } from "../../../helpers/rateLimiter"; -import { AuthMode } from "../../../variables"; - -router.get( - "/redirect/saml2/:ssoIdentifier", - authLimiter, - (req, res, next) => { - const options = { - failureRedirect: "/", - additionalParams: { - RelayState: JSON.stringify({ - spInitiated: true, - callbackPort: req.query.callback_port ?? "" - }) - }, - }; - passport.authenticate("saml", options)(req, res, next); - } -); - -router.post( - "/saml2/:ssoIdentifier", - passport.authenticate("saml", { - failureRedirect: "/login/provider/error", - failureFlash: true, - session: false - }), - ssoController.redirectSSO -); - -router.get( - "/config", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - ssoController.getSSOConfig -); - -router.post( - "/config", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - ssoController.createSSOConfig -); - -router.patch( - "/config", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - ssoController.updateSSOConfig -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v1/users.ts b/backend-mongo/src/ee/routes/v1/users.ts deleted file mode 100644 index d5015401e..000000000 --- a/backend-mongo/src/ee/routes/v1/users.ts +++ /dev/null @@ -1,17 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { - requireAuth -} from "../../../middleware"; -import { AuthMode } from "../../../variables"; -import { usersController } from "../../controllers/v1"; - -router.get( - "/me/ip", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY], - }), - usersController.getMyIp -); - -export default router; \ No newline at end of file diff --git a/backend-mongo/src/ee/routes/v1/workspace.ts b/backend-mongo/src/ee/routes/v1/workspace.ts deleted file mode 100644 index ace4458cc..000000000 --- a/backend-mongo/src/ee/routes/v1/workspace.ts +++ /dev/null @@ -1,79 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { AuthMode } from "../../../variables"; -import { workspaceController } from "../../controllers/v1"; - -router.get( - "/:workspaceId/secret-snapshots", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.getWorkspaceSecretSnapshots -); - -router.get( - "/:workspaceId/secret-snapshots/count", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.getWorkspaceSecretSnapshotsCount -); - -router.post( - "/:workspaceId/secret-snapshots/rollback", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.rollbackWorkspaceSecretSnapshot -); - -router.get( - "/:workspaceId/audit-logs", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.getWorkspaceAuditLogs -); - -router.get( - "/:workspaceId/audit-logs/filters/actors", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - workspaceController.getWorkspaceAuditLogActorFilterOpts -); - -router.get( - "/:workspaceId/trusted-ips", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.getWorkspaceTrustedIps -); - -router.post( - "/:workspaceId/trusted-ips", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.addWorkspaceTrustedIp -); - -router.patch( - "/:workspaceId/trusted-ips/:trustedIpId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.updateWorkspaceTrustedIp -); - -router.delete( - "/:workspaceId/trusted-ips/:trustedIpId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.deleteWorkspaceTrustedIp -); - -export default router; diff --git a/backend-mongo/src/ee/routes/v3/apiKeyData.ts b/backend-mongo/src/ee/routes/v3/apiKeyData.ts deleted file mode 100644 index 6d069a719..000000000 --- a/backend-mongo/src/ee/routes/v3/apiKeyData.ts +++ /dev/null @@ -1,31 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { AuthMode } from "../../../variables"; -import { apiKeyDataController } from "../../controllers/v3"; - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - apiKeyDataController.createAPIKeyData -); - -router.patch( - "/:apiKeyDataId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - apiKeyDataController.updateAPIKeyData -); - -router.delete( - "/:apiKeyDataId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - apiKeyDataController.deleteAPIKeyData -); - -export default router; \ No newline at end of file diff --git a/backend-mongo/src/ee/routes/v3/index.ts b/backend-mongo/src/ee/routes/v3/index.ts deleted file mode 100644 index c534640e3..000000000 --- a/backend-mongo/src/ee/routes/v3/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import apiKeyData from "./apiKeyData"; - -export { - apiKeyData -} \ No newline at end of file diff --git a/backend-mongo/src/ee/secretRotation/models.ts b/backend-mongo/src/ee/secretRotation/models.ts deleted file mode 100644 index 0ddde5d83..000000000 --- a/backend-mongo/src/ee/secretRotation/models.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Schema, model } from "mongoose"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8 -} from "../../variables"; -import { ISecretRotation } from "./types"; - -const secretRotationSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace" - }, - provider: { - type: String, - required: true - }, - customProvider: { - type: Schema.Types.ObjectId, - ref: "SecretRotationProvider" - }, - environment: { - type: String, - required: true - }, - secretPath: { - type: String, - required: true - }, - interval: { - type: Number, - required: true - }, - lastRotatedAt: { - type: String - }, - status: { - type: String, - enum: ["success", "failed"] - }, - statusMessage: { - type: String - }, - // encrypted data on input keys and secrets got - encryptedData: { - type: String, - select: false - }, - encryptedDataIV: { - type: String, - select: false - }, - encryptedDataTag: { - type: String, - select: false - }, - algorithm: { - // the encryption algorithm used - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true, - select: false, - default: ALGORITHM_AES_256_GCM - }, - keyEncoding: { - type: String, - enum: [ENCODING_SCHEME_UTF8, ENCODING_SCHEME_BASE64], - required: true, - select: false, - default: ENCODING_SCHEME_UTF8 - }, - outputs: [ - { - key: { - type: String, - required: true - }, - secret: { - type: Schema.Types.ObjectId, - ref: "Secret" - } - } - ] - }, - { - timestamps: true - } -); - -export const SecretRotation = model("SecretRotation", secretRotationSchema); diff --git a/backend-mongo/src/ee/secretRotation/queue/queue.ts b/backend-mongo/src/ee/secretRotation/queue/queue.ts deleted file mode 100644 index 0127bbd9c..000000000 --- a/backend-mongo/src/ee/secretRotation/queue/queue.ts +++ /dev/null @@ -1,288 +0,0 @@ -import Queue, { Job } from "bull"; -import { client, getEncryptionKey, getRootEncryptionKey } from "../../../config"; -import { BotService, EventService, TelemetryService } from "../../../services"; -import { SecretRotation } from "../models"; -import { rotationTemplates } from "../templates"; -import { - ISecretRotationData, - ISecretRotationEncData, - ISecretRotationProviderTemplate, - TProviderFunctionTypes -} from "../types"; -import { - decryptSymmetric128BitHexKeyUTF8, - encryptSymmetric128BitHexKeyUTF8 -} from "../../../utils/crypto"; -import { ISecret, Secret } from "../../../models"; -import { ENCODING_SCHEME_BASE64, ENCODING_SCHEME_UTF8, SECRET_SHARED } from "../../../variables"; -import { EESecretService } from "../../services"; -import { SecretVersion } from "../../models"; -import { eventPushSecrets } from "../../../events"; -import { logger } from "../../../utils/logging"; - -import { - secretRotationPreSetFn, - secretRotationRemoveFn, - secretRotationSetFn, - secretRotationTestFn -} from "./queue.utils"; - -const secretRotationQueue = new Queue("secret-rotation-service", process.env.REDIS_URL as string); - -secretRotationQueue.process(async (job: Job) => { - logger.info(`secretRotationQueue.process: [rotationDocument=${job.data.rotationDocId}]`); - const rotationStratDocId = job.data.rotationDocId; - const secretRotation = await SecretRotation.findById(rotationStratDocId) - .select("+encryptedData +encryptedDataTag +encryptedDataIV +keyEncoding") - .populate<{ - outputs: [ - { - key: string; - secret: ISecret; - } - ]; - }>("outputs.secret"); - - const infisicalRotationProvider = rotationTemplates.find( - ({ name }) => name === secretRotation?.provider - ); - - try { - if (!infisicalRotationProvider || !secretRotation) - throw new Error("Failed to find rotation strategy"); - - if (secretRotation.outputs.some(({ secret }) => !secret)) - throw new Error("Secrets not found in dashboard"); - - const workspaceId = secretRotation.workspace; - - // deep copy - const provider = JSON.parse( - JSON.stringify(infisicalRotationProvider) - ) as ISecretRotationProviderTemplate; - - // decrypt user provided inputs for secret rotation - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - let decryptedData = ""; - if (rootEncryptionKey && secretRotation.keyEncoding === ENCODING_SCHEME_BASE64) { - // case: encoding scheme is base64 - decryptedData = client.decryptSymmetric( - secretRotation.encryptedData, - rootEncryptionKey, - secretRotation.encryptedDataIV, - secretRotation.encryptedDataTag - ); - } else if (encryptionKey && secretRotation.keyEncoding === ENCODING_SCHEME_UTF8) { - // case: encoding scheme is utf8 - decryptedData = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secretRotation.encryptedData, - iv: secretRotation.encryptedDataIV, - tag: secretRotation.encryptedDataTag, - key: encryptionKey - }); - } - - const variables = JSON.parse(decryptedData) as ISecretRotationEncData; - - // rotation set cycle - const newCredential: ISecretRotationData = { - inputs: variables.inputs, - outputs: {}, - internal: {} - }; - // special glue code for database - if (provider.template.functions.set.type === TProviderFunctionTypes.DB) { - const lastCred = variables.creds.at(-1); - if (lastCred && variables.creds.length === 1) { - newCredential.internal.username = - lastCred.internal.username === variables.inputs.username1 - ? variables.inputs.username2 - : variables.inputs.username1; - } else { - newCredential.internal.username = lastCred - ? lastCred.internal.username - : variables.inputs.username1; - } - } - if (provider.template.functions.set?.pre) { - secretRotationPreSetFn(provider.template.functions.set.pre, newCredential); - } - await secretRotationSetFn(provider.template.functions.set, newCredential); - await secretRotationTestFn(provider.template.functions.test, newCredential); - - if (variables.creds.length === 2) { - const deleteCycleCred = variables.creds.pop(); - if (deleteCycleCred && provider.template.functions.remove) { - const deleteCycleVar = { inputs: variables.inputs, ...deleteCycleCred }; - await secretRotationRemoveFn(provider.template.functions.remove, deleteCycleVar); - } - } - variables.creds.unshift({ outputs: newCredential.outputs, internal: newCredential.internal }); - const { ciphertext, iv, tag } = client.encryptSymmetric( - JSON.stringify(variables), - rootEncryptionKey - ); - - // save the rotation state - await SecretRotation.findByIdAndUpdate(rotationStratDocId, { - encryptedData: ciphertext, - encryptedDataIV: iv, - encryptedDataTag: tag, - status: "success", - statusMessage: "Rotated successfully", - lastRotatedAt: new Date().toUTCString() - }); - - const key = await BotService.getWorkspaceKeyWithBot({ - workspaceId: secretRotation.workspace - }); - - const encryptedSecrets = secretRotation.outputs.map(({ key: outputKey, secret }) => ({ - secret, - value: encryptSymmetric128BitHexKeyUTF8({ - plaintext: - typeof newCredential.outputs[outputKey] === "object" - ? JSON.stringify(newCredential.outputs[outputKey]) - : String(newCredential.outputs[outputKey]), - key - }) - })); - - // now save the secret do a bulk update - // can't use the updateSecret function due to various parameter required issue - // REFACTOR(akhilmhdh): secret module should be lot more flexible. Ability to update bulk or individually by blindIndex, by id etc - await Secret.bulkWrite( - encryptedSecrets.map(({ secret, value }) => ({ - updateOne: { - filter: { - workspace: workspaceId, - environment: secretRotation.environment, - _id: secret._id, - type: SECRET_SHARED - }, - update: { - $inc: { - version: 1 - }, - secretValueCiphertext: value.ciphertext, - secretValueIV: value.iv, - secretValueTag: value.tag - } - } - })) - ); - - await EESecretService.addSecretVersions({ - secretVersions: encryptedSecrets.map(({ secret, value }) => { - const { - _id, - version, - workspace, - type, - folder, - secretBlindIndex, - secretKeyIV, - secretKeyTag, - secretKeyCiphertext, - skipMultilineEncoding, - environment, - algorithm, - keyEncoding - } = secret; - - return new SecretVersion({ - secret: _id, - version: version + 1, - workspace: workspace, - type, - folder, - environment, - isDeleted: false, - secretBlindIndex: secretBlindIndex, - secretKeyCiphertext: secretKeyCiphertext, - secretKeyIV: secretKeyIV, - secretKeyTag: secretKeyTag, - secretValueCiphertext: value.ciphertext, - secretValueIV: value.iv, - secretValueTag: value.tag, - algorithm, - keyEncoding, - skipMultilineEncoding - }); - }) - }); - - // akhilmhdh: @tony need to do something about this as its depend on authData which is not possibile in here - // await EEAuditLogService.createAuditLog( - // {actor:ActorType.Machine}, - // { - // type: EventType.UPDATE_SECRETS, - // metadata: { - // environment, - // secretPath, - // secrets: secretsToBeUpdated.map(({ _id, version, secretBlindIndex }) => ({ - // secretId: _id.toString(), - // secretKey: secretBlindIndexToKey[secretBlindIndex || ""], - // secretVersion: version + 1 - // })) - // } - // }, - // { - // workspaceId - // } - // ); - - const folderId = encryptedSecrets?.[0]?.secret?.folder; - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId, - environment: secretRotation.environment, - folderId - }); - - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: secretRotation.workspace, - environment: secretRotation.environment, - secretPath: secretRotation.secretPath - }) - }); - - const postHogClient = await TelemetryService.getPostHogClient(); - if (postHogClient) { - postHogClient.capture({ - event: "secrets rotated", - properties: { - numberOfSecrets: encryptedSecrets.length, - environment: secretRotation.environment, - workspaceId, - folderId - } - }); - } - } catch (err) { - logger.error(err); - await SecretRotation.findByIdAndUpdate(rotationStratDocId, { - status: "failed", - statusMessage: (err as Error).message, - lastRotatedAt: new Date().toUTCString() - }); - } - - return Promise.resolve(); -}); - -const daysToMillisecond = (days: number) => days * 24 * 60 * 60 * 1000; -export const startSecretRotationQueue = async (rotationDocId: string, interval: number) => { - // when migration to bull mq just use the option immedite to trigger repeatable immediately - secretRotationQueue.add({ rotationDocId }, { jobId: rotationDocId, removeOnComplete: true }); - return secretRotationQueue.add( - { rotationDocId }, - { repeat: { every: daysToMillisecond(interval) }, jobId: rotationDocId } - ); -}; - -export const removeSecretRotationQueue = async (rotationDocId: string, interval: number) => { - return secretRotationQueue.removeRepeatable({ every: interval * 1000, jobId: rotationDocId }); -}; diff --git a/backend-mongo/src/ee/secretRotation/queue/queue.utils.ts b/backend-mongo/src/ee/secretRotation/queue/queue.utils.ts deleted file mode 100644 index c1ddbefc1..000000000 --- a/backend-mongo/src/ee/secretRotation/queue/queue.utils.ts +++ /dev/null @@ -1,179 +0,0 @@ -import axios from "axios"; -import jmespath from "jmespath"; -import { customAlphabet } from "nanoid"; -import { Client as PgClient } from "pg"; -import mysql from "mysql2"; -import { - ISecretRotationData, - TAssignOp, - TDbProviderClients, - TDbProviderFunction, - TDirectAssignOp, - THttpProviderFunction, - TProviderFunction, - TProviderFunctionTypes -} from "../types"; -const REGEX = /\${([^}]+)}/g; -const SLUG_ALPHABETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; -const nanoId = customAlphabet(SLUG_ALPHABETS, 10); - -export const interpolate = (data: any, getValue: (key: string) => unknown) => { - if (!data) return; - - if (typeof data === "number") return data; - - if (typeof data === "string") { - return data.replace(REGEX, (_a, b) => getValue(b) as string); - } - - if (typeof data === "object" && Array.isArray(data)) { - data.forEach((el, index) => { - data[index] = interpolate(el, getValue); - }); - } - - if (typeof data === "object") { - if ((data as { ref: string })?.ref) return getValue((data as { ref: string }).ref); - const temp = data as Record; // for converting ts object to record type - Object.keys(temp).forEach((key) => { - temp[key as keyof typeof temp] = interpolate(data[key as keyof typeof temp], getValue); - }); - } - return data; -}; - -const getInterpolationValue = (variables: ISecretRotationData) => (key: string) => { - if (key.includes("|")) { - const [keyword, ...arg] = key.split("|").map((el) => el.trim()); - switch (keyword) { - case "random": { - return nanoId(parseInt(arg[0], 10)); - } - default: { - throw Error(`Interpolation key not found - ${key}`); - } - } - } - const [type, keyName] = key.split(".").map((el) => el.trim()); - return variables[type as keyof ISecretRotationData][keyName]; -}; - -export const secretRotationHttpFn = async ( - func: THttpProviderFunction, - variables: ISecretRotationData -) => { - // string interpolation - const headers = interpolate(func.header, getInterpolationValue(variables)); - const url = interpolate(func.url, getInterpolationValue(variables)); - const body = interpolate(func.body, getInterpolationValue(variables)); - // axios will automatically throw error if req status is not between 2xx range - return axios({ method: func.method, url, headers, data: body }); -}; - -export const secretRotationDbFn = async ( - func: TDbProviderFunction, - variables: ISecretRotationData -) => { - const { type, client, pre, ...dbConnection } = func; - const { username, password, host, database, port, query, ca } = interpolate( - dbConnection, - getInterpolationValue(variables) - ); - const ssl = ca ? { rejectUnauthorized: false, ca } : undefined; - if (host === "localhost" || host === "127.0.0.1") throw new Error("Invalid db host"); - if (client === TDbProviderClients.Pg) { - const pgClient = new PgClient({ user: username, password, host, database, port, ssl }); - await pgClient.connect(); - const res = await pgClient.query(query); - await pgClient.end(); - return res.rows[0]; - } else if (client === TDbProviderClients.Sql) { - const sqlClient = mysql.createPool({ - user: username, - password, - host, - database, - port, - connectionLimit: 1, - ssl - }); - const res = await new Promise((resolve, reject) => { - sqlClient.query(query, (err, data) => { - if (err) return reject(err); - resolve(data); - }); - }); - await new Promise((resolve, reject) => { - sqlClient.end(function (err) { - if (err) return reject(err); - return resolve({}); - }); - }); - return (res as any)?.[0]; - } -}; - -export const secretRotationPreSetFn = ( - op: Record, - variables: ISecretRotationData -) => { - const getValFn = getInterpolationValue(variables); - Object.entries(op || {}).forEach(([key, assignFn]) => { - const [type, keyName] = key.split(".") as [keyof ISecretRotationData, string]; - variables[type][keyName] = interpolate(assignFn.value, getValFn); - }); -}; - -export const secretRotationSetFn = async ( - func: TProviderFunction, - variables: ISecretRotationData -) => { - const getValFn = getInterpolationValue(variables); - // http setter - if (func.type === TProviderFunctionTypes.HTTP) { - const res = await secretRotationHttpFn(func, variables); - Object.entries(func.setter || {}).forEach(([key, assignFn]) => { - const [type, keyName] = key.split(".") as [keyof ISecretRotationData, string]; - if (assignFn.assign === TAssignOp.JmesPath) { - variables[type][keyName] = jmespath.search(res.data, assignFn.path); - } else if (assignFn.value) { - variables[type][keyName] = interpolate(assignFn.value, getValFn); - } - }); - // db setter - } else if (func.type === TProviderFunctionTypes.DB) { - const data = await secretRotationDbFn(func, variables); - Object.entries(func.setter || {}).forEach(([key, assignFn]) => { - const [type, keyName] = key.split(".") as [keyof ISecretRotationData, string]; - if (assignFn.assign === TAssignOp.JmesPath) { - if (typeof data === "object") { - variables[type][keyName] = jmespath.search(data, assignFn.path); - } - } else if (assignFn.value) { - variables[type][keyName] = interpolate(assignFn.value, getValFn); - } - }); - } -}; - -export const secretRotationTestFn = async ( - func: TProviderFunction, - variables: ISecretRotationData -) => { - if (func.type === TProviderFunctionTypes.HTTP) { - await secretRotationHttpFn(func, variables); - } else if (func.type === TProviderFunctionTypes.DB) { - await secretRotationDbFn(func, variables); - } -}; - -export const secretRotationRemoveFn = async ( - func: TProviderFunction, - variables: ISecretRotationData -) => { - if (!func) return; - if (func.type === TProviderFunctionTypes.HTTP) { - // string interpolation - return await secretRotationHttpFn(func, variables); - } -}; diff --git a/backend-mongo/src/ee/secretRotation/service.ts b/backend-mongo/src/ee/secretRotation/service.ts deleted file mode 100644 index 9e00f20e1..000000000 --- a/backend-mongo/src/ee/secretRotation/service.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { ISecretRotationEncData, TCreateSecretRotation, TGetProviderTemplates } from "./types"; -import { rotationTemplates } from "./templates"; -import { SecretRotation } from "./models"; -import { client, getEncryptionKey, getRootEncryptionKey } from "../../config"; -import { BadRequestError } from "../../utils/errors"; -import Ajv from "ajv"; -import { removeSecretRotationQueue, startSecretRotationQueue } from "./queue/queue"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8 -} from "../../variables"; -import { encryptSymmetric128BitHexKeyUTF8 } from "../../utils/crypto"; - -const ajv = new Ajv({ strict: false }); - -export const getProviderTemplate = async ({ workspaceId }: TGetProviderTemplates) => { - return { - custom: [], - providers: rotationTemplates - }; -}; - -export const createSecretRotation = async ({ - workspaceId, - secretPath, - environment, - provider, - interval, - inputs, - outputs -}: TCreateSecretRotation) => { - const rotationTemplate = rotationTemplates.find(({ name }) => name === provider); - if (!rotationTemplate) throw BadRequestError({ message: "Provider not found" }); - - const formattedInputs: Record = {}; - Object.entries(inputs).forEach(([key, value]) => { - const type = rotationTemplate.template.inputs.properties[key].type; - if (type === "string") { - formattedInputs[key] = value; - return; - } - if (type === "integer") { - formattedInputs[key] = parseInt(value as string, 10); - return; - } - formattedInputs[key] = JSON.parse(value as string); - }); - // ensure input one follows the correct schema - const valid = ajv.validate(rotationTemplate.template.inputs, formattedInputs); - if (!valid) { - throw BadRequestError({ message: ajv.errors?.[0].message }); - } - - const encData: Partial = { - inputs: formattedInputs, - creds: [] - }; - - const secretRotation = new SecretRotation({ - workspace: workspaceId, - provider, - environment, - secretPath, - interval, - outputs: Object.entries(outputs).map(([key, secret]) => ({ key, secret })) - }); - - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - if (rootEncryptionKey) { - const { ciphertext, iv, tag } = client.encryptSymmetric( - JSON.stringify(encData), - rootEncryptionKey - ); - secretRotation.encryptedDataIV = iv; - secretRotation.encryptedDataTag = tag; - secretRotation.encryptedData = ciphertext; - secretRotation.algorithm = ALGORITHM_AES_256_GCM; - secretRotation.keyEncoding = ENCODING_SCHEME_BASE64; - } else if (encryptionKey) { - const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: JSON.stringify(encData), - key: encryptionKey - }); - secretRotation.encryptedDataIV = iv; - secretRotation.encryptedDataTag = tag; - secretRotation.encryptedData = ciphertext; - secretRotation.algorithm = ALGORITHM_AES_256_GCM; - secretRotation.keyEncoding = ENCODING_SCHEME_UTF8; - } - - await secretRotation.save(); - await startSecretRotationQueue(secretRotation._id.toString(), interval); - - return secretRotation; -}; - -export const deleteSecretRotation = async ({ id }: { id: string }) => { - const doc = await SecretRotation.findByIdAndRemove(id); - if (!doc) throw BadRequestError({ message: "Rotation not found" }); - - await removeSecretRotationQueue(doc._id.toString(), doc.interval); - return doc; -}; - -export const restartSecretRotation = async ({ id }: { id: string }) => { - const secretRotation = await SecretRotation.findById(id); - if (!secretRotation) throw BadRequestError({ message: "Rotation not found" }); - - await removeSecretRotationQueue(secretRotation._id.toString(), secretRotation.interval); - await startSecretRotationQueue(secretRotation._id.toString(), secretRotation.interval); - - return secretRotation; -}; - -export const getSecretRotationById = async ({ id }: { id: string }) => { - const doc = await SecretRotation.findById(id); - if (!doc) throw BadRequestError({ message: "Rotation not found" }); - return doc; -}; - -export const getSecretRotationOfWorkspace = async (workspaceId: string) => { - const secretRotations = await SecretRotation.find({ - workspace: workspaceId - }).populate("outputs.secret"); - - return secretRotations; -}; diff --git a/backend-mongo/src/ee/secretRotation/templates/index.ts b/backend-mongo/src/ee/secretRotation/templates/index.ts deleted file mode 100644 index 063d5149f..000000000 --- a/backend-mongo/src/ee/secretRotation/templates/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ISecretRotationProviderTemplate } from "../types"; -import { MYSQL_TEMPLATE } from "./mysql"; -import { POSTGRES_TEMPLATE } from "./postgres"; -import { SENDGRID_TEMPLATE } from "./sendgrid"; - -export const rotationTemplates: ISecretRotationProviderTemplate[] = [ - { - name: "sendgrid", - title: "Twilio Sendgrid", - image: "sendgrid.png", - description: "Rotate Twilio Sendgrid API keys", - template: SENDGRID_TEMPLATE - }, - { - name: "postgres", - title: "PostgreSQL", - image: "postgres.png", - description: "Rotate PostgreSQL/CockroachDB user credentials", - template: POSTGRES_TEMPLATE - }, - { - name: "mysql", - title: "MySQL", - image: "mysql.png", - description: "Rotate MySQL@7/MariaDB user credentials", - template: MYSQL_TEMPLATE - } -]; diff --git a/backend-mongo/src/ee/secretRotation/templates/mysql.ts b/backend-mongo/src/ee/secretRotation/templates/mysql.ts deleted file mode 100644 index ce44c753f..000000000 --- a/backend-mongo/src/ee/secretRotation/templates/mysql.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { TAssignOp, TDbProviderClients, TProviderFunctionTypes } from "../types"; - -export const MYSQL_TEMPLATE = { - inputs: { - type: "object" as const, - properties: { - admin_username: { type: "string" as const }, - admin_password: { type: "string" as const }, - host: { type: "string" as const }, - database: { type: "string" as const }, - port: { type: "integer" as const, default: "3306" }, - username1: { - type: "string", - default: "infisical-sql-user1", - desc: "This user must be created in your database" - }, - username2: { - type: "string", - default: "infisical-sql-user2", - desc: "This user must be created in your database" - }, - ca: { type: "string", desc: "SSL certificate for db auth(string)" } - }, - required: [ - "admin_username", - "admin_password", - "host", - "database", - "username1", - "username2", - "port" - ], - additionalProperties: false - }, - outputs: { - db_username: { type: "string" }, - db_password: { type: "string" } - }, - internal: { - rotated_password: { type: "string" }, - username: { type: "string" } - }, - functions: { - set: { - type: TProviderFunctionTypes.DB as const, - client: TDbProviderClients.Sql, - username: "${inputs.admin_username}", - password: "${inputs.admin_password}", - host: "${inputs.host}", - database: "${inputs.database}", - port: "${inputs.port}", - ca: "${inputs.ca}", - query: "ALTER USER ${internal.username} IDENTIFIED BY '${internal.rotated_password}'", - setter: { - "outputs.db_username": { - assign: TAssignOp.Direct as const, - value: "${internal.username}" - }, - "outputs.db_password": { - assign: TAssignOp.Direct as const, - value: "${internal.rotated_password}" - } - }, - pre: { - "internal.rotated_password": { - assign: TAssignOp.Direct as const, - value: "${random | 32}" - } - } - }, - test: { - type: TProviderFunctionTypes.DB as const, - client: TDbProviderClients.Sql, - username: "${internal.username}", - password: "${internal.rotated_password}", - host: "${inputs.host}", - database: "${inputs.database}", - port: "${inputs.port}", - ca: "${inputs.ca}", - query: "SELECT NOW()" - } - } -}; diff --git a/backend-mongo/src/ee/secretRotation/templates/postgres.ts b/backend-mongo/src/ee/secretRotation/templates/postgres.ts deleted file mode 100644 index 3b3153be1..000000000 --- a/backend-mongo/src/ee/secretRotation/templates/postgres.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { TAssignOp, TDbProviderClients, TProviderFunctionTypes } from "../types"; - -export const POSTGRES_TEMPLATE = { - inputs: { - type: "object" as const, - properties: { - admin_username: { type: "string" as const }, - admin_password: { type: "string" as const }, - host: { type: "string" as const }, - database: { type: "string" as const }, - port: { type: "integer" as const, default: "5432" }, - username1: { - type: "string", - default: "infisical-pg-user1", - desc: "This user must be created in your database" - }, - username2: { - type: "string", - default: "infisical-pg-user2", - desc: "This user must be created in your database" - }, - ca: { type: "string", desc: "SSL certificate for db auth(string)" } - }, - required: [ - "admin_username", - "admin_password", - "host", - "database", - "username1", - "username2", - "port" - ], - additionalProperties: false - }, - outputs: { - db_username: { type: "string" }, - db_password: { type: "string" } - }, - internal: { - rotated_password: { type: "string" }, - username: { type: "string" } - }, - functions: { - set: { - type: TProviderFunctionTypes.DB as const, - client: TDbProviderClients.Pg, - username: "${inputs.admin_username}", - password: "${inputs.admin_password}", - host: "${inputs.host}", - database: "${inputs.database}", - port: "${inputs.port}", - ca: "${inputs.ca}", - query: "ALTER USER ${internal.username} WITH PASSWORD '${internal.rotated_password}'", - setter: { - "outputs.db_username": { - assign: TAssignOp.Direct as const, - value: "${internal.username}" - }, - "outputs.db_password": { - assign: TAssignOp.Direct as const, - value: "${internal.rotated_password}" - } - }, - pre: { - "internal.rotated_password": { - assign: TAssignOp.Direct as const, - value: "${random | 32}" - } - } - }, - test: { - type: TProviderFunctionTypes.DB as const, - client: TDbProviderClients.Pg, - username: "${internal.username}", - password: "${internal.rotated_password}", - host: "${inputs.host}", - database: "${inputs.database}", - port: "${inputs.port}", - ca: "${inputs.ca}", - query: "SELECT NOW()" - } - } -}; diff --git a/backend-mongo/src/ee/secretRotation/templates/sendgrid.ts b/backend-mongo/src/ee/secretRotation/templates/sendgrid.ts deleted file mode 100644 index b600f3e0c..000000000 --- a/backend-mongo/src/ee/secretRotation/templates/sendgrid.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { TAssignOp, TProviderFunctionTypes } from "../types"; - -export const SENDGRID_TEMPLATE = { - inputs: { - type: "object" as const, - properties: { - admin_api_key: { type: "string" as const, desc: "Sendgrid admin api key to create new keys" }, - api_key_scopes: { - type: "array", - items: { type: "string" as const }, - desc: "Scopes for created tokens by rotation(Array)" - } - }, - required: ["admin_api_key", "api_key_scopes"], - additionalProperties: false - }, - outputs: { - api_key: { type: "string" } - }, - internal: { - api_key_id: { type: "string" } - }, - functions: { - set: { - type: TProviderFunctionTypes.HTTP as const, - url: "https://api.sendgrid.com/v3/api_keys", - method: "POST", - header: { - Authorization: "Bearer ${inputs.admin_api_key}" - }, - body: { - name: "infisical-${random | 16}", - scopes: { ref: "inputs.api_key_scopes" } - }, - setter: { - "outputs.api_key": { - assign: TAssignOp.JmesPath as const, - path: "api_key" - }, - "internal.api_key_id": { - assign: TAssignOp.JmesPath as const, - path: "api_key_id" - } - } - }, - remove: { - type: TProviderFunctionTypes.HTTP as const, - url: "https://api.sendgrid.com/v3/api_keys/${internal.api_key_id}", - header: { - Authorization: "Bearer ${inputs.admin_api_key}" - }, - method: "DELETE" - }, - test: { - type: TProviderFunctionTypes.HTTP as const, - url: "https://api.sendgrid.com/v3/api_keys/${internal.api_key_id}", - header: { - Authorization: "Bearer ${inputs.admin_api_key}" - }, - method: "GET" - } - } -}; diff --git a/backend-mongo/src/ee/secretRotation/types.ts b/backend-mongo/src/ee/secretRotation/types.ts deleted file mode 100644 index 36ad36798..000000000 --- a/backend-mongo/src/ee/secretRotation/types.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { Document, Types } from "mongoose"; - -export interface ISecretRotation extends Document { - _id: Types.ObjectId; - name: string; - interval: number; - provider: string; - customProvider: Types.ObjectId; - workspace: Types.ObjectId; - environment: string; - secretPath: string; - outputs: Array<{ - key: string; - secret: Types.ObjectId; - }>; - status?: "success" | "failed"; - lastRotatedAt?: string; - statusMessage?: string; - encryptedData: string; - encryptedDataIV: string; - encryptedDataTag: string; - algorithm: string; - keyEncoding: string; -} - -export type ISecretRotationEncData = { - inputs: Record; - creds: Array<{ - outputs: Record; - internal: Record; - }>; -}; - -export type ISecretRotationData = { - inputs: Record; - outputs: Record; - internal: Record; -}; - -export type ISecretRotationProviderTemplate = { - name: string; - title: string; - image?: string; - description?: string; - template: TProviderTemplate; -}; - -export enum TProviderFunctionTypes { - HTTP = "http", - DB = "database" -} - -export enum TDbProviderClients { - // postgres, cockroack db, amazon red shift - Pg = "pg", - // mysql and maria db - Sql = "sql" -} - -export enum TAssignOp { - Direct = "direct", - JmesPath = "jmesopath" -} - -export type TJmesPathAssignOp = { - assign: TAssignOp.JmesPath; - path: string; -}; - -export type TDirectAssignOp = { - assign: TAssignOp.Direct; - value: string; -}; - -export type TAssignFunction = TJmesPathAssignOp | TDirectAssignOp; - -export type THttpProviderFunction = { - type: TProviderFunctionTypes.HTTP; - url: string; - method: string; - header?: Record; - query?: Record; - body?: Record; - setter?: Record; - pre?: Record; -}; - -export type TDbProviderFunction = { - type: TProviderFunctionTypes.DB; - client: TDbProviderClients; - username: string; - password: string; - host: string; - database: string; - port: string; - query: string; - setter?: Record; - pre?: Record; -}; - -export type TProviderFunction = THttpProviderFunction | TDbProviderFunction; - -export type TProviderTemplate = { - inputs: { - type: "object"; - properties: Record; - required?: string[]; - }; - outputs: Record; - functions: { - set: TProviderFunction; - remove?: TProviderFunction; - test: TProviderFunction; - }; -}; - -// function type args -export type TGetProviderTemplates = { - workspaceId: string; -}; - -export type TCreateSecretRotation = { - provider: string; - customProvider?: string; - workspaceId: string; - secretPath: string; - environment: string; - interval: number; - inputs: Record; - outputs: Record; -}; diff --git a/backend-mongo/src/ee/services/EEAuditLogService.ts b/backend-mongo/src/ee/services/EEAuditLogService.ts deleted file mode 100644 index 9d220feee..000000000 --- a/backend-mongo/src/ee/services/EEAuditLogService.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Types } from "mongoose"; -import { AuditLog, Event } from "../models"; -import { AuthData } from "../../interfaces/middleware"; -import EELicenseService from "./EELicenseService"; -import { Workspace } from "../../models"; - -interface EventScope { - workspaceId?: Types.ObjectId; - organizationId?: Types.ObjectId; -} - -type ValidEventScope = - | Required> - | Required> - | Required - | Record; - -export default class EEAuditLogService { - static async createAuditLog(authData: AuthData, event: Event, eventScope: ValidEventScope = {}, shouldSave = true) { - - const MS_IN_DAY = 24 * 60 * 60 * 1000; - - let organizationId; - if ("organizationId" in eventScope) { - organizationId = eventScope.organizationId; - } - - let workspaceId; - if ("workspaceId" in eventScope) { - workspaceId = eventScope.workspaceId; - - if (!organizationId) { - organizationId = (await Workspace.findById(workspaceId).select("organization").lean())?.organization; - } - } - - let expiresAt; - if (organizationId) { - const ttl = (await EELicenseService.getPlan(organizationId)).auditLogsRetentionDays * MS_IN_DAY; - expiresAt = new Date(Date.now() + ttl); - } - - const auditLog = await new AuditLog({ - actor: authData.actor, - organization: organizationId, - workspace: workspaceId, - ipAddress: authData.ipAddress, - event, - userAgent: authData.userAgent, - userAgentType: authData.userAgentType, - expiresAt - }); - - if (shouldSave) { - await auditLog.save(); - } - - return auditLog; - } -} \ No newline at end of file diff --git a/backend-mongo/src/ee/services/EELicenseService.ts b/backend-mongo/src/ee/services/EELicenseService.ts deleted file mode 100644 index 6baea04dc..000000000 --- a/backend-mongo/src/ee/services/EELicenseService.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { Types } from "mongoose"; -import * as Sentry from "@sentry/node"; -import NodeCache from "node-cache"; -import { - getLicenseKey, - getLicenseServerKey, - getLicenseServerUrl, -} from "../../config"; -import { - licenseKeyRequest, - licenseServerKeyRequest, - refreshLicenseKeyToken, - refreshLicenseServerKeyToken, -} from "../../config/request"; -import { Organization } from "../../models"; -import { OrganizationNotFoundError } from "../../utils/errors"; - -interface FeatureSet { - _id: string | null; - slug: "starter" | "team" | "pro" | "enterprise" | null; - tier: number; - workspaceLimit: number | null; - workspacesUsed: number; - memberLimit: number | null; - membersUsed: number; - environmentLimit: number | null; - environmentsUsed: number; - secretVersioning: boolean; - pitRecovery: boolean; - ipAllowlisting: boolean; - rbac: boolean; - customRateLimits: boolean; - customAlerts: boolean; - auditLogs: boolean; - auditLogsRetentionDays: number; - samlSSO: boolean; - status: "incomplete" | "incomplete_expired" | "trialing" | "active" | "past_due" | "canceled" | "unpaid" | null; - trial_end: number | null; - has_used_trial: boolean; - secretApproval: boolean; - secretRotation: boolean; -} - -/** - * Class to handle license/plan configurations: - * - Infisical Cloud: Fetch and cache customer plans in [localFeatureSet] - * - Self-hosted regular: Use default global feature set - * - Self-hosted enterprise: Fetch and update global feature set - */ -class EELicenseService { - - private readonly _isLicenseValid: boolean; // TODO: deprecate - - public instanceType: "self-hosted" | "enterprise-self-hosted" | "cloud" = "self-hosted"; - - public globalFeatureSet: FeatureSet = { - _id: null, - slug: null, - tier: -1, - workspaceLimit: null, - workspacesUsed: 0, - memberLimit: null, - membersUsed: 0, - environmentLimit: null, - environmentsUsed: 0, - secretVersioning: true, - pitRecovery: false, - ipAllowlisting: false, - rbac: false, - customRateLimits: false, - customAlerts: false, - auditLogs: false, - auditLogsRetentionDays: 0, - samlSSO: false, - status: null, - trial_end: null, - has_used_trial: true, - secretApproval: false, - secretRotation: true, - } - - public localFeatureSet: NodeCache; - - constructor() { - this._isLicenseValid = true; - this.localFeatureSet = new NodeCache({ - stdTTL: 60, - }); - } - - public async getPlan(organizationId: Types.ObjectId, workspaceId?: Types.ObjectId): Promise { - try { - if (this.instanceType === "cloud") { - const cachedPlan = this.localFeatureSet.get(`${organizationId.toString()}-${workspaceId?.toString() ?? ""}`); - if (cachedPlan) { - return cachedPlan; - } - - const organization = await Organization.findById(organizationId); - if (!organization) throw OrganizationNotFoundError(); - - let url = `${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`; - - if (workspaceId) { - url += `?workspaceId=${workspaceId}`; - } - - const { data: { currentPlan } } = await licenseServerKeyRequest.get(url); - - // cache fetched plan for organization - this.localFeatureSet.set(`${organizationId.toString()}-${workspaceId?.toString() ?? ""}`, currentPlan); - - return currentPlan; - } - } catch (err) { - return this.globalFeatureSet; - } - - return this.globalFeatureSet; - } - - public async refreshPlan(organizationId: Types.ObjectId, workspaceId?: Types.ObjectId) { - if (this.instanceType === "cloud") { - this.localFeatureSet.del(`${organizationId.toString()}-${workspaceId?.toString() ?? ""}`); - await this.getPlan(organizationId, workspaceId); - } - } - - public async delPlan(organizationId: Types.ObjectId) { - if (this.instanceType === "cloud") { - this.localFeatureSet.del(`${organizationId.toString()}-`); - } - } - - public async initGlobalFeatureSet() { - const licenseServerKey = await getLicenseServerKey(); - const licenseKey = await getLicenseKey(); - - try { - if (licenseServerKey) { - // license server key is present -> validate it - const token = await refreshLicenseServerKeyToken() - - if (token) { - this.instanceType = "cloud"; - } - - return; - } - - if (licenseKey) { - // license key is present -> validate it - const token = await refreshLicenseKeyToken(); - - if (token) { - const { data: { currentPlan } } = await licenseKeyRequest.get( - `${await getLicenseServerUrl()}/api/license/v1/plan` - ); - - this.globalFeatureSet = currentPlan; - this.instanceType = "enterprise-self-hosted"; - } - } - } catch (err) { - // case: self-hosted free - Sentry.setUser(null); - Sentry.captureException(err); - } - } - - public get isLicenseValid(): boolean { - return this._isLicenseValid; - } -} - -export default new EELicenseService(); diff --git a/backend-mongo/src/ee/services/EESecretService.ts b/backend-mongo/src/ee/services/EESecretService.ts deleted file mode 100644 index 1e065b6cd..000000000 --- a/backend-mongo/src/ee/services/EESecretService.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Types } from "mongoose"; -import { ISecretVersion } from "../models"; -import { - addSecretVersionsHelper, - markDeletedSecretVersionsHelper, - takeSecretSnapshotHelper, -} from "../helpers/secret"; -import EELicenseService from "./EELicenseService"; - -/** - * Class to handle Enterprise Edition secret actions - */ -export default class EESecretService { - /** - * Save a secret snapshot that is a copy of the current state of secrets in workspace with id - * [workspaceId] under a new snapshot with incremented version under the - * SecretSnapshot collection. - * Requires a valid license key [licenseKey] - * @param {Object} obj - * @param {String} obj.workspaceId - * @returns {SecretSnapshot} secretSnapshot - new secret snpashot - */ - static async takeSecretSnapshot({ - workspaceId, - environment, - folderId, - }: { - workspaceId: Types.ObjectId; - environment: string; - folderId?: string; - }) { - if (!EELicenseService.isLicenseValid) return; - return await takeSecretSnapshotHelper({ - workspaceId, - environment, - folderId, - }); - } - - /** - * Add secret versions [secretVersions] to the SecretVersion collection. - * @param {Object} obj - * @param {Object[]} obj.secretVersions - * @returns {SecretVersion[]} newSecretVersions - new secret versions - */ - static async addSecretVersions({ - secretVersions, - }: { - secretVersions: ISecretVersion[]; - }) { - if (!EELicenseService.isLicenseValid) return; - return await addSecretVersionsHelper({ - secretVersions, - }); - } - - /** - * Mark secret versions associated with secrets with ids [secretIds] - * as deleted. - * @param {Object} obj - * @param {ObjectId[]} obj.secretIds - secret ids - */ - static async markDeletedSecretVersions({ - secretIds, - }: { - secretIds: Types.ObjectId[]; - }) { - if (!EELicenseService.isLicenseValid) return; - await markDeletedSecretVersionsHelper({ - secretIds, - }); - } -} diff --git a/backend-mongo/src/ee/services/GithubSecretScanning/GithubSecretScanningService.ts b/backend-mongo/src/ee/services/GithubSecretScanning/GithubSecretScanningService.ts deleted file mode 100644 index e0a45215c..000000000 --- a/backend-mongo/src/ee/services/GithubSecretScanning/GithubSecretScanningService.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Probot } from "probot"; -import { - GitAppOrganizationInstallation, - GitRisks -} from "../../models"; -import { scanGithubPushEventForSecretLeaks } from "../../../queues/secret-scanning/githubScanPushEvent"; -export default async (app: Probot) => { - app.on("installation.deleted", async (context) => { - const { payload } = context; - const { installation, repositories } = payload; - if (repositories) { - for (const repository of repositories) { - await GitRisks.deleteMany({ repositoryId: repository.id }) - } - await GitAppOrganizationInstallation.deleteOne({ installationId: installation.id }) - } - }) - - app.on("installation", async (context) => { - const { payload } = context; - payload.repositories - const { installation, repositories } = payload; - // TODO: start full repo scans - }) - - app.on("push", async (context) => { - const { payload } = context; - const { commits, repository, installation, pusher } = payload; - - if (!commits || !repository || !installation || !pusher) { - return - } - - const installationLinkToOrgExists = await GitAppOrganizationInstallation.findOne({ installationId: installation?.id }).lean() - if (!installationLinkToOrgExists) { - return - } - - scanGithubPushEventForSecretLeaks({ - commits: commits, - pusher: { name: pusher.name, email: pusher.email }, - repository: { fullName: repository.full_name, id: repository.id }, - organizationId: installationLinkToOrgExists.organizationId, - installationId: installation.id - }) - }); -}; diff --git a/backend-mongo/src/ee/services/GithubSecretScanning/helper.ts b/backend-mongo/src/ee/services/GithubSecretScanning/helper.ts deleted file mode 100644 index 3dc46d835..000000000 --- a/backend-mongo/src/ee/services/GithubSecretScanning/helper.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { exec } from "child_process"; -import { mkdir, readFile, rm, writeFile } from "fs"; -import { tmpdir } from "os"; -import { join } from "path" -import { SecretMatch } from "./types"; - -export async function scanFullRepoContentAndGetFindings(octokit: any, installationId: number, repositoryFullName: string): Promise { - const tempFolder = await createTempFolder(); - const findingsPath = join(tempFolder, "findings.json"); - const repoPath = join(tempFolder, "repo.git") - try { - const { data: { token }} = await octokit.apps.createInstallationAccessToken({installation_id: installationId}) - await cloneRepo(token, repositoryFullName, repoPath) - await runInfisicalScanOnRepo(repoPath, findingsPath); - const findingsData = await readFindingsFile(findingsPath); - return JSON.parse(findingsData); - } finally { - await deleteTempFolder(tempFolder); - } -} - -export async function scanContentAndGetFindings(textContent: string): Promise { - const tempFolder = await createTempFolder(); - const filePath = join(tempFolder, "content.txt"); - const findingsPath = join(tempFolder, "findings.json"); - - try { - await writeTextToFile(filePath, textContent); - await runInfisicalScan(filePath, findingsPath); - const findingsData = await readFindingsFile(findingsPath); - return JSON.parse(findingsData); - } finally { - await deleteTempFolder(tempFolder); - } -} - -export function createTempFolder(): Promise { - return new Promise((resolve, reject) => { - const tempDir = tmpdir() - const tempFolderName = Math.random().toString(36).substring(2); - const tempFolderPath = join(tempDir, tempFolderName); - - mkdir(tempFolderPath, (err: any) => { - if (err) { - reject(err); - } else { - resolve(tempFolderPath); - } - }); - }); -} - - - -export function writeTextToFile(filePath: string, content: string): Promise { - return new Promise((resolve, reject) => { - writeFile(filePath, content, (err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); -} - -export async function cloneRepo(installationAcccessToken: string, repositoryFullName: string, repoPath: string): Promise { - const cloneUrl = `https://x-access-token:${installationAcccessToken}@github.com/${repositoryFullName}.git`; - const command = `git clone ${cloneUrl} ${repoPath} --bare` - return new Promise((resolve, reject) => { - exec(command, (error) => { - if (error) { - reject(error); - } else { - resolve(); - } - }); - }) -} - -export function runInfisicalScanOnRepo(repoPath: string, outputPath: string): Promise { - return new Promise((resolve, reject) => { - const command = `cd ${repoPath} && infisical scan --exit-code=77 -r "${outputPath}"`; - exec(command, (error) => { - if (error && error.code != 77) { - reject(error); - } else { - resolve(); - } - }); - }); -} - -export function runInfisicalScan(inputPath: string, outputPath: string): Promise { - return new Promise((resolve, reject) => { - const command = `cat "${inputPath}" | infisical scan --exit-code=77 --pipe -r "${outputPath}"`; - exec(command, (error) => { - if (error && error.code != 77) { - reject(error); - } else { - resolve(); - } - }); - }); -} - -export function readFindingsFile(filePath: string): Promise { - return new Promise((resolve, reject) => { - readFile(filePath, "utf8", (err, data) => { - if (err) { - reject(err); - } else { - resolve(data); - } - }); - }); -} - -export function deleteTempFolder(folderPath: string): Promise { - return new Promise((resolve, reject) => { - rm(folderPath, { recursive: true }, (err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); -} - -export function convertKeysToLowercase(obj: T): T { - const convertedObj = {} as T; - - for (const key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - const lowercaseKey = key.charAt(0).toLowerCase() + key.slice(1); - convertedObj[lowercaseKey as keyof T] = obj[key]; - } - } - - return convertedObj; -} \ No newline at end of file diff --git a/backend-mongo/src/ee/services/GithubSecretScanning/types.ts b/backend-mongo/src/ee/services/GithubSecretScanning/types.ts deleted file mode 100644 index 7bedbbfc3..000000000 --- a/backend-mongo/src/ee/services/GithubSecretScanning/types.ts +++ /dev/null @@ -1,21 +0,0 @@ -export type SecretMatch = { - Description: string; - StartLine: number; - EndLine: number; - StartColumn: number; - EndColumn: number; - Match: string; - Secret: string; - File: string; - SymlinkFile: string; - Commit: string; - Entropy: number; - Author: string; - Email: string; - Date: string; - Message: string; - Tags: string[]; - RuleID: string; - Fingerprint: string; - FingerPrintWithoutCommitId: string -}; \ No newline at end of file diff --git a/backend-mongo/src/ee/services/ProjectRoleService.ts b/backend-mongo/src/ee/services/ProjectRoleService.ts deleted file mode 100644 index 18511948b..000000000 --- a/backend-mongo/src/ee/services/ProjectRoleService.ts +++ /dev/null @@ -1,415 +0,0 @@ -import { Types } from "mongoose"; -import { - AbilityBuilder, - ForcedSubject, - MongoAbility, - RawRuleOf, - buildMongoQueryMatcher, - createMongoAbility -} from "@casl/ability"; -import { UnauthorizedRequestError } from "../../utils/errors"; -import { FieldCondition, FieldInstruction, JsInterpreter } from "@ucast/mongo2js"; -import picomatch from "picomatch"; -import { AuthData } from "../../interfaces/middleware"; -import { ActorType, IRole, Role } from "../models"; -import { - IIdentity, - IdentityMembership, - Membership, - ServiceTokenData -} from "../../models"; -import { ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../../variables"; -import { BadRequestError } from "../../utils/errors"; - -const $glob: FieldInstruction = { - type: "field", - validate(instruction, value) { - if (typeof value !== "string") { - throw new Error(`"${instruction.name}" expects value to be a string`); - } - } -}; - -const glob: JsInterpreter> = (node, object, context) => { - const secretPath = context.get(object, node.field); - const permissionSecretGlobPath = node.value; - return picomatch.isMatch(secretPath, permissionSecretGlobPath, { strictSlashes: false }); -}; - -export const conditionsMatcher = buildMongoQueryMatcher({ $glob }, { glob }); - -export enum ProjectPermissionActions { - Read = "read", - Create = "create", - Edit = "edit", - Delete = "delete" -} - -export enum ProjectPermissionSub { - Role = "role", - Member = "member", - Settings = "settings", - Integrations = "integrations", - Webhooks = "webhooks", - ServiceTokens = "service-tokens", - Environments = "environments", - Tags = "tags", - AuditLogs = "audit-logs", - IpAllowList = "ip-allowlist", - Workspace = "workspace", - Secrets = "secrets", - SecretRollback = "secret-rollback", - SecretApproval = "secret-approval", - SecretRotation = "secret-rotation", - Identity = "identity" -} - -type SubjectFields = { - environment: string; - secretPath: string; -}; - -export type ProjectPermissionSet = - | [ - ProjectPermissionActions, - ProjectPermissionSub.Secrets | (ForcedSubject & SubjectFields) - ] - | [ProjectPermissionActions, ProjectPermissionSub.Role] - | [ProjectPermissionActions, ProjectPermissionSub.Tags] - | [ProjectPermissionActions, ProjectPermissionSub.Member] - | [ProjectPermissionActions, ProjectPermissionSub.Integrations] - | [ProjectPermissionActions, ProjectPermissionSub.Webhooks] - | [ProjectPermissionActions, ProjectPermissionSub.AuditLogs] - | [ProjectPermissionActions, ProjectPermissionSub.Environments] - | [ProjectPermissionActions, ProjectPermissionSub.IpAllowList] - | [ProjectPermissionActions, ProjectPermissionSub.Settings] - | [ProjectPermissionActions, ProjectPermissionSub.ServiceTokens] - | [ProjectPermissionActions, ProjectPermissionSub.SecretApproval] - | [ProjectPermissionActions, ProjectPermissionSub.SecretRotation] - | [ProjectPermissionActions, ProjectPermissionSub.Identity] - | [ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace] - | [ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace] - | [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback] - | [ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback]; - -const buildAdminPermission = () => { - const { can, build } = new AbilityBuilder>(createMongoAbility); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); - can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretApproval); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.SecretApproval); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); - can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRotation); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.SecretRotation); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); - can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Member); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Member); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Member); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Role); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Role); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Role); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Role); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Identity); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Identity); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Identity); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens); - can(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.ServiceTokens); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Settings); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Settings); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Environments); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Tags); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Tags); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs); - can(ProjectPermissionActions.Create, ProjectPermissionSub.AuditLogs); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.AuditLogs); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.AuditLogs); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList); - can(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.IpAllowList); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.IpAllowList); - - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace); - - return build({ conditionsMatcher }); -}; - -export const adminProjectPermissions = buildAdminPermission(); - -const buildMemberPermission = () => { - const { can, build } = new AbilityBuilder>(createMongoAbility); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); - can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); - can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Member); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Member); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Identity); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Identity); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Identity); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens); - can(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.ServiceTokens); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Settings); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Settings); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Environments); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); - can(ProjectPermissionActions.Create, ProjectPermissionSub.Tags); - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Tags); - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Role); - can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs); - can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList); - - return build({ conditionsMatcher }); -}; - -export const memberProjectPermissions = buildMemberPermission(); - -const buildViewerPermission = () => { - const { can, build } = new AbilityBuilder>(createMongoAbility); - - can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets); - can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); - can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); - can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); - can(ProjectPermissionActions.Read, ProjectPermissionSub.Member); - can(ProjectPermissionActions.Read, ProjectPermissionSub.Role); - can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); - can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks); - can(ProjectPermissionActions.Read, ProjectPermissionSub.Identity); - can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens); - can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings); - can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments); - can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); - can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs); - can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList); - - return build({ conditionsMatcher }); -}; - -export const viewerProjectPermission = buildViewerPermission(); - -const buildNoAccessProjectPermission = () => { - const { build } = new AbilityBuilder>(createMongoAbility); - return build({ conditionsMatcher }); -} - -export const noAccessProjectPermissions = buildNoAccessProjectPermission(); - -/** - * Return permissions for user/service pertaining to workspace with id [workspaceId] - * - * Note: should not rely on this function for ST V2 authorization logic - * b/c ST V2 does not support role-based access control - */ -export const getAuthDataProjectPermissions = async ({ - authData, - workspaceId -}: { - authData: AuthData; - workspaceId: Types.ObjectId; -}) => { - let role: "admin" | "member" | "viewer" | "no-access" | "custom"; - let customRole; - - switch (authData.actor.type) { - case ActorType.USER: { - const membership = await Membership.findOne({ - user: authData.authPayload._id, - workspace: workspaceId - }) - .populate<{ - customRole: IRole & { permissions: RawRuleOf>[] }; - }>("customRole") - .exec(); - - if (!membership || (membership.role === "custom" && !membership.customRole)) { - throw UnauthorizedRequestError(); - } - - role = membership.role; - customRole = membership.customRole; - break; - } - case ActorType.SERVICE: { - const serviceTokenData = await ServiceTokenData.findById(authData.authPayload._id); - if (!serviceTokenData || !serviceTokenData.workspace.equals(workspaceId)) throw UnauthorizedRequestError(); - role = "viewer"; - break; - } - case ActorType.IDENTITY: { - const identityMembership = await IdentityMembership.findOne({ - identity: authData.authPayload._id, - workspace: workspaceId - }) - .populate<{ - customRole: IRole & { permissions: RawRuleOf>[] }; - identity: IIdentity - }>("customRole identity") - .exec(); - - if (!identityMembership || (identityMembership.role === "custom" && !identityMembership.customRole)) { - throw UnauthorizedRequestError(); - } - - role = identityMembership.role; - customRole = identityMembership.customRole; - - break; - } - default: - throw UnauthorizedRequestError(); - } - - switch (role) { - case ADMIN: - return { permission: adminProjectPermissions }; - case MEMBER: - return { permission: memberProjectPermissions }; - case VIEWER: - return { permission: viewerProjectPermission }; - case NO_ACCESS: - return { permission: noAccessProjectPermissions }; - case CUSTOM: { - if (!customRole) throw UnauthorizedRequestError(); - return { - permission: createMongoAbility( - customRole.permissions, - { conditionsMatcher } - ) - }; - } - default: - throw UnauthorizedRequestError(); - } -} - -export const getWorkspaceRolePermissions = async (role: string, workspaceId: string) => { - const isCustomRole = ![ADMIN, MEMBER, VIEWER, NO_ACCESS].includes(role); - if (isCustomRole) { - const workspaceRole = await Role.findOne({ - slug: role, - isOrgRole: false, - workspace: new Types.ObjectId(workspaceId) - }); - - if (!workspaceRole) throw BadRequestError({ message: "Role not found" }); - - return createMongoAbility(workspaceRole.permissions as RawRuleOf>[], { - conditionsMatcher - }); - } - - switch (role) { - case ADMIN: - return adminProjectPermissions; - case MEMBER: - return memberProjectPermissions; - case VIEWER: - return viewerProjectPermission; - case NO_ACCESS: - return noAccessProjectPermissions; - default: - throw BadRequestError({ message: "Role not found" }); - } -} - -/** - * Extracts and formats permissions from a CASL Ability object or a raw permission set. - * @param ability - * @returns - */ - const extractPermissions = (ability: any) => { - return ability.A.map((permission: any) => `${permission.action}_${permission.subject}`); -} - -/** - * Compares two sets of permissions to determine if the first set is at least as privileged as the second set. - * The function checks if all permissions in the second set are contained within the first set and if the first set has equal or more permissions. - * -*/ -export const isAtLeastAsPrivilegedWorkspace = (permissions1: MongoAbility | ProjectPermissionSet, permissions2: MongoAbility | ProjectPermissionSet) => { - - const set1 = new Set(extractPermissions(permissions1)); - const set2 = new Set(extractPermissions(permissions2)); - - for (const perm of set2) { - if (!set1.has(perm)) { - return false; - } - } - - return set1.size >= set2.size; -} \ No newline at end of file diff --git a/backend-mongo/src/ee/services/RoleService.ts b/backend-mongo/src/ee/services/RoleService.ts deleted file mode 100644 index 1822c1d71..000000000 --- a/backend-mongo/src/ee/services/RoleService.ts +++ /dev/null @@ -1,301 +0,0 @@ -import { Types } from "mongoose"; -import { AbilityBuilder, MongoAbility, RawRuleOf, createMongoAbility } from "@casl/ability"; -import { - IIdentity, - IdentityMembershipOrg, - MembershipOrg -} from "../../models"; -import { ActorType, IRole, Role } from "../models"; -import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; -import { ACCEPTED, ADMIN, CUSTOM, MEMBER, NO_ACCESS} from "../../variables"; -import { conditionsMatcher } from "./ProjectRoleService"; -import { AuthData } from "../../interfaces/middleware"; - -export enum OrgPermissionActions { - Read = "read", - Create = "create", - Edit = "edit", - Delete = "delete" -} - -export enum OrgPermissionSubjects { - Workspace = "workspace", - Role = "role", - Member = "member", - Settings = "settings", - IncidentAccount = "incident-contact", - Sso = "sso", - Billing = "billing", - SecretScanning = "secret-scanning", - Identity = "identity" -} - -export type OrgPermissionSet = - | [OrgPermissionActions.Read, OrgPermissionSubjects.Workspace] - | [OrgPermissionActions.Create, OrgPermissionSubjects.Workspace] - | [OrgPermissionActions, OrgPermissionSubjects.Role] - | [OrgPermissionActions, OrgPermissionSubjects.Member] - | [OrgPermissionActions, OrgPermissionSubjects.Settings] - | [OrgPermissionActions, OrgPermissionSubjects.IncidentAccount] - | [OrgPermissionActions, OrgPermissionSubjects.Sso] - | [OrgPermissionActions, OrgPermissionSubjects.SecretScanning] - | [OrgPermissionActions, OrgPermissionSubjects.Billing] - | [OrgPermissionActions, OrgPermissionSubjects.Identity]; - -const buildAdminPermission = () => { - const { can, build } = new AbilityBuilder>(createMongoAbility); - // ws permissions - can(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace); - // role permission - can(OrgPermissionActions.Read, OrgPermissionSubjects.Role); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Role); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.Role); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.Role); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.Member); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Member); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.Member); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.Member); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning); - can(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.SecretScanning); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.Settings); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Settings); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.Settings); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.IncidentAccount); - can(OrgPermissionActions.Create, OrgPermissionSubjects.IncidentAccount); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.IncidentAccount); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.IncidentAccount); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.Sso); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Sso); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.Sso); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.Billing); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Identity); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity); - - return build({ conditionsMatcher }); -}; - -export const adminPermissions = buildAdminPermission(); - -const buildMemberPermission = () => { - const { can, build } = new AbilityBuilder>(createMongoAbility); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace); - can(OrgPermissionActions.Read, OrgPermissionSubjects.Member); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Member); - can(OrgPermissionActions.Read, OrgPermissionSubjects.Role); - can(OrgPermissionActions.Read, OrgPermissionSubjects.Settings); - can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); - can(OrgPermissionActions.Read, OrgPermissionSubjects.Sso); - can(OrgPermissionActions.Read, OrgPermissionSubjects.IncidentAccount); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning); - can(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.SecretScanning); - - can(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Identity); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity); - - return build({ conditionsMatcher }); -}; - -export const memberPermissions = buildMemberPermission(); - -const buildNoAccessPermission = () => { - const { build } = new AbilityBuilder>(createMongoAbility); - return build({ conditionsMatcher }); -} - -export const noAccessPermissions = buildNoAccessPermission(); - -export const getUserOrgPermissions = async (userId: string, orgId: string) => { - // TODO(akhilmhdh): speed this up by pulling from cache later - - const membership = await MembershipOrg.findOne({ - user: userId, - organization: orgId, - status: ACCEPTED - }) - .populate<{ customRole: IRole & { permissions: RawRuleOf>[] } }>( - "customRole" - ) - .exec(); - - if (!membership || (membership.role === "custom" && !membership.customRole)) { - throw UnauthorizedRequestError({ message: "User doesn't belong to organization" }); - } - - if (membership.role === ADMIN) return { permission: adminPermissions, membership }; - - if (membership.role === MEMBER) return { permission: memberPermissions, membership }; - - if (membership.role === NO_ACCESS) return { permission: noAccessPermissions, membership } - - if (membership.role === CUSTOM) { - const permission = createMongoAbility(membership.customRole.permissions, { - conditionsMatcher - }); - return { permission, membership }; - } - - throw BadRequestError({ message: "User role not found" }); -}; - -/** - * Return permissions for user/service pertaining to organization with id [organizationId] - * - * Note: should not rely on this function for ST V2 authorization logic - * b/c ST V2 does not support role-based access control but also not organization-level resources - */ - export const getAuthDataOrgPermissions = async ({ - authData, - organizationId -}: { - authData: AuthData; - organizationId: Types.ObjectId; -}) => { - let role: "admin" | "member" | "no-access" | "custom"; - let customRole; - - switch (authData.actor.type) { - case ActorType.USER: { - const membershipOrg = await MembershipOrg.findOne({ - user: authData.authPayload._id, - organization: organizationId, - status: ACCEPTED - }) - .populate<{ customRole: IRole & { permissions: RawRuleOf>[] } }>( - "customRole" - ) - .exec(); - - if (!membershipOrg || (membershipOrg.role === "custom" && !membershipOrg.customRole)) { - throw UnauthorizedRequestError({ message: "User doesn't belong to organization" }); - } - - role = membershipOrg.role; - customRole = membershipOrg.customRole; - break; - } - case ActorType.SERVICE: { - throw UnauthorizedRequestError({ - message: "Failed to access organization-level resources with service token" - }); - } - case ActorType.IDENTITY: { - const identityMembershipOrg = await IdentityMembershipOrg.findOne({ - identity: authData.authPayload._id, - organization: organizationId - }) - .populate<{ - customRole: IRole & { permissions: RawRuleOf>[] }; - identity: IIdentity - }>("customRole identity") - .exec(); - - if (!identityMembershipOrg || (identityMembershipOrg.role === "custom" && !identityMembershipOrg.customRole)) { - throw UnauthorizedRequestError(); - } - - role = identityMembershipOrg.role; - customRole = identityMembershipOrg.customRole; - break; - } - default: - throw UnauthorizedRequestError(); - } - - switch (role) { - case ADMIN: - return { permission: adminPermissions }; - case MEMBER: - return { permission: memberPermissions }; - case NO_ACCESS: - return { permission: noAccessPermissions }; - case CUSTOM: { - if (!customRole) throw UnauthorizedRequestError(); - return { - permission: createMongoAbility( - customRole.permissions, - { conditionsMatcher } - ) - }; - } - } -} - -export const getOrgRolePermissions = async (role: string, orgId: string) => { - const isCustomRole = ![ADMIN, MEMBER, NO_ACCESS].includes(role); - if (isCustomRole) { - const orgRole = await Role.findOne({ - slug: role, - isOrgRole: true, - organization: new Types.ObjectId(orgId) - }); - - if (!orgRole) throw BadRequestError({ message: "Org Role not found" }); - - return createMongoAbility(orgRole.permissions as RawRuleOf>[], { - conditionsMatcher - }); - } - - switch (role) { - case ADMIN: - return adminPermissions; - case MEMBER: - return memberPermissions; - case NO_ACCESS: - return noAccessPermissions; - default: - throw BadRequestError({ message: "User org role not found" }); - } -} - -/** - * Extracts and formats permissions from a CASL Ability object or a raw permission set. - * @param ability - * @returns - */ -const extractPermissions = (ability: any) => { - return ability.A.map((permission: any) => `${permission.action}_${permission.subject}`); -} - -/** - * Compares two sets of permissions to determine if the first set is at least as privileged as the second set. - * The function checks if all permissions in the second set are contained within the first set and if the first set has equal or more permissions. - * -*/ -export const isAtLeastAsPrivilegedOrg = (permissions1: MongoAbility | OrgPermissionSet, permissions2: MongoAbility | OrgPermissionSet) => { - - const set1 = new Set(extractPermissions(permissions1)); - const set2 = new Set(extractPermissions(permissions2)); - - for (const perm of set2) { - if (!set1.has(perm)) { - return false; - } - } - - return set1.size >= set2.size; -} \ No newline at end of file diff --git a/backend-mongo/src/ee/services/SecretApprovalService.ts b/backend-mongo/src/ee/services/SecretApprovalService.ts deleted file mode 100644 index 58e2a9a20..000000000 --- a/backend-mongo/src/ee/services/SecretApprovalService.ts +++ /dev/null @@ -1,656 +0,0 @@ -import picomatch from "picomatch"; -import { Types } from "mongoose"; -import { - containsGlobPatterns, - generateSecretBlindIndexWithSaltHelper, - getSecretBlindIndexSaltHelper -} from "../../helpers/secrets"; -import { Folder, ISecret, Secret } from "../../models"; -import { ISecretApprovalPolicy, SecretApprovalPolicy } from "../models/secretApprovalPolicy"; -import { - CommitType, - ISecretApprovalRequest, - ISecretApprovalSecChange, - ISecretCommits, - SecretApprovalRequest -} from "../models/secretApprovalRequest"; -import { BadRequestError } from "../../utils/errors"; -import { getFolderByPath } from "../../services/FolderService"; -import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_UTF8, SECRET_SHARED } from "../../variables"; -import TelemetryService from "../../services/TelemetryService"; -import { EEAuditLogService, EESecretService } from "../services"; -import { EventType, SecretVersion } from "../models"; -import { AuthData } from "../../interfaces/middleware"; - -// if glob pattern score is 1, if not exist score is 0 and if its not both then its exact path meaning score 2 -const getPolicyScore = (policy: ISecretApprovalPolicy) => - policy.secretPath ? (containsGlobPatterns(policy.secretPath) ? 1 : 2) : 0; - -// this will fetch the policy that gets priority for an environment and secret path -export const getSecretPolicyOfBoard = async ( - workspaceId: string, - environment: string, - secretPath: string -) => { - const policies = await SecretApprovalPolicy.find({ workspace: workspaceId, environment }); - if (!policies) return; - // this will filter policies either without scoped to secret path or the one that matches with secret path - const policiesFilteredByPath = policies.filter( - ({ secretPath: policyPath }) => - !policyPath || picomatch.isMatch(secretPath, policyPath, { strictSlashes: false }) - ); - // now sort by priority. exact secret path gets first match followed by glob followed by just env scoped - // if that is tie get by first createdAt - const policiesByPriority = policiesFilteredByPath.sort( - (a, b) => getPolicyScore(b) - getPolicyScore(a) - ); - const finalPolicy = policiesByPriority.shift(); - return finalPolicy; -}; - -const getLatestSecretVersion = async (secretIds: Types.ObjectId[]) => { - const latestSecretVersions = await SecretVersion.aggregate([ - { - $match: { - secret: { - $in: secretIds - }, - type: SECRET_SHARED - } - }, - { - $sort: { version: -1 } - }, - { - $group: { - _id: "$secret", - version: { $max: "$version" }, - versionId: { $max: "$_id" }, // id of latest secret versionId - secret: { $first: "$$ROOT" } - } - } - ]).exec(); - // reduced with secret id and latest version as document - return latestSecretVersions.reduce( - (prev, curr) => ({ ...prev, [curr._id.toString()]: curr.secret }), - {} - ); -}; - -type TApprovalCreateSecret = Omit & { - secretName: string; -}; -type TApprovalUpdateSecret = Partial> & { - secretName: string; - newSecretName?: string; -}; - -type TGenerateSecretApprovalRequestArg = { - workspaceId: string; - environment: string; - secretPath: string; - policy: ISecretApprovalPolicy; - data: { - [CommitType.CREATE]?: TApprovalCreateSecret[]; - [CommitType.UPDATE]?: TApprovalUpdateSecret[]; - [CommitType.DELETE]?: { secretName: string }[]; - }; - commiterMembershipId: string; - authData: AuthData; -}; - -export const generateSecretApprovalRequest = async ({ - workspaceId, - environment, - secretPath, - policy, - data, - commiterMembershipId, - authData -}: TGenerateSecretApprovalRequestArg) => { - // calculate folder id from secret path - let folderId = "root"; - const rootFolder = await Folder.findOne({ workspace: workspaceId, environment }); - if (!rootFolder && secretPath !== "/") throw BadRequestError({ message: "Folder not found" }); - if (rootFolder) { - const folder = getFolderByPath(rootFolder.nodes, secretPath); - if (!folder) throw BadRequestError({ message: "Folder not found" }); - folderId = folder.id; - } - - // generate secret blindIndexes - const salt = await getSecretBlindIndexSaltHelper({ - workspaceId: new Types.ObjectId(workspaceId) - }); - const commits: ISecretApprovalRequest["commits"] = []; - - // ----- - // for created secret approval change - const createdSecret = data[CommitType.CREATE]; - if (createdSecret && createdSecret?.length) { - // validation checks whether secret exists for creation - const secretBlindIndexes = await Promise.all( - createdSecret.map(({ secretName }) => - generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }) - ) - ).then((blindIndexes) => - blindIndexes.reduce>((prev, curr, i) => { - prev[createdSecret[i].secretName] = curr; - return prev; - }, {}) - ); - // check created secret exists - const exists = await Secret.exists({ - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - environment - }) - .or( - createdSecret.map(({ secretName }) => ({ - secretBlindIndex: secretBlindIndexes[secretName], - type: SECRET_SHARED - })) - ) - .exec(); - if (exists) throw BadRequestError({ message: "Secrets already exist" }); - commits.push( - ...createdSecret.map((el) => ({ - op: CommitType.CREATE as const, - newVersion: { - ...el, - version: 0, - _id: new Types.ObjectId(), - secretBlindIndex: secretBlindIndexes[el.secretName] - } - })) - ); - } - - // ---- - // updated secrets approval change - const updatedSecret = data[CommitType.UPDATE]; - if (updatedSecret && updatedSecret?.length) { - // validation checks whether secret doesn't exists for update - const secretBlindIndexes = await Promise.all( - updatedSecret.map(({ secretName }) => - generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }) - ) - ).then((blindIndexes) => - blindIndexes.reduce>((prev, curr, i) => { - prev[updatedSecret[i].secretName] = curr; - return prev; - }, {}) - ); - // check update secret exists - const oldSecrets = await Secret.find({ - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - environment, - type: SECRET_SHARED, - secretBlindIndex: { - $in: updatedSecret.map(({ secretName }) => secretBlindIndexes[secretName]) - } - }) - .select("+secretBlindIndex") - .lean() - .exec(); - if (oldSecrets.length !== updatedSecret.length) - throw BadRequestError({ message: "Secrets already exist" }); - - // finally check updating blindindex exist - const nameUpdatedSecrets = updatedSecret.filter(({ newSecretName }) => Boolean(newSecretName)); - const newSecretBlindIndexes = await Promise.all( - nameUpdatedSecrets.map(({ newSecretName }) => - generateSecretBlindIndexWithSaltHelper({ - secretName: newSecretName as string, - salt - }) - ) - ).then((blindIndexes) => - blindIndexes.reduce>((prev, curr, i) => { - prev[nameUpdatedSecrets[i].secretName] = curr; - return prev; - }, {}) - ); - const doesAnySecretExistWithNewIndex = await Secret.find({ - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - environment, - secretBlindIndex: { $in: Object.values(newSecretBlindIndexes) } - }); - if (doesAnySecretExistWithNewIndex.length) - throw BadRequestError({ message: "Secret with new name already exist" }); - - const oldSecretsGroupById = oldSecrets.reduce>( - (prev, curr) => ({ ...prev, [curr?.secretBlindIndex || ""]: curr }), - {} - ); - const latestSecretVersions = await getLatestSecretVersion( - updatedSecret.map((el) => oldSecretsGroupById[secretBlindIndexes[el.secretName]]._id) - ); - - commits.push( - ...updatedSecret.map((el) => { - const secretId = oldSecretsGroupById[secretBlindIndexes[el.secretName]]._id; - return { - op: CommitType.UPDATE as const, - secret: secretId, - secretVersion: latestSecretVersions[secretId.toString()]._id, - newVersion: { - ...el, - secretBlindIndex: newSecretBlindIndexes?.[el.secretName], - _id: new Types.ObjectId(), - version: oldSecretsGroupById[secretBlindIndexes[el.secretName]].version || 1 - } - }; - }) - ); - } - - // ----- - // deleted secrets - const deletedSecrets = data[CommitType.DELETE]; - if (deletedSecrets && deletedSecrets.length) { - const secretBlindIndexes = await Promise.all( - deletedSecrets.map(({ secretName }) => - generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }) - ) - ).then((blindIndexes) => - blindIndexes.reduce>((prev, curr, i) => { - prev[deletedSecrets[i].secretName] = curr; - return prev; - }, {}) - ); - - const secretsToDelete = await Secret.find({ - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - environment, - type: SECRET_SHARED, - secretBlindIndex: { - $in: deletedSecrets.map(({ secretName }) => secretBlindIndexes[secretName]) - } - }) - .select({ secretBlindIndex: 1, _id: 1 }) - .lean() - .exec(); - if (secretsToDelete.length !== deletedSecrets.length) - throw BadRequestError({ message: "Deleted secrets not found" }); - - const oldSecretsGroupById = secretsToDelete.reduce>( - (prev, curr) => ({ ...prev, [curr?.secretBlindIndex || ""]: curr }), - {} - ); - const latestSecretVersions = await getLatestSecretVersion( - deletedSecrets.map((el) => oldSecretsGroupById[secretBlindIndexes[el.secretName]]._id) - ); - - commits.push( - ...deletedSecrets.map((el) => { - const secretId = oldSecretsGroupById[secretBlindIndexes[el.secretName]]._id; - return { - op: CommitType.DELETE as const, - secret: secretId, - secretVersion: latestSecretVersions[secretId.toString()] - }; - }) - ); - } - - const secretApprovalRequest = new SecretApprovalRequest({ - workspace: workspaceId, - environment, - folderId, - policy, - commits, - committer: commiterMembershipId - }); - await secretApprovalRequest.save(); - - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.SECRET_APPROVAL_REQUEST, - metadata: { - committedBy: commiterMembershipId, - secretApprovalRequestId: secretApprovalRequest._id.toString(), - secretApprovalRequestSlug: secretApprovalRequest.slug - } - }, - { - workspaceId: secretApprovalRequest.workspace - } - ); - - return secretApprovalRequest; -}; - -// validation for a merge conditions happen in another function in controller -export const performSecretApprovalRequestMerge = async ( - id: string, - authData: AuthData, - userMembershipId: string -) => { - const secretApprovalRequest = await SecretApprovalRequest.findById(id) - .populate<{ commits: ISecretCommits }>({ - path: "commits.secret", - select: "+secretBlindIndex", - populate: { - path: "tags" - } - }) - .select("+commits.newVersion.secretBlindIndex"); - if (!secretApprovalRequest) throw BadRequestError({ message: "Approval request not found" }); - - const workspaceId = secretApprovalRequest.workspace; - const environment = secretApprovalRequest.environment; - const folderId = secretApprovalRequest.folderId; - const postHogClient = await TelemetryService.getPostHogClient(); - const conflicts: Array<{ secretId: string; op: CommitType }> = []; - - const secretCreationCommits = secretApprovalRequest.commits.filter( - ({ op }) => op === CommitType.CREATE - ) as Array<{ op: CommitType.CREATE; newVersion: ISecretApprovalSecChange }>; - if (secretCreationCommits.length) { - // the created secrets already exist thus creation conflict ones - const conflictedSecrets = await Secret.find({ - workspace: workspaceId, - environment, - folder: folderId, - secretBlindIndex: { - $in: secretCreationCommits.map(({ newVersion }) => newVersion.secretBlindIndex) - } - }) - .select("+secretBlindIndex") - .lean(); - const conflictGroupByBlindIndex = conflictedSecrets.reduce>( - (prev, curr) => ({ ...prev, [curr.secretBlindIndex || ""]: true }), - {} - ); - const nonConflictSecrets = secretCreationCommits.filter( - ({ newVersion }) => !conflictGroupByBlindIndex[newVersion.secretBlindIndex || ""] - ); - secretCreationCommits - .filter(({ newVersion }) => conflictGroupByBlindIndex[newVersion.secretBlindIndex || ""]) - .forEach((el) => { - conflicts.push({ op: CommitType.CREATE, secretId: el.newVersion._id.toString() }); - }); - - // create secret - const newlyCreatedSecrets: ISecret[] = await Secret.insertMany( - nonConflictSecrets.map( - ({ - newVersion: { - secretKeyIV, - secretKeyTag, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretKeyCiphertext, - secretValueCiphertext, - secretCommentCiphertext, - skipMultilineEncoding, - secretBlindIndex, - algorithm, - keyEncoding, - tags - } - }) => ({ - version: 1, - workspace: new Types.ObjectId(workspaceId), - environment, - type: SECRET_SHARED, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - folder: folderId, - algorithm: algorithm || ALGORITHM_AES_256_GCM, - keyEncoding: keyEncoding || ENCODING_SCHEME_UTF8, - tags, - skipMultilineEncoding, - secretBlindIndex - }) - ) - ); - - await EESecretService.addSecretVersions({ - secretVersions: newlyCreatedSecrets.map( - (secret) => - new SecretVersion({ - secret: secret._id, - version: secret.version, - workspace: secret.workspace, - type: secret.type, - folder: folderId, - tags: secret.tags, - skipMultilineEncoding: secret?.skipMultilineEncoding, - environment: secret.environment, - isDeleted: false, - secretBlindIndex: secret.secretBlindIndex, - secretKeyCiphertext: secret.secretKeyCiphertext, - secretKeyIV: secret.secretKeyIV, - secretKeyTag: secret.secretKeyTag, - secretValueCiphertext: secret.secretValueCiphertext, - secretValueIV: secret.secretValueIV, - secretValueTag: secret.secretValueTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }) - ) - }); - } - - const secretUpdationCommits = secretApprovalRequest.commits.filter( - ({ op }) => op === CommitType.UPDATE - ) as Array<{ - op: CommitType.UPDATE; - newVersion: Partial> & { _id: Types.ObjectId }; - secret: ISecret; - }>; - if (secretUpdationCommits.length) { - const conflictedByNewBlindIndex = await Secret.find({ - workspace: workspaceId, - environment, - folder: folderId, - secretBlindIndex: { - $in: secretUpdationCommits - .map(({ newVersion }) => newVersion?.secretBlindIndex) - .filter(Boolean) - } - }) - .select("+secretBlindIndex") - .lean(); - const conflictGroupByBlindIndex = conflictedByNewBlindIndex.reduce>( - (prev, curr) => (curr?.secretBlindIndex ? { ...prev, [curr.secretBlindIndex]: true } : prev), - {} - ); - secretUpdationCommits - .filter( - ({ newVersion, secret }) => - (newVersion.secretBlindIndex && conflictGroupByBlindIndex[newVersion.secretBlindIndex]) || - !secret - ) - .forEach((el) => { - conflicts.push({ op: CommitType.UPDATE, secretId: el.newVersion._id.toString() }); - }); - - const nonConflictSecrets = secretUpdationCommits.filter( - ({ newVersion, secret }) => - Boolean(secret) && - (newVersion?.secretBlindIndex - ? !conflictGroupByBlindIndex[newVersion.secretBlindIndex] - : true) - ); - await Secret.bulkWrite( - // id and version are stripped off - nonConflictSecrets.map( - ({ - newVersion: { - secretKeyIV, - secretKeyTag, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretKeyCiphertext, - secretValueCiphertext, - secretCommentCiphertext, - skipMultilineEncoding, - secretBlindIndex, - tags - }, - secret - }) => ({ - updateOne: { - filter: { - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - secretBlindIndex: secret.secretBlindIndex, - type: SECRET_SHARED - }, - update: { - $inc: { - version: 1 - }, - secretKeyIV, - secretKeyTag, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretKeyCiphertext, - secretValueCiphertext, - secretCommentCiphertext, - skipMultilineEncoding, - secretBlindIndex, - tags, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - } - } - }) - ) - ); - - await EESecretService.addSecretVersions({ - secretVersions: nonConflictSecrets.map(({ newVersion, secret }) => { - return new SecretVersion({ - secret: secret._id, - version: secret.version + 1, - workspace: workspaceId, - type: SECRET_SHARED, - folder: folderId, - environment, - isDeleted: false, - secretBlindIndex: newVersion?.secretBlindIndex ?? secret.secretBlindIndex, - secretKeyCiphertext: newVersion?.secretKeyCiphertext ?? secret.secretKeyCiphertext, - secretKeyIV: newVersion?.secretKeyIV ?? secret.secretKeyCiphertext, - secretKeyTag: newVersion?.secretKeyTag ?? secret.secretKeyTag, - secretValueCiphertext: newVersion?.secretValueCiphertext ?? secret.secretValueCiphertext, - secretValueIV: newVersion?.secretValueIV ?? secret.secretValueIV, - secretValueTag: newVersion?.secretValueTag ?? secret.secretValueTag, - tags: newVersion?.tags ?? secret.tags, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - skipMultilineEncoding: newVersion?.skipMultilineEncoding ?? secret.skipMultilineEncoding - }); - }) - }); - } - - const secretDeletionCommits = secretApprovalRequest.commits.filter( - ({ op }) => op === CommitType.DELETE - ) as Array<{ - op: CommitType.DELETE; - secret: ISecret; - }>; - if (secretDeletionCommits.length) { - await Secret.deleteMany({ - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - environment - }) - .or( - secretDeletionCommits.map(({ secret: { secretBlindIndex } }) => ({ - secretBlindIndex, - type: { $in: ["shared", "personal"] } - })) - ) - .exec(); - - await EESecretService.markDeletedSecretVersions({ - secretIds: secretDeletionCommits.map(({ secret }) => secret._id) - }); - } - - const updatedSecretApproval = await SecretApprovalRequest.findByIdAndUpdate( - id, - { - conflicts, - hasMerged: true, - status: "close", - statusChangeBy: userMembershipId - }, - { new: true } - ); - - if (postHogClient) { - if (postHogClient) { - postHogClient.capture({ - event: "secrets merged", - distinctId: await TelemetryService.getDistinctId({ - authData - }), - properties: { - numberOfSecrets: secretApprovalRequest.commits.length, - environment, - workspaceId, - folderId, - channel: authData.userAgentType, - userAgent: authData.userAgent - } - }); - } - } - - await EESecretService.takeSecretSnapshot({ - workspaceId, - environment, - folderId - }); - - // question to team where to keep secretKey - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.SECRET_APPROVAL_MERGED, - metadata: { - mergedBy: userMembershipId, - secretApprovalRequestId: id, - secretApprovalRequestSlug: secretApprovalRequest.slug - } - }, - { - workspaceId - } - ); - - return updatedSecretApproval; -}; diff --git a/backend-mongo/src/ee/services/index.ts b/backend-mongo/src/ee/services/index.ts deleted file mode 100644 index 4ec55e725..000000000 --- a/backend-mongo/src/ee/services/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import EELicenseService from "./EELicenseService"; -import EESecretService from "./EESecretService"; -import EEAuditLogService from "./EEAuditLogService"; -import GithubSecretScanningService from "./GithubSecretScanning/GithubSecretScanningService" - -export { - EELicenseService, - EESecretService, - EEAuditLogService, - GithubSecretScanningService -} \ No newline at end of file diff --git a/backend-mongo/src/ee/validation/role.ts b/backend-mongo/src/ee/validation/role.ts deleted file mode 100644 index e3ecafe59..000000000 --- a/backend-mongo/src/ee/validation/role.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { z } from "zod"; - -export const CreateRoleSchema = z.object({ - body: z.object({ - slug: z.string().trim(), - name: z.string().trim(), - description: z.string().trim().optional(), - workspaceId: z.string().trim().optional(), - orgId: z.string().trim(), - permissions: z - .object({ - subject: z.string().trim(), - action: z.string().trim(), - conditions: z - .record(z.union([z.string().trim(), z.number(), z.object({ $glob: z.string() })])) - .optional() - }) - .array() - }) -}); - -export const UpdateRoleSchema = z.object({ - params: z.object({ - id: z.string().trim() - }), - body: z.object({ - slug: z.string().trim().optional(), - name: z.string().trim().optional(), - description: z.string().trim().optional(), - workspaceId: z.string().trim().optional(), - orgId: z.string().trim(), - permissions: z - .object({ - subject: z.string().trim(), - action: z.string().trim(), - conditions: z - .record(z.union([z.string().trim(), z.number(), z.object({ $glob: z.string() })])) - .optional() - }) - .array() - .optional() - }) -}); - -export const DeleteRoleSchema = z.object({ - params: z.object({ - id: z.string().trim() - }) -}); - -export const GetRoleSchema = z.object({ - query: z.object({ - workspaceId: z.string().trim().optional(), - orgId: z.string().trim() - }) -}); - -export const GetUserPermission = z.object({ - params: z.object({ - orgId: z.string().trim() - }) -}); - -export const GetUserProjectPermission = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); diff --git a/backend-mongo/src/ee/validation/secretApproval.ts b/backend-mongo/src/ee/validation/secretApproval.ts deleted file mode 100644 index 999820e48..000000000 --- a/backend-mongo/src/ee/validation/secretApproval.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { z } from "zod"; - -export const GetSecretApprovalRuleList = z.object({ - query: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetSecretApprovalPolicyOfABoard = z.object({ - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretPath: z.string().trim() - }) -}); - -export const CreateSecretApprovalRule = z.object({ - body: z - .object({ - workspaceId: z.string(), - name: z.string().optional(), - environment: z.string(), - secretPath: z.string().optional().nullable(), - approvers: z.string().array().min(1), - approvals: z.number().min(1).default(1) - }) - .refine((data) => data.approvals <= data.approvers.length, { - path: ["approvals"], - message: "The number of approvals should be lower than the number of approvers." - }) -}); - -export const UpdateSecretApprovalRule = z.object({ - params: z.object({ - id: z.string() - }), - body: z - .object({ - name: z.string().optional(), - approvers: z.string().array().min(1), - approvals: z.number().min(1).default(1), - secretPath: z.string().optional().nullable() - }) - .refine((data) => data.approvals <= data.approvers.length, { - path: ["approvals"], - message: "The number of approvals should be lower than the number of approvers." - }) -}); - -export const DeleteSecretApprovalRule = z.object({ - params: z.object({ - id: z.string() - }) -}); diff --git a/backend-mongo/src/ee/validation/secretApprovalRequest.ts b/backend-mongo/src/ee/validation/secretApprovalRequest.ts deleted file mode 100644 index 07aff586c..000000000 --- a/backend-mongo/src/ee/validation/secretApprovalRequest.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { z } from "zod"; -import { ApprovalStatus } from "../models/secretApprovalRequest"; - -export const getSecretApprovalRequests = z.object({ - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim().optional(), - committer: z.string().trim().optional(), - status: z.enum(["open", "close"]).optional(), - limit: z.coerce.number().default(20), - offset: z.coerce.number().default(0) - }) -}); - -export const getSecretApprovalRequestCount = z.object({ - query: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const getSecretApprovalRequestDetails = z.object({ - params: z.object({ - id: z.string().trim() - }) -}); - -export const updateSecretApprovalReviewStatus = z.object({ - body: z.object({ - status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED]) - }), - params: z.object({ - id: z.string().trim() - }) -}); - -export const mergeSecretApprovalRequest = z.object({ - params: z.object({ - id: z.string().trim() - }) -}); - -export const updateSecretApprovalRequestStatus = z.object({ - params: z.object({ - id: z.string().trim() - }), - body: z.object({ - status: z.enum(["open", "close"]) - }) -}); diff --git a/backend-mongo/src/ee/validation/secretRotation.ts b/backend-mongo/src/ee/validation/secretRotation.ts deleted file mode 100644 index 616844aaf..000000000 --- a/backend-mongo/src/ee/validation/secretRotation.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { z } from "zod"; - -export const createSecretRotationV1 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - secretPath: z.string().trim(), - environment: z.string().trim(), - interval: z.number().min(1), - provider: z.string().trim(), - customProvider: z.string().trim().optional(), - inputs: z.record(z.unknown()), - outputs: z.record(z.string()) - }) -}); - -export const restartSecretRotationV1 = z.object({ - body: z.object({ - id: z.string().trim() - }) -}); - -export const getSecretRotationV1 = z.object({ - query: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const removeSecretRotationV1 = z.object({ - params: z.object({ - id: z.string().trim() - }) -}); diff --git a/backend-mongo/src/ee/validation/secretRotationProvider.ts b/backend-mongo/src/ee/validation/secretRotationProvider.ts deleted file mode 100644 index d322939bb..000000000 --- a/backend-mongo/src/ee/validation/secretRotationProvider.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from "zod"; - -export const getSecretRotationProvidersV1 = z.object({ - params: z.object({ - workspaceId: z.string() - }) -}); diff --git a/backend-mongo/src/events/index.ts b/backend-mongo/src/events/index.ts deleted file mode 100644 index ac9ad176d..000000000 --- a/backend-mongo/src/events/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { eventPushSecrets } from "./secret"; -import { eventStartIntegration } from "./integration"; - -export { eventPushSecrets, eventStartIntegration }; diff --git a/backend-mongo/src/events/integration.ts b/backend-mongo/src/events/integration.ts deleted file mode 100644 index 746858e46..000000000 --- a/backend-mongo/src/events/integration.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Types } from "mongoose"; -import { EVENT_START_INTEGRATION } from "../variables"; - -/* - * Return event for starting integrations - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace to push secrets to - * @returns - */ -export const eventStartIntegration = ({ - workspaceId, - environment -}: { - workspaceId: Types.ObjectId; - environment: string; -}) => { - return { - name: EVENT_START_INTEGRATION, - workspaceId, - environment, - payload: {} - }; -}; diff --git a/backend-mongo/src/events/secret.ts b/backend-mongo/src/events/secret.ts deleted file mode 100644 index 894e3300d..000000000 --- a/backend-mongo/src/events/secret.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Types } from "mongoose"; -import { EVENT_PULL_SECRETS, EVENT_PUSH_SECRETS } from "../variables"; - -interface PushSecret { - ciphertextKey: string; - ivKey: string; - tagKey: string; - hashKey: string; - ciphertextValue: string; - ivValue: string; - tagValue: string; - hashValue: string; - type: "shared" | "personal"; -} - -/** - * Return event for pushing secrets - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace to push secrets to - * @returns - */ -const eventPushSecrets = ({ - workspaceId, - environment, - secretPath -}: { - workspaceId: Types.ObjectId; - environment: string; - secretPath: string; -}) => { - return { - name: EVENT_PUSH_SECRETS, - workspaceId, - environment, - secretPath, - payload: {} - }; -}; - -/** - * Return event for pulling secrets - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace to pull secrets from - * @returns - */ -const eventPullSecrets = ({ workspaceId }: { workspaceId: string }) => { - return { - name: EVENT_PULL_SECRETS, - workspaceId, - payload: {} - }; -}; - -export { eventPushSecrets }; diff --git a/backend-mongo/src/helpers/auth.ts b/backend-mongo/src/helpers/auth.ts deleted file mode 100644 index 14024bd03..000000000 --- a/backend-mongo/src/helpers/auth.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { Types } from "mongoose"; -import jwt from "jsonwebtoken"; -import { ITokenVersion, TokenVersion } from "../models"; -import { UnauthorizedRequestError } from "../utils/errors"; -import { - getAuthSecret, - getJwtAuthLifetime, - getJwtRefreshLifetime -} from "../config"; -import { AuthTokenType } from "../variables"; - -/** - * Return newly issued (JWT) auth and refresh tokens to user with id [userId] - * @param {Object} obj - * @param {String} obj.userId - id of user who we are issuing tokens for - * @return {Object} obj - * @return {String} obj.token - issued JWT token - * @return {String} obj.refreshToken - issued refresh token - */ -export const issueAuthTokens = async ({ - userId, - ip, - userAgent, -}: { - userId: Types.ObjectId; - ip: string; - userAgent: string; -}) => { - let tokenVersion: ITokenVersion | null; - - // continue with (session) token version matching existing ip and user agent - tokenVersion = await TokenVersion.findOne({ - user: userId, - ip, - userAgent, - }); - - if (!tokenVersion) { - // case: no existing ip and user agent exists - // -> create new (session) token version for ip and user agent - tokenVersion = await new TokenVersion({ - user: userId, - refreshVersion: 0, - accessVersion: 0, - ip, - userAgent, - lastUsed: new Date(), - }).save(); - } - - // issue tokens - const token = createToken({ - payload: { - authTokenType: AuthTokenType.ACCESS_TOKEN, - userId, - tokenVersionId: tokenVersion._id.toString(), - accessVersion: tokenVersion.accessVersion, - }, - expiresIn: await getJwtAuthLifetime(), - secret: await getAuthSecret(), - }); - - const refreshToken = createToken({ - payload: { - authTokenType: AuthTokenType.REFRESH_TOKEN, - userId, - tokenVersionId: tokenVersion._id.toString(), - refreshVersion: tokenVersion.refreshVersion, - }, - expiresIn: await getJwtRefreshLifetime(), - secret: await getAuthSecret(), - }); - - return { - token, - refreshToken, - }; -}; - -/** - * Remove JWT and refresh tokens for user with id [userId] - * @param {Object} obj - * @param {String} obj.userId - id of user whose tokens are cleared. - */ -export const clearTokens = async (tokenVersionId: Types.ObjectId): Promise => { - // increment refreshVersion on user by 1 - - await TokenVersion.findOneAndUpdate({ - _id: tokenVersionId, - }, { - $inc: { - refreshVersion: 1, - accessVersion: 1, - }, - }); -}; - -/** - * Return a new (JWT) token for user with id [userId] that expires in [expiresIn]; can be used to, for instance, generate - * bearer/auth, refresh, and temporary signup tokens - * @param {Object} obj - * @param {Object} obj.payload - payload of (JWT) token - * @param {String} obj.secret - (JWT) secret such as [AUTH_SECRET] - * @param {String} obj.expiresIn - string describing time span such as '10h' or '7d' - */ -export const createToken = ({ - payload, - expiresIn, - secret, -}: { - payload: any; - expiresIn?: string | number; - secret: string; -}) => { - return jwt.sign(payload, secret, { - ...( - (expiresIn !== undefined && expiresIn !== null) - ? { expiresIn } - : {} - ) - }); -}; - -export const validateProviderAuthToken = async ({ - email, - providerAuthToken, -}: { - email: string; - providerAuthToken?: string; -}) => { - - if (!providerAuthToken) { - throw new Error("Invalid authentication request."); - } - - const decodedToken = ( - jwt.verify(providerAuthToken, await getAuthSecret()) - ); - - if (decodedToken.authTokenType !== AuthTokenType.PROVIDER_TOKEN) throw UnauthorizedRequestError(); - - if (decodedToken.email !== email) { - throw new Error("Invalid authentication credentials.") - } -} diff --git a/backend-mongo/src/helpers/bot.ts b/backend-mongo/src/helpers/bot.ts deleted file mode 100644 index 63814434d..000000000 --- a/backend-mongo/src/helpers/bot.ts +++ /dev/null @@ -1,394 +0,0 @@ -import { Types } from "mongoose"; -import { Bot, BotKey, ISecret, IUser, Secret } from "../models"; -import { - decryptAsymmetric, - decryptSymmetric128BitHexKeyUTF8, - encryptSymmetric128BitHexKeyUTF8, - generateKeyPair -} from "../utils/crypto"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, - SECRET_SHARED -} from "../variables"; -import { client, getEncryptionKey, getRootEncryptionKey } from "../config"; -import { BotNotFoundError, InternalServerError } from "../utils/errors"; -import { Folder } from "../models"; -import { getFolderByPath } from "../services/FolderService"; -import { getAllImportedSecrets } from "../services/SecretImportService"; -import { expandSecrets } from "./secrets"; - -/** - * Create an inactive bot with name [name] for workspace with id [workspaceId] - * @param {Object} obj - * @param {String} obj.name - name of bot - * @param {String} obj.workspaceId - id of workspace that bot belongs to - */ -export const createBot = async ({ - name, - workspaceId -}: { - name: string; - workspaceId: Types.ObjectId; -}) => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - const { publicKey, privateKey } = generateKeyPair(); - - if (rootEncryptionKey) { - const { ciphertext, iv, tag } = client.encryptSymmetric(privateKey, rootEncryptionKey); - - return await new Bot({ - name, - workspace: workspaceId, - isActive: false, - publicKey, - encryptedPrivateKey: ciphertext, - iv, - tag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_BASE64 - }).save(); - } else if (encryptionKey) { - const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: privateKey, - key: await getEncryptionKey() - }); - - return await new Bot({ - name, - workspace: workspaceId, - isActive: false, - publicKey, - encryptedPrivateKey: ciphertext, - iv, - tag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }).save(); - } - - throw InternalServerError({ - message: "Failed to create new bot due to missing encryption key" - }); -}; - -/** - * Return whether or not workspace with id [workspaceId] is end-to-end encrypted - * @param {Types.ObjectId} workspaceId - id of workspace to check - */ -export const getIsWorkspaceE2EEHelper = async (workspaceId: Types.ObjectId) => { - const botKey = await BotKey.exists({ - workspace: workspaceId - }); - - return botKey ? false : true; -}; - -/** - * Return decrypted secrets for workspace with id [workspaceId] - * and [environment] using bot - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace - * @param {String} obj.environment - environment - */ -export const getSecretsBotHelper = async ({ - workspaceId, - environment, - secretPath -}: { - workspaceId: Types.ObjectId; - environment: string; - secretPath: string; -}) => { - const content: Record< - string, - { value: string; comment?: string; skipMultilineEncoding?: boolean } - > = {}; - const key = await getKey({ workspaceId: workspaceId }); - - let folderId = "root"; - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }); - - if (!folders && secretPath !== "/") { - throw InternalServerError({ message: "Folder not found" }); - } - - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath); - if (!folder) { - throw InternalServerError({ message: "Folder not found" }); - } - folderId = folder.id; - } - - const secrets = await Secret.find({ - workspace: workspaceId, - environment, - type: SECRET_SHARED, - folder: folderId - }); - - const importedSecrets = await getAllImportedSecrets( - workspaceId.toString(), - environment, - folderId, - () => true // integrations are setup to read all the ones - ); - - importedSecrets.forEach(({ secrets }) => { - secrets.forEach((secret) => { - const secretKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretKeyCiphertext, - iv: secret.secretKeyIV, - tag: secret.secretKeyTag, - key - }); - - const secretValue = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretValueCiphertext, - iv: secret.secretValueIV, - tag: secret.secretValueTag, - key - }); - - content[secretKey] = { value: secretValue }; - - if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) { - const commentValue = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretCommentCiphertext, - iv: secret.secretCommentIV, - tag: secret.secretCommentTag, - key - }); - content[secretKey].comment = commentValue; - } - - content[secretKey].skipMultilineEncoding = secret.skipMultilineEncoding; - }); - }); - - secrets.forEach((secret: ISecret) => { - const secretKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretKeyCiphertext, - iv: secret.secretKeyIV, - tag: secret.secretKeyTag, - key - }); - - const secretValue = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretValueCiphertext, - iv: secret.secretValueIV, - tag: secret.secretValueTag, - key - }); - - content[secretKey] = { value: secretValue }; - - if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) { - const commentValue = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretCommentCiphertext, - iv: secret.secretCommentIV, - tag: secret.secretCommentTag, - key - }); - content[secretKey].comment = commentValue; - } - - content[secretKey].skipMultilineEncoding = secret.skipMultilineEncoding; - }); - - await expandSecrets(workspaceId.toString(), key, content); - - return content; -}; - -/** - * Return bot's copy of the workspace key for workspace - * with id [workspaceId] - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace - * @returns {String} key - decrypted workspace key - */ -export const getKey = async ({ workspaceId }: { workspaceId: Types.ObjectId }) => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - const botKey = await BotKey.findOne({ - workspace: workspaceId - }).populate<{ sender: IUser }>("sender", "publicKey"); - - if (!botKey) throw BotNotFoundError({ message: `getKey: Failed to find bot key for [workspaceId=${workspaceId}]` }) - - const bot = await Bot.findOne({ - workspace: workspaceId - }).select("+encryptedPrivateKey +iv +tag +algorithm +keyEncoding"); - - if (!bot) throw new Error("Failed to find bot"); - if (!bot.isActive) throw new Error("Bot is not active"); - - if (rootEncryptionKey && bot.keyEncoding === ENCODING_SCHEME_BASE64) { - // case: encoding scheme is base64 - const privateKeyBot = client.decryptSymmetric( - bot.encryptedPrivateKey, - rootEncryptionKey, - bot.iv, - bot.tag - ); - - return decryptAsymmetric({ - ciphertext: botKey.encryptedKey, - nonce: botKey.nonce, - publicKey: botKey.sender.publicKey as string, - privateKey: privateKeyBot - }); - } else if (encryptionKey && bot.keyEncoding === ENCODING_SCHEME_UTF8) { - // case: encoding scheme is utf8 - const privateKeyBot = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: bot.encryptedPrivateKey, - iv: bot.iv, - tag: bot.tag, - key: encryptionKey - }); - - return decryptAsymmetric({ - ciphertext: botKey.encryptedKey, - nonce: botKey.nonce, - publicKey: botKey.sender.publicKey as string, - privateKey: privateKeyBot - }); - } - - throw InternalServerError({ - message: "Failed to obtain bot's copy of workspace key needed for bot operations" - }); -}; - -/** - * Return symmetrically encrypted [plaintext] using the - * key for workspace with id [workspaceId] - * @param {Object} obj1 - * @param {String} obj1.workspaceId - id of workspace - * @param {String} obj1.plaintext - plaintext to encrypt - */ -export const encryptSymmetricHelper = async ({ - workspaceId, - plaintext -}: { - workspaceId: Types.ObjectId; - plaintext: string; -}) => { - const key = await getKey({ workspaceId: workspaceId }); - const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({ - plaintext, - key - }); - - return { - ciphertext, - iv, - tag - }; -}; -/** - * Return symmetrically decrypted [ciphertext] using the - * key for workspace with id [workspaceId] - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace - * @param {String} obj.ciphertext - ciphertext to decrypt - * @param {String} obj.iv - iv - * @param {String} obj.tag - tag - */ -export const decryptSymmetricHelper = async ({ - workspaceId, - ciphertext, - iv, - tag -}: { - workspaceId: Types.ObjectId; - ciphertext: string; - iv: string; - tag: string; -}) => { - const key = await getKey({ workspaceId: workspaceId }); - const plaintext = decryptSymmetric128BitHexKeyUTF8({ - ciphertext, - iv, - tag, - key - }); - - return plaintext; -}; - -/** - * Return decrypted comments for workspace secrets with id [workspaceId] - * and [envionment] using bot - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace - * @param {String} obj.environment - environment - */ -export const getSecretsCommentBotHelper = async ({ - workspaceId, - environment, - secretPath -}: { - workspaceId: Types.ObjectId; - environment: string; - secretPath: string; -}) => { - const content = {} as any; - const key = await getKey({ workspaceId: workspaceId }); - - let folderId = "root"; - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }); - - if (!folders && secretPath !== "/") { - throw InternalServerError({ message: "Folder not found" }); - } - - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath); - if (!folder) { - throw InternalServerError({ message: "Folder not found" }); - } - folderId = folder.id; - } - - const secrets = await Secret.find({ - workspace: workspaceId, - environment, - type: SECRET_SHARED, - folder: folderId - }); - - secrets.forEach((secret: ISecret) => { - if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) { - const secretKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretKeyCiphertext, - iv: secret.secretKeyIV, - tag: secret.secretKeyTag, - key - }); - - const commentValue = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretCommentCiphertext, - iv: secret.secretCommentIV, - tag: secret.secretCommentTag, - key - }); - - content[secretKey] = commentValue; - } - }); - - return content; -}; diff --git a/backend-mongo/src/helpers/botOrg.ts b/backend-mongo/src/helpers/botOrg.ts deleted file mode 100644 index 003cabbdc..000000000 --- a/backend-mongo/src/helpers/botOrg.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { Types } from "mongoose"; -import { client, getEncryptionKey, getRootEncryptionKey } from "../config"; -import { BotOrg } from "../models"; -import { decryptSymmetric128BitHexKeyUTF8 } from "../utils/crypto"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8 -} from "../variables"; -import { InternalServerError } from "../utils/errors"; -import { encryptSymmetric128BitHexKeyUTF8, generateKeyPair } from "../utils/crypto"; - -/** - * Create a bot with name [name] for organization with id [organizationId] - * @param {Object} obj - * @param {String} obj.name - name of bot - * @param {String} obj.organizationId - id of organization that bot belongs to - */ -export const createBotOrg = async ({ - name, - organizationId, -}: { - name: string; - organizationId: Types.ObjectId; -}) => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - const { publicKey, privateKey } = generateKeyPair(); - const key = client.createSymmetricKey(); - - if (rootEncryptionKey) { - const { - ciphertext: encryptedPrivateKey, - iv: privateKeyIV, - tag: privateKeyTag - } = client.encryptSymmetric(privateKey, rootEncryptionKey); - - const { - ciphertext: encryptedSymmetricKey, - iv: symmetricKeyIV, - tag: symmetricKeyTag - } = client.encryptSymmetric(key, rootEncryptionKey); - - return await new BotOrg({ - name, - organization: organizationId, - publicKey, - encryptedSymmetricKey, - symmetricKeyIV, - symmetricKeyTag, - symmetricKeyAlgorithm: ALGORITHM_AES_256_GCM, - symmetricKeyKeyEncoding: ENCODING_SCHEME_BASE64, - encryptedPrivateKey, - privateKeyIV, - privateKeyTag, - privateKeyAlgorithm: ALGORITHM_AES_256_GCM, - privateKeyKeyEncoding: ENCODING_SCHEME_BASE64 - }).save(); - } else if (encryptionKey) { - const { - ciphertext: encryptedPrivateKey, - iv: privateKeyIV, - tag: privateKeyTag - } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: privateKey, - key: encryptionKey - }); - - const { - ciphertext: encryptedSymmetricKey, - iv: symmetricKeyIV, - tag: symmetricKeyTag - } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: key, - key: encryptionKey - }); - - return await new BotOrg({ - name, - organization: organizationId, - publicKey, - encryptedSymmetricKey, - symmetricKeyIV, - symmetricKeyTag, - symmetricKeyAlgorithm: ALGORITHM_AES_256_GCM, - symmetricKeyKeyEncoding: ENCODING_SCHEME_UTF8, - encryptedPrivateKey, - privateKeyIV, - privateKeyTag, - privateKeyAlgorithm: ALGORITHM_AES_256_GCM, - privateKeyKeyEncoding: ENCODING_SCHEME_UTF8 - }).save(); - } - - throw InternalServerError({ - message: "Failed to create new organization bot due to missing encryption key", - }); -}; - -export const getSymmetricKeyHelper = async (organizationId: Types.ObjectId) => { - const rootEncryptionKey = await getRootEncryptionKey(); - const encryptionKey = await getEncryptionKey(); - - const botOrg = await BotOrg.findOne({ - organization: organizationId - }); - - if (!botOrg) throw new Error("Failed to find organization bot"); - - if (rootEncryptionKey && botOrg.symmetricKeyKeyEncoding == ENCODING_SCHEME_BASE64) { - const key = client.decryptSymmetric( - botOrg.encryptedSymmetricKey, - rootEncryptionKey, - botOrg.symmetricKeyIV, - botOrg.symmetricKeyTag - ); - - return key; - } else if (encryptionKey && botOrg.symmetricKeyKeyEncoding === ENCODING_SCHEME_UTF8) { - const key = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: botOrg.encryptedSymmetricKey, - iv: botOrg.symmetricKeyIV, - tag: botOrg.symmetricKeyTag, - key: encryptionKey - }); - - return key; - } - - throw InternalServerError({ - message: "Failed to match encryption key with organization bot symmetric key encoding" - }); -} \ No newline at end of file diff --git a/backend-mongo/src/helpers/database.ts b/backend-mongo/src/helpers/database.ts deleted file mode 100644 index dc6d2faa6..000000000 --- a/backend-mongo/src/helpers/database.ts +++ /dev/null @@ -1,40 +0,0 @@ -import mongoose from "mongoose"; -import { logger } from "../utils/logging"; - -/** - * Initialize database connection - * @param {Object} obj - * @param {String} obj.mongoURL - mongo connection string - * @returns - */ -export const initDatabaseHelper = async ({ - mongoURL, -}: { - mongoURL: string; -}) => { - try { - await mongoose.connect(mongoURL); - - // allow empty strings to pass the required validator - mongoose.Schema.Types.String.checkRequired(v => typeof v === "string"); - - logger.info("Database connection established"); - - } catch (err) { - logger.error(err, "Unable to establish database connection"); - } - - return mongoose.connection; -} - -/** - * Close database conection - */ -export const closeDatabaseHelper = async () => { - if (mongoose.connection && mongoose.connection.readyState === 1) { - await mongoose.connection.close(); - return "Database connection closed"; - } else { - return "Database connection already closed"; - } -}; \ No newline at end of file diff --git a/backend-mongo/src/helpers/event.ts b/backend-mongo/src/helpers/event.ts deleted file mode 100644 index 231ac9c2e..000000000 --- a/backend-mongo/src/helpers/event.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Types } from "mongoose"; -import { Bot } from "../models"; -import { EVENT_PUSH_SECRETS, EVENT_START_INTEGRATION } from "../variables"; -import { IntegrationService } from "../services"; -import { triggerWebhook } from "../services/WebhookService"; - -interface Event { - name: string; - workspaceId: Types.ObjectId; - environment?: string; - secretPath?: string; - payload: any; -} - -/** - * Handle event [event] - * @param {Object} obj - * @param {Event} obj.event - an event - * @param {String} obj.event.name - name of event - * @param {String} obj.event.workspaceId - id of workspace that event is part of - * @param {Object} obj.event.payload - payload of event (depends on event) - */ -export const handleEventHelper = async ({ event }: { event: Event }) => { - const { workspaceId, environment, secretPath } = event; - - // TODO: moduralize bot check into separate function - const bot = await Bot.findOne({ - workspace: workspaceId, - isActive: true - }); - - switch (event.name) { - case EVENT_PUSH_SECRETS: - if (bot) { - IntegrationService.syncIntegrations({ - workspaceId, - environment - }); - } - triggerWebhook(workspaceId.toString(), environment || "", secretPath || ""); - break; - case EVENT_START_INTEGRATION: - if (bot) { - IntegrationService.syncIntegrations({ - workspaceId, - environment - }); - } - break; - } -}; diff --git a/backend-mongo/src/helpers/index.ts b/backend-mongo/src/helpers/index.ts deleted file mode 100644 index f9a0009fc..000000000 --- a/backend-mongo/src/helpers/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -export * from "./auth"; -export * from "./bot"; -export * from "./database"; -export * from "./event"; -export * from "./integration"; -export * from "./key"; -export * from "./membership"; -export * from "./membershipOrg"; -export * from "./nodemailer"; -export * from "./organization"; -export * from "./rateLimiter"; -export * from "./secret"; -export * from "./secrets"; -export * from "./signup"; -export * from "./token"; -export * from "./user"; -export * from "./workspace"; \ No newline at end of file diff --git a/backend-mongo/src/helpers/integration.ts b/backend-mongo/src/helpers/integration.ts deleted file mode 100644 index 6b94d5916..000000000 --- a/backend-mongo/src/helpers/integration.ts +++ /dev/null @@ -1,330 +0,0 @@ -import { Types } from "mongoose"; -import { Bot, IIntegrationAuth, IntegrationAuth } from "../models"; -import { exchangeCode, exchangeRefresh } from "../integrations"; -import { BotService } from "../services"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_UTF8, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_NETLIFY, - INTEGRATION_VERCEL, -} from "../variables"; -import { InternalServerError, UnauthorizedRequestError } from "../utils/errors"; -import { IntegrationAuthMetadata } from "../models/integrationAuth/types"; - -interface Update { - workspace: string; - integration: string; - url?: string; - teamId?: string; - accountId?: string; - metadata?: IntegrationAuthMetadata -} - -/** - * Perform OAuth2 code-token exchange for workspace with id [workspaceId] and integration - * named [integration] - * - Store integration access and refresh tokens returned from the OAuth2 code-token exchange - * - Add placeholder inactive integration - * - Create bot sequence for integration - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace - * @param {String} obj.integration - name of integration - * @param {String} obj.code - code - * @returns {IntegrationAuth} integrationAuth - integration auth after OAuth2 code-token exchange - */ -export const handleOAuthExchangeHelper = async ({ - workspaceId, - integration, - code, - environment, - url -}: { - workspaceId: string; - integration: string; - code: string; - environment: string; - url?: string; -}) => { - const bot = await Bot.findOne({ - workspace: workspaceId, - isActive: true - }); - - if (!bot) throw new Error("Bot must be enabled for OAuth2 code-token exchange"); - - // exchange code for access and refresh tokens - const res = await exchangeCode({ - integration, - code, - url - }); - - const update: Update = { - workspace: workspaceId, - integration - }; - - if (res.url) { - update.url = res.url; - } - - switch (integration) { - case INTEGRATION_VERCEL: - update.teamId = res.teamId; - break; - case INTEGRATION_NETLIFY: - update.accountId = res.accountId; - break; - case INTEGRATION_GCP_SECRET_MANAGER: - update.metadata = { - authMethod: "oauth2" - } - break; - } - - const integrationAuth = await IntegrationAuth.findOneAndUpdate( - { - workspace: workspaceId, - integration - }, - update, - { - new: true, - upsert: true - } - ); - - if (res.refreshToken) { - // case: refresh token returned from exchange - // set integration auth refresh token - await setIntegrationAuthRefreshHelper({ - integrationAuthId: integrationAuth._id.toString(), - refreshToken: res.refreshToken - }); - } - - if (res.accessToken) { - // case: access token returned from exchange - // set integration auth access token - await setIntegrationAuthAccessHelper({ - integrationAuthId: integrationAuth._id.toString(), - accessToken: res.accessToken, - accessExpiresAt: res.accessExpiresAt - }); - } - - return integrationAuth; -}; - -/** - * Return decrypted refresh token using the bot's copy - * of the workspace key for workspace belonging to integration auth - * with id [integrationAuthId] - * @param {Object} obj - * @param {String} obj.integrationAuthId - id of integration auth - * @param {String} refreshToken - decrypted refresh token - */ -export const getIntegrationAuthRefreshHelper = async ({ - integrationAuthId -}: { - integrationAuthId: Types.ObjectId; -}) => { - const integrationAuth = await IntegrationAuth.findById(integrationAuthId).select( - "+refreshCiphertext +refreshIV +refreshTag" - ); - - if (!integrationAuth) - throw UnauthorizedRequestError({ - message: "Failed to locate Integration Authentication credentials" - }); - - const refreshToken = await BotService.decryptSymmetric({ - workspaceId: integrationAuth.workspace, - ciphertext: integrationAuth.refreshCiphertext as string, - iv: integrationAuth.refreshIV as string, - tag: integrationAuth.refreshTag as string - }); - - return refreshToken; -}; - -/** - * Return decrypted access token using the bot's copy - * of the workspace key for workspace belonging to integration auth - * with id [integrationAuthId] - * @param {Object} obj - * @param {String} obj.integrationAuthId - id of integration auth - * @returns {String} accessToken - decrypted access token - */ -export const getIntegrationAuthAccessHelper = async ({ - integrationAuthId -}: { - integrationAuthId: Types.ObjectId; -}) => { - let accessId; - let accessToken; - const integrationAuth = await IntegrationAuth.findById(integrationAuthId).select( - "workspace integration +accessCiphertext +accessIV +accessTag +accessExpiresAt +refreshCiphertext +refreshIV +refreshTag +accessIdCiphertext +accessIdIV +accessIdTag metadata teamId url" - ); - - if (!integrationAuth) - throw UnauthorizedRequestError({ - message: "Failed to locate Integration Authentication credentials" - }); - - if (integrationAuth.accessCiphertext && integrationAuth.accessIV && integrationAuth.accessTag) { - accessToken = await BotService.decryptSymmetric({ - workspaceId: integrationAuth.workspace, - ciphertext: integrationAuth.accessCiphertext as string, - iv: integrationAuth.accessIV as string, - tag: integrationAuth.accessTag as string - }); - } - - if (integrationAuth?.refreshCiphertext) { - // there is a access token expiration date - // and refresh token to exchange with the OAuth2 server - const refreshToken = await getIntegrationAuthRefreshHelper({ - integrationAuthId - }); - - if (integrationAuth?.accessExpiresAt && integrationAuth.accessExpiresAt < new Date()) { - // access token is expired - accessToken = await exchangeRefresh({ - integrationAuth, - refreshToken - }); - } - } - - if ( - integrationAuth?.accessIdCiphertext && - integrationAuth?.accessIdIV && - integrationAuth?.accessIdTag - ) { - accessId = await BotService.decryptSymmetric({ - workspaceId: integrationAuth.workspace, - ciphertext: integrationAuth.accessIdCiphertext as string, - iv: integrationAuth.accessIdIV as string, - tag: integrationAuth.accessIdTag as string - }); - } - - if (!accessToken) throw InternalServerError(); - - return { - integrationAuth, - accessId, - accessToken - }; -}; - -/** - * Encrypt refresh token [refreshToken] using the bot's copy - * of the workspace key for workspace belonging to integration auth - * with id [integrationAuthId] and store it - * @param {Object} obj - * @param {String} obj.integrationAuthId - id of integration auth - * @param {String} obj.refreshToken - refresh token - */ -export const setIntegrationAuthRefreshHelper = async ({ - integrationAuthId, - refreshToken -}: { - integrationAuthId: string; - refreshToken: string; -}): Promise => { - let integrationAuth = await IntegrationAuth.findById(integrationAuthId); - - if (!integrationAuth) throw new Error("Failed to find integration auth"); - - const obj = await BotService.encryptSymmetric({ - workspaceId: integrationAuth.workspace, - plaintext: refreshToken - }); - - integrationAuth = await IntegrationAuth.findOneAndUpdate( - { - _id: integrationAuthId - }, - { - refreshCiphertext: obj.ciphertext, - refreshIV: obj.iv, - refreshTag: obj.tag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }, - { - new: true - } - ); - - if (!integrationAuth) throw InternalServerError(); - - return integrationAuth; -}; - -/** - * Encrypt access token [accessToken] and (optionally) access id [accessId] - * using the bot's copy of the workspace key for workspace belonging to - * integration auth with id [integrationAuthId] and store it along with [accessExpiresAt] - * @param {Object} obj - * @param {String} obj.integrationAuthId - id of integration auth - * @param {String} obj.accessToken - access token - * @param {Date} obj.accessExpiresAt - expiration date of access token - */ -export const setIntegrationAuthAccessHelper = async ({ - integrationAuthId, - accessId, - accessToken, - accessExpiresAt -}: { - integrationAuthId: string; - accessId?: string; - accessToken?: string; - accessExpiresAt: Date | undefined; -}) => { - let integrationAuth = await IntegrationAuth.findById(integrationAuthId); - - if (!integrationAuth) throw new Error("Failed to find integration auth"); - - let encryptedAccessTokenObj; - let encryptedAccessIdObj; - - if (accessToken) { - encryptedAccessTokenObj = await BotService.encryptSymmetric({ - workspaceId: integrationAuth.workspace, - plaintext: accessToken - }); - } - - if (accessId) { - encryptedAccessIdObj = await BotService.encryptSymmetric({ - workspaceId: integrationAuth.workspace, - plaintext: accessId - }); - } - - integrationAuth = await IntegrationAuth.findOneAndUpdate( - { - _id: integrationAuthId - }, - { - accessIdCiphertext: encryptedAccessIdObj?.ciphertext ?? undefined, - accessIdIV: encryptedAccessIdObj?.iv, - accessIdTag: encryptedAccessIdObj?.tag, - accessCiphertext: encryptedAccessTokenObj?.ciphertext, - accessIV: encryptedAccessTokenObj?.iv, - accessTag: encryptedAccessTokenObj?.tag, - accessExpiresAt, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }, - { - new: true - } - ); - - return integrationAuth; -}; diff --git a/backend-mongo/src/helpers/key.ts b/backend-mongo/src/helpers/key.ts deleted file mode 100644 index 88bf28f47..000000000 --- a/backend-mongo/src/helpers/key.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { IKey, Key } from "../models"; - -interface Key { - encryptedKey: string; - nonce: string; - userId: string; -} - -/** - * Push (access) [keys] for workspace with id [workspaceId] with - * user with id [userId] as the sender - * @param {Object} obj - * @param {String} obj.userId - id of sender user - * @param {String} obj.workspaceId - id of workspace that keys belong to - * @param {Object[]} obj.keys - (access) keys to push - * @param {String} obj.keys.encryptedKey - encrypted key under receiver's public key - * @param {String} obj.keys.nonce - nonce for encryption - * @param {String} obj.keys.userId - id of receiver user - */ -export const pushKeys = async ({ - userId, - workspaceId, - keys, -}: { - userId: string; - workspaceId: string; - keys: Key[]; -}): Promise => { - // filter out already-inserted keys - const keysSet = new Set( - ( - await Key.find( - { - workspace: workspaceId, - }, - "receiver" - ) - ).map((k: IKey) => k.receiver.toString()) - ); - - keys = keys.filter((key) => !keysSet.has(key.userId)); - - // add new shared keys only - await Key.insertMany( - keys.map((k) => ({ - encryptedKey: k.encryptedKey, - nonce: k.nonce, - sender: userId, - receiver: k.userId, - workspace: workspaceId, - })) - ); -}; \ No newline at end of file diff --git a/backend-mongo/src/helpers/membership.ts b/backend-mongo/src/helpers/membership.ts deleted file mode 100644 index d08d07a45..000000000 --- a/backend-mongo/src/helpers/membership.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { Types } from "mongoose"; -import { Key, Membership } from "../models"; -import { BadRequestError, MembershipNotFoundError } from "../utils/errors"; - -/** - * Validate that user with id [userId] is a member of workspace with id [workspaceId] - * and has at least one of the roles in [acceptedRoles] - * @param {Object} obj - * @param {String} obj.userId - id of user to validate - * @param {String} obj.workspaceId - id of workspace - * @returns {Membership} membership - membership of user with id [userId] for workspace with id [workspaceId] - */ -export const validateMembership = async ({ - userId, - workspaceId, - acceptedRoles -}: { - userId: Types.ObjectId | string; - workspaceId: Types.ObjectId | string; - acceptedRoles?: Array<"admin" | "member" | "custom" | "viewer" | "no-access">; -}) => { - const membership = await Membership.findOne({ - user: userId, - workspace: workspaceId - }).populate("workspace"); - - if (!membership) { - throw MembershipNotFoundError({ - message: "Failed to find workspace membership" - }); - } - - if (acceptedRoles) { - if (!acceptedRoles.includes(membership.role)) { - throw BadRequestError({ - message: "Failed authorization for membership role" - }); - } - } - - return membership; -}; - -/** - * Return membership matching criteria specified in query [queryObj] - * @param {Object} queryObj - query object - * @return {Object} membership - membership - */ -export const findMembership = async (queryObj: any) => { - const membership = await Membership.findOne(queryObj); - return membership; -}; - -/** - * Add memberships for users with ids [userIds] to workspace with - * id [workspaceId] - * @param {Object} obj - * @param {String[]} obj.userIds - id of users. - * @param {String} obj.workspaceId - id of workspace. - * @param {String[]} obj.roles - roles of users. - */ -export const addMemberships = async ({ - userIds, - workspaceId, - roles -}: { - userIds: string[]; - workspaceId: string; - roles: string[]; -}): Promise => { - const operations = userIds.map((userId, idx) => { - return { - updateOne: { - filter: { - user: userId, - workspace: workspaceId, - role: roles[idx] - }, - update: { - user: userId, - workspace: workspaceId, - role: roles[idx] - }, - upsert: true - } - }; - }); - await Membership.bulkWrite(operations as any); -}; - -/** - * Delete membership with id [membershipId] - * @param {Object} obj - * @param {String} obj.membershipId - id of membership to delete - */ -export const deleteMembership = async ({ membershipId }: { membershipId: string }) => { - const deletedMembership = await Membership.findOneAndDelete({ - _id: membershipId - }); - - // delete keys associated with the membership - if (deletedMembership?.user) { - // case: membership had a registered user - await Key.deleteMany({ - receiver: deletedMembership.user, - workspace: deletedMembership.workspace - }); - } - - return deletedMembership; -}; diff --git a/backend-mongo/src/helpers/membershipOrg.ts b/backend-mongo/src/helpers/membershipOrg.ts deleted file mode 100644 index 9f2e8d93c..000000000 --- a/backend-mongo/src/helpers/membershipOrg.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { Types } from "mongoose"; -import { Key, Membership, MembershipOrg, Workspace } from "../models"; -import { MembershipOrgNotFoundError, UnauthorizedRequestError } from "../utils/errors"; - -/** - * Validate that user with id [userId] is a member of organization with id [organizationId] - * and has at least one of the roles in [acceptedRoles] - * @param {Object} obj - * @param {Types.ObjectId} obj.userId - * @param {Types.ObjectId} obj.organizationId - * @param {String[]} obj.acceptedRoles - */ -export const validateMembershipOrg = async ({ - userId, - organizationId, - acceptedRoles, - acceptedStatuses -}: { - userId: Types.ObjectId; - organizationId: Types.ObjectId; - acceptedRoles?: Array<"owner" | "admin" | "member" | "custom" | "no-access">; - acceptedStatuses?: Array<"invited" | "accepted">; -}) => { - const membershipOrg = await MembershipOrg.findOne({ - user: userId, - organization: organizationId - }); - - if (!membershipOrg) { - throw MembershipOrgNotFoundError({ message: "Failed to find organization membership" }); - } - - if (acceptedRoles) { - if (!acceptedRoles.includes(membershipOrg.role)) { - throw UnauthorizedRequestError({ - message: "Failed to validate organization membership role" - }); - } - } - - if (acceptedStatuses) { - if (!acceptedStatuses.includes(membershipOrg.status)) { - throw UnauthorizedRequestError({ - message: "Failed to validate organization membership status" - }); - } - } - - return membershipOrg; -}; - -/** - * Return organization membership matching criteria specified in - * query [queryObj] - * @param {Object} queryObj - query object - * @return {Object} membershipOrg - membership - */ -export const findMembershipOrg = (queryObj: any) => { - const membershipOrg = MembershipOrg.findOne(queryObj); - return membershipOrg; -}; - -/** - * Add organization memberships for users with ids [userIds] to organization with - * id [organizationId] - * @param {Object} obj - * @param {String[]} obj.userIds - id of users. - * @param {String} obj.organizationId - id of organization. - * @param {String[]} obj.roles - roles of users. - */ -export const addMembershipsOrg = async ({ - userIds, - organizationId, - roles, - statuses -}: { - userIds: string[]; - organizationId: string; - roles: string[]; - statuses: string[]; -}) => { - const operations = userIds.map((userId, idx) => { - return { - updateOne: { - filter: { - user: userId, - organization: organizationId, - role: roles[idx], - status: statuses[idx] - }, - update: { - user: userId, - organization: organizationId, - role: roles[idx], - status: statuses[idx] - }, - upsert: true - } - }; - }); - - await MembershipOrg.bulkWrite(operations as any); -}; - -/** - * Delete organization membership with id [membershipOrgId] - * @param {Object} obj - * @param {String} obj.membershipOrgId - id of organization membership to delete - */ -export const deleteMembershipOrg = async ({ membershipOrgId }: { membershipOrgId: string }) => { - const deletedMembershipOrg = await MembershipOrg.findOneAndDelete({ - _id: membershipOrgId - }); - - if (!deletedMembershipOrg) throw new Error("Failed to delete organization membership"); - - // delete keys associated with organization membership - if (deletedMembershipOrg?.user) { - // case: organization membership had a registered user - - const workspaces = ( - await Workspace.find({ - organization: deletedMembershipOrg.organization - }) - ).map((w) => w._id.toString()); - - await Membership.deleteMany({ - user: deletedMembershipOrg.user, - workspace: { - $in: workspaces - } - }); - - await Key.deleteMany({ - receiver: deletedMembershipOrg.user, - workspace: { - $in: workspaces - } - }); - } - - return deletedMembershipOrg; -}; diff --git a/backend-mongo/src/helpers/nodemailer.ts b/backend-mongo/src/helpers/nodemailer.ts deleted file mode 100644 index b83d9bf61..000000000 --- a/backend-mongo/src/helpers/nodemailer.ts +++ /dev/null @@ -1,46 +0,0 @@ -import fs from "fs"; -import path from "path"; -import handlebars from "handlebars"; -import nodemailer from "nodemailer"; -import { getSmtpConfigured, getSmtpFromAddress, getSmtpFromName } from "../config"; - -let smtpTransporter: nodemailer.Transporter; - -/** - * @param {Object} obj - * @param {String} obj.template - email template to use from /templates folder (e.g. testEmail.handlebars) - * @param {String[]} obj.subjectLine - email subject line - * @param {String[]} obj.recipients - email addresses of people to send email to - * @param {Object} obj.substitutions - object containing template substitutions - */ -export const sendMail = async ({ - template, - subjectLine, - recipients, - substitutions, -}: { - template: string; - subjectLine: string; - recipients: string[]; - substitutions: any; -}) => { - if (await getSmtpConfigured()) { - const html = fs.readFileSync( - path.resolve(__dirname, "../templates/" + template), - "utf8" - ); - const temp = handlebars.compile(html); - const htmlToSend = temp(substitutions); - - await smtpTransporter.sendMail({ - from: `"${await getSmtpFromName()}" <${await getSmtpFromAddress()}>`, - to: recipients.join(", "), - subject: subjectLine, - html: htmlToSend, - }); - } -}; - -export const setTransporter = (transporter: nodemailer.Transporter) => { - smtpTransporter = transporter; -}; \ No newline at end of file diff --git a/backend-mongo/src/helpers/organization.ts b/backend-mongo/src/helpers/organization.ts deleted file mode 100644 index 36d1baeb6..000000000 --- a/backend-mongo/src/helpers/organization.ts +++ /dev/null @@ -1,385 +0,0 @@ -import { Types } from "mongoose"; -import { - Bot, - BotKey, - BotOrg, - Folder, - Identity, - IdentityMembership, - IdentityMembershipOrg, - IdentityUniversalAuth, - IdentityUniversalAuthClientSecret, - IncidentContactOrg, - Integration, - IntegrationAuth, - Key, - Membership, - MembershipOrg, - Organization, - Secret, - SecretBlindIndexData, - SecretImport, - ServiceToken, - ServiceTokenData, - Tag, - Webhook, - Workspace -} from "../models"; -import { - AuditLog, - FolderVersion, - GitAppInstallationSession, - GitAppOrganizationInstallation, - GitRisks, - Role, - SSOConfig, - SecretApprovalPolicy, - SecretApprovalRequest, - SecretSnapshot, - SecretVersion, - TrustedIP -} from "../ee/models"; -import { - ACCEPTED, -} from "../variables"; -import { - EELicenseService, -} from "../ee/services"; -import { - getLicenseServerKey, - getLicenseServerUrl, -} from "../config"; -import { - licenseKeyRequest, - licenseServerKeyRequest, -} from "../config/request"; -import { - createBotOrg -} from "./botOrg"; -import { ResourceNotFoundError } from "../utils/errors"; - -/** - * Create an organization with name [name] - * @param {Object} obj - * @param {String} obj.name - name of organization to create. - * @param {String} obj.email - POC email that will receive invoice info - * @param {Object} organization - new organization - */ -export const createOrganization = async ({ - name, - email, -}: { - name: string; - email: string; -}) => { - - const licenseServerKey = await getLicenseServerKey(); - let organization; - - if (licenseServerKey) { - const { data: { customerId } } = await licenseServerKeyRequest.post( - `${await getLicenseServerUrl()}/api/license-server/v1/customers`, - { - email, - name - } - ); - - organization = await new Organization({ - name, - customerId - }).save(); - - } else { - organization = await new Organization({ - name, - }).save(); - } - - // initialize bot for organization - await createBotOrg({ - name, - organizationId: organization._id - }); - - return organization; -}; - -/** - * Delete organization with id [organizationId] - * @param {Object} obj - * @param {Types.ObjectId} obj.organizationId - id of organization to delete - * @returns - */ -export const deleteOrganization = async ({ - organizationId -}: { - organizationId: Types.ObjectId; -}) => { - - const organization = await Organization.findByIdAndDelete( - organizationId - ); - - if (!organization) throw ResourceNotFoundError(); - - await MembershipOrg.deleteMany({ - organization: organization._id - }); - - const identityIds = await IdentityMembershipOrg.distinct("identity", { - organization: organization._id - }); - - await IdentityMembershipOrg.deleteMany({ - organization: organization._id - }); - - await Identity.deleteMany({ - _id: { - $in: identityIds - } - }); - - await IdentityUniversalAuth.deleteMany({ - identity: { - $in: identityIds - } - }); - - await IdentityUniversalAuthClientSecret.deleteMany({ - identity: { - $in: identityIds - } - }); - - await BotOrg.deleteMany({ - organization: organization._id - }); - - await SSOConfig.deleteMany({ - organization: organization._id - }); - - await Role.deleteMany({ - organization: organization._id - }); - - await IncidentContactOrg.deleteMany({ - organization: organization._id - }); - - await GitRisks.deleteMany({ - organization: organization._id - }); - - await GitAppInstallationSession.deleteMany({ - organization: organization._id - }); - - await GitAppOrganizationInstallation.deleteMany({ - organization: organization._id - }); - - const workspaceIds = await Workspace.distinct("_id", { - organization: organization._id - }); - - await Workspace.deleteMany({ - organization: organization._id - }); - - await Membership.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await Key.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await Bot.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await BotKey.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await SecretBlindIndexData.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await Secret.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await SecretVersion.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await SecretSnapshot.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await SecretImport.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await Folder.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await FolderVersion.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await Webhook.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await TrustedIP.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await Tag.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await IntegrationAuth.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await Integration.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await ServiceToken.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await ServiceTokenData.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await IdentityMembership.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await AuditLog.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await SecretApprovalPolicy.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await SecretApprovalRequest.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - if (organization.customerId) { - // delete from stripe here - await licenseServerKeyRequest.delete( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}` - ); - } - - return organization; -} - -/** - * Update organization subscription quantity to reflect number of members in - * the organization. - * @param {Object} obj - * @param {Number} obj.organizationId - id of subscription's organization - */ -export const updateSubscriptionOrgQuantity = async ({ - organizationId, -}: { - organizationId: string; -}) => { - // find organization - const organization = await Organization.findOne({ - _id: organizationId, - }); - - if (organization && organization.customerId) { - if (EELicenseService.instanceType === "cloud") { - // instance of Infisical is a cloud instance - const quantity = await MembershipOrg.countDocuments({ - organization: new Types.ObjectId(organizationId), - status: ACCEPTED, - }); - - await licenseServerKeyRequest.patch( - `${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`, - { - quantity, - } - ); - - EELicenseService.localFeatureSet.del(organizationId); - } - } - - if (EELicenseService.instanceType === "enterprise-self-hosted") { - // instance of Infisical is an enterprise self-hosted instance - - const usedSeats = await MembershipOrg.countDocuments({ - status: ACCEPTED, - }); - - await licenseKeyRequest.patch( - `${await getLicenseServerUrl()}/api/license/v1/license`, - { - usedSeats, - } - ); - } - - await EELicenseService.refreshPlan(new Types.ObjectId(organizationId)); -}; \ No newline at end of file diff --git a/backend-mongo/src/helpers/rateLimiter.ts b/backend-mongo/src/helpers/rateLimiter.ts deleted file mode 100644 index 451291072..000000000 --- a/backend-mongo/src/helpers/rateLimiter.ts +++ /dev/null @@ -1,64 +0,0 @@ -import rateLimit from "express-rate-limit"; -// const MongoStore = require('rate-limit-mongo'); - -// 200 per minute -export const apiLimiter = rateLimit({ - // store: new MongoStore({ - // uri: process.env.MONGO_URL, - // expireTimeMs: 1000 * 60, - // collectionName: "expressRateRecords-apiLimiter", - // errorHandler: console.error.bind(null, 'rate-limit-mongo') - // }), - windowMs: 60 * 1000, - max: 480, - standardHeaders: true, - legacyHeaders: false, - skip: (request) => { - return request.path === "/healthcheck" || request.path === "/api/status" - }, - keyGenerator: (req, res) => { - return req.realIP - }, -}); - -// 50 requests per 1 hours -const authLimit = rateLimit({ - // store: new MongoStore({ - // uri: process.env.MONGO_URL, - // expireTimeMs: 1000 * 60 * 60, - // errorHandler: console.error.bind(null, 'rate-limit-mongo'), - // collectionName: "expressRateRecords-authLimit", - // }), - windowMs: 60 * 1000, - max: 300, - standardHeaders: true, - legacyHeaders: false, - keyGenerator: (req, res) => { - return req.realIP - }, -}); - -// 5 requests per 1 hour -export const passwordLimiter = rateLimit({ - // store: new MongoStore({ - // uri: process.env.MONGO_URL, - // expireTimeMs: 1000 * 60 * 60, - // errorHandler: console.error.bind(null, 'rate-limit-mongo'), - // collectionName: "expressRateRecords-passwordLimiter", - // }), - windowMs: 60 * 1000, - max: 300, - standardHeaders: true, - legacyHeaders: false, - keyGenerator: (req, res) => { - return req.realIP - }, -}); - -export const authLimiter = (req: any, res: any, next: any) => { - if (process.env.NODE_ENV === "production") { - authLimit(req, res, next); - } else { - next(); - } -}; \ No newline at end of file diff --git a/backend-mongo/src/helpers/reminder.ts b/backend-mongo/src/helpers/reminder.ts deleted file mode 100644 index d896a2f3d..000000000 --- a/backend-mongo/src/helpers/reminder.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { ISecret } from "../models"; -import { - createRecurringSecretReminder, - deleteRecurringSecretReminder, - updateRecurringSecretReminder -} from "../queues/reminders/sendSecretReminders"; - -type TPartialSecret = Pick< - ISecret, - "_id" | "secretReminderRepeatDays" | "secretReminderNote" | "workspace" ->; -type TPartialSecretDeleteReminder = Pick; - -export const createReminder = async (oldSecret: TPartialSecret, newSecret: TPartialSecret) => { - if (oldSecret._id !== newSecret._id) { - throw new Error("Secret id's don't match"); - } - - if (!newSecret.secretReminderRepeatDays) { - throw new Error("No repeat days provided"); - } - - const secretId = oldSecret._id.toString(); - const workspaceId = oldSecret.workspace.toString(); - - if (oldSecret.secretReminderRepeatDays) { - // This will first delete the existing recurring job, and then create a new one. - await updateRecurringSecretReminder({ - workspaceId, - secretId, - repeatDays: newSecret.secretReminderRepeatDays, - note: newSecret.secretReminderNote - }); - } else { - // This will create a new recurring job. - await createRecurringSecretReminder({ - workspaceId, - secretId, - repeatDays: newSecret.secretReminderRepeatDays, - note: newSecret.secretReminderNote - }); - } -}; - -export const deleteReminder = async (secret: TPartialSecretDeleteReminder) => { - if (!secret._id) { - throw new Error("No secret id provided"); - } - - if (!secret.secretReminderRepeatDays) { - throw new Error("No repeat days provided"); - } - - await deleteRecurringSecretReminder({ - secretId: secret._id.toString(), - repeatDays: secret.secretReminderRepeatDays - }); -}; diff --git a/backend-mongo/src/helpers/secret.ts b/backend-mongo/src/helpers/secret.ts deleted file mode 100644 index 6bdc21e99..000000000 --- a/backend-mongo/src/helpers/secret.ts +++ /dev/null @@ -1,589 +0,0 @@ -import { Types } from "mongoose"; -import { ISecret, Secret } from "../models"; -import { EESecretService } from "../ee/services"; -import { SecretVersion } from "../ee/models"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_UTF8, - SECRET_PERSONAL, - SECRET_SHARED, -} from "../variables"; - -interface V1PushSecret { - ciphertextKey: string; - ivKey: string; - tagKey: string; - hashKey: string; - ciphertextValue: string; - ivValue: string; - tagValue: string; - hashValue: string; - ciphertextComment: string; - ivComment: string; - tagComment: string; - hashComment: string; - type: "shared" | "personal"; -} - -interface V2PushSecret { - type: string; // personal or shared - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretKeyHash: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretValueHash: string; - secretCommentCiphertext?: string; - secretCommentIV?: string; - secretCommentTag?: string; - secretCommentHash?: string; -} - -interface Update { - [index: string]: any; -} - -/** - * Push secrets for user with id [userId] to workspace - * with id [workspaceId] with environment [environment]. Follow steps: - * 1. Handle shared secrets (insert, delete) - * 2. handle personal secrets (insert, delete) - * @param {Object} obj - * @param {String} obj.userId - id of user to push secrets for - * @param {String} obj.workspaceId - id of workspace to push to - * @param {String} obj.environment - environment for secrets - * @param {Object[]} obj.secrets - secrets to push - */ -export const v1PushSecrets = async ({ - userId, - workspaceId, - environment, - secrets, -}: { - userId: string; - workspaceId: string; - environment: string; - secrets: V1PushSecret[]; -}): Promise => { - // TODO: clean up function and fix up types - // construct useful data structures - const oldSecrets = await getSecrets({ - userId, - workspaceId, - environment, - }); - - const oldSecretsObj: any = oldSecrets.reduce( - (accumulator, s: any) => ({ - ...accumulator, - [`${s.type}-${s.secretKeyHash}`]: s, - }), - {} - ); - const newSecretsObj: any = secrets.reduce( - (accumulator, s) => ({ ...accumulator, [`${s.type}-${s.hashKey}`]: s }), - {} - ); - - // handle deleting secrets - const toDelete = oldSecrets - .filter((s: ISecret) => !(`${s.type}-${s.secretKeyHash}` in newSecretsObj)) - .map((s) => s._id); - if (toDelete.length > 0) { - await Secret.deleteMany({ - _id: { $in: toDelete }, - }); - - await EESecretService.markDeletedSecretVersions({ - secretIds: toDelete, - }); - } - - const toUpdate = oldSecrets.filter((s) => { - if (`${s.type}-${s.secretKeyHash}` in newSecretsObj) { - if ( - s.secretValueHash !== - newSecretsObj[`${s.type}-${s.secretKeyHash}`].hashValue || - s.secretCommentHash !== - newSecretsObj[`${s.type}-${s.secretKeyHash}`].hashComment - ) { - // case: filter secrets where value or comment changed - return true; - } - - if (!s.version) { - // case: filter (legacy) secrets that were not versioned - return true; - } - } - - return false; - }); - - const operations = toUpdate.map((s) => { - const { - ciphertextValue, - ivValue, - tagValue, - hashValue, - ciphertextComment, - ivComment, - tagComment, - hashComment, - } = newSecretsObj[`${s.type}-${s.secretKeyHash}`]; - - const update: Update = { - secretValueCiphertext: ciphertextValue, - secretValueIV: ivValue, - secretValueTag: tagValue, - secretValueHash: hashValue, - secretCommentCiphertext: ciphertextComment, - secretCommentIV: ivComment, - secretCommentTag: tagComment, - secretCommentHash: hashComment, - }; - - if (!s.version) { - // case: (legacy) secret was not versioned - update.version = 1; - } else { - update["$inc"] = { - version: 1, - }; - } - - if (s.type === SECRET_PERSONAL) { - // attach user associated with the personal secret - update["user"] = userId; - } - - return { - updateOne: { - filter: { - _id: oldSecretsObj[`${s.type}-${s.secretKeyHash}`]._id, - }, - update, - }, - }; - }); - await Secret.bulkWrite(operations as any); - - // (EE) add secret versions for updated secrets - await EESecretService.addSecretVersions({ - secretVersions: toUpdate.map(({ _id, version, type, secretKeyHash }) => { - const newSecret = newSecretsObj[`${type}-${secretKeyHash}`]; - return new SecretVersion({ - secret: _id, - version: version ? version + 1 : 1, - workspace: new Types.ObjectId(workspaceId), - type: newSecret.type, - user: new Types.ObjectId(userId), - environment, - isDeleted: false, - secretKeyCiphertext: newSecret.ciphertextKey, - secretKeyIV: newSecret.ivKey, - secretKeyTag: newSecret.tagKey, - secretKeyHash: newSecret.hashKey, - secretValueCiphertext: newSecret.ciphertextValue, - secretValueIV: newSecret.ivValue, - secretValueTag: newSecret.tagValue, - secretValueHash: newSecret.hashValue, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - }); - }), - }); - - // handle adding new secrets - const toAdd = secrets.filter( - (s) => !(`${s.type}-${s.hashKey}` in oldSecretsObj) - ); - - if (toAdd.length > 0) { - // add secrets - const newSecrets: ISecret[] = ( - await Secret.insertMany( - toAdd.map((s, idx) => { - const obj: any = { - version: 1, - workspace: workspaceId, - type: toAdd[idx].type, - environment, - secretKeyCiphertext: s.ciphertextKey, - secretKeyIV: s.ivKey, - secretKeyTag: s.tagKey, - secretKeyHash: s.hashKey, - secretValueCiphertext: s.ciphertextValue, - secretValueIV: s.ivValue, - secretValueTag: s.tagValue, - secretValueHash: s.hashValue, - secretCommentCiphertext: s.ciphertextComment, - secretCommentIV: s.ivComment, - secretCommentTag: s.tagComment, - secretCommentHash: s.hashComment, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - }; - - if (toAdd[idx].type === "personal") { - obj["user" as keyof typeof obj] = userId; - } - - return obj; - }) - ) - ).map((insertedSecret) => insertedSecret.toObject()); - - // (EE) add secret versions for new secrets - EESecretService.addSecretVersions({ - secretVersions: newSecrets.map( - ({ - _id, - version, - workspace, - type, - user, - environment, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretKeyHash, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretValueHash, - algorithm, - keyEncoding, - }) => - new SecretVersion({ - secret: _id, - version, - workspace, - type, - user, - environment, - isDeleted: false, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretKeyHash, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretValueHash, - algorithm, - keyEncoding, - }) - ), - }); - } - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - }); -}; - -/** - * Push secrets for user with id [userId] to workspace - * with id [workspaceId] with environment [environment]. Follow steps: - * 1. Handle shared secrets (insert, delete) - * 2. handle personal secrets (insert, delete) - * @param {Object} obj - * @param {String} obj.userId - id of user to push secrets for - * @param {String} obj.workspaceId - id of workspace to push to - * @param {String} obj.environment - environment for secrets - * @param {Object[]} obj.secrets - secrets to push - * @param {String} obj.channel - channel (web/cli/auto) - * @param {String} obj.ipAddress - ip address of request to push secrets - */ -export const v2PushSecrets = async ({ - userId, - workspaceId, - environment, - secrets, - channel, - ipAddress, -}: { - userId: string; - workspaceId: string; - environment: string; - secrets: V2PushSecret[]; - channel: string; - ipAddress: string; -}): Promise => { - // TODO: clean up function and fix up types - - // construct useful data structures - const oldSecrets = await getSecrets({ - userId, - workspaceId, - environment, - }); - - const oldSecretsObj: any = oldSecrets.reduce( - (accumulator, s: any) => ({ - ...accumulator, - [`${s.type}-${s.secretKeyHash}`]: s, - }), - {} - ); - const newSecretsObj: any = secrets.reduce( - (accumulator, s) => ({ - ...accumulator, - [`${s.type}-${s.secretKeyHash}`]: s, - }), - {} - ); - - // handle deleting secrets - const toDelete = oldSecrets - .filter((s: ISecret) => !(`${s.type}-${s.secretKeyHash}` in newSecretsObj)) - .map((s) => s._id); - if (toDelete.length > 0) { - await Secret.deleteMany({ - _id: { $in: toDelete }, - }); - - await EESecretService.markDeletedSecretVersions({ - secretIds: toDelete, - }); - } - - const toUpdate = oldSecrets.filter((s) => { - if (`${s.type}-${s.secretKeyHash}` in newSecretsObj) { - if ( - s.secretValueHash !== - newSecretsObj[`${s.type}-${s.secretKeyHash}`].secretValueHash || - s.secretCommentHash !== - newSecretsObj[`${s.type}-${s.secretKeyHash}`].secretCommentHash - ) { - // case: filter secrets where value or comment changed - return true; - } - - if (!s.version) { - // case: filter (legacy) secrets that were not versioned - return true; - } - } - - return false; - }); - - if (toUpdate.length > 0) { - const operations = toUpdate.map((s) => { - const { - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretValueHash, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - secretCommentHash, - } = newSecretsObj[`${s.type}-${s.secretKeyHash}`]; - - const update: Update = { - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretValueHash, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - secretCommentHash, - }; - - if (!s.version) { - // case: (legacy) secret was not versioned - update.version = 1; - } else { - update["$inc"] = { - version: 1, - }; - } - - if (s.type === SECRET_PERSONAL) { - // attach user associated with the personal secret - update["user"] = userId; - } - - return { - updateOne: { - filter: { - _id: oldSecretsObj[`${s.type}-${s.secretKeyHash}`]._id, - }, - update, - }, - }; - }); - await Secret.bulkWrite(operations as any); - - // (EE) add secret versions for updated secrets - await EESecretService.addSecretVersions({ - secretVersions: toUpdate.map((s) => { - return { - ...newSecretsObj[`${s.type}-${s.secretKeyHash}`], - secret: s._id, - version: s.version ? s.version + 1 : 1, - workspace: new Types.ObjectId(workspaceId), - user: s.user, - environment: s.environment, - isDeleted: false, - }; - }), - }); - } - - // handle adding new secrets - const toAdd = secrets.filter( - (s) => !(`${s.type}-${s.secretKeyHash}` in oldSecretsObj) - ); - - if (toAdd.length > 0) { - // add secrets - const newSecrets = await Secret.insertMany( - toAdd.map((s, idx) => ({ - ...s, - version: 1, - workspace: workspaceId, - type: toAdd[idx].type, - environment, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - ...(toAdd[idx].type === "personal" ? { user: userId } : {}), - })) - ); - - // (EE) add secret versions for new secrets - EESecretService.addSecretVersions({ - secretVersions: newSecrets.map((secretDocument) => { - return new SecretVersion({ - ...secretDocument, - secret: secretDocument._id, - isDeleted: false, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - }); - }), - }); - } - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: new Types.ObjectId(workspaceId), - environment, - }); -}; - -/** - * Get secrets for user with id [userId] for workspace - * with id [workspaceId] with environment [environment] - * @param {Object} obj - * @param {String} obj.userId -id of user to pull secrets for - * @param {String} obj.workspaceId - id of workspace to pull from - * @param {String} obj.environment - environment for secrets - */ -export const getSecrets = async ({ - userId, - workspaceId, - environment, -}: { - userId: string; - workspaceId: string; - environment: string; -}): Promise => { - // get shared workspace secrets - const sharedSecrets = await Secret.find({ - workspace: workspaceId, - environment, - type: SECRET_SHARED, - }); - - // get personal workspace secrets - const personalSecrets = await Secret.find({ - workspace: workspaceId, - environment, - type: SECRET_PERSONAL, - user: userId, - }); - - // concat shared and personal workspace secrets - const secrets = personalSecrets.concat(sharedSecrets); - - return secrets; -}; - -/** - * Pull secrets for user with id [userId] for workspace - * with id [workspaceId] with environment [environment] - * @param {Object} obj - * @param {String} obj.userId -id of user to pull secrets for - * @param {String} obj.workspaceId - id of workspace to pull from - * @param {String} obj.environment - environment for secrets - * @param {String} obj.channel - channel (web/cli/auto) - * @param {String} obj.ipAddress - ip address of request to push secrets - */ -export const pullSecrets = async ({ - userId, - workspaceId, - environment, - channel, - ipAddress, -}: { - userId: string; - workspaceId: string; - environment: string; - channel: string; - ipAddress: string; -}): Promise => { - const secrets = await getSecrets({ - userId, - workspaceId, - environment, - }); - - return secrets; -}; - -/** - * Reformat output of pullSecrets() to be compatible with how existing - * web client handle secrets - * @param {Object} obj - * @param {Object} obj.secrets - */ -export const reformatPullSecrets = ({ secrets }: { secrets: ISecret[] }) => { - const reformatedSecrets = secrets.map((s) => ({ - _id: s._id, - workspace: s.workspace, - type: s.type, - environment: s.environment, - secretKey: { - workspace: s.workspace, - ciphertext: s.secretKeyCiphertext, - iv: s.secretKeyIV, - tag: s.secretKeyTag, - hash: s.secretKeyHash, - }, - secretValue: { - workspace: s.workspace, - ciphertext: s.secretValueCiphertext, - iv: s.secretValueIV, - tag: s.secretValueTag, - hash: s.secretValueHash, - }, - secretComment: { - workspace: s.workspace, - ciphertext: s.secretCommentCiphertext, - iv: s.secretCommentIV, - tag: s.secretCommentTag, - hash: s.secretCommentHash, - }, - })); - - return reformatedSecrets; -}; \ No newline at end of file diff --git a/backend-mongo/src/helpers/secrets.ts b/backend-mongo/src/helpers/secrets.ts deleted file mode 100644 index eccef634d..000000000 --- a/backend-mongo/src/helpers/secrets.ts +++ /dev/null @@ -1,1748 +0,0 @@ -import { Types } from "mongoose"; -import { - CreateSecretBatchParams, - CreateSecretParams, - DeleteSecretBatchParams, - DeleteSecretParams, - GetSecretParams, - GetSecretsParams, - UpdateSecretBatchParams, - UpdateSecretParams -} from "../interfaces/services/SecretService"; -import { - Folder, - ISecret, - IServiceTokenData, - Secret, - SecretBlindIndexData, - ServiceTokenData, - TFolderRootSchema -} from "../models"; -import { EventType, SecretVersion } from "../ee/models"; -import { - BadRequestError, - InternalServerError, - SecretBlindIndexDataNotFoundError, - SecretNotFoundError, - UnauthorizedRequestError -} from "../utils/errors"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, - K8_USER_AGENT_NAME, - SECRET_PERSONAL, - SECRET_SHARED -} from "../variables"; -import crypto from "crypto"; -import * as argon2 from "argon2"; -import { - decryptSymmetric128BitHexKeyUTF8, - encryptSymmetric128BitHexKeyUTF8 -} from "../utils/crypto"; -import { TelemetryService } from "../services"; -import { client, getEncryptionKey, getRootEncryptionKey } from "../config"; -import { EEAuditLogService, EESecretService } from "../ee/services"; -import { getAuthDataPayloadUserObj } from "../utils/authn/helpers"; -import { getFolderByPath, getFolderIdFromServiceToken } from "../services/FolderService"; -import picomatch from "picomatch"; -import path from "path"; -import { getAnImportedSecret } from "../services/SecretImportService"; - -/** - * Validate scope for service token v2 - * @param authPayload - * @param environment - * @param secretPath - * @returns - */ -export const isValidScope = ( - authPayload: IServiceTokenData, - environment: string, - secretPath: string -) => { - const { scopes: tkScopes } = authPayload; - const validScope = tkScopes.find( - (scope) => - picomatch.isMatch(secretPath, scope.secretPath, { strictSlashes: false }) && - scope.environment === environment - ); - - return Boolean(validScope); -}; - -export function containsGlobPatterns(secretPath: string) { - const globChars = ["*", "?", "[", "]", "{", "}", "**"]; - const normalizedPath = path.normalize(secretPath); - return globChars.some((char) => normalizedPath.includes(char)); -} - -const ERR_FOLDER_NOT_FOUND = BadRequestError({ message: "Folder not found" }); - -/** - * Returns an object containing secret [secret] but with its value, key, comment decrypted. - * - * Precondition: the workspace for secret [secret] must have E2EE disabled - * @param {ISecret} secret - secret to repackage to raw - * @param {String} key - symmetric key to use to decrypt secret - * @returns - */ -export const repackageSecretToRaw = ({ secret, key }: { secret: ISecret; key: string }) => { - const secretKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretKeyCiphertext, - iv: secret.secretKeyIV, - tag: secret.secretKeyTag, - key - }); - - const secretValue = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretValueCiphertext, - iv: secret.secretValueIV, - tag: secret.secretValueTag, - key - }); - - let secretComment = ""; - - if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) { - secretComment = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretCommentCiphertext, - iv: secret.secretCommentIV, - tag: secret.secretCommentTag, - key - }); - } - - return { - _id: secret._id, - version: secret.version, - workspace: secret.workspace, - type: secret.type, - environment: secret.environment, - user: secret.user, - secretKey, - secretValue, - secretComment - }; -}; - -/** - * Create secret blind index data containing encrypted blind index [salt] - * for workspace with id [workspaceId] - * @param {Object} obj - * @param {Types.ObjectId} obj.workspaceId - */ -export const createSecretBlindIndexDataHelper = async ({ - workspaceId -}: { - workspaceId: Types.ObjectId; -}) => { - // initialize random blind index salt for workspace - const salt = crypto.randomBytes(16).toString("base64"); - - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - if (rootEncryptionKey) { - const { - ciphertext: encryptedSaltCiphertext, - iv: saltIV, - tag: saltTag - } = client.encryptSymmetric(salt, rootEncryptionKey); - - return await new SecretBlindIndexData({ - workspace: workspaceId, - encryptedSaltCiphertext, - saltIV, - saltTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_BASE64 - }).save(); - } else { - const { - ciphertext: encryptedSaltCiphertext, - iv: saltIV, - tag: saltTag - } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: salt, - key: encryptionKey - }); - - return await new SecretBlindIndexData({ - workspace: workspaceId, - encryptedSaltCiphertext, - saltIV, - saltTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }).save(); - } -}; - -/** - * Get secret blind index salt for workspace with id [workspaceId] - * @param {Object} obj - * @param {Types.ObjectId} obj.workspaceId - id of workspace to get salt for - * @returns - */ -export const getSecretBlindIndexSaltHelper = async ({ - workspaceId -}: { - workspaceId: Types.ObjectId; -}) => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - const secretBlindIndexData = await SecretBlindIndexData.findOne({ - workspace: workspaceId - }).select("+algorithm +keyEncoding"); - - if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError(); - - if (rootEncryptionKey && secretBlindIndexData.keyEncoding === ENCODING_SCHEME_BASE64) { - return client.decryptSymmetric( - secretBlindIndexData.encryptedSaltCiphertext, - rootEncryptionKey, - secretBlindIndexData.saltIV, - secretBlindIndexData.saltTag - ); - } else if (encryptionKey && secretBlindIndexData.keyEncoding === ENCODING_SCHEME_UTF8) { - // decrypt workspace salt - return decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secretBlindIndexData.encryptedSaltCiphertext, - iv: secretBlindIndexData.saltIV, - tag: secretBlindIndexData.saltTag, - key: encryptionKey - }); - } - - throw InternalServerError({ - message: "Failed to obtain workspace salt needed for secret blind indexing" - }); -}; - -/** - * Generate blind index for secret with name [secretName] - * and salt [salt] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to generate blind index for - * @param {String} obj.salt - base64-salt - */ -export const generateSecretBlindIndexWithSaltHelper = async ({ - secretName, - salt -}: { - secretName: string; - salt: string; -}) => { - // generate secret blind index - const secretBlindIndex = ( - await argon2.hash(secretName, { - type: argon2.argon2id, - salt: Buffer.from(salt, "base64"), - saltLength: 16, // default 16 bytes - memoryCost: 65536, // default pool of 64 MiB per thread. - hashLength: 32, - parallelism: 1, - raw: true - }) - ).toString("base64"); - - return secretBlindIndex; -}; - -/** - * Generate blind index for secret with name [secretName] - * for workspace with id [workspaceId] - * @param {Object} obj - * @param {Stringj} obj.secretName - name of secret to generate blind index for - * @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to - */ -export const generateSecretBlindIndexHelper = async ({ - secretName, - workspaceId -}: { - secretName: string; - workspaceId: Types.ObjectId; -}) => { - // check if workspace blind index data exists - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - const secretBlindIndexData = await SecretBlindIndexData.findOne({ - workspace: workspaceId - }).select("+algorithm +keyEncoding"); - - if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError(); - - let salt; - if (rootEncryptionKey && secretBlindIndexData.keyEncoding === ENCODING_SCHEME_BASE64) { - salt = client.decryptSymmetric( - secretBlindIndexData.encryptedSaltCiphertext, - rootEncryptionKey, - secretBlindIndexData.saltIV, - secretBlindIndexData.saltTag - ); - - const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }); - - return secretBlindIndex; - } else if (encryptionKey && secretBlindIndexData.keyEncoding === ENCODING_SCHEME_UTF8) { - // decrypt workspace salt - salt = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secretBlindIndexData.encryptedSaltCiphertext, - iv: secretBlindIndexData.saltIV, - tag: secretBlindIndexData.saltTag, - key: encryptionKey - }); - - const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }); - - return secretBlindIndex; - } - - throw InternalServerError({ - message: "Failed to generate secret blind index" - }); -}; - -/** - * Create secret with name [secretName] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to create - * @param {Types.ObjectId} obj.workspaceId - id of workspace to create secret for - * @param {String} obj.environment - environment in workspace to create secret for - * @param {'shared' | 'personal'} obj.type - type of secret - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ -export const createSecretHelper = async ({ - secretName, - workspaceId, - environment, - type, - authData, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - secretPath = "/", - metadata, - skipMultilineEncoding -}: CreateSecretParams) => { - const secretBlindIndex = await generateSecretBlindIndexHelper({ - secretName, - workspaceId: new Types.ObjectId(workspaceId) - }); - - // if using service token filter towards the folderId by secretpath - if (authData.authPayload instanceof ServiceTokenData) { - if (!isValidScope(authData.authPayload, environment, secretPath)) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - } - const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath); - - const exists = await Secret.exists({ - secretBlindIndex, - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - type, - environment, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}) - }); - - if (exists) - throw BadRequestError({ - message: "Failed to create secret that already exists" - }); - - if (type === SECRET_PERSONAL) { - // case: secret type is personal -> check if a corresponding shared secret - // with the same blind index [secretBlindIndex] exists - - const exists = await Secret.exists({ - secretBlindIndex, - folder: folderId, - workspace: new Types.ObjectId(workspaceId), - environment, - type: SECRET_SHARED - }); - - if (!exists) - throw BadRequestError({ - message: "Failed to create personal secret override for no corresponding shared secret" - }); - } - - // create secret - const secret = await new Secret({ - version: 1, - workspace: new Types.ObjectId(workspaceId), - environment, - type, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}), - secretBlindIndex, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - skipMultilineEncoding, - folder: folderId, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - metadata - }).save(); - - const secretVersion = new SecretVersion({ - secret: secret._id, - version: secret.version, - workspace: secret.workspace, - type, - folder: folderId, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}), - environment: secret.environment, - isDeleted: false, - secretBlindIndex, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - skipMultilineEncoding, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }); - - // (EE) add version for new secret - await EESecretService.addSecretVersions({ - secretVersions: [secretVersion] - }); - - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.CREATE_SECRET, - metadata: { - environment, - secretPath, - secretId: secret._id.toString(), - secretKey: secretName, - secretVersion: secret.version - } - }, - { - workspaceId - } - ); - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId, - environment, - folderId - }); - - const postHogClient = await TelemetryService.getPostHogClient(); - - if (postHogClient && metadata?.source !== "signup") { - postHogClient.capture({ - event: "secrets added", - distinctId: await TelemetryService.getDistinctId({ - authData - }), - properties: { - numberOfSecrets: 1, - environment, - workspaceId, - folderId, - channel: authData.userAgentType, - userAgent: authData.userAgent - } - }); - } - - return secret; -}; - -/** - * Get secrets for workspace with id [workspaceId] and environment [environment] - * @param {Object} obj - * @param {Types.ObjectId} obj.workspaceId - id of workspace - * @param {String} obj.environment - environment in workspace - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ -export const getSecretsHelper = async ({ - workspaceId, - environment, - authData, - secretPath = "/" -}: GetSecretsParams) => { - let secrets: ISecret[] = []; - // if using service token filter towards the folderId by secretpath - - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }); - let folderId = "root"; - if (!folders && folderId !== "root") return []; - // get folder from folder tree - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath); - if (!folder) return []; - folderId = folder?.id; - } - - // get personal secrets first - secrets = await Secret.find({ - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - type: SECRET_PERSONAL, - ...getAuthDataPayloadUserObj(authData) - }) - .populate("tags") - .lean(); - - // concat with shared secrets - secrets = secrets.concat( - await Secret.find({ - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - type: SECRET_SHARED, - secretBlindIndex: { - $nin: secrets.map((secret) => secret.secretBlindIndex) - } - }) - .populate("tags") - .lean() - ); - - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.GET_SECRETS, - metadata: { - environment, - secretPath, - numberOfSecrets: secrets.length - } - }, - { - workspaceId - } - ); - - const postHogClient = await TelemetryService.getPostHogClient(); - - // reduce the number of events captured - let shouldRecordK8Event = false; - if (authData.userAgent == K8_USER_AGENT_NAME) { - const randomNumber = Math.random(); - if (randomNumber > 0.9) { - shouldRecordK8Event = true; - } - } - - const numberOfSignupSecrets = secrets.filter( - (secret) => secret?.metadata?.source === "signup" - ).length; - const atLeastOneNonSignUpSecret = secrets.length - numberOfSignupSecrets > 0; - - if (postHogClient && atLeastOneNonSignUpSecret) { - const shouldCapture = authData.userAgent !== K8_USER_AGENT_NAME || shouldRecordK8Event; - const approximateForNoneCapturedEvents = secrets.length * 10; - - if (shouldCapture) { - if (workspaceId.toString() != "650e71fbae3e6c8572f436d4") { - postHogClient.capture({ - event: "secrets pulled", - distinctId: await TelemetryService.getDistinctId({ authData }), - properties: { - numberOfSecrets: shouldRecordK8Event - ? approximateForNoneCapturedEvents - : secrets.length, - environment, - workspaceId, - folderId, - channel: authData.userAgentType, - userAgent: authData.userAgent - } - }); - } - } - } - - return secrets; -}; - -/** - * Get secret with name [secretName] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to get - * @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to - * @param {String} obj.environment - environment in workspace that secret belongs to - * @param {'shared' | 'personal'} obj.type - type of secret - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ -export const getSecretHelper = async ({ - secretName, - workspaceId, - environment, - type, - authData, - secretPath = "/", - include_imports = true, - version -}: GetSecretParams) => { - const secretBlindIndex = await generateSecretBlindIndexHelper({ - secretName, - workspaceId: new Types.ObjectId(workspaceId) - }); - let secret: ISecret | null | undefined = null; - - // if using service token filter towards the folderId by secretpath - - const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath); - - // try getting personal secret first (if exists) - if (version === undefined) { - secret = await Secret.findOne({ - secretBlindIndex, - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - type: type ?? SECRET_PERSONAL, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}) - }).lean(); - } else { - const secretVersion = await SecretVersion.findOne({ - secretBlindIndex, - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - type: type ?? SECRET_PERSONAL, - version - }).lean(); - - if (secretVersion) { - secret = await new Secret({ - ...secretVersion, - _id: secretVersion?.secret - }); - } - } - - if (!secret) { - // case: failed to find personal secret matching criteria - // -> find shared secret matching criteria - if (version === undefined) { - secret = await Secret.findOne({ - secretBlindIndex, - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - type: SECRET_SHARED - }).lean(); - } else { - const secretVersion = await SecretVersion.findOne({ - secretBlindIndex, - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - type: SECRET_SHARED, - version - }).lean(); - - if (secretVersion) { - secret = await new Secret({ - ...secretVersion, - _id: secretVersion?.secret - }); - } - } - } - - if (!secret && include_imports) { - // if still no secret found search in imported secret and retreive - secret = await getAnImportedSecret( - secretName, - workspaceId.toString(), - environment, - folderId, - version - ); - } - - if (!secret) throw SecretNotFoundError(); - - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.GET_SECRET, - metadata: { - environment, - secretPath, - secretId: secret._id.toString(), - secretKey: secretName, - secretVersion: secret.version - } - }, - { - workspaceId - } - ); - - const postHogClient = await TelemetryService.getPostHogClient(); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets pulled", - distinctId: await TelemetryService.getDistinctId({ - authData - }), - properties: { - numberOfSecrets: 1, - environment, - workspaceId, - folderId, - channel: authData.userAgentType, - userAgent: authData.userAgent - } - }); - } - - return secret; -}; - -/** - * Update secret with name [secretName] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to update - * @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to - * @param {String} obj.environment - environment in workspace that secret belongs to - * @param {'shared' | 'personal'} obj.type - type of secret - * @param {String} obj.secretValueCiphertext - ciphertext of secret value - * @param {String} obj.secretValueIV - IV of secret value - * @param {String} obj.secretValueTag - tag of secret value - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ - -export const updateSecretHelper = async ({ - secretName, - workspaceId, - secretId, - environment, - type, - authData, - newSecretName, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretPath, - secretReminderRepeatDays, - secretReminderNote, - tags, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - skipMultilineEncoding -}: UpdateSecretParams) => { - // get secret blind index salt - const salt = await getSecretBlindIndexSaltHelper({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - let oldSecretBlindIndex = await generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }); - - if (secretId) { - const secret = await Secret.findOne({ - workspace: workspaceId, - environment, - _id: secretId - }).select("secretBlindIndex"); - if (secret && secret.secretBlindIndex) oldSecretBlindIndex = secret.secretBlindIndex; - } - - let secret: ISecret | null = null; - const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath); - - let newSecretNameBlindIndex = undefined; - if (newSecretName) { - newSecretNameBlindIndex = await generateSecretBlindIndexWithSaltHelper({ - secretName: newSecretName, - salt - }); - const doesSecretAlreadyExist = await Secret.exists({ - secretBlindIndex: newSecretNameBlindIndex, - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - type - }); - - if (doesSecretAlreadyExist) { - throw BadRequestError({ message: "Secret with the provided name already exist" }); - } - } - - if (type === SECRET_SHARED) { - // case: update shared secret - secret = await Secret.findOneAndUpdate( - { - secretBlindIndex: oldSecretBlindIndex, - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - type - }, - { - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - - secretReminderRepeatDays, - secretReminderNote, - - skipMultilineEncoding, - secretBlindIndex: newSecretNameBlindIndex, - secretKeyIV, - secretKeyTag, - secretKeyCiphertext, - tags, - $inc: { version: 1 } - }, - { - new: true - } - ); - } else { - // case: update personal secret - - secret = await Secret.findOneAndUpdate( - { - secretBlindIndex: oldSecretBlindIndex, - workspace: new Types.ObjectId(workspaceId), - environment, - type, - folder: folderId, - ...getAuthDataPayloadUserObj(authData) - }, - { - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretKeyIV, - secretKeyTag, - secretKeyCiphertext, - tags, - skipMultilineEncoding, - secretBlindIndex: newSecretNameBlindIndex, - $inc: { version: 1 } - }, - { - new: true - } - ); - } - - if (!secret) throw SecretNotFoundError(); - - const secretVersion = new SecretVersion({ - secret: secret._id, - version: secret.version, - workspace: secret.workspace, - folder: folderId, - type, - tags, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}), - environment: secret.environment, - isDeleted: false, - secretBlindIndex: newSecretName ? newSecretNameBlindIndex : oldSecretBlindIndex, - secretKeyCiphertext: secret.secretKeyCiphertext, - secretKeyIV: secret.secretKeyIV, - secretKeyTag: secret.secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - skipMultilineEncoding, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }); - - // (EE) add version for new secret - await EESecretService.addSecretVersions({ - secretVersions: [secretVersion] - }); - - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.UPDATE_SECRET, - metadata: { - environment, - secretPath, - secretId: secret._id.toString(), - secretKey: secretName, - secretVersion: secret.version - } - }, - { - workspaceId - } - ); - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId, - environment, - folderId: secret?.folder - }); - - const postHogClient = await TelemetryService.getPostHogClient(); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets modified", - distinctId: await TelemetryService.getDistinctId({ - authData - }), - properties: { - numberOfSecrets: 1, - environment, - workspaceId, - folderId, - channel: authData.userAgentType, - userAgent: authData.userAgent - } - }); - } - - return secret; -}; - -/** - * Delete secret with name [secretName] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to delete - * @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to - * @param {String} obj.environment - environment in workspace that secret belongs to - * @param {'shared' | 'personal'} obj.type - type of secret - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ -export const deleteSecretHelper = async ({ - secretName, - workspaceId, - environment, - type, - authData, - secretPath = "/", - // used for update corner case and blindIndex goes wrong way - secretId -}: DeleteSecretParams) => { - let secretBlindIndex = await generateSecretBlindIndexHelper({ - secretName, - workspaceId: new Types.ObjectId(workspaceId) - }); - if (secretId) { - const secret = await Secret.findOne({ - workspace: workspaceId, - environment, - _id: secretId - }).select("secretBlindIndex"); - if (secret && secret.secretBlindIndex) secretBlindIndex = secret.secretBlindIndex; - } - - const folderId = await getFolderIdFromServiceToken(workspaceId, environment, secretPath); - - let secrets: ISecret[] = []; - let secret: ISecret | null = null; - - if (type === SECRET_SHARED) { - secrets = await Secret.find({ - secretBlindIndex, - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId - }).lean(); - - secret = await Secret.findOneAndDelete({ - secretBlindIndex, - workspace: new Types.ObjectId(workspaceId), - environment, - type, - folder: folderId - }).lean(); - - await Secret.deleteMany({ - secretBlindIndex, - workspaceId: new Types.ObjectId(workspaceId), - environment, - folder: folderId - }); - } else { - secret = await Secret.findOneAndDelete({ - secretBlindIndex, - folder: folderId, - workspace: new Types.ObjectId(workspaceId), - environment, - type, - ...getAuthDataPayloadUserObj(authData) - }).lean(); - - if (secret) { - secrets = [secret]; - } - } - - if (!secret) throw SecretNotFoundError(); - - await EESecretService.markDeletedSecretVersions({ - secretIds: secrets.map((secret) => secret._id) - }); - - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.DELETE_SECRET, - metadata: { - environment, - secretPath, - secretId: secret._id.toString(), - secretKey: secretName, - secretVersion: secret.version - } - }, - { - workspaceId - } - ); - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId, - environment, - folderId: secret?.folder - }); - - const postHogClient = await TelemetryService.getPostHogClient(); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets deleted", - distinctId: await TelemetryService.getDistinctId({ - authData - }), - properties: { - numberOfSecrets: secrets.length, - environment, - workspaceId, - folderId, - channel: authData.userAgentType, - userAgent: authData.userAgent - } - }); - } - - return { - secrets, - secret - }; -}; - -const fetchSecretsCrossEnv = (workspaceId: string, folders: TFolderRootSchema[], key: string) => { - const fetchCache: Record> = {}; - - return async (secRefEnv: string, secRefPath: string[], secRefKey: string) => { - const secRefPathUrl = path.join("/", ...secRefPath); - const uniqKey = `${secRefEnv}-${secRefPathUrl}`; - - if (fetchCache?.[uniqKey]) { - return fetchCache[uniqKey][secRefKey]; - } - - let folderId = "root"; - const folder = folders.find(({ environment }) => environment === secRefEnv); - if (!folder && secRefPathUrl !== "/") { - throw BadRequestError({ message: "Folder not found" }); - } - - if (folder) { - const selectedFolder = getFolderByPath(folder.nodes, secRefPathUrl); - if (!selectedFolder) { - throw BadRequestError({ message: "Folder not found" }); - } - folderId = selectedFolder.id; - } - - const secrets = await Secret.find({ - workspace: workspaceId, - environment: secRefEnv, - type: SECRET_SHARED, - folder: folderId - }); - - const decryptedSec = secrets.reduce>((prev, secret) => { - const secretKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretKeyCiphertext, - iv: secret.secretKeyIV, - tag: secret.secretKeyTag, - key - }); - const secretValue = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretValueCiphertext, - iv: secret.secretValueIV, - tag: secret.secretValueTag, - key - }); - - prev[secretKey] = secretValue; - return prev; - }, {}); - - fetchCache[uniqKey] = decryptedSec; - - return fetchCache[uniqKey][secRefKey]; - }; -}; - -const INTERPOLATION_SYNTAX_REG = new RegExp(/\${([^}]+)}/g); -const recursivelyExpandSecret = async ( - expandedSec: Record, - interpolatedSec: Record, - fetchCrossEnv: (env: string, secPath: string[], secKey: string) => Promise, - recursionChainBreaker: Record, - key: string -) => { - if (expandedSec?.[key]) { - return expandedSec[key]; - } - if (recursionChainBreaker?.[key]) { - return ""; - } - recursionChainBreaker[key] = true; - - let interpolatedValue = interpolatedSec[key]; - if (!interpolatedValue) { - // eslint-disable-next-line no-console - console.error(`Couldn't find referenced value - ${key}`); - return ""; - } - - const refs = interpolatedValue.match(INTERPOLATION_SYNTAX_REG); - if (refs) { - for (const interpolationSyntax of refs) { - const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1); - const entities = interpolationKey.trim().split("."); - - if (entities.length === 1) { - const val = await recursivelyExpandSecret( - expandedSec, - interpolatedSec, - fetchCrossEnv, - recursionChainBreaker, - interpolationKey - ); - if (val) { - interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val); - } - continue; - } - - if (entities.length > 1) { - const secRefEnv = entities[0]; - const secRefPath = entities.slice(1, entities.length - 1); - const secRefKey = entities[entities.length - 1]; - - const val = await fetchCrossEnv(secRefEnv, secRefPath, secRefKey); - if (val !== undefined) { - interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val); - } - } - } - } - expandedSec[key] = interpolatedValue; - return interpolatedValue; -}; - -// used to convert multi line ones to quotes ones with \n -const formatMultiValueEnv = (val?: string) => { - if (!val) return ""; - if (!val.match("\n")) return val; - return `"${val.replace(/\n/g, "\\n")}"`; -}; - -export const expandSecrets = async ( - workspaceId: string, - rootEncKey: string, - secrets: Record -) => { - const expandedSec: Record = {}; - const interpolatedSec: Record = {}; - - const folders = await Folder.find({ workspace: workspaceId }); - const crossSecEnvFetch = fetchSecretsCrossEnv(workspaceId, folders, rootEncKey); - - Object.keys(secrets).forEach((key) => { - if (secrets[key].value.match(INTERPOLATION_SYNTAX_REG)) { - interpolatedSec[key] = secrets[key].value; - } else { - expandedSec[key] = secrets[key].value; - } - }); - - for (const key of Object.keys(secrets)) { - if (expandedSec?.[key]) { - // should not do multi line encoding if user has set it to skip - secrets[key].value = secrets[key].skipMultilineEncoding - ? expandedSec[key] - : formatMultiValueEnv(expandedSec[key]); - continue; - } - - // this is to avoid recursion loop. So the graph should be direct graph rather than cyclic - // so for any recursion building if there is an entity two times same key meaning it will be looped - const recursionChainBreaker: Record = {}; - const expandedVal = await recursivelyExpandSecret( - expandedSec, - interpolatedSec, - crossSecEnvFetch, - recursionChainBreaker, - key - ); - - secrets[key].value = secrets[key].skipMultilineEncoding - ? expandedVal - : formatMultiValueEnv(expandedVal); - } - - return secrets; -}; - -export const createSecretBatchHelper = async ({ - secrets, - workspaceId, - authData, - secretPath, - environment -}: CreateSecretBatchParams) => { - let folderId = "root"; - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }); - - if (!folders && secretPath !== "/") throw ERR_FOLDER_NOT_FOUND; - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath); - if (!folder) throw ERR_FOLDER_NOT_FOUND; - folderId = folder.id; - } - - // get secret blind index salt - const salt = await getSecretBlindIndexSaltHelper({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - const secretBlindIndexToKey: Record = {}; // used at audit log point - const secretBlindIndexes = await Promise.all( - secrets.map(({ secretName }) => - generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }) - ) - ).then((blindIndexes) => - blindIndexes.reduce>((prev, curr, i) => { - prev[secrets[i].secretName] = curr; - secretBlindIndexToKey[curr] = secrets[i].secretName; - return prev; - }, {}) - ); - - const exists = await Secret.exists({ - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - environment - }) - .or( - secrets.map(({ secretName, type }) => ({ - secretBlindIndex: secretBlindIndexes[secretName], - type: type, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}) - })) - ) - .exec(); - - if (exists) - throw BadRequestError({ - message: "Failed to create secret that already exists" - }); - - // create secret - const newlyCreatedSecrets: ISecret[] = await Secret.insertMany( - secrets.map( - ({ - type, - secretName, - secretKeyIV, - metadata, - secretKeyTag, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretKeyCiphertext, - secretValueCiphertext, - secretCommentCiphertext, - skipMultilineEncoding - }) => ({ - version: 1, - workspace: new Types.ObjectId(workspaceId), - environment, - type, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - folder: folderId, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - metadata, - skipMultilineEncoding, - secretBlindIndex: secretBlindIndexes[secretName], - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}) - }) - ) - ); - - await EESecretService.addSecretVersions({ - secretVersions: newlyCreatedSecrets.map( - (secret) => - new SecretVersion({ - secret: secret._id, - version: secret.version, - workspace: secret.workspace, - type: secret.type, - folder: folderId, - skipMultilineEncoding: secret?.skipMultilineEncoding, - ...(secret.type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}), - environment: secret.environment, - isDeleted: false, - secretBlindIndex: secret.secretBlindIndex, - secretKeyCiphertext: secret.secretKeyCiphertext, - secretKeyIV: secret.secretKeyIV, - secretKeyTag: secret.secretKeyTag, - secretValueCiphertext: secret.secretValueCiphertext, - secretValueIV: secret.secretValueIV, - secretValueTag: secret.secretValueTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }) - ) - }); - - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.CREATE_SECRETS, - metadata: { - environment, - secretPath, - secrets: newlyCreatedSecrets.map(({ secretBlindIndex, version, _id }) => ({ - secretId: _id.toString(), - secretKey: secretBlindIndexToKey[secretBlindIndex || ""], - secretVersion: version - })) - } - }, - { - workspaceId - } - ); - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId, - environment, - folderId - }); - - const postHogClient = await TelemetryService.getPostHogClient(); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets added", - distinctId: await TelemetryService.getDistinctId({ - authData - }), - properties: { - numberOfSecrets: 1, - environment, - workspaceId, - folderId, - channel: authData.userAgentType, - userAgent: authData.userAgent - } - }); - } - - return newlyCreatedSecrets; -}; - -export const updateSecretBatchHelper = async ({ - workspaceId, - environment, - authData, - secretPath, - secrets -}: UpdateSecretBatchParams) => { - let folderId = "root"; - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }); - - if (!folders && secretPath !== "/") throw ERR_FOLDER_NOT_FOUND; - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath); - if (!folder) throw ERR_FOLDER_NOT_FOUND; - folderId = folder.id; - } - - // get secret blind index salt - const salt = await getSecretBlindIndexSaltHelper({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - const secretBlindIndexToKey: Record = {}; // used at audit log point - const secretBlindIndexes = await Promise.all( - secrets.map(({ secretName }) => - generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }) - ) - ).then((blindIndexes) => - blindIndexes.reduce>((prev, curr, i) => { - prev[secrets[i].secretName] = curr; - secretBlindIndexToKey[curr] = secrets[i].secretName; - return prev; - }, {}) - ); - - const secretsToBeUpdated = await Secret.find({ - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - environment - }) - .select("+secretBlindIndex") - .or( - secrets.map(({ secretName, type }) => ({ - secretBlindIndex: secretBlindIndexes[secretName], - type: type, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}) - })) - ) - .lean(); - - if (secretsToBeUpdated.length !== secrets.length) - throw BadRequestError({ message: "Some secrets not found" }); - - await Secret.bulkWrite( - secrets.map( - ({ - type, - secretName, - tags, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretValueCiphertext, - secretCommentCiphertext, - skipMultilineEncoding - }) => ({ - updateOne: { - filter: { - workspace: new Types.ObjectId(workspaceId), - environment, - folder: folderId, - secretBlindIndex: secretBlindIndexes[secretName], - type, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}) - }, - update: { - $inc: { - version: 1 - }, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentCiphertext, - secretCommentIV, - secretCommentTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - tags, - skipMultilineEncoding - } - } - }) - ) - ); - - const secretsGroupedByBlindIndex = secretsToBeUpdated.reduce>( - (prev, curr) => { - if (curr.secretBlindIndex) prev[curr.secretBlindIndex] = curr; - return prev; - }, - {} - ); - - await EESecretService.addSecretVersions({ - secretVersions: secrets.map((secret) => { - const { - _id, - version, - workspace, - type, - secretBlindIndex, - secretKeyIV, - secretKeyTag, - secretKeyCiphertext, - skipMultilineEncoding - } = secretsGroupedByBlindIndex[secretBlindIndexes[secret.secretName]]; - - return new SecretVersion({ - secret: _id, - version: version + 1, - workspace: workspace, - type, - folder: folderId, - ...(secret.type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}), - environment, - isDeleted: false, - secretBlindIndex: secretBlindIndex, - secretKeyCiphertext: secretKeyCiphertext, - secretKeyIV: secretKeyIV, - secretKeyTag: secretKeyTag, - secretValueCiphertext: secret.secretValueCiphertext, - secretValueIV: secret.secretValueIV, - secretValueTag: secret.secretValueTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - skipMultilineEncoding - }); - }) - }); - - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.UPDATE_SECRETS, - metadata: { - environment, - secretPath, - secrets: secretsToBeUpdated.map(({ _id, version, secretBlindIndex }) => ({ - secretId: _id.toString(), - secretKey: secretBlindIndexToKey[secretBlindIndex || ""], - secretVersion: version + 1 - })) - } - }, - { - workspaceId - } - ); - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId, - environment, - folderId - }); - - const postHogClient = await TelemetryService.getPostHogClient(); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets modified", - distinctId: await TelemetryService.getDistinctId({ - authData - }), - properties: { - numberOfSecrets: 1, - environment, - workspaceId, - folderId, - channel: authData.userAgentType, - userAgent: authData.userAgent - } - }); - } - - return; -}; - -export const deleteSecretBatchHelper = async ({ - workspaceId, - environment, - authData, - secretPath = "/", - secrets -}: DeleteSecretBatchParams) => { - let folderId = "root"; - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }); - - if (!folders && secretPath !== "/") throw ERR_FOLDER_NOT_FOUND; - if (folders) { - const folder = getFolderByPath(folders.nodes, secretPath); - if (!folder) throw ERR_FOLDER_NOT_FOUND; - folderId = folder.id; - } - - // get secret blind index salt - const salt = await getSecretBlindIndexSaltHelper({ - workspaceId: new Types.ObjectId(workspaceId) - }); - - const secretBlindIndexToKey: Record = {}; // used at audit log point - const secretBlindIndexes = await Promise.all( - secrets.map(({ secretName }) => - generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }) - ) - ).then((blindIndexes) => - blindIndexes.reduce>((prev, curr, i) => { - prev[secrets[i].secretName] = curr; - secretBlindIndexToKey[curr] = secrets[i].secretName; - return prev; - }, {}) - ); - - const deletedSecrets = await Secret.find({ - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - environment - }) - .or( - secrets.map(({ secretName, type }) => ({ - secretBlindIndex: secretBlindIndexes[secretName], - type: type === "shared" ? { $in: ["shared", "personal"] } : type, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}) - })) - ) - .select({ secretBlindIndexes: 1 }) - .lean() - .exec(); - - await Secret.deleteMany({ - workspace: new Types.ObjectId(workspaceId), - folder: folderId, - environment - }) - .or( - secrets.map(({ secretName, type }) => ({ - secretBlindIndex: secretBlindIndexes[secretName], - type: type === "shared" ? { $in: ["shared", "personal"] } : type, - ...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}) - })) - ) - .exec(); - - await EESecretService.markDeletedSecretVersions({ - secretIds: deletedSecrets.map((secret) => secret._id) - }); - - await EEAuditLogService.createAuditLog( - authData, - { - type: EventType.DELETE_SECRETS, - metadata: { - environment, - secretPath, - secrets: deletedSecrets.map(({ _id, version, secretBlindIndex }) => ({ - secretId: _id.toString(), - secretKey: secretBlindIndexToKey[secretBlindIndex || ""], - secretVersion: version - })) - } - }, - { - workspaceId - } - ); - - // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId, - environment, - folderId - }); - - const postHogClient = await TelemetryService.getPostHogClient(); - - if (postHogClient) { - postHogClient.capture({ - event: "secrets deleted", - distinctId: await TelemetryService.getDistinctId({ - authData - }), - properties: { - numberOfSecrets: secrets.length, - environment, - workspaceId, - folderId, - channel: authData.userAgentType, - userAgent: authData.userAgent - } - }); - } - - return { - secrets: deletedSecrets - }; -}; diff --git a/backend-mongo/src/helpers/signup.ts b/backend-mongo/src/helpers/signup.ts deleted file mode 100644 index 27b1c16ab..000000000 --- a/backend-mongo/src/helpers/signup.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { IUser } from "../models"; -import { createOrganization } from "./organization"; -import { addMembershipsOrg } from "./membershipOrg"; -import { ACCEPTED, ADMIN } from "../variables"; -import { sendMail } from "../helpers/nodemailer"; -import { TokenService } from "../services"; -import { TOKEN_EMAIL_CONFIRMATION } from "../variables"; - -/** - * Send magic link to verify email to [email] - * for user and workspace. - * @param {Object} obj - * @param {String} obj.email - email - * @returns {Boolean} success - whether or not operation was successful - */ -export const sendEmailVerification = async ({ email }: { email: string }) => { - const token = await TokenService.createToken({ - type: TOKEN_EMAIL_CONFIRMATION, - email - }); - - // send mail - await sendMail({ - template: "emailVerification.handlebars", - subjectLine: "Infisical confirmation code", - recipients: [email], - substitutions: { - code: token - } - }); -}; - -/** - * Validate [code] sent to [email] - * @param {Object} obj - * @param {String} obj.email - emai - * @param {String} obj.code - code that was sent to [email] - */ -export const checkEmailVerification = async ({ email, code }: { email: string; code: string }) => { - await TokenService.validateToken({ - type: TOKEN_EMAIL_CONFIRMATION, - email, - token: code - }); -}; - -/** - * Initialize default organization named [organizationName] with workspace - * for user [user] - * @param {Object} obj - * @param {String} obj.organizationName - name of organization to initialize - * @param {IUser} obj.user - user who we are initializing for - */ -export const initializeDefaultOrg = async ({ - organizationName, - user -}: { - organizationName: string; - user: IUser; -}) => { - try { - // create organization with user as owner and initialize a free - // subscription - const organization = await createOrganization({ - email: user.email, - name: organizationName - }); - - await addMembershipsOrg({ - userIds: [user._id.toString()], - organizationId: organization._id.toString(), - roles: [ADMIN], - statuses: [ACCEPTED] - }); - } catch (err) { - throw new Error(`Failed to initialize default organization and workspace [err=${err}]`); - } -}; diff --git a/backend-mongo/src/helpers/token.ts b/backend-mongo/src/helpers/token.ts deleted file mode 100644 index 66bae1a15..000000000 --- a/backend-mongo/src/helpers/token.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { Types } from "mongoose"; -import { TokenData } from "../models"; -import crypto from "crypto"; -import bcrypt from "bcrypt"; -import { - TOKEN_EMAIL_CONFIRMATION, - TOKEN_EMAIL_MFA, - TOKEN_EMAIL_ORG_INVITATION, - TOKEN_EMAIL_PASSWORD_RESET, -} from "../variables"; -import { UnauthorizedRequestError } from "../utils/errors"; -import { getSaltRounds } from "../config"; - -/** - * Create and store a token in the database for purpose [type] - * @param {Object} obj - * @param {String} obj.type - * @param {String} obj.email - * @param {String} obj.phoneNumber - * @param {Types.ObjectId} obj.organizationId - * @returns {String} token - the created token - */ -export const createTokenHelper = async ({ - type, - email, - phoneNumber, - organizationId, -}: { - type: - | "emailConfirmation" - | "emailMfa" - | "organizationInvitation" - | "passwordReset"; - email?: string; - phoneNumber?: string; - organizationId?: Types.ObjectId; -}) => { - let token, expiresAt, triesLeft; - // generate random token based on specified token use-case - // type [type] - switch (type) { - case TOKEN_EMAIL_CONFIRMATION: - // generate random 6-digit code - token = String(crypto.randomInt(Math.pow(10, 5), Math.pow(10, 6) - 1)); - expiresAt = new Date(new Date().getTime() + 86400000); - break; - case TOKEN_EMAIL_MFA: - // generate random 6-digit code - token = String(crypto.randomInt(Math.pow(10, 5), Math.pow(10, 6) - 1)); - triesLeft = 5; - expiresAt = new Date(new Date().getTime() + 300000); - break; - case TOKEN_EMAIL_ORG_INVITATION: - // generate random hex - token = crypto.randomBytes(16).toString("hex"); - expiresAt = new Date(new Date().getTime() + 259200000); - break; - case TOKEN_EMAIL_PASSWORD_RESET: - // generate random hex - token = crypto.randomBytes(16).toString("hex"); - expiresAt = new Date(new Date().getTime() + 86400000); - break; - default: - token = crypto.randomBytes(16).toString("hex"); - expiresAt = new Date(); - break; - } - - interface TokenDataQuery { - type: string; - email?: string; - phoneNumber?: string; - organization?: Types.ObjectId; - } - - interface TokenDataUpdate { - type: string; - email?: string; - phoneNumber?: string; - organization?: Types.ObjectId; - tokenHash: string; - triesLeft?: number; - expiresAt: Date; - } - - const query: TokenDataQuery = { type }; - const update: TokenDataUpdate = { - type, - tokenHash: await bcrypt.hash(token, await getSaltRounds()), - expiresAt, - }; - - if (email) { - query.email = email; - update.email = email; - } - if (phoneNumber) { - query.phoneNumber = phoneNumber; - update.phoneNumber = phoneNumber; - } - if (organizationId) { - query.organization = organizationId; - update.organization = organizationId; - } - - if (triesLeft) { - update.triesLeft = triesLeft; - } - - await TokenData.findOneAndUpdate(query, update, { - new: true, - upsert: true, - }); - - return token; -}; - -/** - * - * @param {Object} obj - * @param {String} obj.email - email associated with the token - * @param {String} obj.token - value of the token - */ -export const validateTokenHelper = async ({ - type, - email, - phoneNumber, - organizationId, - token, -}: { - type: - | "emailConfirmation" - | "emailMfa" - | "organizationInvitation" - | "passwordReset"; - email?: string; - phoneNumber?: string; - organizationId?: Types.ObjectId; - token: string; -}) => { - interface Query { - type: string; - email?: string; - phoneNumber?: string; - organization?: Types.ObjectId; - } - - const query: Query = { type }; - - if (email) { - query.email = email; - } - if (phoneNumber) { - query.phoneNumber = phoneNumber; - } - if (organizationId) { - query.organization = organizationId; - } - - const tokenData = await TokenData.findOne(query).select("+tokenHash"); - - if (!tokenData) throw new Error("Failed to find token to validate"); - - if (tokenData.expiresAt < new Date()) { - // case: token expired - await TokenData.findByIdAndDelete(tokenData._id); - throw UnauthorizedRequestError({ - message: "MFA session expired. Please log in again", - context: { - code: "mfa_expired", - }, - }); - } - - const isValid = await bcrypt.compare(token, tokenData.tokenHash); - if (!isValid) { - // case: token is not valid - if (tokenData?.triesLeft !== undefined) { - // case: token has a try-limit - if (tokenData.triesLeft === 1) { - // case: token is out of tries - await TokenData.findByIdAndDelete(tokenData._id); - } else { - // case: token has more than 1 try left - await TokenData.findByIdAndUpdate( - tokenData._id, - { - triesLeft: tokenData.triesLeft - 1, - }, - { - new: true, - } - ); - } - - throw UnauthorizedRequestError({ - message: "MFA code is invalid", - context: { - code: "mfa_invalid", - triesLeft: tokenData.triesLeft - 1, - }, - }); - } - - throw UnauthorizedRequestError({ - message: "MFA code is invalid", - context: { - code: "mfa_invalid", - }, - }); - } - - // case: token is valid - await TokenData.findByIdAndDelete(tokenData._id); -}; \ No newline at end of file diff --git a/backend-mongo/src/helpers/user.ts b/backend-mongo/src/helpers/user.ts deleted file mode 100644 index 8085dd599..000000000 --- a/backend-mongo/src/helpers/user.ts +++ /dev/null @@ -1,301 +0,0 @@ -import { Types } from "mongoose"; -import { - APIKeyData, - BackupPrivateKey, - IUser, - Key, - Membership, - MembershipOrg, - TokenVersion, - User, - UserAction -} from "../models"; -import { sendMail } from "./nodemailer"; -import { - InternalServerError, - ResourceNotFoundError -} from "../utils/errors"; -import { ADMIN } from "../variables"; -import { deleteOrganization } from "../helpers/organization"; -import { deleteWorkspace } from "../helpers/workspace"; - -/** - * Initialize a user under email [email] - * @param {Object} obj - * @param {String} obj.email - email of user to initialize - * @returns {Object} user - the initialized user - */ -export const setupAccount = async ({ email }: { email: string }) => { - const user = await new User({ - email - }).save(); - - return user; -}; - -/** - * Finish setting up user - * @param {Object} obj - * @param {String} obj.userId - id of user to finish setting up - * @param {String} obj.firstName - first name of user - * @param {String} obj.lastName - last name of user - * @param {Number} obj.encryptionVersion - version of auth encryption scheme used - * @param {String} obj.protectedKey - protected key in encryption version 2 - * @param {String} obj.protectedKeyIV - IV of protected key in encryption version 2 - * @param {String} obj.protectedKeyTag - tag of protected key in encryption version 2 - * @param {String} obj.publicKey - publickey of user - * @param {String} obj.encryptedPrivateKey - (encrypted) private key of user - * @param {String} obj.encryptedPrivateKeyIV - iv for (encrypted) private key of user - * @param {String} obj.encryptedPrivateKeyTag - tag for (encrypted) private key of user - * @param {String} obj.salt - salt for auth SRP - * @param {String} obj.verifier - verifier for auth SRP - * @returns {Object} user - the completed user - */ -export const completeAccount = async ({ - userId, - firstName, - lastName, - encryptionVersion, - protectedKey, - protectedKeyIV, - protectedKeyTag, - publicKey, - encryptedPrivateKey, - encryptedPrivateKeyIV, - encryptedPrivateKeyTag, - salt, - verifier -}: { - userId: string; - firstName: string; - lastName?: string; - encryptionVersion: number; - protectedKey: string; - protectedKeyIV: string; - protectedKeyTag: string; - publicKey: string; - encryptedPrivateKey: string; - encryptedPrivateKeyIV: string; - encryptedPrivateKeyTag: string; - salt: string; - verifier: string; -}) => { - const options = { - new: true - }; - const user = await User.findByIdAndUpdate( - userId, - { - firstName, - lastName, - encryptionVersion, - protectedKey, - protectedKeyIV, - protectedKeyTag, - publicKey, - encryptedPrivateKey, - iv: encryptedPrivateKeyIV, - tag: encryptedPrivateKeyTag, - salt, - verifier - }, - options - ); - - return user; -}; - -/** - * Check if device with ip [ip] and user-agent [userAgent] has been seen for user [user]. - * If the device is unseen, then notify the user of the new device - * @param {Object} obj - * @param {String} obj.ip - login ip address - * @param {String} obj.userAgent - login user-agent - */ -export const checkUserDevice = async ({ - user, - ip, - userAgent -}: { - user: IUser; - ip: string; - userAgent: string; -}) => { - const isDeviceSeen = user.devices.some( - (device) => device.ip === ip && device.userAgent === userAgent - ); - - if (!isDeviceSeen) { - // case: unseen login ip detected for user - // -> notify user about the sign-in from new ip - - user.devices = user.devices.concat([ - { - ip: String(ip), - userAgent - } - ]); - - await user.save(); - - // send MFA code [code] to [email] - await sendMail({ - template: "newDevice.handlebars", - subjectLine: "Successful login from new device", - recipients: [user.email], - substitutions: { - email: user.email, - timestamp: new Date().toString(), - ip, - userAgent - } - }); - } -}; - -/** - * Check that if we delete user with id [userId] then - * there won't be any admin-less organizations or projects - * @param {Object} obj - * @param {String} obj.userId - id of user to check deletion conditions for - */ -const checkDeleteUserConditions = async ({ - userId -}: { - userId: Types.ObjectId; -}) => { - const memberships = await Membership.find({ - user: userId - }); - - const membershipOrgs = await MembershipOrg.find({ - user: userId - }); - - // delete organizations where user is only member - for await (const membershipOrg of membershipOrgs) { - const orgMemberCount = await MembershipOrg.countDocuments({ - organization: membershipOrg.organization, - }); - - const otherOrgAdminCount = await MembershipOrg.countDocuments({ - organization: membershipOrg.organization, - user: { $ne: userId }, - role: ADMIN - }); - - if (orgMemberCount > 1 && otherOrgAdminCount === 0) { - throw InternalServerError({ - message: "Failed to delete account because an org would be admin-less" - }); - } - } - - // delete workspaces where user is only member - for await (const membership of memberships) { - const workspaceMemberCount = await Membership.countDocuments({ - workspace: membership.workspace - }); - - const otherWorkspaceAdminCount = await Membership.countDocuments({ - workspace: membership.workspace, - user: { $ne: userId }, - role: ADMIN - }); - - if (workspaceMemberCount > 1 && otherWorkspaceAdminCount === 0) { - throw InternalServerError({ - message: "Failed to delete account because a workspace would be admin-less" - }); - } - } -} - -/** - * Delete account with id [userId] - * @param {Object} obj - * @param {Types.ObjectId} obj.userId - id of user to delete - * @returns {User} user - deleted user - */ -export const deleteUser = async ({ - userId -}: { - userId: Types.ObjectId; -}) => { - - const user = await User.findByIdAndDelete(userId); - - if (!user) throw ResourceNotFoundError(); - - await checkDeleteUserConditions({ - userId: user._id - }); - - await UserAction.deleteMany({ - user: user._id - }); - - await BackupPrivateKey.deleteMany({ - user: user._id - }); - - await APIKeyData.deleteMany({ - user: user._id - }); - - await TokenVersion.deleteMany({ - user: user._id - }); - - await Key.deleteMany({ - receiver: user._id - }); - - const membershipOrgs = await MembershipOrg.find({ - user: userId - }); - - // delete organizations where user is only member - for await (const membershipOrg of membershipOrgs) { - const memberCount = await MembershipOrg.countDocuments({ - organization: membershipOrg.organization - }); - - if (memberCount === 1) { - // organization only has 1 member (the current user) - - await deleteOrganization({ - organizationId: membershipOrg.organization - }); - } - } - - const memberships = await Membership.find({ - user: userId - }); - - // delete workspaces where user is only member - for await (const membership of memberships) { - const memberCount = await Membership.countDocuments({ - workspace: membership.workspace - }); - - if (memberCount === 1) { - // workspace only has 1 member (the current user) -> delete workspace - - await deleteWorkspace({ - workspaceId: membership.workspace - }); - } - } - - await MembershipOrg.deleteMany({ - user: userId - }); - - await Membership.deleteMany({ - user: userId - }); - - return user; -} \ No newline at end of file diff --git a/backend-mongo/src/helpers/validation.ts b/backend-mongo/src/helpers/validation.ts deleted file mode 100644 index f552eb69b..000000000 --- a/backend-mongo/src/helpers/validation.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Request } from "express"; -import { AnyZodObject, ZodError, z } from "zod"; -import { BadRequestError } from "../utils/errors"; - -export async function validateRequest( - schema: T, - req: Request -): Promise> { - try { - return schema.parseAsync(req); - } catch (error) { - if (error instanceof ZodError) { - throw BadRequestError({ message: error.message }); - } - return BadRequestError({ message: JSON.stringify(error) }); - } -} diff --git a/backend-mongo/src/helpers/workspace.ts b/backend-mongo/src/helpers/workspace.ts deleted file mode 100644 index 3a031a066..000000000 --- a/backend-mongo/src/helpers/workspace.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { Types } from "mongoose"; -import { - Bot, - BotKey, - Folder, - IdentityMembership, - Integration, - IntegrationAuth, - Key, - Membership, - Secret, - SecretBlindIndexData, - SecretImport, - ServiceToken, - ServiceTokenData, - Tag, - Webhook, - Workspace -} from "../models"; -import { - AuditLog, - FolderVersion, - IPType, - SecretApprovalPolicy, - SecretApprovalRequest, - SecretSnapshot, - SecretVersion, - TrustedIP -} from "../ee/models"; -import { createBot } from "../helpers/bot"; -import { EELicenseService } from "../ee/services"; -import { SecretService } from "../services"; -import { - ResourceNotFoundError -} from "../utils/errors"; - -/** - * Create a workspace with name [name] in organization with id [organizationId] - * and a bot for it. - * @param {String} name - name of workspace to create. - * @param {String} organizationId - id of organization to create workspace in - * @param {Object} workspace - new workspace - */ -export const createWorkspace = async ({ - name, - organizationId, -}: { - name: string; - organizationId: Types.ObjectId; -}) => { - // create workspace - const workspace = await new Workspace({ - name, - organization: organizationId, - autoCapitalization: true, - }).save(); - - // initialize bot for workspace - await createBot({ - name: "Infisical Bot", - workspaceId: workspace._id, - }); - - // initialize blind index salt for workspace - await SecretService.createSecretBlindIndexData({ - workspaceId: workspace._id, - }); - - // initialize default trusted IPv4 CIDR - 0.0.0.0/0 - await new TrustedIP({ - workspace: workspace._id, - ipAddress: "0.0.0.0", - type: IPType.IPV4, - prefix: 0, - isActive: true, - comment: "" - }).save() - - // initialize default trusted IPv6 CIDR - ::/0 - await new TrustedIP({ - workspace: workspace._id, - ipAddress: "::", - type: IPType.IPV6, - prefix: 0, - isActive: true, - comment: "" - }); - - await EELicenseService.refreshPlan(organizationId); - - return workspace; -}; - -/** - * Delete workspace and all associated materials including memberships, - * secrets, keys, etc. - * @param {Object} obj - * @param {String} obj.id - id of workspace to delete - */ -export const deleteWorkspace = async ({ - workspaceId -}: { - workspaceId: Types.ObjectId; -}) => { - const workspace = await Workspace.findByIdAndDelete(workspaceId); - - if (!workspace) throw ResourceNotFoundError(); - - await Membership.deleteMany({ - workspace: workspace._id - }); - - await Key.deleteMany({ - workspace: workspace._id - }); - - await Bot.deleteMany({ - workspace: workspace._id - }); - - await BotKey.deleteMany({ - workspace: workspace._id - }); - - await SecretBlindIndexData.deleteMany({ - workspace: workspace._id - }); - - await Secret.deleteMany({ - workspace: workspace._id - }); - - await SecretVersion.deleteMany({ - workspace: workspace._id - }); - - await SecretSnapshot.deleteMany({ - workspace: workspace._id - }); - - await SecretImport.deleteMany({ - workspace: workspace._id - }); - - await Folder.deleteMany({ - workspace: workspace._id - }); - - await FolderVersion.deleteMany({ - workspace: workspace._id - }); - - await Webhook.deleteMany({ - workspace: workspace._id - }); - - await TrustedIP.deleteMany({ - workspace: workspace._id - }); - - await Tag.deleteMany({ - workspace: workspace._id - }); - - await IntegrationAuth.deleteMany({ - workspace: workspace._id - }); - - await Integration.deleteMany({ - workspace: workspace._id - }); - - await ServiceToken.deleteMany({ - workspace: workspace._id - }); - - await ServiceTokenData.deleteMany({ - workspace: workspace._id - }); - - await IdentityMembership.deleteMany({ - workspace: workspace._id - }); - - await AuditLog.deleteMany({ - workspace: workspace._id - }); - - await SecretApprovalPolicy.deleteMany({ - workspace: workspace._id - }); - - await SecretApprovalRequest.deleteMany({ - workspace: workspace._id - }); - - return workspace; -}; diff --git a/backend-mongo/src/index.ts b/backend-mongo/src/index.ts deleted file mode 100644 index 3f85221c6..000000000 --- a/backend-mongo/src/index.ts +++ /dev/null @@ -1,344 +0,0 @@ -import dotenv from "dotenv"; -dotenv.config(); -import express from "express"; -// eslint-disable-next-line @typescript-eslint/no-var-requires -import "express-async-errors"; -import helmet from "helmet"; -import cors from "cors"; -import { initLogger, logger } from "./utils/logging"; -import httpLogger from "pino-http"; -import { DatabaseService } from "./services"; -import { EELicenseService, GithubSecretScanningService } from "./ee/services"; -import { setUpHealthEndpoint } from "./services/health"; -import cookieParser from "cookie-parser"; -import swaggerUi = require("swagger-ui-express"); -import { Probot, createNodeMiddleware } from "probot"; -// eslint-disable-next-line @typescript-eslint/no-var-requires -const swaggerFile = require("../spec.json"); -// eslint-disable-next-line @typescript-eslint/no-var-requires -import { apiLimiter } from "./helpers/rateLimiter"; -import { - cloudProducts as eeCloudProductsRouter, - organizations as eeOrganizationsRouter, - sso as eeSSORouter, - secret as eeSecretRouter, - secretSnapshot as eeSecretSnapshotRouter, - users as eeUsersRouter, - workspace as eeWorkspaceRouter, - identities as v1IdentitiesRouter, - roles as v1RoleRouter, - secretApprovalPolicy as v1SecretApprovalPolicyRouter, - secretApprovalRequest as v1SecretApprovalRequestRouter, - secretRotation as v1SecretRotation, - secretRotationProvider as v1SecretRotationProviderRouter, - secretScanning as v1SecretScanningRouter -} from "./ee/routes/v1"; -import { apiKeyData as v3apiKeyDataRouter } from "./ee/routes/v3"; -import { - admin as v1AdminRouter, - auth as v1AuthRouter, - bot as v1BotRouter, - integrationAuth as v1IntegrationAuthRouter, - integration as v1IntegrationRouter, - inviteOrg as v1InviteOrgRouter, - key as v1KeyRouter, - membershipOrg as v1MembershipOrgRouter, - membership as v1MembershipRouter, - organization as v1OrganizationRouter, - password as v1PasswordRouter, - sso as v1SSORouter, - secretImps as v1SecretImpsRouter, - secret as v1SecretRouter, - secretsFolder as v1SecretsFolder, - serviceToken as v1ServiceTokenRouter, - signup as v1SignupRouter, - universalAuth as v1UniversalAuthRouter, - userAction as v1UserActionRouter, - user as v1UserRouter, - webhooks as v1WebhooksRouter, - workspace as v1WorkspaceRouter -} from "./routes/v1"; -import { - auth as v2AuthRouter, - environment as v2EnvironmentRouter, - organizations as v2OrganizationsRouter, - secret as v2SecretRouter, // begin to phase out - secrets as v2SecretsRouter, - serviceTokenData as v2ServiceTokenDataRouter, - signup as v2SignupRouter, - tags as v2TagsRouter, - users as v2UsersRouter, - workspace as v2WorkspaceRouter, - membership as v2MembershipController -} from "./routes/v2"; -import { - auth as v3AuthRouter, - secrets as v3SecretsRouter, - signup as v3SignupRouter, - users as v3UsersRouter, - workspaces as v3WorkspacesRouter -} from "./routes/v3"; -import { healthCheck } from "./routes/status"; -// import { getLogger } from "./utils/logger"; -import { RouteNotFoundError } from "./utils/errors"; -import { requestErrorHandler } from "./middleware/requestErrorHandler"; -import { - getIsMigrationMode, - getMongoURL, - getNodeEnv, - getPort, - getSecretScanningGitAppId, - getSecretScanningPrivateKey, - getSecretScanningWebhookProxy, - getSecretScanningWebhookSecret, - getSiteURL -} from "./config"; -import { setup } from "./utils/setup"; -import { syncSecretsToThirdPartyServices } from "./queues/integrations/syncSecretsToThirdPartyServices"; -import { githubPushEventSecretScan } from "./queues/secret-scanning/githubScanPushEvent"; -const SmeeClient = require("smee-client"); // eslint-disable-line -import path from "path"; -import { serverConfigInit } from "./config/serverConfig"; -import { initRedis } from "./services/RedisService"; - -let handler: null | any = null; - -const main = async () => { - await initLogger(); - - const port = await getPort(); - - // initializing the database connection + redis - await initRedis(); - await DatabaseService.initDatabase(await getMongoURL()); - const serverCfg = await serverConfigInit(); - await setup(); - - await EELicenseService.initGlobalFeatureSet(); - - const app = express(); - app.enable("trust proxy"); - - app.use( - httpLogger({ - logger, - autoLogging: false - }) - ); - - app.use(express.json()); - app.use(express.urlencoded({ extended: false })); - app.use(cookieParser()); - app.use( - cors({ - credentials: true, - origin: await getSiteURL() - }) - ); - - if ( - (await getSecretScanningGitAppId()) && - (await getSecretScanningWebhookSecret()) && - (await getSecretScanningPrivateKey()) - ) { - const probot = new Probot({ - appId: await getSecretScanningGitAppId(), - privateKey: await getSecretScanningPrivateKey(), - secret: await getSecretScanningWebhookSecret() - }); - - if ((await getNodeEnv()) != "production") { - const smee = new SmeeClient({ - source: await getSecretScanningWebhookProxy(), - target: "http://backend:4000/ss-webhook", - logger: console - }); - - smee.start(); - } - - app.use( - createNodeMiddleware(GithubSecretScanningService, { probot, webhooksPath: "/ss-webhook" }) - ); // secret scanning webhook - } - - if ((await getNodeEnv()) === "production") { - // enable app-wide rate-limiting + helmet security - // in production - app.disable("x-powered-by"); - app.use(apiLimiter); - app.use(helmet()); - } - - app.use((req, res, next) => { - // default to IP address provided by Cloudflare - // #swagger.ignore = true - const cfIp = req.headers["cf-connecting-ip"]; - req.realIP = Array.isArray(cfIp) ? cfIp[0] : (cfIp as string) || req.ip; - next(); - }); - - if ((await getNodeEnv()) === "production" && process.env.STANDALONE_BUILD === "true") { - const nextJsBuildPath = path.join(__dirname, "../frontend-build"); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // eslint-disable-next-line @typescript-eslint/no-var-requires - const conf = require("../frontend-build/.next/required-server-files.json").config; - const NextServer = - // eslint-disable-next-line @typescript-eslint/no-var-requires - require("../frontend-build/node_modules/next/dist/server/next-server").default; - const nextApp = new NextServer({ - dev: false, - dir: nextJsBuildPath, - port, - conf, - hostname: "local", - customServer: false - }); - - handler = nextApp.getRequestHandler(); - } - - app.use((req, _res, next) => { - getIsMigrationMode() - .then((el) => { - if (el && req.method !== "GET") { - next(new Error("Migration mode")); - } else { - next(); - } - }) - .catch(next); - }); - - // (EE) routes - app.use("/api/v1/identities", v1IdentitiesRouter); - app.use("/api/v1/secret", eeSecretRouter); - app.use("/api/v1/secret-snapshot", eeSecretSnapshotRouter); - app.use("/api/v1/users", eeUsersRouter); - app.use("/api/v1/workspace", eeWorkspaceRouter); - app.use("/api/v1/organizations", eeOrganizationsRouter); - app.use("/api/v1/sso", eeSSORouter); - app.use("/api/v1/cloud-products", eeCloudProductsRouter); - app.use("/api/v3/api-key", v3apiKeyDataRouter); - app.use("/api/v1/secret-rotation-providers", v1SecretRotationProviderRouter); - app.use("/api/v1/secret-rotations", v1SecretRotation); - - // v1 routes - app.use("/api/v1/signup", v1SignupRouter); - app.use("/api/v1/auth", v1AuthRouter); - app.use("/api/v1/auth", v1UniversalAuthRouter); // new - app.use("/api/v1/admin", v1AdminRouter); - app.use("/api/v1/bot", v1BotRouter); - app.use("/api/v1/user", v1UserRouter); - app.use("/api/v1/user-action", v1UserActionRouter); - app.use("/api/v1/organization", v1OrganizationRouter); - app.use("/api/v1/workspace", v1WorkspaceRouter); - app.use("/api/v1/membership-org", v1MembershipOrgRouter); - app.use("/api/v1/membership", v1MembershipRouter); - app.use("/api/v1/key", v1KeyRouter); - app.use("/api/v1/invite-org", v1InviteOrgRouter); - app.use("/api/v1/secret", v1SecretRouter); // deprecate - app.use("/api/v1/service-token", v1ServiceTokenRouter); // deprecate - app.use("/api/v1/password", v1PasswordRouter); - app.use("/api/v1/integration", v1IntegrationRouter); - app.use("/api/v1/integration-auth", v1IntegrationAuthRouter); - app.use("/api/v1/folders", v1SecretsFolder); - app.use("/api/v1/secret-scanning", v1SecretScanningRouter); - app.use("/api/v1/webhooks", v1WebhooksRouter); - app.use("/api/v1/secret-imports", v1SecretImpsRouter); - app.use("/api/v1/roles", v1RoleRouter); - app.use("/api/v1/secret-approvals", v1SecretApprovalPolicyRouter); - app.use("/api/v1/sso", v1SSORouter); - app.use("/api/v1/secret-approval-requests", v1SecretApprovalRequestRouter); - - // v2 routes (improvements) - app.use("/api/v2/signup", v2SignupRouter); - app.use("/api/v2/auth", v2AuthRouter); - app.use("/api/v2/users", v2UsersRouter); - app.use("/api/v2/organizations", v2OrganizationsRouter); - app.use("/api/v2/workspace", v2MembershipController); - app.use("/api/v2/workspace", v2EnvironmentRouter); - app.use("/api/v2/workspace", v2TagsRouter); - app.use("/api/v2/workspace", v2WorkspaceRouter); - app.use("/api/v2/secret", v2SecretRouter); // deprecate - app.use("/api/v2/secrets", v2SecretsRouter); - app.use("/api/v2/service-token", v2ServiceTokenDataRouter); - - // v3 routes (experimental) - app.use("/api/v3/auth", v3AuthRouter); - app.use("/api/v3/secrets", v3SecretsRouter); - app.use("/api/v3/workspaces", v3WorkspacesRouter); - app.use("/api/v3/signup", v3SignupRouter); - app.use("/api/v3/us", v3UsersRouter); - - // api docs - app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerFile)); - - // server status - app.use("/api", healthCheck); - - if (handler) { - app.all("*", (req, res) => { - return handler(req, res); - }); - } - - //* Handle unrouted requests and respond with proper error message as well as status code - app.use((req, res, next) => { - if (res.headersSent) return next(); - next( - RouteNotFoundError({ - message: `The requested source '(${req.method})${req.url}' was not found` - }) - ); - }); - - app.use(requestErrorHandler); - - const server = app.listen(port, async () => { - if (!serverCfg.initialized) { - logger.info(`Welcome to Infisical - -Create your Infisical administrator account at: -http://localhost:${port}/admin/signup -`); - } else { - logger.info(`Welcome back! - -To access Infisical Administrator Panel open -http://localhost:${port}/admin - -To access Infisical server -http://localhost:${port} -`); - } - }); - - // await createTestUserForDevelopment(); - setUpHealthEndpoint(server); - - const serverCleanup = async () => { - await DatabaseService.closeDatabase(); - syncSecretsToThirdPartyServices.close(); - githubPushEventSecretScan.close(); - - process.exit(0); - }; - - process.on("SIGINT", function () { - server.close(async () => { - await serverCleanup(); - }); - }); - - process.on("SIGTERM", function () { - server.close(async () => { - await serverCleanup(); - }); - }); - - return server; -}; - -export default main(); diff --git a/backend-mongo/src/integrations/apps.ts b/backend-mongo/src/integrations/apps.ts deleted file mode 100644 index 00196b3d0..000000000 --- a/backend-mongo/src/integrations/apps.ts +++ /dev/null @@ -1,1355 +0,0 @@ -import { - INTEGRATION_AWS_PARAMETER_STORE, - INTEGRATION_AWS_SECRET_MANAGER, - INTEGRATION_AZURE_KEY_VAULT, - INTEGRATION_BITBUCKET, - INTEGRATION_BITBUCKET_API_URL, - INTEGRATION_CHECKLY, - INTEGRATION_CHECKLY_API_URL, - INTEGRATION_CIRCLECI, - INTEGRATION_CIRCLECI_API_URL, - INTEGRATION_CLOUDFLARE_PAGES, - INTEGRATION_CLOUDFLARE_PAGES_API_URL, - INTEGRATION_CLOUDFLARE_WORKERS, - INTEGRATION_CLOUDFLARE_WORKERS_API_URL, - INTEGRATION_CLOUD_66, - INTEGRATION_CLOUD_66_API_URL, - INTEGRATION_CODEFRESH, - INTEGRATION_CODEFRESH_API_URL, - INTEGRATION_DIGITAL_OCEAN_API_URL, - INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM, - INTEGRATION_FLYIO, - INTEGRATION_FLYIO_API_URL, - INTEGRATION_GCP_API_URL, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_GCP_SECRET_MANAGER_SERVICE_NAME, - INTEGRATION_GCP_SERVICE_USAGE_URL, - INTEGRATION_GITHUB, - INTEGRATION_GITLAB, - INTEGRATION_GITLAB_API_URL, - INTEGRATION_HASURA_CLOUD, - INTEGRATION_HASURA_CLOUD_API_URL, - INTEGRATION_HEROKU, - INTEGRATION_HEROKU_API_URL, - INTEGRATION_LARAVELFORGE, - INTEGRATION_LARAVELFORGE_API_URL, - INTEGRATION_NETLIFY, - INTEGRATION_NETLIFY_API_URL, - INTEGRATION_NORTHFLANK, - INTEGRATION_NORTHFLANK_API_URL, - INTEGRATION_RAILWAY, - INTEGRATION_RAILWAY_API_URL, - INTEGRATION_RENDER, - INTEGRATION_RENDER_API_URL, - INTEGRATION_SUPABASE, - INTEGRATION_SUPABASE_API_URL, - INTEGRATION_TEAMCITY, - INTEGRATION_TERRAFORM_CLOUD, - INTEGRATION_TERRAFORM_CLOUD_API_URL, - INTEGRATION_TRAVISCI, - INTEGRATION_TRAVISCI_API_URL, - INTEGRATION_VERCEL, - INTEGRATION_VERCEL_API_URL, - INTEGRATION_WINDMILL, - INTEGRATION_WINDMILL_API_URL -} from "../variables"; -import { IIntegrationAuth } from "../models"; -import { Octokit } from "@octokit/rest"; -import { standardRequest } from "../config/request"; - -interface App { - name: string; - appId?: string; - owner?: string; -} - -/** - * Return list of names of apps for integration named [integration] - * @param {Object} obj - * @param {String} obj.integration - name of integration - * @param {String} obj.accessToken - access token for integration - * @param {String} obj.teamId - (optional) id of team for getting integration apps (used for integrations like GitLab) - * @returns {Object[]} apps - names of integration apps - * @returns {String} apps.name - name of integration app - */ -const getApps = async ({ - integrationAuth, - accessToken, - accessId, - teamId, - workspaceSlug -}: { - integrationAuth: IIntegrationAuth; - accessToken: string; - accessId?: string; - teamId?: string; - workspaceSlug?: string; -}) => { - let apps: App[] = []; - switch (integrationAuth.integration) { - case INTEGRATION_GCP_SECRET_MANAGER: - apps = await getAppsGCPSecretManager({ - accessToken - }); - break; - case INTEGRATION_AZURE_KEY_VAULT: - apps = []; - break; - case INTEGRATION_AWS_PARAMETER_STORE: - apps = []; - break; - case INTEGRATION_AWS_SECRET_MANAGER: - apps = []; - break; - case INTEGRATION_HEROKU: - apps = await getAppsHeroku({ - accessToken - }); - break; - case INTEGRATION_VERCEL: - apps = await getAppsVercel({ - integrationAuth, - accessToken - }); - break; - case INTEGRATION_NETLIFY: - apps = await getAppsNetlify({ - accessToken - }); - break; - case INTEGRATION_GITHUB: - apps = await getAppsGithub({ - accessToken - }); - break; - case INTEGRATION_GITLAB: - apps = await getAppsGitlab({ - integrationAuth, - accessToken, - teamId - }); - break; - case INTEGRATION_RENDER: - apps = await getAppsRender({ - accessToken - }); - break; - case INTEGRATION_RAILWAY: - apps = await getAppsRailway({ - accessToken - }); - break; - case INTEGRATION_FLYIO: - apps = await getAppsFlyio({ - accessToken - }); - break; - case INTEGRATION_CIRCLECI: - apps = await getAppsCircleCI({ - accessToken - }); - break; - case INTEGRATION_LARAVELFORGE: - apps = await getAppsLaravelForge({ - accessToken, - serverId: accessId - }); - break; - case INTEGRATION_TERRAFORM_CLOUD: - apps = await getAppsTerraformCloud({ - accessToken, - workspacesId: accessId - }); - break; - case INTEGRATION_TRAVISCI: - apps = await getAppsTravisCI({ - accessToken - }); - break; - case INTEGRATION_TEAMCITY: - apps = await getAppsTeamCity({ - integrationAuth, - accessToken - }); - break; - case INTEGRATION_SUPABASE: - apps = await getAppsSupabase({ - accessToken - }); - break; - case INTEGRATION_CHECKLY: - apps = await getAppsCheckly({ - accessToken - }); - break; - case INTEGRATION_CLOUDFLARE_PAGES: - apps = await getAppsCloudflarePages({ - accessToken, - accountId: accessId - }); - break; - case INTEGRATION_CLOUDFLARE_WORKERS: - apps = await getAppsCloudflareWorkers({ - accessToken, - accountId: accessId - }); - break; - case INTEGRATION_NORTHFLANK: - apps = await getAppsNorthflank({ - accessToken - }); - break; - case INTEGRATION_BITBUCKET: - apps = await getAppsBitBucket({ - accessToken, - workspaceSlug - }); - break; - case INTEGRATION_CODEFRESH: - apps = await getAppsCodefresh({ - accessToken - }); - break; - case INTEGRATION_WINDMILL: - apps = await getAppsWindmill({ - accessToken - }); - break; - case INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM: - apps = await getAppsDigitalOceanAppPlatform({ - accessToken - }); - break; - case INTEGRATION_CLOUD_66: - apps = await getAppsCloud66({ - accessToken - }); - break; - - case INTEGRATION_HASURA_CLOUD: - apps = await getAppsHasuraCloud({ - accessToken - }); - break; - } - - return apps; -}; - -/** - * Return list of apps for GCP secret manager integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for GCP API - * @returns {Object[]} apps - list of GCP projects - * @returns {String} apps.name - name of GCP project - * @returns {String} apps.appId - id of GCP project - */ -const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string }) => { - interface GCPApp { - projectNumber: string; - projectId: string; - lifecycleState: - | "ACTIVE" - | "LIFECYCLE_STATE_UNSPECIFIED" - | "DELETE_REQUESTED" - | "DELETE_IN_PROGRESS"; - name: string; - createTime: string; - parent: { - type: "organization" | "folder" | "project"; - id: string; - }; - } - - interface GCPGetProjectsRes { - projects: GCPApp[]; - nextPageToken?: string; - } - - interface GCPGetServiceRes { - name: string; - parent: string; - state: "ENABLED" | "DISABLED" | "STATE_UNSPECIFIED"; - } - - let gcpApps: GCPApp[] = []; - const apps: App[] = []; - - const pageSize = 100; - let pageToken: string | undefined; - let hasMorePages = true; - - while (hasMorePages) { - const params = new URLSearchParams({ - pageSize: String(pageSize), - ...(pageToken ? { pageToken } : {}) - }); - - const res: GCPGetProjectsRes = ( - await standardRequest.get(`${INTEGRATION_GCP_API_URL}/v1/projects`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }) - ).data; - - gcpApps = gcpApps.concat(res.projects); - - if (!res.nextPageToken) { - hasMorePages = false; - } - - pageToken = res.nextPageToken; - } - - for await (const gcpApp of gcpApps) { - try { - const res: GCPGetServiceRes = ( - await standardRequest.get( - `${INTEGRATION_GCP_SERVICE_USAGE_URL}/v1/projects/${gcpApp.projectId}/services/${INTEGRATION_GCP_SECRET_MANAGER_SERVICE_NAME}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ) - ).data; - - if (res.state === "ENABLED") { - apps.push({ - name: gcpApp.name, - appId: gcpApp.projectId - }); - } - } catch { - continue; - } - } - - return apps; -}; - -/** - * Return list of apps for Heroku integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Heroku API - * @returns {Object[]} apps - names of Heroku apps - * @returns {String} apps.name - name of Heroku app - */ -const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { - const res = ( - await standardRequest.get(`${INTEGRATION_HEROKU_API_URL}/apps`, { - headers: { - Accept: "application/vnd.heroku+json; version=3", - Authorization: `Bearer ${accessToken}` - } - }) - ).data; - - const apps = res.map((a: any) => ({ - name: a.name - })); - - return apps; -}; - -/** - * Return list of names of apps for Vercel integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Vercel API - * @returns {Object[]} apps - names of Vercel apps - * @returns {String} apps.name - name of Vercel app - */ -const getAppsVercel = async ({ - integrationAuth, - accessToken -}: { - integrationAuth: IIntegrationAuth; - accessToken: string; -}) => { - const res = ( - await standardRequest.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - }, - ...(integrationAuth?.teamId - ? { - params: { - teamId: integrationAuth.teamId - } - } - : {}) - }) - ).data; - - const apps = res.projects.map((a: any) => ({ - name: a.name, - appId: a.id - })); - - return apps; -}; - -/** - * Return list of sites for Netlify integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Netlify API - * @returns {Object[]} apps - names of Netlify sites - * @returns {String} apps.name - name of Netlify site - */ -const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => { - const apps: any = []; - let page = 1; - const perPage = 10; - let hasMorePages = true; - - // paginate through all sites - while (hasMorePages) { - const params = new URLSearchParams({ - page: String(page), - per_page: String(perPage), - filter: "all" - }); - - const { data } = await standardRequest.get(`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }); - - data.map((a: any) => { - apps.push({ - name: a.name, - appId: a.site_id - }); - }); - - if (data.length < perPage) { - hasMorePages = false; - } - - page++; - } - - return apps; -}; - -/** - * Return list of repositories for Github integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Github API - * @returns {Object[]} apps - names of Github sites - * @returns {String} apps.name - name of Github site - */ -const getAppsGithub = async ({ accessToken }: { accessToken: string }) => { - interface GitHubApp { - id: string; - name: string; - permissions: { - admin: boolean; - }; - owner: { - login: string; - }; - } - - const octokit = new Octokit({ - auth: accessToken - }); - - const getAllRepos = async () => { - let repos: GitHubApp[] = []; - let page = 1; - const per_page = 100; - let hasMore = true; - - while (hasMore) { - const response = await octokit.request( - "GET /user/repos{?visibility,affiliation,type,sort,direction,per_page,page,since,before}", - { - per_page, - page - } - ); - - if (response.data.length > 0) { - repos = repos.concat(response.data); - page++; - } else { - hasMore = false; - } - } - - return repos; - }; - - const repos = await getAllRepos(); - - const apps = repos - .filter((a: GitHubApp) => a.permissions.admin === true) - .map((a: GitHubApp) => { - return { - appId: a.id, - name: a.name, - owner: a.owner.login - }; - }); - - return apps; -}; - -/** - * Return list of services for Render integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Render API - * @returns {Object[]} apps - names and ids of Render services - * @returns {String} apps.name - name of Render service - * @returns {String} apps.appId - id of Render service - */ -const getAppsRender = async ({ accessToken }: { accessToken: string }) => { - const res = ( - await standardRequest.get(`${INTEGRATION_RENDER_API_URL}/v1/services`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "Accept-Encoding": "application/json" - } - }) - ).data; - - const apps = res.map((a: any) => ({ - name: a.service.name, - appId: a.service.id - })); - - return apps; -}; - -/** - * Return list of projects for Railway integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Railway API - * @returns {Object[]} apps - names and ids of Railway services - * @returns {String} apps.name - name of Railway project - * @returns {String} apps.appId - id of Railway project - * - */ -const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { - const query = ` - query GetProjects($userId: String, $teamId: String) { - projects(userId: $userId, teamId: $teamId) { - edges { - node { - id - name - } - } - } - } - `; - - const variables = {}; - - const { - data: { - data: { - projects: { edges } - } - } - } = await standardRequest.post( - INTEGRATION_RAILWAY_API_URL, - { - query, - variables - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ); - - const apps = edges.map((e: any) => ({ - name: e.node.name, - appId: e.node.id - })); - - return apps; -}; - -/** - * Return list of sites for Laravel Forge integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Laravel Forge API - * @param {String} obj.serverId - server id of Laravel Forge - * @returns {Object[]} apps - names and ids of Laravel Forge sites - * @returns {String} apps.name - name of Laravel Forge sites - * @returns {String} apps.appId - id of Laravel Forge sites - */ -const getAppsLaravelForge = async ({ - accessToken, - serverId -}: { - accessToken: string; - serverId?: string; -}) => { - const res = ( - await standardRequest.get( - `${INTEGRATION_LARAVELFORGE_API_URL}/api/v1/servers/${serverId}/sites`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "Content-Type": "application/json" - } - } - ) - ).data.sites; - - const apps = res.map((a: any) => ({ - name: a.name, - appId: a.id - })); - - return apps; -}; - -/** - * Return list of apps for Fly.io integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Fly.io API - * @returns {Object[]} apps - names and ids of Fly.io apps - * @returns {String} apps.name - name of Fly.io apps - */ -const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => { - interface FlyioApp { - id: string; - name: string; - hostname: string; - } - - const query = ` - query($role: String) { - apps(type: "container", first: 400, role: $role) { - nodes { - id - name - hostname - } - } - } - `; - - const res: FlyioApp[] = ( - await standardRequest.post( - INTEGRATION_FLYIO_API_URL, - { - query, - variables: { - role: null - } - }, - { - headers: { - Authorization: "Bearer " + accessToken, - Accept: "application/json", - "Accept-Encoding": "application/json" - } - } - ) - ).data.data.apps.nodes; - - const apps = res.map((a: FlyioApp) => ({ - name: a.name, - appId: a.id - })); - - return apps; -}; - -/** - * Return list of projects for CircleCI integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for CircleCI API - * @returns {Object[]} apps - - * @returns {String} apps.name - name of CircleCI apps - */ -const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { - const res = ( - await standardRequest.get(`${INTEGRATION_CIRCLECI_API_URL}/v1.1/projects`, { - headers: { - "Circle-Token": accessToken, - "Accept-Encoding": "application/json" - } - }) - ).data; - - const apps = res?.map((a: any) => { - return { - name: a?.reponame - }; - }); - - return apps; -}; - -const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => { - const res = ( - await standardRequest.get(`${INTEGRATION_TRAVISCI_API_URL}/repos`, { - headers: { - Authorization: `token ${accessToken}`, - "Accept-Encoding": "application/json" - } - }) - ).data; - - const apps = res?.map((a: any) => { - return { - name: a?.slug?.split("/")[1], - appId: a?.id - }; - }); - - return apps; -}; - -/** - * Return list of projects for Terraform Cloud integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Terraform Cloud API - * @param {String} obj.workspacesId - workspace id of Terraform Cloud projects - * @returns {Object[]} apps - names and ids of Terraform Cloud projects - * @returns {String} apps.name - name of Terraform Cloud projects - */ -const getAppsTerraformCloud = async ({ - accessToken, - workspacesId -}: { - accessToken: string; - workspacesId?: string; -}) => { - const res = ( - await standardRequest.get( - `${INTEGRATION_TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${workspacesId}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ) - ).data.data; - - const apps = []; - - const appsObj = { - name: res?.attributes.name, - appId: res?.id - }; - - apps.push(appsObj); - - return apps; -}; - -/** - * Return list of repositories for GitLab integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for GitLab API - * @returns {Object[]} apps - names of GitLab sites - * @returns {String} apps.name - name of GitLab site - */ -const getAppsGitlab = async ({ - integrationAuth, - accessToken, - teamId -}: { - integrationAuth: IIntegrationAuth; - accessToken: string; - teamId?: string; -}) => { - const gitLabApiUrl = integrationAuth.url - ? `${integrationAuth.url}/api` - : INTEGRATION_GITLAB_API_URL; - - const apps: App[] = []; - - let page = 1; - const perPage = 10; - let hasMorePages = true; - - if (teamId) { - // case: fetch projects for group with id [teamId] in GitLab - - while (hasMorePages) { - const params = new URLSearchParams({ - page: String(page), - per_page: String(perPage) - }); - - const { data } = await standardRequest.get(`${gitLabApiUrl}/v4/groups/${teamId}/projects`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }); - - data.map((a: any) => { - apps.push({ - name: a.name, - appId: a.id - }); - }); - - if (data.length < perPage) { - hasMorePages = false; - } - - page++; - } - } else { - // case: fetch projects for individual in GitLab - - const { id } = ( - await standardRequest.get(`${gitLabApiUrl}/v4/user`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }) - ).data; - - while (hasMorePages) { - const params = new URLSearchParams({ - page: String(page), - per_page: String(perPage) - }); - - const { data } = await standardRequest.get(`${gitLabApiUrl}/v4/users/${id}/projects`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }); - - data.map((a: any) => { - apps.push({ - name: a.name, - appId: a.id - }); - }); - - if (data.length < perPage) { - hasMorePages = false; - } - - page++; - } - } - - return apps; -}; - -/** - * Return list of projects for TeamCity integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for TeamCity API - * @returns {Object[]} apps - names and ids of TeamCity projects - * @returns {String} apps.name - name of TeamCity projects - */ -const getAppsTeamCity = async ({ - integrationAuth, - accessToken -}: { - integrationAuth: IIntegrationAuth; - accessToken: string; -}) => { - const res = ( - await standardRequest.get(`${integrationAuth.url}/app/rest/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - }) - ).data.project.slice(1); - - const apps = res.map((a: any) => { - return { - name: a.name, - appId: a.id - }; - }); - - return apps; -}; - -/** - * Return list of projects for Supabase integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Supabase API - * @returns {Object[]} apps - names of Supabase apps - * @returns {String} apps.name - name of Supabase app - */ -const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => { - const { data } = await standardRequest.get(`${INTEGRATION_SUPABASE_API_URL}/v1/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }); - - const apps = data.map((a: any) => { - return { - name: a.name, - appId: a.id - }; - }); - - return apps; -}; - -/** - * Return list of accounts for the Checkly integration - * @param {Object} obj - * @param {String} obj.accessToken - api key for the Checkly API - * @returns {Object[]} apps - ะกheckly accounts - * @returns {String} apps.name - name of Checkly account - */ -const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => { - const { data } = await standardRequest.get(`${INTEGRATION_CHECKLY_API_URL}/v1/accounts`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - }); - - const apps = data.map((a: any) => { - return { - name: a.name, - appId: a.id - }; - }); - - return apps; -}; - -/** - * Return list of projects for the Cloudflare Pages integration - * @param {Object} obj - * @param {String} obj.accessToken - api key for the Cloudflare API - * @returns {Object[]} apps - Cloudflare Pages projects - * @returns {String} apps.name - name of Cloudflare Pages project - */ -const getAppsCloudflarePages = async ({ - accessToken, - accountId -}: { - accessToken: string; - accountId?: string; -}) => { - const { data } = await standardRequest.get( - `${INTEGRATION_CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accountId}/pages/projects`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - - const apps = data.result.map((a: any) => { - return { - name: a.name, - appId: a.id - }; - }); - return apps; -}; - -/** - * Return list of projects for the Cloudflare Workers integration - * @param {Object} obj - * @param {String} obj.accessToken - api key for the Cloudflare API - * @returns {Object[]} apps - Cloudflare Workers projects - * @returns {String} apps.id - Id of Cloudflare Workers project - * @returns {String} apps.name - Id of Cloudflare Workers project (Cloudflare workers API does not return the name) - */ -const getAppsCloudflareWorkers = async ({ - accessToken, - accountId -}: { - accessToken: string; - accountId?: string; -}) => { - const { data } = await standardRequest.get( - `${INTEGRATION_CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accountId}/workers/services`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - - const apps = data.result.map((a: any) => { - return { - name: a.id, - appId: a.id - }; - }); - return apps; -}; - -/** - * Return list of repositories for the BitBucket integration based on provided BitBucket workspace - * @param {Object} obj - * @param {String} obj.accessToken - access token for BitBucket API - * @param {String} obj.workspaceSlug - Workspace identifier for fetching BitBucket repositories - * @returns {Object[]} apps - BitBucket repositories - * @returns {String} apps.name - name of BitBucket repository - */ -const getAppsBitBucket = async ({ - accessToken, - workspaceSlug -}: { - accessToken: string; - workspaceSlug?: string; -}) => { - interface RepositoriesResponse { - size: number; - page: number; - pageLen: number; - next: string; - previous: string; - values: Array; - } - - interface Repository { - type: string; - uuid: string; - name: string; - is_private: boolean; - created_on: string; - updated_on: string; - } - - if (!workspaceSlug) { - return []; - } - - const repositories: Repository[] = []; - let hasNextPage = true; - let repositoriesUrl = `${INTEGRATION_BITBUCKET_API_URL}/2.0/repositories/${workspaceSlug}`; - - while (hasNextPage) { - const { data }: { data: RepositoriesResponse } = await standardRequest.get(repositoriesUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - }); - - if (data?.values.length > 0) { - data.values.forEach((repository) => { - repositories.push(repository); - }); - } - - if (data.next) { - repositoriesUrl = data.next; - } else { - hasNextPage = false; - } - } - - const apps = repositories.map((repository) => { - return { - name: repository.name, - appId: repository.uuid - }; - }); - return apps; -}; - -/** Return list of projects for Northflank integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Northflank API - * @returns {Object[]} apps - names of Northflank apps - * @returns {String} apps.name - name of Northflank app - */ -const getAppsNorthflank = async ({ accessToken }: { accessToken: string }) => { - const { - data: { - data: { projects } - } - } = await standardRequest.get(`${INTEGRATION_NORTHFLANK_API_URL}/v1/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }); - - const apps = projects.map((a: any) => { - return { - name: a.name, - appId: a.id - }; - }); - - return apps; -}; - -/** - * Return list of projects for Supabase integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Supabase API - * @returns {Object[]} apps - names of Supabase apps - * @returns {String} apps.name - name of Supabase app - */ -const getAppsCodefresh = async ({ accessToken }: { accessToken: string }) => { - const res = ( - await standardRequest.get(`${INTEGRATION_CODEFRESH_API_URL}/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }) - ).data; - - const apps = res.projects.map((a: any) => ({ - name: a.projectName, - appId: a.id - })); - - return apps; -}; - -/** - * Return list of projects for Windmill integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for Windmill API - * @returns {Object[]} apps - names of Windmill workspaces - * @returns {String} apps.name - name of Windmill workspace - */ -const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => { - const { data } = await standardRequest.get(`${INTEGRATION_WINDMILL_API_URL}/workspaces/list`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }); - - // check for write access of secrets in windmill workspaces - const writeAccessCheck = data.map(async (app: any) => { - try { - const userPath = "u/user/variable"; - const folderPath = "f/folder/variable"; - - const { data: writeUser } = await standardRequest.post( - `${INTEGRATION_WINDMILL_API_URL}/w/${app.id}/variables/create`, - { - path: userPath, - value: "variable", - is_secret: true, - description: "variable description" - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - - const { data: writeFolder } = await standardRequest.post( - `${INTEGRATION_WINDMILL_API_URL}/w/${app.id}/variables/create`, - { - path: folderPath, - value: "variable", - is_secret: true, - description: "variable description" - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - - // is write access is allowed then delete the created secrets from workspace - if (writeUser && writeFolder) { - await standardRequest.delete( - `${INTEGRATION_WINDMILL_API_URL}/w/${app.id}/variables/delete/${userPath}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - - await standardRequest.delete( - `${INTEGRATION_WINDMILL_API_URL}/w/${app.id}/variables/delete/${folderPath}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - - return app; - } else { - return { error: "cannot write secret" }; - } - } catch (err: any) { - return { error: err.message }; - } - }); - - const appsWriteResponses = await Promise.all(writeAccessCheck); - const appsWithWriteAccess = appsWriteResponses.filter((appRes: any) => !appRes.error); - - const apps = appsWithWriteAccess.map((a: any) => { - return { - name: a.name, - appId: a.id - }; - }); - - return apps; -}; - -/** - * Return list of applications for DigitalOcean App Platform integration - * @param {Object} obj - * @param {String} obj.accessToken - personal access token for DigitalOcean - * @returns {Object[]} apps - names of DigitalOcean apps - * @returns {String} apps.name - name of DigitalOcean app - * @returns {String} apps.appId - id of DigitalOcean app - */ -const getAppsDigitalOceanAppPlatform = async ({ accessToken }: { accessToken: string }) => { - interface DigitalOceanApp { - id: string; - owner_uuid: string; - spec: Spec; - } - - interface Spec { - name: string; - region: string; - envs: Env[]; - } - - interface Env { - key: string; - value: string; - scope: string; - } - - const res = ( - await standardRequest.get(`${INTEGRATION_DIGITAL_OCEAN_API_URL}/v2/apps`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }) - ).data; - - return (res.apps ?? []).map((a: DigitalOceanApp) => ({ - name: a.spec.name, - appId: a.id - })); -}; - -const getAppsHasuraCloud = async ({ accessToken }: { accessToken: string }) => { - const res = await standardRequest.post( - INTEGRATION_HASURA_CLOUD_API_URL, - { - query: "query MyQuery { projects { name tenant { id } } }" - }, - { - headers: { - Authorization: `pat ${accessToken}`, - "Content-Type": "application/json" - } - } - ); - - const data = (res?.data?.data?.projects ?? []).map( - ({ name, tenant: { id: appId } }: { name: string; tenant: { id: string } }) => ({ name, appId }) - ); - return data; -}; - -/** - * Return list of applications for Cloud66 integration - * @param {Object} obj - * @param {String} obj.accessToken - personal access token for Cloud66 API - * @returns {Object[]} apps - Cloud66 apps - * @returns {String} apps.name - name of Cloud66 app - * @returns {String} apps.appId - uid of Cloud66 app - */ -const getAppsCloud66 = async ({ accessToken }: { accessToken: string }) => { - interface Cloud66Apps { - uid: string; - name: string; - account_id: number; - git: string; - git_branch: string; - environment: string; - cloud: string; - fqdn: string; - language: string; - framework: string; - status: number; - health: number; - last_activity: string; - last_activity_iso: string; - maintenance_mode: boolean; - has_loadbalancer: boolean; - created_at: string; - updated_at: string; - deploy_directory: string; - cloud_status: string; - backend: string; - version: string; - revision: string; - is_busy: boolean; - account_name: string; - is_cluster: boolean; - is_inside_cluster: boolean; - cluster_name: any; - application_address: string; - configstore_namespace: string; - } - - const stacks = ( - await standardRequest.get(`${INTEGRATION_CLOUD_66_API_URL}/3/stacks`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }) - ).data.response as Cloud66Apps[]; - - const apps = stacks.map((app) => ({ - name: app.name, - appId: app.uid - })); - - return apps; -}; - -export { getApps }; diff --git a/backend-mongo/src/integrations/exchange.ts b/backend-mongo/src/integrations/exchange.ts deleted file mode 100644 index 37382e79e..000000000 --- a/backend-mongo/src/integrations/exchange.ts +++ /dev/null @@ -1,469 +0,0 @@ -import { standardRequest } from "../config/request"; -import { - INTEGRATION_AZURE_KEY_VAULT, - INTEGRATION_AZURE_TOKEN_URL, - INTEGRATION_BITBUCKET, - INTEGRATION_BITBUCKET_TOKEN_URL, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_GCP_TOKEN_URL, - INTEGRATION_GITHUB, - INTEGRATION_GITHUB_TOKEN_URL, - INTEGRATION_GITLAB, - INTEGRATION_GITLAB_TOKEN_URL, - INTEGRATION_HEROKU, - INTEGRATION_HEROKU_TOKEN_URL, - INTEGRATION_NETLIFY, - INTEGRATION_NETLIFY_TOKEN_URL, - INTEGRATION_VERCEL, - INTEGRATION_VERCEL_TOKEN_URL -} from "../variables"; -import { - getClientIdAzure, - getClientIdBitBucket, - getClientIdGCPSecretManager, - getClientIdGitHub, - getClientIdGitLab, - getClientIdNetlify, - getClientIdVercel, - getClientSecretAzure, - getClientSecretBitBucket, - getClientSecretGCPSecretManager, - getClientSecretGitHub, - getClientSecretGitLab, - getClientSecretHeroku, - getClientSecretNetlify, - getClientSecretVercel, - getSiteURL, -} from "../config"; - -interface ExchangeCodeAzureResponse { - token_type: string; - scope: string; - expires_in: number; - ext_expires_in: number; - access_token: string; - refresh_token: string; - id_token: string; -} - -interface ExchangeCodeGCPResponse { - access_token: string; - expires_in: number; - refresh_token: string; - scope: string; - token_type: string; -} - -interface ExchangeCodeHerokuResponse { - token_type: string; - access_token: string; - expires_in: number; - refresh_token: string; - user_id: string; - session_nonce?: string; -} - -interface ExchangeCodeVercelResponse { - token_type: string; - access_token: string; - installation_id: string; - user_id: string; - team_id?: string; -} - -interface ExchangeCodeNetlifyResponse { - access_token: string; - token_type: string; - refresh_token: string; - scope: string; - created_at: number; -} - -interface ExchangeCodeGithubResponse { - access_token: string; - scope: string; - token_type: string; -} - -interface ExchangeCodeGitlabResponse { - access_token: string; - token_type: string; - expires_in: number; - refresh_token: string; - scope: string; - created_at: number; -} - -interface ExchangeCodeBitBucketResponse { - access_token: string; - token_type: string; - expires_in: number; - refresh_token: string; - scopes: string; - state: string; -} - -/** - * Return [accessToken], [accessExpiresAt], and [refreshToken] for OAuth2 - * code-token exchange for integration named [integration] - * @param {Object} obj1 - * @param {String} obj1.integration - name of integration - * @param {String} obj1.code - code for code-token exchange - * @returns {Object} obj - * @returns {String} obj.accessToken - access token for integration - * @returns {String} obj.refreshToken - refresh token for integration - * @returns {Date} obj.accessExpiresAt - date of expiration for access token - * @returns {String} obj.action - integration action for bot sequence - */ -const exchangeCode = async ({ - integration, - code, - url -}: { - integration: string; - code: string; - url?: string; -}) => { - let obj = {} as any; - - switch (integration) { - case INTEGRATION_GCP_SECRET_MANAGER: - obj = await exchangeCodeGCP({ - code, - }); - break; - case INTEGRATION_AZURE_KEY_VAULT: - obj = await exchangeCodeAzure({ - code, - }); - break; - case INTEGRATION_HEROKU: - obj = await exchangeCodeHeroku({ - code, - }); - break; - case INTEGRATION_VERCEL: - obj = await exchangeCodeVercel({ - code, - }); - break; - case INTEGRATION_NETLIFY: - obj = await exchangeCodeNetlify({ - code, - }); - break; - case INTEGRATION_GITHUB: - obj = await exchangeCodeGithub({ - code, - }); - break; - case INTEGRATION_GITLAB: - obj = await exchangeCodeGitlab({ - code, - url - }); - break; - case INTEGRATION_BITBUCKET: - obj = await exchangeCodeBitBucket({ - code, - }); - break; - } - - return obj; -}; - -/** - * Return [accessToken] for GCP OAuth2 code-token exchange - * @param {Object} obj - * @param {String} obj.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for GCP API - * @returns {String} obj2.refreshToken - refresh token for GCP API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token - */ -const exchangeCodeGCP = async ({ code }: { code: string }) => { - const accessExpiresAt = new Date(); - - const res: ExchangeCodeGCPResponse = ( - await standardRequest.post( - INTEGRATION_GCP_TOKEN_URL, - new URLSearchParams({ - grant_type: "authorization_code", - code: code, - client_id: await getClientIdGCPSecretManager(), - client_secret: await getClientSecretGCPSecretManager(), - redirect_uri: `${await getSiteURL()}/integrations/gcp-secret-manager/oauth2/callback`, - } as any) - ) - ).data; - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in); - - return { - accessToken: res.access_token, - refreshToken: res.refresh_token, - accessExpiresAt, - }; -}; - -/** - * Return [accessToken] for Azure OAuth2 code-token exchange - * @param param0 - */ -const exchangeCodeAzure = async ({ code }: { code: string }) => { - const accessExpiresAt = new Date(); - - const res: ExchangeCodeAzureResponse = ( - await standardRequest.post( - INTEGRATION_AZURE_TOKEN_URL, - new URLSearchParams({ - grant_type: "authorization_code", - code: code, - scope: "https://vault.azure.net/.default openid offline_access", - client_id: await getClientIdAzure(), - client_secret: await getClientSecretAzure(), - redirect_uri: `${await getSiteURL()}/integrations/azure-key-vault/oauth2/callback`, - } as any) - ) - ).data; - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in); - - return { - accessToken: res.access_token, - refreshToken: res.refresh_token, - accessExpiresAt, - }; -}; - -/** - * Return [accessToken], [accessExpiresAt], and [refreshToken] for Heroku - * OAuth2 code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for Heroku API - * @returns {String} obj2.refreshToken - refresh token for Heroku API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token - */ -const exchangeCodeHeroku = async ({ code }: { code: string }) => { - const accessExpiresAt = new Date(); - - const res: ExchangeCodeHerokuResponse = ( - await standardRequest.post( - INTEGRATION_HEROKU_TOKEN_URL, - new URLSearchParams({ - grant_type: "authorization_code", - code: code, - client_secret: await getClientSecretHeroku(), - } as any) - ) - ).data; - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in); - - return { - accessToken: res.access_token, - refreshToken: res.refresh_token, - accessExpiresAt, - }; -}; - -/** - * Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel - * code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for Heroku API - * @returns {String} obj2.refreshToken - refresh token for Heroku API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token - */ -const exchangeCodeVercel = async ({ code }: { code: string }) => { - const res: ExchangeCodeVercelResponse = ( - await standardRequest.post( - INTEGRATION_VERCEL_TOKEN_URL, - new URLSearchParams({ - code: code, - client_id: await getClientIdVercel(), - client_secret: await getClientSecretVercel(), - redirect_uri: `${await getSiteURL()}/integrations/vercel/oauth2/callback`, - } as any) - ) - ).data; - - return { - accessToken: res.access_token, - refreshToken: null, - accessExpiresAt: null, - teamId: res.team_id, - }; -}; - -/** - * Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel - * code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for Heroku API - * @returns {String} obj2.refreshToken - refresh token for Heroku API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token - */ -const exchangeCodeNetlify = async ({ code }: { code: string }) => { - const res: ExchangeCodeNetlifyResponse = ( - await standardRequest.post( - INTEGRATION_NETLIFY_TOKEN_URL, - new URLSearchParams({ - grant_type: "authorization_code", - code: code, - client_id: await getClientIdNetlify(), - client_secret: await getClientSecretNetlify(), - redirect_uri: `${await getSiteURL()}/integrations/netlify/oauth2/callback`, - } as any) - ) - ).data; - - const res2 = await standardRequest.get("https://api.netlify.com/api/v1/sites", { - headers: { - Authorization: `Bearer ${res.access_token}`, - }, - }); - - const res3 = ( - await standardRequest.get("https://api.netlify.com/api/v1/accounts", { - headers: { - Authorization: `Bearer ${res.access_token}`, - }, - }) - ).data; - - const accountId = res3[0].id; - - return { - accessToken: res.access_token, - refreshToken: res.refresh_token, - accountId, - }; -}; - -/** - * Return [accessToken], [accessExpiresAt], and [refreshToken] for Github - * code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for Github API - * @returns {String} obj2.refreshToken - refresh token for Github API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token - */ -const exchangeCodeGithub = async ({ code }: { code: string }) => { - const res: ExchangeCodeGithubResponse = ( - await standardRequest.get(INTEGRATION_GITHUB_TOKEN_URL, { - params: { - client_id: await getClientIdGitHub(), - client_secret: await getClientSecretGitHub(), - code: code, - redirect_uri: `${await getSiteURL()}/integrations/github/oauth2/callback`, - }, - headers: { - Accept: "application/json", - "Accept-Encoding": "application/json", - }, - }) - ).data; - - return { - accessToken: res.access_token, - refreshToken: null, - accessExpiresAt: null, - }; -}; - -/** - * Return [accessToken], [accessExpiresAt], and [refreshToken] for Gitlab - * code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for Gitlab API - * @returns {String} obj2.refreshToken - refresh token for Gitlab API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token - */ -const exchangeCodeGitlab = async ({ - code, - url -}: { - code: string, - url?: string; -}) => { - const accessExpiresAt = new Date(); - const res: ExchangeCodeGitlabResponse = ( - await standardRequest.post( - url ? `${url}/oauth/token` : INTEGRATION_GITLAB_TOKEN_URL, - new URLSearchParams({ - grant_type: "authorization_code", - code: code, - client_id: await getClientIdGitLab(), - client_secret: await getClientSecretGitLab(), - redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback`, - } as any), - { - headers: { - "Accept-Encoding": "application/json", - }, - } - ) - ).data; - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in); - - return { - accessToken: res.access_token, - refreshToken: res.refresh_token, - accessExpiresAt, - url - }; -}; - -/** - * Return [accessToken], [accessExpiresAt], and [refreshToken] for BitBucket - * code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for BitBucket API - * @returns {String} obj2.refreshToken - refresh token for BitBucket API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token - */ -const exchangeCodeBitBucket = async ({ code }: { code: string }) => { - const accessExpiresAt = new Date(); - const res: ExchangeCodeBitBucketResponse = ( - await standardRequest.post( - INTEGRATION_BITBUCKET_TOKEN_URL, - new URLSearchParams({ - grant_type: "authorization_code", - code: code, - client_id: await getClientIdBitBucket(), - client_secret: await getClientSecretBitBucket(), - redirect_uri: `${await getSiteURL()}/integrations/bitbucket/oauth2/callback`, - } as any), - { - headers: { - "Accept-Encoding": "application/json", - }, - } - ) - ).data; - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in); - - return { - accessToken: res.access_token, - refreshToken: res.refresh_token, - accessExpiresAt, - }; -}; - -export { exchangeCode }; diff --git a/backend-mongo/src/integrations/index.ts b/backend-mongo/src/integrations/index.ts deleted file mode 100644 index e1bf23ba1..000000000 --- a/backend-mongo/src/integrations/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { exchangeCode } from "./exchange"; -import { exchangeRefresh } from "./refresh"; -import { getApps } from "./apps"; -import { getTeams } from "./teams"; -import { revokeAccess } from "./revoke"; - -export { - exchangeCode, - exchangeRefresh, - getApps, - getTeams, - revokeAccess, -} \ No newline at end of file diff --git a/backend-mongo/src/integrations/refresh.ts b/backend-mongo/src/integrations/refresh.ts deleted file mode 100644 index dee4931c8..000000000 --- a/backend-mongo/src/integrations/refresh.ts +++ /dev/null @@ -1,382 +0,0 @@ -import jwt from "jsonwebtoken"; -import { standardRequest } from "../config/request"; -import { IIntegrationAuth } from "../models"; -import { - INTEGRATION_AZURE_KEY_VAULT, - INTEGRATION_BITBUCKET, - INTEGRATION_BITBUCKET_TOKEN_URL, - INTEGRATION_GCP_CLOUD_PLATFORM_SCOPE, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_GCP_TOKEN_URL, - INTEGRATION_GITLAB, - INTEGRATION_HEROKU -} from "../variables"; -import { - INTEGRATION_AZURE_TOKEN_URL, - INTEGRATION_GITLAB_TOKEN_URL, - INTEGRATION_HEROKU_TOKEN_URL, -} from "../variables"; -import { IntegrationService } from "../services"; -import { - getClientIdAzure, - getClientIdBitBucket, - getClientIdGCPSecretManager, - getClientIdGitLab, - getClientSecretAzure, - getClientSecretBitBucket, - getClientSecretGCPSecretManager, - getClientSecretGitLab, - getClientSecretHeroku, - getSiteURL, -} from "../config"; - -interface RefreshTokenAzureResponse { - token_type: string; - scope: string; - expires_in: number; - ext_expires_in: 4871; - access_token: string; - refresh_token: string; -} - -interface RefreshTokenHerokuResponse { - access_token: string; - expires_in: number; - refresh_token: string; - token_type: string; - user_id: string; -} - -interface RefreshTokenGitLabResponse { - token_type: string; - scope: string; - expires_in: number; - access_token: string; - refresh_token: string; - created_at: number; -} - -interface RefreshTokenBitBucketResponse { - access_token: string; - token_type: string; - expires_in: number; - refresh_token: string; - scopes: string; - state: string; -} - -interface ServiceAccountAccessTokenGCPSecretManagerResponse { - access_token: string; - expires_in: number; - token_type: string; -} - -interface RefreshTokenGCPSecretManagerResponse { - access_token: string; - expires_in: number; - scope: string; - token_type: string; -} - -/** - * Return new access token by exchanging refresh token [refreshToken] for integration - * named [integration] - * @param {Object} obj - * @param {String} obj.integration - name of integration - * @param {String} obj.refreshToken - refresh token to use to get new access token for Heroku - */ -const exchangeRefresh = async ({ - integrationAuth, - refreshToken, -}: { - integrationAuth: IIntegrationAuth; - refreshToken: string; -}) => { - interface TokenDetails { - accessToken: string; - refreshToken: string; - accessExpiresAt: Date; - } - - let tokenDetails: TokenDetails; - switch (integrationAuth.integration) { - case INTEGRATION_AZURE_KEY_VAULT: - tokenDetails = await exchangeRefreshAzure({ - refreshToken, - }); - break; - case INTEGRATION_HEROKU: - tokenDetails = await exchangeRefreshHeroku({ - refreshToken, - }); - break; - case INTEGRATION_GITLAB: - tokenDetails = await exchangeRefreshGitLab({ - integrationAuth, - refreshToken, - }); - break; - case INTEGRATION_BITBUCKET: - tokenDetails = await exchangeRefreshBitBucket({ - refreshToken, - }); - break; - case INTEGRATION_GCP_SECRET_MANAGER: - tokenDetails = await exchangeRefreshGCPSecretManager({ - integrationAuth, - refreshToken, - }); - break; - default: - throw new Error("Failed to exchange token for incompatible integration"); - } - - if ( - tokenDetails.accessToken && - tokenDetails.refreshToken && - tokenDetails.accessExpiresAt - ) { - await IntegrationService.setIntegrationAuthAccess({ - integrationAuthId: integrationAuth._id.toString(), - accessToken: tokenDetails.accessToken, - accessExpiresAt: tokenDetails.accessExpiresAt, - }); - - await IntegrationService.setIntegrationAuthRefresh({ - integrationAuthId: integrationAuth._id.toString(), - refreshToken: tokenDetails.refreshToken, - }); - } - - return tokenDetails.accessToken; -}; - -/** - * Return new access token by exchanging refresh token [refreshToken] for the - * Azure integration - * @param {Object} obj - * @param {String} obj.refreshToken - refresh token to use to get new access token for Azure - * @returns - */ -const exchangeRefreshAzure = async ({ - refreshToken, -}: { - refreshToken: string; -}) => { - const accessExpiresAt = new Date(); - const { data }: { data: RefreshTokenAzureResponse } = await standardRequest.post( - INTEGRATION_AZURE_TOKEN_URL, - new URLSearchParams({ - client_id: await getClientIdAzure(), - scope: "openid offline_access", - refresh_token: refreshToken, - grant_type: "refresh_token", - client_secret: await getClientSecretAzure(), - } as any) - ); - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); - - return { - accessToken: data.access_token, - refreshToken: data.refresh_token, - accessExpiresAt, - }; -}; - -/** - * Return new access token by exchanging refresh token [refreshToken] for the - * Heroku integration - * @param {Object} obj - * @param {String} obj.refreshToken - refresh token to use to get new access token for Heroku - * @returns - */ -const exchangeRefreshHeroku = async ({ - refreshToken, -}: { - refreshToken: string; -}) => { - const accessExpiresAt = new Date(); - const { - data, - }: { - data: RefreshTokenHerokuResponse; - } = await standardRequest.post( - INTEGRATION_HEROKU_TOKEN_URL, - new URLSearchParams({ - grant_type: "refresh_token", - refresh_token: refreshToken, - client_secret: await getClientSecretHeroku(), - } as any) - ); - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); - - return { - accessToken: data.access_token, - refreshToken: data.refresh_token, - accessExpiresAt, - }; -}; - -/** - * Return new access token by exchanging refresh token [refreshToken] for the - * GitLab integration - * @param {Object} obj - * @param {String} obj.refreshToken - refresh token to use to get new access token for GitLab - * @returns - */ -const exchangeRefreshGitLab = async ({ - integrationAuth, - refreshToken, -}: { - integrationAuth: IIntegrationAuth; - refreshToken: string; -}) => { - const accessExpiresAt = new Date(); - const url = integrationAuth.url; - - const { - data, - }: { - data: RefreshTokenGitLabResponse; - } = await standardRequest.post( - url ? `${url}/oauth/token` : INTEGRATION_GITLAB_TOKEN_URL, - new URLSearchParams({ - grant_type: "refresh_token", - refresh_token: refreshToken, - client_id: await getClientIdGitLab(), - client_secret: await getClientSecretGitLab(), - redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback`, - } as any), - { - headers: { - "Accept-Encoding": "application/json", - }, - } - ); - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); - - return { - accessToken: data.access_token, - refreshToken: data.refresh_token, - accessExpiresAt, - }; -}; - -/** - * Return new access token by exchanging refresh token [refreshToken] for the - * BitBucket integration - * @param {Object} obj - * @param {String} obj.refreshToken - refresh token to use to get new access token for BitBucket - * @returns - */ -const exchangeRefreshBitBucket = async ({ - refreshToken, -}: { - refreshToken: string; -}) => { - const accessExpiresAt = new Date(); - const { - data, - }: { - data: RefreshTokenBitBucketResponse; - } = await standardRequest.post( - INTEGRATION_BITBUCKET_TOKEN_URL, - new URLSearchParams({ - grant_type: "refresh_token", - refresh_token: refreshToken, - client_id: await getClientIdBitBucket(), - client_secret: await getClientSecretBitBucket(), - redirect_uri: `${await getSiteURL()}/integrations/bitbucket/oauth2/callback`, - } as any), - { - headers: { - "Accept-Encoding": "application/json", - }, - } - ); - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); - - return { - accessToken: data.access_token, - refreshToken: data.refresh_token, - accessExpiresAt, - }; -}; - -/** - * Return new access token by exchanging refresh token [refreshToken] for the - * GCP Secret Manager integration - * @param {Object} obj - * @param {String} obj.refreshToken - refresh token to use to get new access token for GCP Secret Manager - * @returns - */ -const exchangeRefreshGCPSecretManager = async ({ - integrationAuth, - refreshToken, -}: { - integrationAuth: IIntegrationAuth; - refreshToken: string; -}) => { - const accessExpiresAt = new Date(); - - if (integrationAuth.metadata?.authMethod === "serviceAccount") { - const serviceAccount = JSON.parse(refreshToken); - - const payload = { - iss: serviceAccount.client_email, - aud: serviceAccount.token_uri, - scope: INTEGRATION_GCP_CLOUD_PLATFORM_SCOPE, - iat: Math.floor(Date.now() / 1000), - exp: Math.floor(Date.now() / 1000) + 3600, - }; - - const token = jwt.sign(payload, serviceAccount.private_key, { algorithm: "RS256" }); - - const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = await standardRequest.post( - INTEGRATION_GCP_TOKEN_URL, - new URLSearchParams({ - grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", - assertion: token - }).toString(), - { - headers: { - "Content-Type": "application/x-www-form-urlencoded" - } - } - ); - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); - - return { - accessToken: data.access_token, - refreshToken, - accessExpiresAt - }; - } - - const { data }: { data: RefreshTokenGCPSecretManagerResponse } = ( - await standardRequest.post( - INTEGRATION_GCP_TOKEN_URL, - new URLSearchParams({ - client_id: await getClientIdGCPSecretManager(), - client_secret: await getClientSecretGCPSecretManager(), - refresh_token: refreshToken, - grant_type: "refresh_token", - } as any) - ) - ); - - accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); - - return { - accessToken: data.access_token, - refreshToken, - accessExpiresAt, - }; -}; - -export { exchangeRefresh }; \ No newline at end of file diff --git a/backend-mongo/src/integrations/revoke.ts b/backend-mongo/src/integrations/revoke.ts deleted file mode 100644 index 4d4f790c6..000000000 --- a/backend-mongo/src/integrations/revoke.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - IIntegrationAuth, - Integration, - IntegrationAuth, -} from "../models"; -import { - INTEGRATION_GITHUB, - INTEGRATION_GITLAB, - INTEGRATION_HEROKU, - INTEGRATION_NETLIFY, - INTEGRATION_VERCEL, -} from "../variables"; - -const revokeAccess = async ({ - integrationAuth, - accessToken, -}: { - integrationAuth: IIntegrationAuth; - accessToken: string; -}) => { - // add any integration-specific revocation logic - switch (integrationAuth.integration) { - case INTEGRATION_HEROKU: - break; - case INTEGRATION_VERCEL: - break; - case INTEGRATION_NETLIFY: - break; - case INTEGRATION_GITHUB: - break; - case INTEGRATION_GITLAB: - break; - } - - const deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({ - _id: integrationAuth._id, - }); - - if (deletedIntegrationAuth) { - await Integration.deleteMany({ - integrationAuth: deletedIntegrationAuth._id, - }); - } - - return deletedIntegrationAuth; -}; - -export { revokeAccess }; diff --git a/backend-mongo/src/integrations/sync.ts b/backend-mongo/src/integrations/sync.ts deleted file mode 100644 index 615e3114c..000000000 --- a/backend-mongo/src/integrations/sync.ts +++ /dev/null @@ -1,3383 +0,0 @@ -import { - CreateSecretCommand, - GetSecretValueCommand, - ResourceNotFoundException, - SecretsManagerClient, - UpdateSecretCommand -} from "@aws-sdk/client-secrets-manager"; -import { IIntegration, IIntegrationAuth } from "../models"; -import { - INTEGRATION_AWS_PARAMETER_STORE, - INTEGRATION_AWS_SECRET_MANAGER, - INTEGRATION_AZURE_KEY_VAULT, - INTEGRATION_BITBUCKET, - INTEGRATION_BITBUCKET_API_URL, - INTEGRATION_CHECKLY, - INTEGRATION_CHECKLY_API_URL, - INTEGRATION_CIRCLECI, - INTEGRATION_CIRCLECI_API_URL, - INTEGRATION_CLOUDFLARE_PAGES, - INTEGRATION_CLOUDFLARE_PAGES_API_URL, - INTEGRATION_CLOUDFLARE_WORKERS, - INTEGRATION_CLOUDFLARE_WORKERS_API_URL, - INTEGRATION_CLOUD_66, - INTEGRATION_CLOUD_66_API_URL, - INTEGRATION_CODEFRESH, - INTEGRATION_CODEFRESH_API_URL, - INTEGRATION_DIGITAL_OCEAN_API_URL, - INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM, - INTEGRATION_FLYIO, - INTEGRATION_FLYIO_API_URL, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_GCP_SECRET_MANAGER_URL, - INTEGRATION_GITHUB, - INTEGRATION_GITLAB, - INTEGRATION_GITLAB_API_URL, - INTEGRATION_HASHICORP_VAULT, - INTEGRATION_HASURA_CLOUD, - INTEGRATION_HASURA_CLOUD_API_URL, - INTEGRATION_HEROKU, - INTEGRATION_HEROKU_API_URL, - INTEGRATION_LARAVELFORGE, - INTEGRATION_LARAVELFORGE_API_URL, - INTEGRATION_NETLIFY, - INTEGRATION_NETLIFY_API_URL, - INTEGRATION_NORTHFLANK, - INTEGRATION_NORTHFLANK_API_URL, - INTEGRATION_QOVERY, - INTEGRATION_QOVERY_API_URL, - INTEGRATION_RAILWAY, - INTEGRATION_RAILWAY_API_URL, - INTEGRATION_RENDER, - INTEGRATION_RENDER_API_URL, - INTEGRATION_SUPABASE, - INTEGRATION_SUPABASE_API_URL, - INTEGRATION_TEAMCITY, - INTEGRATION_TERRAFORM_CLOUD, - INTEGRATION_TERRAFORM_CLOUD_API_URL, - INTEGRATION_TRAVISCI, - INTEGRATION_TRAVISCI_API_URL, - INTEGRATION_VERCEL, - INTEGRATION_VERCEL_API_URL, - INTEGRATION_WINDMILL, - INTEGRATION_WINDMILL_API_URL -} from "../variables"; -import AWS from "aws-sdk"; -import { Octokit } from "@octokit/rest"; -import _ from "lodash"; -import sodium from "libsodium-wrappers"; -import { standardRequest } from "../config/request"; -import { - ZGetTenantEnv, - ZUpdateTenantEnv -} from "../validation/hasuraCloudIntegration"; - -const getSecretKeyValuePair = ( - secrets: Record -) => - Object.keys(secrets).reduce>((prev, key) => { - prev[key] = secrets?.[key] === null ? null : secrets?.[key]?.value; - return prev; - }, {}); - -/** - * Sync/push [secrets] to [app] in integration named [integration] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessId - access id for integration - * @param {String} obj.accessToken - access token for integration - * @param {Object} obj.secretComments - secret comments to push to integration (object where keys are secret keys and values are comment values) - */ -const syncSecrets = async ({ - integration, - integrationAuth, - secrets, - accessId, - accessToken, - appendices -}: { - integration: IIntegration; - integrationAuth: IIntegrationAuth; - secrets: Record; - accessId: string | null; - accessToken: string; - appendices?: { prefix: string; suffix: string }; -}) => { - switch (integration.integration) { - case INTEGRATION_GCP_SECRET_MANAGER: - await syncSecretsGCPSecretManager({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_AZURE_KEY_VAULT: - await syncSecretsAzureKeyVault({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_AWS_PARAMETER_STORE: - await syncSecretsAWSParameterStore({ - integration, - secrets, - accessId, - accessToken - }); - break; - case INTEGRATION_AWS_SECRET_MANAGER: - await syncSecretsAWSSecretManager({ - integration, - secrets, - accessId, - accessToken - }); - break; - case INTEGRATION_HEROKU: - await syncSecretsHeroku({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_VERCEL: - await syncSecretsVercel({ - integration, - integrationAuth, - secrets, - accessToken - }); - break; - case INTEGRATION_NETLIFY: - await syncSecretsNetlify({ - integration, - integrationAuth, - secrets, - accessToken - }); - break; - case INTEGRATION_GITHUB: - await syncSecretsGitHub({ - integration, - secrets, - accessToken, - appendices - }); - break; - case INTEGRATION_GITLAB: - await syncSecretsGitLab({ - integrationAuth, - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_RENDER: - await syncSecretsRender({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_RAILWAY: - await syncSecretsRailway({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_FLYIO: - await syncSecretsFlyio({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_CIRCLECI: - await syncSecretsCircleCI({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_LARAVELFORGE: - await syncSecretsLaravelForge({ - integration, - secrets, - accessId, - accessToken - }); - break; - case INTEGRATION_TRAVISCI: - await syncSecretsTravisCI({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_SUPABASE: - await syncSecretsSupabase({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_CHECKLY: - await syncSecretsCheckly({ - integration, - secrets, - accessToken, - appendices - }); - break; - case INTEGRATION_QOVERY: - await syncSecretsQovery({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_TERRAFORM_CLOUD: - await syncSecretsTerraformCloud({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_HASHICORP_VAULT: - await syncSecretsHashiCorpVault({ - integration, - integrationAuth, - secrets, - accessId, - accessToken - }); - break; - case INTEGRATION_CLOUDFLARE_PAGES: - await syncSecretsCloudflarePages({ - integration, - secrets, - accessId, - accessToken - }); - break; - case INTEGRATION_CLOUDFLARE_WORKERS: - await syncSecretsCloudflareWorkers({ - integration, - secrets, - accessId, - accessToken - }); - break; - case INTEGRATION_CODEFRESH: - await syncSecretsCodefresh({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_TEAMCITY: - await syncSecretsTeamCity({ - integrationAuth, - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_BITBUCKET: - await syncSecretsBitBucket({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM: - await syncSecretsDigitalOceanAppPlatform({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_CLOUD_66: - await syncSecretsCloud66({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_NORTHFLANK: - await syncSecretsNorthflank({ - integration, - secrets, - accessToken - }); - break; - case INTEGRATION_WINDMILL: - await syncSecretsWindmill({ - integration, - secrets, - accessToken - }); - break; - - case INTEGRATION_HASURA_CLOUD: - await syncSecretsHasuraCloud({ - integration, - secrets, - accessToken - }); - break; - } -}; - -/** - * Sync/push [secrets] to GCP secret manager project - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for GCP secret manager - */ -const syncSecretsGCPSecretManager = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - interface GCPSecret { - name: string; - createTime: string; - } - - interface GCPSMListSecretsRes { - secrets?: GCPSecret[]; - totalSize?: number; - nextPageToken?: string; - } - - let gcpSecrets: GCPSecret[] = []; - - const pageSize = 100; - let pageToken: string | undefined; - let hasMorePages = true; - - const filterParam = integration.metadata.secretGCPLabel - ? `?filter=labels.${integration.metadata.secretGCPLabel.labelName}=${integration.metadata.secretGCPLabel.labelValue}` - : ""; - - while (hasMorePages) { - const params = new URLSearchParams({ - pageSize: String(pageSize), - ...(pageToken ? { pageToken } : {}) - }); - - const res: GCPSMListSecretsRes = ( - await standardRequest.get( - `${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets${filterParam}`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ) - ).data; - - if (res.secrets) { - const filteredSecrets = res.secrets?.filter((gcpSecret) => { - const arr = gcpSecret.name.split("/"); - const key = arr[arr.length - 1]; - - let isValid = true; - - if ( - integration.metadata.secretPrefix && - !key.startsWith(integration.metadata.secretPrefix) - ) { - isValid = false; - } - - if (integration.metadata.secretSuffix && !key.endsWith(integration.metadata.secretSuffix)) { - isValid = false; - } - - return isValid; - }); - - gcpSecrets = gcpSecrets.concat(filteredSecrets); - } - - if (!res.nextPageToken) { - hasMorePages = false; - } - - pageToken = res.nextPageToken; - } - - const res: { [key: string]: string } = {}; - - interface GCPLatestSecretVersionAccess { - name: string; - payload: { - data: string; - }; - } - - for await (const gcpSecret of gcpSecrets) { - const arr = gcpSecret.name.split("/"); - const key = arr[arr.length - 1]; - - const secretLatest: GCPLatestSecretVersionAccess = ( - await standardRequest.get( - `${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets/${key}/versions/latest:access`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ) - ).data; - - res[key] = Buffer.from(secretLatest.payload.data, "base64").toString("utf-8"); - } - - for await (const key of Object.keys(secrets)) { - if (!(key in res)) { - // case: create secret - await standardRequest.post( - `${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets`, - { - replication: { - automatic: {} - }, - ...(integration.metadata.secretGCPLabel - ? { - labels: { - [integration.metadata.secretGCPLabel.labelName]: - integration.metadata.secretGCPLabel.labelValue - } - } - : {}) - }, - { - params: { - secretId: key - }, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - - await standardRequest.post( - `${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets/${key}:addVersion`, - { - payload: { - data: Buffer.from(secrets[key].value).toString("base64") - } - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } - } - - for await (const key of Object.keys(res)) { - if (!(key in secrets)) { - // case: delete secret - await standardRequest.delete( - `${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets/${key}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } else { - // case: update secret - if (secrets[key].value !== res[key]) { - await standardRequest.post( - `${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets/${key}:addVersion`, - { - payload: { - data: Buffer.from(secrets[key].value).toString("base64") - } - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } - } - } -}; - -/** - * Sync/push [secrets] to Azure Key Vault with vault URI [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Azure Key Vault integration - */ -const syncSecretsAzureKeyVault = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - interface GetAzureKeyVaultSecret { - id: string; // secret URI - attributes: { - enabled: true; - created: number; - updated: number; - recoveryLevel: string; - recoverableDays: number; - }; - } - - interface AzureKeyVaultSecret extends GetAzureKeyVaultSecret { - key: string; - } - - /** - * Return all secrets from Azure Key Vault by paginating through URL [url] - * @param {String} url - pagination URL to get next set of secrets from Azure Key Vault - * @returns - */ - const paginateAzureKeyVaultSecrets = async (url: string) => { - let result: GetAzureKeyVaultSecret[] = []; - while (url) { - const res = await standardRequest.get(url, { - headers: { - Authorization: `Bearer ${accessToken}` - } - }); - - result = result.concat(res.data.value); - - url = res.data.nextLink; - } - - return result; - }; - - const getAzureKeyVaultSecrets = await paginateAzureKeyVaultSecrets( - `${integration.app}/secrets?api-version=7.3` - ); - - let lastSlashIndex: number; - const res = ( - await Promise.all( - getAzureKeyVaultSecrets.map(async (getAzureKeyVaultSecret) => { - if (!lastSlashIndex) { - lastSlashIndex = getAzureKeyVaultSecret.id.lastIndexOf("/"); - } - - const azureKeyVaultSecret = await standardRequest.get( - `${getAzureKeyVaultSecret.id}?api-version=7.3`, - { - headers: { - Authorization: `Bearer ${accessToken}` - } - } - ); - - return { - ...azureKeyVaultSecret.data, - key: getAzureKeyVaultSecret.id.substring(lastSlashIndex + 1) - }; - }) - ) - ).reduce( - (obj: any, secret: any) => ({ - ...obj, - [secret.key]: secret - }), - {} - ); - - const setSecrets: { - key: string; - value: string; - }[] = []; - - Object.keys(secrets).forEach((key) => { - const hyphenatedKey = key.replace(/_/g, "-"); - if (!(hyphenatedKey in res)) { - // case: secret has been created - setSecrets.push({ - key: hyphenatedKey, - value: secrets[key].value - }); - } else { - if (secrets[key] !== res[hyphenatedKey].value) { - // case: secret has been updated - setSecrets.push({ - key: hyphenatedKey, - value: secrets[key].value - }); - } - } - }); - - const deleteSecrets: AzureKeyVaultSecret[] = []; - - Object.keys(res).forEach((key) => { - const underscoredKey = key.replace(/-/g, "_"); - if (!(underscoredKey in secrets)) { - deleteSecrets.push(res[key]); - } - }); - - const setSecretAzureKeyVault = async ({ - key, - value, - integration, - accessToken - }: { - key: string; - value: string; - integration: IIntegration; - accessToken: string; - }) => { - let isSecretSet = false; - let maxTries = 6; - - while (!isSecretSet && maxTries > 0) { - // try to set secret - try { - await standardRequest.put( - `${integration.app}/secrets/${key}?api-version=7.3`, - { - value - }, - { - headers: { - Authorization: `Bearer ${accessToken}` - } - } - ); - - isSecretSet = true; - } catch (err) { - const error: any = err; - if (error?.response?.data?.error?.innererror?.code === "ObjectIsDeletedButRecoverable") { - await standardRequest.post( - `${integration.app}/deletedsecrets/${key}/recover?api-version=7.3`, - {}, - { - headers: { - Authorization: `Bearer ${accessToken}` - } - } - ); - await new Promise((resolve) => setTimeout(resolve, 10000)); - } else { - await new Promise((resolve) => setTimeout(resolve, 10000)); - maxTries--; - } - } - } - }; - - // Sync/push set secrets - for await (const setSecret of setSecrets) { - const { key, value } = setSecret; - setSecretAzureKeyVault({ - key, - value, - integration, - accessToken - }); - } - - for await (const deleteSecret of deleteSecrets) { - const { key } = deleteSecret; - await standardRequest.delete(`${integration.app}/secrets/${key}?api-version=7.3`, { - headers: { - Authorization: `Bearer ${accessToken}` - } - }); - } -}; - -/** - * Sync/push [secrets] to AWS parameter store - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessId - access id for AWS parameter store integration - * @param {String} obj.accessToken - access token for AWS parameter store integration - */ -const syncSecretsAWSParameterStore = async ({ - integration, - secrets, - accessId, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessId: string | null; - accessToken: string; -}) => { - if (!accessId) return; - - AWS.config.update({ - region: integration.region, - accessKeyId: accessId, - secretAccessKey: accessToken - }); - - const ssm = new AWS.SSM({ - apiVersion: "2014-11-06", - region: integration.region - }); - - const params = { - Path: integration.path, - Recursive: true, - WithDecryption: true - }; - - const parameterList = (await ssm.getParametersByPath(params).promise()).Parameters; - - let awsParameterStoreSecretsObj: { - [key: string]: any; - } = {}; - - if (parameterList) { - awsParameterStoreSecretsObj = parameterList.reduce((obj: any, secret: any) => { - return { - ...obj, - [secret.Name.substring(integration.path.length)]: secret - }; - }, {}); - } - - // Identify secrets to create - Object.keys(secrets).map(async (key) => { - if (!(key in awsParameterStoreSecretsObj)) { - // case: secret does not exist in AWS parameter store - // -> create secret - await ssm - .putParameter({ - Name: `${integration.path}${key}`, - Type: "SecureString", - Value: secrets[key].value, - Overwrite: true - }) - .promise(); - } else { - // case: secret exists in AWS parameter store - - if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) { - // case: secret value doesn't match one in AWS parameter store - // -> update secret - await ssm - .putParameter({ - Name: `${integration.path}${key}`, - Type: "SecureString", - Value: secrets[key].value, - Overwrite: true - }) - .promise(); - } - } - }); - - // Identify secrets to delete - Object.keys(awsParameterStoreSecretsObj).map(async (key) => { - if (!(key in secrets)) { - // case: - // -> delete secret - await ssm - .deleteParameter({ - Name: awsParameterStoreSecretsObj[key].Name - }) - .promise(); - } - }); - - AWS.config.update({ - region: undefined, - accessKeyId: undefined, - secretAccessKey: undefined - }); -}; - -/** - * Sync/push [secrets] to AWS Secrets Manager - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessId - access id for AWS Secrets Manager integration - * @param {String} obj.accessToken - access token for AWS Secrets Manager integration - */ -const syncSecretsAWSSecretManager = async ({ - integration, - secrets, - accessId, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessId: string | null; - accessToken: string; -}) => { - let secretsManager; - const secKeyVal = getSecretKeyValuePair(secrets); - try { - if (!accessId) return; - - AWS.config.update({ - region: integration.region, - accessKeyId: accessId, - secretAccessKey: accessToken - }); - - secretsManager = new SecretsManagerClient({ - region: integration.region, - credentials: { - accessKeyId: accessId, - secretAccessKey: accessToken - } - }); - - const awsSecretManagerSecret = await secretsManager.send( - new GetSecretValueCommand({ - SecretId: integration.app - }) - ); - - let awsSecretManagerSecretObj: { [key: string]: any } = {}; - - if (awsSecretManagerSecret?.SecretString) { - awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString); - } - - if (!_.isEqual(awsSecretManagerSecretObj, secKeyVal)) { - await secretsManager.send( - new UpdateSecretCommand({ - SecretId: integration.app, - SecretString: JSON.stringify(secKeyVal) - }) - ); - } - - AWS.config.update({ - region: undefined, - accessKeyId: undefined, - secretAccessKey: undefined - }); - } catch (err) { - if (err instanceof ResourceNotFoundException && secretsManager) { - await secretsManager.send( - new CreateSecretCommand({ - Name: integration.app, - SecretString: JSON.stringify(secKeyVal) - }) - ); - } - AWS.config.update({ - region: undefined, - accessKeyId: undefined, - secretAccessKey: undefined - }); - } -}; - -/** - * Sync/push [secrets] to Heroku app named [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Heroku integration - */ -const syncSecretsHeroku = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - const herokuSecrets = ( - await standardRequest.get(`${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`, { - headers: { - Accept: "application/vnd.heroku+json; version=3", - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }) - ).data; - - Object.keys(herokuSecrets).forEach((key) => { - if (!(key in secrets)) { - secrets[key] = null; - } - }); - - await standardRequest.patch( - `${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`, - getSecretKeyValuePair(secrets), - { - headers: { - Accept: "application/vnd.heroku+json; version=3", - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); -}; - -/** - * Sync/push [secrets] to Vercel project named [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - */ -const syncSecretsVercel = async ({ - integration, - integrationAuth, - secrets, - accessToken -}: { - integration: IIntegration; - integrationAuth: IIntegrationAuth; - secrets: Record; - accessToken: string; -}) => { - interface VercelSecret { - id?: string; - type: string; - key: string; - value: string; - target: string[]; - gitBranch?: string; - } - // Get all (decrypted) secrets back from Vercel in - // decrypted format - const params: { [key: string]: string } = { - decrypt: "true", - ...(integrationAuth?.teamId - ? { - teamId: integrationAuth.teamId - } - : {}), - ...(integration?.path - ? { - gitBranch: integration?.path - } - : {}) - }; - - const vercelSecrets: VercelSecret[] = ( - await standardRequest.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - }) - ).data.envs.filter((secret: VercelSecret) => { - if (!secret.target.includes(integration.targetEnvironment)) { - // case: secret does not have the same target environment - return false; - } - - if ( - integration.targetEnvironment === "preview" && - secret.gitBranch && - integration.path !== secret.gitBranch - ) { - // case: secret on preview environment does not have same target git branch - return false; - } - - return true; - }); - - const res: { [key: string]: VercelSecret } = {}; - - for await (const vercelSecret of vercelSecrets) { - if (vercelSecret.type === "encrypted") { - // case: secret is encrypted -> need to decrypt - const decryptedSecret = ( - await standardRequest.get( - `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ) - ).data; - - res[vercelSecret.key] = decryptedSecret; - } else { - res[vercelSecret.key] = vercelSecret; - } - } - - const updateSecrets: VercelSecret[] = []; - const deleteSecrets: VercelSecret[] = []; - const newSecrets: VercelSecret[] = []; - - // Identify secrets to create - Object.keys(secrets).map((key) => { - if (!(key in res)) { - // case: secret has been created - newSecrets.push({ - key: key, - value: secrets[key].value, - type: "encrypted", - target: [integration.targetEnvironment], - ...(integration.path - ? { - gitBranch: integration.path - } - : {}) - }); - } - }); - - // Identify secrets to update and delete - Object.keys(res).map((key) => { - if (key in secrets) { - if (res[key].value !== secrets[key].value) { - // case: secret value has changed - updateSecrets.push({ - id: res[key].id, - key: key, - value: secrets[key].value, - type: res[key].type, - target: res[key].target.includes(integration.targetEnvironment) - ? [...res[key].target] - : [...res[key].target, integration.targetEnvironment], - ...(integration.path - ? { - gitBranch: integration.path - } - : {}) - }); - } - } else { - // case: secret has been deleted - deleteSecrets.push({ - id: res[key].id, - key: key, - value: res[key].value, - type: "encrypted", // value doesn't matter - target: [integration.targetEnvironment], - ...(integration.path - ? { - gitBranch: integration.path - } - : {}) - }); - } - }); - - // Sync/push new secrets - if (newSecrets.length > 0) { - await standardRequest.post( - `${INTEGRATION_VERCEL_API_URL}/v10/projects/${integration.app}/env`, - newSecrets, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } - - for await (const secret of updateSecrets) { - if (secret.type !== "sensitive") { - const { id, ...updatedSecret } = secret; - await standardRequest.patch( - `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${id}`, - updatedSecret, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } - } - - for await (const secret of deleteSecrets) { - await standardRequest.delete( - `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } -}; - -/** - * Sync/push [secrets] to Netlify site with id [integration.appId] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {Object} obj.accessToken - access token for Netlify integration - */ -const syncSecretsNetlify = async ({ - integration, - integrationAuth, - secrets, - accessToken -}: { - integration: IIntegration; - integrationAuth: IIntegrationAuth; - secrets: Record; - accessToken: string; -}) => { - interface NetlifyValue { - id?: string; - context: string; // 'dev' | 'branch-deploy' | 'deploy-preview' | 'production', - value: string; - } - - interface NetlifySecret { - key: string; - values: NetlifyValue[]; - } - - const getParams = new URLSearchParams({ - context_name: "all", // integration.context or all - site_id: integration.appId - }); - - const res = ( - await standardRequest.get( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, - { - params: getParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ) - ).data.reduce( - (obj: any, secret: any) => ({ - ...obj, - [secret.key]: secret - }), - {} - ); - - const newSecrets: NetlifySecret[] = []; // createEnvVars - const deleteSecrets: string[] = []; // deleteEnvVar - const deleteSecretValues: NetlifySecret[] = []; // deleteEnvVarValue - const updateSecrets: NetlifySecret[] = []; // setEnvVarValue - - // identify secrets to create and update - Object.keys(secrets).map((key) => { - if (!(key in res)) { - // case: Infisical secret does not exist in Netlify -> create secret - newSecrets.push({ - key, - values: [ - { - value: secrets[key].value, - context: integration.targetEnvironment - } - ] - }); - } else { - // case: Infisical secret exists in Netlify - const contexts = res[key].values.reduce( - (obj: any, value: NetlifyValue) => ({ - ...obj, - [value.context]: value - }), - {} - ); - - if (integration.targetEnvironment in contexts) { - // case: Netlify secret value exists in integration context - if (secrets[key] !== contexts[integration.targetEnvironment].value) { - // case: Infisical and Netlify secret values are different - // -> update Netlify secret context and value - updateSecrets.push({ - key, - values: [ - { - context: integration.targetEnvironment, - value: secrets[key].value - } - ] - }); - } - } else { - // case: Netlify secret value does not exist in integration context - // -> add the new Netlify secret context and value - updateSecrets.push({ - key, - values: [ - { - context: integration.targetEnvironment, - value: secrets[key].value - } - ] - }); - } - } - }); - - // identify secrets to delete - // TODO: revise (patch case where 1 context was deleted but others still there - Object.keys(res).map((key) => { - // loop through each key's context - if (!(key in secrets)) { - // case: Netlify secret does not exist in Infisical - - const numberOfValues = res[key].values.length; - - res[key].values.forEach((value: NetlifyValue) => { - if (value.context === integration.targetEnvironment) { - if (numberOfValues <= 1) { - // case: Netlify secret value has less than 1 context -> delete secret - deleteSecrets.push(key); - } else { - // case: Netlify secret value has more than 1 context -> delete secret value context - deleteSecretValues.push({ - key, - values: [ - { - id: value.id, - context: integration.targetEnvironment, - value: value.value - } - ] - }); - } - } - }); - } - }); - - const syncParams = new URLSearchParams({ - site_id: integration.appId - }); - - if (newSecrets.length > 0) { - await standardRequest.post( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, - newSecrets, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } - - if (updateSecrets.length > 0) { - updateSecrets.forEach(async (secret: NetlifySecret) => { - await standardRequest.patch( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`, - { - context: secret.values[0].context, - value: secret.values[0].value - }, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - }); - } - - if (deleteSecrets.length > 0) { - deleteSecrets.forEach(async (key: string) => { - await standardRequest.delete( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - }); - } - - if (deleteSecretValues.length > 0) { - deleteSecretValues.forEach(async (secret: NetlifySecret) => { - await standardRequest.delete( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - }); - } -}; - -/** - * Sync/push [secrets] to GitHub repo with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for GitHub integration - */ -const syncSecretsGitHub = async ({ - integration, - secrets, - accessToken, - appendices -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; - appendices?: { prefix: string; suffix: string }; -}) => { - interface GitHubRepoKey { - key_id: string; - key: string; - } - - interface GitHubSecret { - name: string; - created_at: string; - updated_at: string; - } - - interface GitHubSecretRes { - [index: string]: GitHubSecret; - } - - const octokit = new Octokit({ - auth: accessToken - }); - - // const user = (await octokit.request('GET /user', {})).data; - const repoPublicKey: GitHubRepoKey = ( - await octokit.request("GET /repos/{owner}/{repo}/actions/secrets/public-key", { - owner: integration.owner, - repo: integration.app - }) - ).data; - - // Get local copy of decrypted secrets. We cannot decrypt them as we dont have access to GH private key - let encryptedSecrets: GitHubSecretRes = ( - await octokit.request("GET /repos/{owner}/{repo}/actions/secrets", { - owner: integration.owner, - repo: integration.app - }) - ).data.secrets.reduce( - (obj: any, secret: any) => ({ - ...obj, - [secret.name]: secret - }), - {} - ); - - encryptedSecrets = Object.keys(encryptedSecrets).reduce( - ( - result: { - [key: string]: GitHubSecret; - }, - key - ) => { - if ( - (appendices?.prefix !== undefined ? key.startsWith(appendices?.prefix) : true) && - (appendices?.suffix !== undefined ? key.endsWith(appendices?.suffix) : true) - ) { - result[key] = encryptedSecrets[key]; - } - return result; - }, - {} - ); - - Object.keys(encryptedSecrets).map(async (key) => { - if (!(key in secrets)) { - await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", { - owner: integration.owner, - repo: integration.app, - secret_name: key - }); - } - }); - - Object.keys(secrets).map((key) => { - // let encryptedSecret; - sodium.ready.then(async () => { - // convert secret & base64 key to Uint8Array. - const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL); - const binsec = sodium.from_string(secrets[key].value); - - // encrypt secret using libsodium - const encBytes = sodium.crypto_box_seal(binsec, binkey); - - // convert encrypted Uint8Array to base64 - const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL); - - await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", { - owner: integration.owner, - repo: integration.app, - secret_name: key, - encrypted_value: encryptedSecret, - key_id: repoPublicKey.key_id - }); - }); - }); -}; - -/** - * Sync/push [secrets] to Render service with id [integration.appId] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Render integration - */ -const syncSecretsRender = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - await standardRequest.put( - `${INTEGRATION_RENDER_API_URL}/v1/services/${integration.appId}/env-vars`, - Object.keys(secrets).map((key) => ({ - key, - value: secrets[key].value - })), - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); -}; - -/** - * Sync/push [secrets] to Laravel Forge sites with id [integration.appId] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Laravel Forge integration - */ -const syncSecretsLaravelForge = async ({ - integration, - secrets, - accessId, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessId: string | null; - accessToken: string; -}) => { - function transformObjectToString(obj: any) { - let result = ""; - for (const key in obj) { - result += `${key}=${obj[key].value}\n`; - } - return result; - } - - await standardRequest.put( - `${INTEGRATION_LARAVELFORGE_API_URL}/api/v1/servers/${accessId}/sites/${integration.appId}/env`, - { - content: transformObjectToString(secrets) - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "Content-Type": "application/json" - } - } - ); -}; - -/** - * Sync/push [secrets] to Railway project with id [integration.appId] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Railway integration - */ -const syncSecretsRailway = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - const query = ` - mutation UpsertVariables($input: VariableCollectionUpsertInput!) { - variableCollectionUpsert(input: $input) - } - `; - - const input = { - projectId: integration.appId, - environmentId: integration.targetEnvironmentId, - ...(integration.targetServiceId ? { serviceId: integration.targetServiceId } : {}), - replace: true, - variables: getSecretKeyValuePair(secrets) - }; - - await standardRequest.post( - INTEGRATION_RAILWAY_API_URL, - { - query, - variables: { - input - } - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ); -}; - -/** - * Sync/push [secrets] to Fly.io app - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Render integration - */ -const syncSecretsFlyio = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - // set secrets - const SetSecrets = ` - mutation($input: SetSecretsInput!) { - setSecrets(input: $input) { - release { - id - version - reason - description - user { - id - email - name - } - evaluationId - createdAt - } - } - } - `; - - await standardRequest.post( - INTEGRATION_FLYIO_API_URL, - { - query: SetSecrets, - variables: { - input: { - appId: integration.app, - secrets: Object.entries(secrets).map(([key, data]) => ({ - key, - value: data.value - })) - } - } - }, - { - headers: { - Authorization: "Bearer " + accessToken, - "Accept-Encoding": "application/json" - } - } - ); - - // get secrets - interface FlyioSecret { - name: string; - digest: string; - createdAt: string; - } - - const GetSecrets = `query ($appName: String!) { - app(name: $appName) { - secrets { - name - digest - createdAt - } - } - }`; - - const getSecretsRes = ( - await standardRequest.post( - INTEGRATION_FLYIO_API_URL, - { - query: GetSecrets, - variables: { - appName: integration.app - } - }, - { - headers: { - Authorization: "Bearer " + accessToken, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ) - ).data.data.app.secrets; - - const deleteSecretsKeys = getSecretsRes - .filter((secret: FlyioSecret) => !(secret.name in secrets)) - .map((secret: FlyioSecret) => secret.name); - - // unset (delete) secrets - const DeleteSecrets = `mutation($input: UnsetSecretsInput!) { - unsetSecrets(input: $input) { - release { - id - version - reason - description - user { - id - email - name - } - evaluationId - createdAt - } - } - }`; - - await standardRequest.post( - INTEGRATION_FLYIO_API_URL, - { - query: DeleteSecrets, - variables: { - input: { - appId: integration.app, - keys: deleteSecretsKeys - } - } - }, - { - headers: { - Authorization: "Bearer " + accessToken, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ); -}; - -/** - * Sync/push [secrets] to CircleCI project - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for CircleCI integration - */ -const syncSecretsCircleCI = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - const circleciOrganizationDetail = ( - await standardRequest.get(`${INTEGRATION_CIRCLECI_API_URL}/v2/me/collaborations`, { - headers: { - "Circle-Token": accessToken, - "Accept-Encoding": "application/json" - } - }) - ).data[0]; - - const { slug } = circleciOrganizationDetail; - - // sync secrets to CircleCI - Object.keys(secrets).forEach( - async (key) => - await standardRequest.post( - `${INTEGRATION_CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar`, - { - name: key, - value: secrets[key].value - }, - { - headers: { - "Circle-Token": accessToken, - "Content-Type": "application/json" - } - } - ) - ); - - // get secrets from CircleCI - const getSecretsRes = ( - await standardRequest.get( - `${INTEGRATION_CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar`, - { - headers: { - "Circle-Token": accessToken, - "Accept-Encoding": "application/json" - } - } - ) - ).data?.items; - - // delete secrets from CircleCI - getSecretsRes.forEach(async (sec: any) => { - if (!(sec.name in secrets)) { - await standardRequest.delete( - `${INTEGRATION_CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar/${sec.name}`, - { - headers: { - "Circle-Token": accessToken, - "Content-Type": "application/json" - } - } - ); - } - }); -}; - -/** - * Sync/push [secrets] to TravisCI project - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for TravisCI integration - */ -const syncSecretsTravisCI = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - // get secrets from travis-ci - const getSecretsRes = ( - await standardRequest.get( - `${INTEGRATION_TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`, - { - headers: { - Authorization: `token ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ) - ).data?.env_vars.reduce( - (obj: any, secret: any) => ({ - ...obj, - [secret.name]: secret - }), - {} - ); - - // add secrets - for await (const key of Object.keys(secrets)) { - if (!(key in getSecretsRes)) { - // case: secret does not exist in travis ci - // -> add secret - await standardRequest.post( - `${INTEGRATION_TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`, - { - env_var: { - name: key, - value: secrets[key].value - } - }, - { - headers: { - Authorization: `token ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ); - } else { - // case: secret exists in travis ci - // -> update/set secret - await standardRequest.patch( - `${INTEGRATION_TRAVISCI_API_URL}/settings/env_vars/${getSecretsRes[key].id}?repository_id=${getSecretsRes[key].repository_id}`, - { - env_var: { - name: key, - value: secrets[key].value - } - }, - { - headers: { - Authorization: `token ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ); - } - } - - for await (const key of Object.keys(getSecretsRes)) { - if (!(key in secrets)) { - // delete secret - await standardRequest.delete( - `${INTEGRATION_TRAVISCI_API_URL}/settings/env_vars/${getSecretsRes[key].id}?repository_id=${getSecretsRes[key].repository_id}`, - { - headers: { - Authorization: `token ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ); - } - } -}; - -/** - * Sync/push [secrets] to GitLab repo with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for GitLab integration - */ -const syncSecretsGitLab = async ({ - integrationAuth, - integration, - secrets, - accessToken -}: { - integrationAuth: IIntegrationAuth; - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - interface GitLabSecret { - key: string; - value: string; - environment_scope: string; - } - - const gitLabApiUrl = integrationAuth.url - ? `${integrationAuth.url}/api` - : INTEGRATION_GITLAB_API_URL; - - const getAllEnvVariables = async (integrationAppId: string, accessToken: string) => { - const headers = { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json", - "Content-Type": "application/json" - }; - - let allEnvVariables: GitLabSecret[] = []; - let url: - | string - | null = `${gitLabApiUrl}/v4/projects/${integrationAppId}/variables?per_page=100`; - - while (url) { - const response: any = await standardRequest.get(url, { headers }); - allEnvVariables = [...allEnvVariables, ...response.data]; - - const linkHeader = response.headers.link; - const nextLink = linkHeader?.split(",").find((part: string) => part.includes('rel="next"')); - - if (nextLink) { - url = nextLink.trim().split(";")[0].slice(1, -1); - } else { - url = null; - } - } - - return allEnvVariables; - }; - - const allEnvVariables = await getAllEnvVariables(integration?.appId, accessToken); - const getSecretsRes: GitLabSecret[] = allEnvVariables - .filter((secret: GitLabSecret) => secret.environment_scope === integration.targetEnvironment) - .filter((gitLabSecret) => { - let isValid = true; - - if ( - integration.metadata.secretPrefix && - !gitLabSecret.key.startsWith(integration.metadata.secretPrefix) - ) { - isValid = false; - } - - if ( - integration.metadata.secretSuffix && - !gitLabSecret.key.endsWith(integration.metadata.secretSuffix) - ) { - isValid = false; - } - - return isValid; - }); - - for await (const key of Object.keys(secrets)) { - const existingSecret = getSecretsRes.find((s: any) => s.key == key); - if (!existingSecret) { - await standardRequest.post( - `${gitLabApiUrl}/v4/projects/${integration?.appId}/variables`, - { - key: key, - value: secrets[key].value, - protected: false, - masked: false, - raw: false, - environment_scope: integration.targetEnvironment - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ); - } else { - // update secret - if (secrets[key].value !== existingSecret.value) { - await standardRequest.put( - `${gitLabApiUrl}/v4/projects/${integration?.appId}/variables/${existingSecret.key}?filter[environment_scope]=${integration.targetEnvironment}`, - { - ...existingSecret, - value: secrets[existingSecret.key].value - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ); - } - } - } - - // delete secrets - for await (const sec of getSecretsRes) { - if (!(sec.key in secrets)) { - await standardRequest.delete( - `${gitLabApiUrl}/v4/projects/${integration?.appId}/variables/${sec.key}?filter[environment_scope]=${integration.targetEnvironment}`, - { - headers: { - Authorization: `Bearer ${accessToken}` - } - } - ); - } - } -}; - -/** - * Sync/push [secrets] to Supabase with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Supabase integration - */ -const syncSecretsSupabase = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - const { data: getSecretsRes } = await standardRequest.get( - `${INTEGRATION_SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - - // convert the secrets to [{}] format - const modifiedFormatForSecretInjection = Object.keys(secrets).map((key) => { - return { - name: key, - value: secrets[key].value - }; - }); - - await standardRequest.post( - `${INTEGRATION_SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, - modifiedFormatForSecretInjection, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - - const secretsToDelete: any = []; - getSecretsRes?.forEach((secretObj: any) => { - if ( - !(secretObj.name in secrets) && - // supbase reserved secret ref: https://supabase.com/docs/guides/functions/secrets#default-secrets - ![ - "SUPABASE_ANON_KEY", - "SUPABASE_SERVICE_ROLE_KEY", - "SUPABASE_DB_URL", - "SUPABASE_URL" - ].includes(secretObj.name) - ) { - secretsToDelete.push(secretObj.name); - } - }); - - await standardRequest.delete( - `${INTEGRATION_SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - }, - data: secretsToDelete - } - ); -}; - -/** - * Sync/push [secrets] to Checkly app/group - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Checkly integration - */ -const syncSecretsCheckly = async ({ - integration, - secrets, - accessToken, - appendices -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; - appendices?: { prefix: string; suffix: string }; -}) => { - - if (integration.targetServiceId) { - // sync secrets to checkly group envars - - let getGroupSecretsRes = ( - await standardRequest.get(`${INTEGRATION_CHECKLY_API_URL}/v1/check-groups/${integration.targetServiceId}`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "X-Checkly-Account": integration.appId - } - }) - ).data.environmentVariables.reduce( - (obj: any, secret: any) => ({ - ...obj, - [secret.key]: secret.value - }), - {} - ); - - getGroupSecretsRes = Object.keys(getGroupSecretsRes).reduce( - ( - result: { - [key: string]: string; - }, - key - ) => { - if ( - (appendices?.prefix !== undefined ? key.startsWith(appendices?.prefix) : true) && - (appendices?.suffix !== undefined ? key.endsWith(appendices?.suffix) : true) - ) { - result[key] = getGroupSecretsRes[key]; - } - return result; - }, - {} - ); - - const groupEnvironmentVariables = Object.keys(secrets).map(key => ({ - key, - value: secrets[key].value - })); - - await standardRequest.put( - `${INTEGRATION_CHECKLY_API_URL}/v1/check-groups/${integration.targetServiceId}`, - { - environmentVariables: groupEnvironmentVariables - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "X-Checkly-Account": integration.appId - } - } - ); - } else { - // sync secrets to checkly global envars - - let getSecretsRes = ( - await standardRequest.get(`${INTEGRATION_CHECKLY_API_URL}/v1/variables`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json", - "X-Checkly-Account": integration.appId - } - }) - ).data.reduce( - (obj: any, secret: any) => ({ - ...obj, - [secret.key]: secret.value - }), - {} - ); - - getSecretsRes = Object.keys(getSecretsRes).reduce( - ( - result: { - [key: string]: string; - }, - key - ) => { - if ( - (appendices?.prefix !== undefined ? key.startsWith(appendices?.prefix) : true) && - (appendices?.suffix !== undefined ? key.endsWith(appendices?.suffix) : true) - ) { - result[key] = getSecretsRes[key]; - } - return result; - }, - {} - ); - - // add secrets - for await (const key of Object.keys(secrets)) { - if (!(key in getSecretsRes)) { - // case: secret does not exist in checkly - // -> add secret - await standardRequest.post( - `${INTEGRATION_CHECKLY_API_URL}/v1/variables`, - { - key, - value: secrets[key].value - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "Content-Type": "application/json", - "X-Checkly-Account": integration.appId - } - } - ); - } else { - // case: secret exists in checkly - // -> update/set secret - - if (secrets[key] !== getSecretsRes[key]) { - await standardRequest.put( - `${INTEGRATION_CHECKLY_API_URL}/v1/variables/${key}`, - { - value: secrets[key].value - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - Accept: "application/json", - "X-Checkly-Account": integration.appId - } - } - ); - } - } - } - - for await (const key of Object.keys(getSecretsRes)) { - if (!(key in secrets)) { - // delete secret - await standardRequest.delete(`${INTEGRATION_CHECKLY_API_URL}/v1/variables/${key}`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "X-Checkly-Account": integration.appId - } - }); - } - } - } -}; - -/** - * Sync/push [secrets] to Qovery app - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Qovery integration - */ -const syncSecretsQovery = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - const getSecretsRes = ( - await standardRequest.get( - `${INTEGRATION_QOVERY_API_URL}/${integration.scope}/${integration.appId}/environmentVariable`, - { - headers: { - Authorization: `Token ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ) - ).data.results.reduce( - (obj: any, secret: any) => ({ - ...obj, - [secret.key]: { id: secret.id, value: secret.value } - }), - {} - ); - - // add secrets - for await (const key of Object.keys(secrets)) { - if (!(key in getSecretsRes)) { - // case: secret does not exist in qovery - // -> add secret - await standardRequest.post( - `${INTEGRATION_QOVERY_API_URL}/${integration.scope}/${integration.appId}/environmentVariable`, - { - key, - value: secrets[key].value - }, - { - headers: { - Authorization: `Token ${accessToken}`, - Accept: "application/json", - "Content-Type": "application/json" - } - } - ); - } else { - // case: secret exists in qovery - // -> update/set secret - - if (secrets[key].value !== getSecretsRes[key].value) { - await standardRequest.put( - `${INTEGRATION_QOVERY_API_URL}/${integration.scope}/${integration.appId}/environmentVariable/${getSecretsRes[key].id}`, - { - key, - value: secrets[key].value - }, - { - headers: { - Authorization: `Token ${accessToken}`, - "Content-Type": "application/json", - Accept: "application/json" - } - } - ); - } - } - } - - // This one is dangerous because there might be a lot of qovery-specific secrets - - // for await (const key of Object.keys(getSecretsRes)) { - // if (!(key in secrets)) { - // console.log(3) - // // delete secret - // await standardRequest.delete(`${INTEGRATION_QOVERY_API_URL}/application/${integration.appId}/environmentVariable/${getSecretsRes[key].id}`, { - // headers: { - // Authorization: `Token ${accessToken}`, - // Accept: "application/json", - // "X-Qovery-Account": integration.appId - // } - // }); - // } - // } -}; - -/** - * Sync/push [secrets] to Terraform Cloud project with id [integration.appId] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Terraform Cloud API - */ -const syncSecretsTerraformCloud = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - // get secrets from Terraform Cloud - const getSecretsRes = ( - await standardRequest.get( - `${INTEGRATION_TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ) - ).data.data.reduce( - (obj: any, secret: any) => ({ - ...obj, - [secret.attributes.key]: secret - }), - {} - ); - - // create or update secrets on Terraform Cloud - for await (const key of Object.keys(secrets)) { - if (!(key in getSecretsRes)) { - // case: secret does not exist in Terraform Cloud - // -> add secret - await standardRequest.post( - `${INTEGRATION_TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars`, - { - data: { - type: "vars", - attributes: { - key, - value: secrets[key].value, - category: integration.targetService - } - } - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/vnd.api+json", - Accept: "application/vnd.api+json" - } - } - ); - } else { - // case: secret exists in Terraform Cloud - if (secrets[key].value !== getSecretsRes[key].attributes.value) { - // -> update secret - await standardRequest.patch( - `${INTEGRATION_TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${getSecretsRes[key].id}`, - { - data: { - type: "vars", - id: getSecretsRes[key].id, - attributes: { - ...getSecretsRes[key], - value: secrets[key].value - } - } - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/vnd.api+json", - Accept: "application/vnd.api+json" - } - } - ); - } - } - } - - for await (const key of Object.keys(getSecretsRes)) { - if (!(key in secrets)) { - // case: delete secret - await standardRequest.delete( - `${INTEGRATION_TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${getSecretsRes[key].id}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/vnd.api+json", - Accept: "application/vnd.api+json" - } - } - ); - } - } -}; - -/** - * Sync/push [secrets] to TeamCity project (and optionally build config) - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration - * @param {String} obj.accessToken - access token for TeamCity integration - */ -const syncSecretsTeamCity = async ({ - integrationAuth, - integration, - secrets, - accessToken -}: { - integrationAuth: IIntegrationAuth; - integration: IIntegration; - secrets: any; - accessToken: string; -}) => { - interface TeamCitySecret { - name: string; - value: string; - } - - interface TeamCityBuildConfigParameter { - name: string; - value: string; - inherited: boolean; - } - interface GetTeamCityBuildConfigParametersRes { - href: string; - count: number; - property: TeamCityBuildConfigParameter[]; - } - - if (integration.targetEnvironment && integration.targetEnvironmentId) { - // case: sync to specific build-config in TeamCity project - const res = ( - await standardRequest.get( - `${integrationAuth.url}/app/rest/buildTypes/${integration.targetEnvironmentId}/parameters`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ) - ).data.property - .filter((parameter) => !parameter.inherited) - .reduce((obj: any, secret: TeamCitySecret) => { - const secretName = secret.name.replace(/^env\./, ""); - return { - ...obj, - [secretName]: secret.value - }; - }, {}); - - for await (const key of Object.keys(secrets)) { - if (!(key in res) || (key in res && secrets[key].value !== res[key])) { - // case: secret does not exist in TeamCity or secret value has changed - // -> create/update secret - await standardRequest.post( - `${integrationAuth.url}/app/rest/buildTypes/${integration.targetEnvironmentId}/parameters`, - { - name: `env.${key}`, - value: secrets[key].value - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - } - } - - for await (const key of Object.keys(res)) { - if (!(key in secrets)) { - // delete secret - await standardRequest.delete( - `${integrationAuth.url}/app/rest/buildTypes/${integration.targetEnvironmentId}/parameters/env.${key}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - } - } - } else { - // case: sync to TeamCity project - const res = ( - await standardRequest.get( - `${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ) - ).data.property.reduce((obj: any, secret: TeamCitySecret) => { - const secretName = secret.name.replace(/^env\./, ""); - return { - ...obj, - [secretName]: secret.value - }; - }, {}); - - for await (const key of Object.keys(secrets)) { - if (!(key in res) || (key in res && secrets[key] !== res[key])) { - // case: secret does not exist in TeamCity or secret value has changed - // -> create/update secret - await standardRequest.post( - `${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`, - { - name: `env.${key}`, - value: secrets[key].value - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - } - } - - for await (const key of Object.keys(res)) { - if (!(key in secrets)) { - // delete secret - await standardRequest.delete( - `${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters/env.${key}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - } - } - } -}; - -/** - * Sync/push [secrets] to HashiCorp Vault path - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for HashiCorp Vault integration - */ -const syncSecretsHashiCorpVault = async ({ - integration, - integrationAuth, - secrets, - accessId, - accessToken -}: { - integration: IIntegration; - integrationAuth: IIntegrationAuth; - secrets: Record; - accessId: string | null; - accessToken: string; -}) => { - if (!accessId) return; - - interface LoginAppRoleRes { - auth: { - client_token: string; - }; - } - - // get Vault client token (could be optimized) - const { data }: { data: LoginAppRoleRes } = await standardRequest.post( - `${integrationAuth.url}/v1/auth/approle/login`, - { - role_id: accessId, - secret_id: accessToken - }, - { - headers: { - "X-Vault-Namespace": integrationAuth.namespace - } - } - ); - - const clientToken = data.auth.client_token; - - await standardRequest.post( - `${integrationAuth.url}/v1/${integration.app}/data/${integration.path}`, - { - data: getSecretKeyValuePair(secrets) - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "Content-Type": "application/json", - "X-Vault-Token": clientToken, - "X-Vault-Namespace": integrationAuth.namespace - } - } - ); -}; - -/** - * Sync/push [secrets] to Cloudflare Pages project with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - API token for Cloudflare - */ -const syncSecretsCloudflarePages = async ({ - integration, - secrets, - accessId, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessId: string | null; - accessToken: string; -}) => { - // get secrets from cloudflare pages - const getSecretsRes = ( - await standardRequest.get( - `${INTEGRATION_CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ) - ).data.result["deployment_configs"][integration.targetEnvironment]["env_vars"]; - - // copy the secrets object, so we can set deleted keys to null - const secretsObj: any = getSecretKeyValuePair(secrets); - - for (const [key, val] of Object.entries(secretsObj)) { - secretsObj[key] = { type: "secret_text", value: val }; - } - - if (getSecretsRes) { - for await (const key of Object.keys(getSecretsRes)) { - if (!(key in secrets)) { - // case: secret does not exist in infisical - // -> delete secret from cloudflare pages - secretsObj[key] = null; - } - } - } - - const data = { - deployment_configs: { - [integration.targetEnvironment]: { - env_vars: secretsObj - } - } - }; - - await standardRequest.patch( - `${INTEGRATION_CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`, - data, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); -}; - -/** - * Sync/push [secrets] to Cloudflare Workers project with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - API token for Cloudflare workers - */ -const syncSecretsCloudflareWorkers = async ({ - integration, - secrets, - accessId, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessId: string | null; - accessToken: string; -}) => { - // get secrets from cloudflare workers - const getSecretsRes = ( - await standardRequest.get( - `${INTEGRATION_CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ) - ).data.result; - - const secretsObj: any = getSecretKeyValuePair(secrets); - - for (const [key, val] of Object.entries(secretsObj)) { - secretsObj[key] = { type: "secret_text", value: val }; - } - - // get deleted secrets list - const deletedSecretKeys: string[] = []; - if (getSecretsRes) { - getSecretsRes.forEach((secretRes: any) => { - if (!(Object.keys(secrets).includes(secretRes.name))) { - deletedSecretKeys.push(secretRes.name); - } - }) - } - - deletedSecretKeys.forEach(async (secretKey) => { - await standardRequest.delete( - `${INTEGRATION_CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets/${secretKey}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - }); - - interface ConvertedSecret { - name: string; - text: string; - type: string; - } - - interface SecretsObj { - [key: string]: { - type: string; - value: string; - }; - } - - const data: ConvertedSecret[] = Object.entries(secretsObj as SecretsObj).map(([name, secret]) => ({ - name, - text: secret.value, - type: "secret_text" - })); - - data.forEach(async (secret) => { - await standardRequest.put( - `${INTEGRATION_CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`, - secret, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - }) -}; - -/** - * Sync/push [secrets] to BitBucket repo with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for BitBucket integration - */ -const syncSecretsBitBucket = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - interface VariablesResponse { - size: number; - page: number; - pageLen: number; - next: string; - previous: string; - values: Array; - } - - interface BitbucketVariable { - type: string; - uuid: string; - key: string; - value: string; - secured: boolean; - } - - const res: { [key: string]: BitbucketVariable } = {}; - - let hasNextPage = true; - let variablesUrl = `${INTEGRATION_BITBUCKET_API_URL}/2.0/repositories/${integration.targetEnvironmentId}/${integration.appId}/pipelines_config/variables`; - - while (hasNextPage) { - const { data }: { data: VariablesResponse } = await standardRequest.get(variablesUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - }); - - if (data?.values.length > 0) { - data.values.forEach((variable) => { - res[variable.key] = variable; - }); - } - - if (data.next) { - variablesUrl = data.next; - } else { - hasNextPage = false; - } - } - - for await (const key of Object.keys(secrets)) { - if (key in res) { - // update existing secret - await standardRequest.put( - `${variablesUrl}/${res[key].uuid}`, - { - key, - value: secrets[key].value, - secured: true - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - } else { - // create new secret - await standardRequest.post( - variablesUrl, - { - key, - value: secrets[key].value, - secured: true - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - } - } - - for await (const key of Object.keys(res)) { - if (!(key in secrets)) { - // delete secret - await standardRequest.delete(`${variablesUrl}/${res[key].uuid}`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - }); - } - } -}; - -/** - * Sync/push [secrets] to Codefresh project with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Codefresh integration - */ -const syncSecretsCodefresh = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - await standardRequest.patch( - `${INTEGRATION_CODEFRESH_API_URL}/projects/${integration.appId}`, - { - variables: Object.keys(secrets).map((key) => ({ - key, - value: secrets[key].value - })) - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); -}; - -/** - * Sync/push [secrets] to DigitalOcean App Platform application with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for integration - */ -const syncSecretsDigitalOceanAppPlatform = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - // get current app settings - const appSettings = ( - await standardRequest.get(`${INTEGRATION_DIGITAL_OCEAN_API_URL}/v2/apps/${integration.appId}`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - }) - ).data.app.spec; - - await standardRequest.put( - `${INTEGRATION_DIGITAL_OCEAN_API_URL}/v2/apps/${integration.appId}`, - { - spec: { - name: integration.app, - ...appSettings, - envs: Object.entries(secrets).map(([key, data]) => ({ key, value: data.value })) - } - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); -}; - -/** - * Sync/push [secrets] to Windmill with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for windmill integration - * @param {Object} obj.secretComments - secret comments to push to integration (object where keys are secret keys and values are comment values) - */ -const syncSecretsWindmill = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - interface WindmillSecret { - path: string; - value: string; - is_secret: boolean; - description?: string; - } - - // get secrets stored in windmill workspace - const res = ( - await standardRequest.get( - `${INTEGRATION_WINDMILL_API_URL}/w/${integration.appId}/variables/list`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ) - ).data.reduce( - (obj: any, secret: WindmillSecret) => ({ - ...obj, - [secret.path]: secret - }), - {} - ); - - // eslint-disable-next-line no-useless-escape - const pattern = new RegExp("^(u/|f/)[a-zA-Z0-9_-]+/([a-zA-Z0-9_-]+/)*[a-zA-Z0-9_-]*[^/]$"); - - for await (const key of Object.keys(secrets)) { - if ((key.startsWith("u/") || key.startsWith("f/")) && pattern.test(key)) { - if (!(key in res)) { - // case: secret does not exist in windmill - // -> create secret - - await standardRequest.post( - `${INTEGRATION_WINDMILL_API_URL}/w/${integration.appId}/variables/create`, - { - path: key, - value: secrets[key].value, - is_secret: true, - description: secrets[key]?.comment || "" - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } else { - // -> update secret - await standardRequest.post( - `${INTEGRATION_WINDMILL_API_URL}/w/${integration.appId}/variables/update/${res[key].path}`, - { - path: key, - value: secrets[key].value, - is_secret: true, - description: secrets[key]?.comment || "" - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } - } - } - - for await (const key of Object.keys(res)) { - if (!(key in secrets)) { - // -> delete secret - await standardRequest.delete( - `${INTEGRATION_WINDMILL_API_URL}/w/${integration.appId}/variables/delete/${res[key].path}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - } - } - ); - } - } -}; - -/** - * Sync/push [secrets] to Cloud66 application with name [integration.app] - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {IIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Cloud66 integration - */ -const syncSecretsCloud66 = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - interface Cloud66Secret { - id: number; - key: string; - value: string; - readonly: boolean; - created_at: string; - updated_at: string; - is_password: boolean; - is_generated: boolean; - history: any[]; - } - - // get all current secrets - const res = ( - await standardRequest.get( - `${INTEGRATION_CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ) - ).data.response - .filter((secret: Cloud66Secret) => !secret.readonly || !secret.is_generated) - .reduce( - (obj: any, secret: any) => ({ - ...obj, - [secret.key]: secret - }), - {} - ); - - for await (const key of Object.keys(secrets)) { - if (key in res) { - // update existing secret - await standardRequest.put( - `${INTEGRATION_CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments/${key}`, - { - key, - value: secrets[key].value - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - } else { - // create new secret - await standardRequest.post( - `${INTEGRATION_CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments`, - { - key, - value: secrets[key].value - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - } - } - - for await (const key of Object.keys(res)) { - if (!(key in secrets)) { - // delete secret - await standardRequest.delete( - `${INTEGRATION_CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments/${key}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - } - } -}; - -/** Sync/push [secrets] to Northflank - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Northflank integration - */ -const syncSecretsNorthflank = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - await standardRequest.patch( - `${INTEGRATION_NORTHFLANK_API_URL}/v1/projects/${integration.appId}/secrets/${integration.targetServiceId}`, - { - secrets: { - variables: getSecretKeyValuePair(secrets) - } - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); -}; - -/** Sync/push [secrets] to Hasura Cloud - * @param {Object} obj - * @param {IIntegration} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Hasura Cloud integration - */ -const syncSecretsHasuraCloud = async ({ - integration, - secrets, - accessToken -}: { - integration: IIntegration; - secrets: Record; - accessToken: string; -}) => { - const res = await standardRequest.post( - INTEGRATION_HASURA_CLOUD_API_URL, - { - query: - "query MyQuery($tenantId: uuid!) { getTenantEnv(tenantId: $tenantId) { hash envVars } }", - variables: { - tenantId: integration.appId - } - }, - { - headers: { - Authorization: `pat ${accessToken}`, - "Content-Type": "application/json" - } - } - ); - - const { - data: { - getTenantEnv: { hash, envVars } - } - } = ZGetTenantEnv.parse(res.data); - - let currentHash = hash; - - const secretsToUpdate = Object.keys(secrets).map((key) => { - return ({ - key, - value: secrets[key].value - }); - }); - - if (secretsToUpdate.length) { - // update secrets - - const addRequest = await standardRequest.post( - INTEGRATION_HASURA_CLOUD_API_URL, - { - query: - "mutation MyQuery($currentHash: String!, $envs: [UpdateEnvObject!]!, $tenantId: uuid!) { updateTenantEnv(currentHash: $currentHash, envs: $envs, tenantId: $tenantId) { hash envVars} }", - variables: { - currentHash, - envs: secretsToUpdate, - tenantId: integration.appId - } - }, - { - headers: { - Authorization: `pat ${accessToken}`, - "Content-Type": "application/json" - } - } - ); - - const addRequestResponse = ZUpdateTenantEnv.safeParse(addRequest.data); - if (addRequestResponse.success) { - currentHash = addRequestResponse.data.data.updateTenantEnv.hash; - } - } - - const secretsToDelete = envVars.environment - ? Object.keys(envVars.environment).filter((key) => !(key in secrets)) - : []; - - if (secretsToDelete.length) { - await standardRequest.post( - INTEGRATION_HASURA_CLOUD_API_URL, - { - query: ` - mutation deleteTenantEnv($id: uuid!, $currentHash: String!, $env: [String!]!) { - deleteTenantEnv(tenantId: $id, currentHash: $currentHash, deleteEnvs: $env) { - hash - envVars - } - } - `, - variables: { - id: integration.appId, - currentHash, - env: secretsToDelete - } - }, - { - headers: { - Authorization: `pat ${accessToken}`, - "Content-Type": "application/json" - } - } - ); - } -}; - -export { syncSecrets }; diff --git a/backend-mongo/src/integrations/teams.ts b/backend-mongo/src/integrations/teams.ts deleted file mode 100644 index 46791c5b3..000000000 --- a/backend-mongo/src/integrations/teams.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { - IIntegrationAuth, -} from "../models"; -import { - INTEGRATION_GITLAB, - INTEGRATION_GITLAB_API_URL, -} from "../variables"; -import { standardRequest } from "../config/request"; - -interface Team { - name: string; - teamId: string; -} - -/** - * Return list of teams for integration authorization [integrationAuth] - * @param {Object} obj - * @param {String} obj.integrationAuth - integration authorization to get teams for - * @param {String} obj.accessToken - access token for integration authorization - * @returns {Object[]} teams - teams of integration authorization - * @returns {String} teams.name - name of team - * @returns {String} teams.teamId - id of team -*/ -const getTeams = async ({ - integrationAuth, - accessToken, -}: { - integrationAuth: IIntegrationAuth; - accessToken: string; -}) => { - - let teams: Team[] = []; - - switch (integrationAuth.integration) { - case INTEGRATION_GITLAB: - teams = await getTeamsGitLab({ - integrationAuth, - accessToken, - }); - break; - } - - return teams; -} - -/** - * Return list of teams for GitLab integration - * @param {Object} obj - * @param {String} obj.accessToken - access token for GitLab API - * @returns {Object[]} teams - teams that user is part of in GitLab - * @returns {String} teams.name - name of team - * @returns {String} teams.teamId - id of team -*/ -const getTeamsGitLab = async ({ - integrationAuth, - accessToken, -}: { - integrationAuth: IIntegrationAuth; - accessToken: string; -}) => { - const gitLabApiUrl = integrationAuth.url ? `${integrationAuth.url}/api` : INTEGRATION_GITLAB_API_URL; - - let teams: Team[] = []; - const res = (await standardRequest.get( - `${gitLabApiUrl}/v4/groups`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json", - }, - } - )).data; - - teams = res.map((t: any) => ({ - name: t.name, - teamId: t.id, - })); - - return teams; -} - -export { - getTeams, -} diff --git a/backend-mongo/src/interfaces/middleware/index.ts b/backend-mongo/src/interfaces/middleware/index.ts deleted file mode 100644 index acd992162..000000000 --- a/backend-mongo/src/interfaces/middleware/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Types } from "mongoose"; -import { IIdentity, IServiceTokenData, IUser } from "../../models"; -import { IdentityActor, ServiceActor, UserActor, UserAgentType } from "../../ee/models"; - -interface BaseAuthData { - ipAddress: string; - userAgent: string; - userAgentType: UserAgentType; - tokenVersionId?: Types.ObjectId; -} - -export interface UserAuthData extends BaseAuthData { - actor: UserActor; - authPayload: IUser; -} - -export interface IdentityAuthData extends BaseAuthData { - actor: IdentityActor; - authPayload: IIdentity; -} - -export interface ServiceTokenAuthData extends BaseAuthData { - actor: ServiceActor; - authPayload: IServiceTokenData; -} - -export type AuthData = UserAuthData | IdentityAuthData | ServiceTokenAuthData; \ No newline at end of file diff --git a/backend-mongo/src/interfaces/services/BotService/index.ts b/backend-mongo/src/interfaces/services/BotService/index.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/backend-mongo/src/interfaces/services/SecretService/index.ts b/backend-mongo/src/interfaces/services/SecretService/index.ts deleted file mode 100644 index 495abf726..000000000 --- a/backend-mongo/src/interfaces/services/SecretService/index.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { Types } from "mongoose"; -import { AuthData } from "../../middleware"; - -export interface CreateSecretParams { - secretName: string; - workspaceId: Types.ObjectId; - environment: string; - type: "shared" | "personal"; - authData: AuthData; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretCommentCiphertext?: string; - secretCommentIV?: string; - secretCommentTag?: string; - skipMultilineEncoding?: boolean; - secretPath: string; - metadata?: { - source?: string; - }; -} - -export interface GetSecretsParams { - workspaceId: Types.ObjectId; - environment: string; - secretPath: string; - authData: AuthData; -} - -export interface GetSecretParams { - secretName: string; - workspaceId: Types.ObjectId; - secretPath: string; - environment: string; - type?: "shared" | "personal"; - authData: AuthData; - include_imports?: boolean; - version?: number; -} - -export interface UpdateSecretParams { - secretName: string; - newSecretName?: string; - secretId?: string; - secretKeyCiphertext?: string; - secretKeyIV?: string; - secretKeyTag?: string; - workspaceId: Types.ObjectId; - environment: string; - type: "shared" | "personal"; - authData: AuthData; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretPath: string; - secretCommentCiphertext?: string; - secretCommentIV?: string; - secretCommentTag?: string; - - secretReminderRepeatDays?: number | null; - secretReminderNote?: string | null; - - skipMultilineEncoding?: boolean; - tags?: string[]; -} - -export interface DeleteSecretParams { - secretName: string; - secretId?: string; - workspaceId: Types.ObjectId; - environment: string; - type: "shared" | "personal"; - authData: AuthData; - secretPath: string; -} - -export interface CreateSecretBatchParams { - workspaceId: Types.ObjectId; - environment: string; - authData: AuthData; - secretPath: string; - secrets: Array<{ - secretName: string; - type: "shared" | "personal"; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretCommentCiphertext?: string; - secretCommentIV?: string; - secretCommentTag?: string; - skipMultilineEncoding?: boolean; - metadata?: { - source?: string; - }; - }>; -} - -export interface UpdateSecretBatchParams { - workspaceId: Types.ObjectId; - environment: string; - authData: AuthData; - secretPath: string; - secrets: Array<{ - secretName: string; - type: "shared" | "personal"; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretCommentCiphertext?: string; - secretCommentIV?: string; - secretCommentTag?: string; - skipMultilineEncoding?: boolean; - tags?: string[]; - }>; -} - -export interface DeleteSecretBatchParams { - workspaceId: Types.ObjectId; - environment: string; - authData: AuthData; - secretPath: string; - secrets: Array<{ - secretName: string; - type: "shared" | "personal"; - }>; -} diff --git a/backend-mongo/src/interfaces/utils/crypto.ts b/backend-mongo/src/interfaces/utils/crypto.ts deleted file mode 100644 index cc2c54e3b..000000000 --- a/backend-mongo/src/interfaces/utils/crypto.ts +++ /dev/null @@ -1,41 +0,0 @@ -export interface IGenerateKeyPairOutput { - publicKey: string; - privateKey: string -} - -export interface IEncryptAsymmetricInput { - plaintext: string; - publicKey: string; - privateKey: string; -} - -export interface IEncryptAsymmetricOutput { - ciphertext: string; - nonce: string; -} - -export interface IDecryptAsymmetricInput { - ciphertext: string; - nonce: string; - publicKey: string; - privateKey: string; -} - -export interface IEncryptSymmetricInput { - plaintext: string; - key: string; -} - -export interface IEncryptSymmetricOutput { - ciphertext: string; - iv: string; - tag: string; -} - -export interface IDecryptSymmetricInput { - ciphertext: string; - iv: string; - tag: string; - key: string; -} - diff --git a/backend-mongo/src/interfaces/utils/index.ts b/backend-mongo/src/interfaces/utils/index.ts deleted file mode 100644 index b781a39bb..000000000 --- a/backend-mongo/src/interfaces/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./crypto"; \ No newline at end of file diff --git a/backend-mongo/src/middleware/index.ts b/backend-mongo/src/middleware/index.ts deleted file mode 100644 index 347519b6e..000000000 --- a/backend-mongo/src/middleware/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import requireAuth from "./requireAuth"; -import requireMfaAuth from "./requireMfaAuth"; -import requireSignupAuth from "./requireSignupAuth"; -import requireWorkspaceAuth from "./requireWorkspaceAuth"; -import requireServiceTokenAuth from "./requireServiceTokenAuth"; -import requireSecretAuth from "./requireSecretAuth"; -import requireSecretsAuth from "./requireSecretsAuth"; -import requireBlindIndicesEnabled from "./requireBlindIndicesEnabled"; -import requireE2EEOff from "./requireE2EEOff"; -import { requireSuperAdminAccess } from "./requireSuperAdminAccess"; -import validateRequest from "./validateRequest"; -import { disableSignUpByServerCfg } from "./serverAdmin"; - -export { - requireAuth, - requireMfaAuth, - requireSignupAuth, - requireWorkspaceAuth, - requireServiceTokenAuth, - requireSecretAuth, - requireSecretsAuth, - requireBlindIndicesEnabled, - requireE2EEOff, - validateRequest, - requireSuperAdminAccess, - disableSignUpByServerCfg -}; diff --git a/backend-mongo/src/middleware/requestErrorHandler.ts b/backend-mongo/src/middleware/requestErrorHandler.ts deleted file mode 100644 index 99417d030..000000000 --- a/backend-mongo/src/middleware/requestErrorHandler.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as Sentry from "@sentry/node"; -import { ErrorRequestHandler } from "express"; -import { TokenExpiredError } from "jsonwebtoken"; -import { InternalServerError, UnauthorizedRequestError } from "../utils/errors"; -import { logger } from "../utils/logging"; -import RequestError, { mapToPinoLogLevel } from "../utils/requestError"; -import { ForbiddenError } from "@casl/ability"; - -export const requestErrorHandler: ErrorRequestHandler = async ( - err: RequestError | Error, - req, - res, - next -) => { - if (res.headersSent) return next(); - - let error: RequestError; - - switch (true) { - case err instanceof TokenExpiredError: - error = UnauthorizedRequestError({ stack: err.stack, message: "Token expired" }); - break; - case err instanceof ForbiddenError: - error = UnauthorizedRequestError({ context: { exception: err.message }, stack: err.stack }) - break; - case err instanceof RequestError: - error = err as RequestError; - break; - default: - error = InternalServerError({ context: { exception: err.message }, stack: err.stack }); - break; - } - - logger[mapToPinoLogLevel(error.level)]({ msg: error }); - - if (req.user) { - Sentry.setUser({ email: (req.user as any).email }); - } - - Sentry.captureException(error); - - res.status((error).statusCode).send( - await error.format(req) - ); - - next(); -}; diff --git a/backend-mongo/src/middleware/requireAuth.ts b/backend-mongo/src/middleware/requireAuth.ts deleted file mode 100644 index e68de3133..000000000 --- a/backend-mongo/src/middleware/requireAuth.ts +++ /dev/null @@ -1,73 +0,0 @@ -import jwt from "jsonwebtoken"; -import { NextFunction, Request, Response } from "express"; -import { AuthMode } from "../variables"; -import { AuthData } from "../interfaces/middleware"; -import { extractAuthMode, getAuthData } from "../utils/authn/helpers"; -import { UnauthorizedRequestError } from "../utils/errors"; - -declare module "jsonwebtoken" { - export interface UserIDJwtPayload extends jwt.JwtPayload { - userId: string; - } -} - -/** - * Validate if token on request is valid (e.g. not expired) for various auth modes: - * - If token is a JWT token, then check if there is an associated user - * and if user is fully setup. - * - If token is a service token (st), then check if there is associated - * service token data. - * @param {Object} obj - * @param {String[]} obj.acceptedAuthModes - accepted modes of authentication (jwt/st) - * @returns - */ -const requireAuth = ({ - acceptedAuthModes = [AuthMode.JWT], -}: { - acceptedAuthModes: AuthMode[]; -}) => { - return async (req: Request, res: Response, next: NextFunction) => { - - // extract auth mode - const { authMode, authTokenValue } = await extractAuthMode({ - headers: req.headers - }); - - // validate auth mode - if (!acceptedAuthModes.includes(authMode)) throw UnauthorizedRequestError({ - message: "Failed to authenticate unaccepted authentication mode" - }); - - // get auth data / payload - const authData: AuthData = await getAuthData({ - authMode, - authTokenValue, - ipAddress: req.realIP, - userAgent: req.headers["user-agent"] ?? "" - }); - - switch (authMode) { - case AuthMode.SERVICE_TOKEN: - req.serviceTokenData = authData.authPayload; - break; - case AuthMode.IDENTITY_ACCESS_TOKEN: - req.serviceTokenData = authData.authPayload; - break; - case AuthMode.API_KEY: - req.user = authData.authPayload; - break; - case AuthMode.API_KEY_V2: - req.user = authData.authPayload; - break; - case AuthMode.JWT: - req.user = authData.authPayload; - break; - } - - req.authData = authData; - - return next(); - } -} - -export default requireAuth; diff --git a/backend-mongo/src/middleware/requireBlindIndicesEnabled.ts b/backend-mongo/src/middleware/requireBlindIndicesEnabled.ts deleted file mode 100644 index b1288dd2b..000000000 --- a/backend-mongo/src/middleware/requireBlindIndicesEnabled.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { Types } from "mongoose"; -import { SecretBlindIndexData } from "../models"; -import { UnauthorizedRequestError } from "../utils/errors"; - -type req = "params" | "body" | "query"; - -/** - * Validate if workspace with [workspaceId] has blind indices enabled - * @param {Object} obj - * @param {String} obj.locationWorkspaceId - location of [workspaceId] on request (e.g. params, body) for parsing - * @returns - */ -const requireBlindIndicesEnabled = ({ - locationWorkspaceId -}: { - locationWorkspaceId: req; -}) => { - return async (req: Request, res: Response, next: NextFunction) => { - const workspaceId = req[locationWorkspaceId]?.workspaceId; - - const secretBlindIndexData = await SecretBlindIndexData.exists({ - workspace: new Types.ObjectId(workspaceId) - }); - - if (!secretBlindIndexData) throw UnauthorizedRequestError({ - message: "Failed workspace authorization due to blind indices not being enabled" - }); - - return next(); - } -} - -export default requireBlindIndicesEnabled; \ No newline at end of file diff --git a/backend-mongo/src/middleware/requireE2EEOff.ts b/backend-mongo/src/middleware/requireE2EEOff.ts deleted file mode 100644 index a9e5a735b..000000000 --- a/backend-mongo/src/middleware/requireE2EEOff.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { BadRequestError } from "../utils/errors"; -import { BotService } from "../services"; - -type req = "params" | "body" | "query"; - -/** - * Validate if workspace with [workspaceId] has E2EE off/disabled - * @param {Object} obj - * @param {String} obj.locationWorkspaceId - location of [workspaceId] on request (e.g. params, body) for parsing - * @returns - */ -const requireE2EEOff = ({ - locationWorkspaceId -}: { - locationWorkspaceId: req; -}) => { - return async (req: Request, _: Response, next: NextFunction) => { - const workspaceId = req[locationWorkspaceId]?.workspaceId; - - const isWorkspaceE2EE = await BotService.getIsWorkspaceE2EE(workspaceId); - - if (isWorkspaceE2EE) throw BadRequestError({ - message: "Failed workspace authorization due to end-to-end encryption not being disabled" - }); - - return next(); - } -} - -export default requireE2EEOff; \ No newline at end of file diff --git a/backend-mongo/src/middleware/requireMfaAuth.ts b/backend-mongo/src/middleware/requireMfaAuth.ts deleted file mode 100644 index 9c5313b05..000000000 --- a/backend-mongo/src/middleware/requireMfaAuth.ts +++ /dev/null @@ -1,46 +0,0 @@ -import jwt from "jsonwebtoken"; -import { NextFunction, Request, Response } from "express"; -import { User } from "../models"; -import { BadRequestError, UnauthorizedRequestError } from "../utils/errors"; -import { getAuthSecret } from "../config"; -import { AuthTokenType } from "../variables"; - -declare module "jsonwebtoken" { - export interface UserIDJwtPayload extends jwt.JwtPayload { - userId: string; - } -} - -/** - * Validate if (MFA) JWT temporary token on request is valid (e.g. not expired) - * and if there is an associated user. - */ -const requireMfaAuth = async ( - req: Request, - res: Response, - next: NextFunction -) => { - // JWT (temporary) authentication middleware for complete signup - const [ AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE ] = <[string, string]>req.headers["authorization"]?.split(" ", 2) ?? [null, null] - if(AUTH_TOKEN_TYPE === null) return next(BadRequestError({message: "Missing Authorization Header in the request header."})) - if(AUTH_TOKEN_TYPE.toLowerCase() !== "bearer") return next(BadRequestError({message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.`})) - if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: "Missing Authorization Body in the request header"})) - - const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, await getAuthSecret()) - ); - - if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN) throw UnauthorizedRequestError(); - - const user = await User.findOne({ - _id: decodedToken.userId, - }).select("+publicKey"); - - if (!user) - return next(UnauthorizedRequestError({message: "Unable to authenticate for User account completion. Try logging in again"})) - - req.user = user; - return next(); -}; - -export default requireMfaAuth; diff --git a/backend-mongo/src/middleware/requireSecretAuth.ts b/backend-mongo/src/middleware/requireSecretAuth.ts deleted file mode 100644 index 06ad6019e..000000000 --- a/backend-mongo/src/middleware/requireSecretAuth.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { Types } from "mongoose"; -import { validateClientForSecret } from "../validation"; - -// note: used for old /v1/secret and /v2/secret routes. -// newer /v2/secrets routes use [requireSecretsAuth] middleware with the exception -// of some /ee endpoints - -/** - * Validate if user on request has proper membership to modify secret. - * @param {Object} obj - * @param {String[]} obj.acceptedRoles - accepted workspace roles - * @param {String[]} obj.location - location of [workspaceId] on request (e.g. params, body) for parsing - */ -const requireSecretAuth = ({ - acceptedRoles, - requiredPermissions, -}: { - acceptedRoles: Array<"admin" | "member">; - requiredPermissions: string[]; -}) => { - return async (req: Request, res: Response, next: NextFunction) => { - const { secretId } = req.params; - - const secret = await validateClientForSecret({ - authData: req.authData, - secretId: new Types.ObjectId(secretId), - acceptedRoles, - requiredPermissions, - }); - - req._secret = secret; - - next(); - } -} - -export default requireSecretAuth; \ No newline at end of file diff --git a/backend-mongo/src/middleware/requireSecretsAuth.ts b/backend-mongo/src/middleware/requireSecretsAuth.ts deleted file mode 100644 index 3dabdb25c..000000000 --- a/backend-mongo/src/middleware/requireSecretsAuth.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { Types } from "mongoose"; -import { validateClientForSecrets } from "../validation"; - -const requireSecretsAuth = ({ - acceptedRoles, - requiredPermissions = [], -}: { - acceptedRoles: string[]; - requiredPermissions?: string[]; -}) => { - return async (req: Request, res: Response, next: NextFunction) => { - let secretIds = []; - if (Array.isArray(req.body.secrets)) { - secretIds = req.body.secrets.map((s: any) => s.id); - } else if (typeof req.body.secrets === "object") { - secretIds = [req.body.secrets.id]; - } else if (Array.isArray(req.body.secretIds)) { - secretIds = req.body.secretIds; - } else if (typeof req.body.secretIds === "string") { - secretIds = [req.body.secretIds]; - } - - req.secrets = await validateClientForSecrets({ - authData: req.authData, - secretIds: secretIds.map((secretId: string) => new Types.ObjectId(secretId)), - requiredPermissions, - }); - - return next(); - } -} - -export default requireSecretsAuth; \ No newline at end of file diff --git a/backend-mongo/src/middleware/requireServiceTokenAuth.ts b/backend-mongo/src/middleware/requireServiceTokenAuth.ts deleted file mode 100644 index 340f03cc0..000000000 --- a/backend-mongo/src/middleware/requireServiceTokenAuth.ts +++ /dev/null @@ -1,51 +0,0 @@ -import jwt from "jsonwebtoken"; -import { NextFunction, Request, Response } from "express"; -import { ServiceToken } from "../models"; -import { BadRequestError, UnauthorizedRequestError } from "../utils/errors"; -import { getJwtServiceSecret } from "../config"; - -// TODO: deprecate -declare module "jsonwebtoken" { - export interface UserIDJwtPayload extends jwt.JwtPayload { - userId: string; - } -} - -/** - * Validate if JWT (service) token on request is valid (e.g. not expired), - * and if there is an associated service token - * @param req - express request object - * @param res - express response object - * @param next - express next function - * @returns - */ -const requireServiceTokenAuth = async ( - req: Request, - res: Response, - next: NextFunction -) => { - // JWT service token middleware - - const [ AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE ] = <[string, string]>req.headers["authorization"]?.split(" ", 2) ?? [null, null] - if(AUTH_TOKEN_TYPE === null) return next(BadRequestError({message: "Missing Authorization Header in the request header."})) - //TODO: Determine what is the actual Token Type for Service Token Authentication (ex. Bearer) - //if(AUTH_TOKEN_TYPE.toLowerCase() !== 'bearer') return next(UnauthorizedRequestError({message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.`})) - if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: "Missing Authorization Body in the request header"})) - - const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, await getJwtServiceSecret()) - ); - - const serviceToken = await ServiceToken.findOne({ - _id: decodedToken.serviceTokenId, - }) - .populate("user", "+publicKey") - .select("+encryptedKey +publicKey +nonce"); - - if (!serviceToken) return next(UnauthorizedRequestError({message: "The service token does not match the record in the database"})) - - req.serviceToken = serviceToken; - return next(); -}; - -export default requireServiceTokenAuth; diff --git a/backend-mongo/src/middleware/requireSignupAuth.ts b/backend-mongo/src/middleware/requireSignupAuth.ts deleted file mode 100644 index 510cb3d03..000000000 --- a/backend-mongo/src/middleware/requireSignupAuth.ts +++ /dev/null @@ -1,47 +0,0 @@ -import jwt from "jsonwebtoken"; -import { NextFunction, Request, Response } from "express"; -import { User } from "../models"; -import { BadRequestError, UnauthorizedRequestError } from "../utils/errors"; -import { getAuthSecret } from "../config"; -import { AuthTokenType } from "../variables"; - -declare module "jsonwebtoken" { - export interface UserIDJwtPayload extends jwt.JwtPayload { - userId: string; - } -} - -/** - * Validate if JWT temporary token on request is valid (e.g. not expired) - * and if there is an associated user. - */ -const requireSignupAuth = async ( - req: Request, - res: Response, - next: NextFunction -) => { - // JWT (temporary) authentication middleware for complete signup - - const [ AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE ] = <[string, string]>req.headers["authorization"]?.split(" ", 2) ?? [null, null] - if(AUTH_TOKEN_TYPE === null) return next(BadRequestError({message: "Missing Authorization Header in the request header."})) - if(AUTH_TOKEN_TYPE.toLowerCase() !== "bearer") return next(BadRequestError({message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.`})) - if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: "Missing Authorization Body in the request header"})) - - const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, await getAuthSecret()) - ); - - if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw UnauthorizedRequestError(); - - const user = await User.findOne({ - _id: decodedToken.userId, - }).select("+publicKey"); - - if (!user) - return next(UnauthorizedRequestError({message: "Unable to authenticate for User account completion. Try logging in again"})) - - req.user = user; - return next(); -}; - -export default requireSignupAuth; diff --git a/backend-mongo/src/middleware/requireSuperAdminAccess.ts b/backend-mongo/src/middleware/requireSuperAdminAccess.ts deleted file mode 100644 index 4445433ff..000000000 --- a/backend-mongo/src/middleware/requireSuperAdminAccess.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { UnauthorizedRequestError } from "../utils/errors"; - -export const requireSuperAdminAccess = (req: Request, _res: Response, next: NextFunction) => { - const isSuperAdmin = req.user.superAdmin; - if (!isSuperAdmin) throw UnauthorizedRequestError({ message: "Requires superadmin access" }); - return next(); -}; diff --git a/backend-mongo/src/middleware/requireWorkspaceAuth.ts b/backend-mongo/src/middleware/requireWorkspaceAuth.ts deleted file mode 100644 index bbf829565..000000000 --- a/backend-mongo/src/middleware/requireWorkspaceAuth.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { Types } from "mongoose"; -import { validateClientForWorkspace } from "../validation"; - -type req = "params" | "body" | "query"; - -/** - * Validate if user on request is a member with proper roles for workspace - * on request params. - * @param {Object} obj - * @param {String[]} obj.acceptedRoles - accepted workspace roles for JWT auth - * @param {String} obj.locationWorkspaceId - location of [workspaceId] on request (e.g. params, body) for parsing - */ -const requireWorkspaceAuth = ({ - acceptedRoles, - locationWorkspaceId, - locationEnvironment = undefined, - requiredPermissions = [], -}: { - acceptedRoles: Array<"admin" | "member">; - locationWorkspaceId: req; - locationEnvironment?: req | undefined; - requiredPermissions?: string[]; -}) => { - return async (req: Request, res: Response, next: NextFunction) => { - const workspaceId = req[locationWorkspaceId]?.workspaceId; - const environment = locationEnvironment ? req[locationEnvironment]?.environment : undefined; - - // validate clients - const { membership, workspace } = await validateClientForWorkspace({ - authData: req.authData, - workspaceId: new Types.ObjectId(workspaceId), - environment, - acceptedRoles, - requiredPermissions - }); - - if (membership) { - req.membership = membership; - } - - if (workspace) { - req.workspace = workspace; - } - - return next(); - }; -}; - -export default requireWorkspaceAuth; diff --git a/backend-mongo/src/middleware/serverAdmin.ts b/backend-mongo/src/middleware/serverAdmin.ts deleted file mode 100644 index d57dbf714..000000000 --- a/backend-mongo/src/middleware/serverAdmin.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { getServerConfig } from "../config/serverConfig"; -import { BadRequestError } from "../utils/errors"; - -export const disableSignUpByServerCfg = (_req: Request, _res: Response, next: NextFunction) => { - const cfg = getServerConfig(); - if (!cfg.allowSignUp) throw BadRequestError({ message: "Signup are disabled" }); - return next(); -}; diff --git a/backend-mongo/src/middleware/validateRequest.ts b/backend-mongo/src/middleware/validateRequest.ts deleted file mode 100644 index 56ea2653c..000000000 --- a/backend-mongo/src/middleware/validateRequest.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { validationResult } from "express-validator"; -import { UnauthorizedRequestError, ValidationError } from "../utils/errors"; - -/** - * Validate intended inputs on [req] via express-validator - * @param req - express request object - * @param res - express response object - * @param next - express next function - * @returns - */ -const validate = (req: Request, res: Response, next: NextFunction) => { - // express validator middleware - - try { - const errors = validationResult(req); - if (!errors.isEmpty()) { - return next(ValidationError({ context: { errors: `One or more of your parameters are invalid [error(s)=${(JSON.stringify(errors))}]` } })) - } - - return next(); - } catch (err) { - return next(UnauthorizedRequestError({ message: "Unauthenticated requests are not allowed. Try logging in" })) - } -}; - -export default validate; diff --git a/backend-mongo/src/models/apiKeyData.ts b/backend-mongo/src/models/apiKeyData.ts deleted file mode 100644 index 0b88c5ddb..000000000 --- a/backend-mongo/src/models/apiKeyData.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface IAPIKeyData { - name: string; - user: Types.ObjectId; - lastUsed: Date; - expiresAt: Date; - secretHash: string; -} - -const apiKeyDataSchema = new Schema( - { - name: { - type: String, - required: true, - }, - user: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - lastUsed: { - type: Date, - }, - expiresAt: { - type: Date, - }, - secretHash: { - type: String, - required: true, - select: false, - }, - }, - { - timestamps: true, - } -); - -export const APIKeyData = model("APIKeyData", apiKeyDataSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/apiKeyDataV2.ts b/backend-mongo/src/models/apiKeyDataV2.ts deleted file mode 100644 index 6775a0878..000000000 --- a/backend-mongo/src/models/apiKeyDataV2.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; - -export interface IAPIKeyDataV2 extends Document { - _id: Types.ObjectId; - name: string; - user: Types.ObjectId; - lastUsed?: Date - usageCount: number; - expiresAt?: Date; -} - -const apiKeyDataV2Schema = new Schema( - { - name: { - type: String, - required: true - }, - user: { - type: Schema.Types.ObjectId, - ref: "User", - required: true - }, - lastUsed: { - type: Date, - required: false - }, - usageCount: { - type: Number, - default: 0, - required: true - } - }, - { - timestamps: true - } -); - -export const APIKeyDataV2 = model("APIKeyDataV2", apiKeyDataV2Schema); \ No newline at end of file diff --git a/backend-mongo/src/models/backupPrivateKey.ts b/backend-mongo/src/models/backupPrivateKey.ts deleted file mode 100644 index 09df1dda7..000000000 --- a/backend-mongo/src/models/backupPrivateKey.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, -} from "../variables"; - -export interface IBackupPrivateKey { - _id: Types.ObjectId; - user: Types.ObjectId; - encryptedPrivateKey: string; - iv: string; - tag: string; - salt: string; - algorithm: string; - keyEncoding: "base64" | "utf8"; - verifier: string; -} - -const backupPrivateKeySchema = new Schema( - { - user: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - encryptedPrivateKey: { - type: String, - select: false, - required: true, - }, - iv: { - type: String, - select: false, - required: true, - }, - tag: { - type: String, - select: false, - required: true, - }, - algorithm: { // the encryption algorithm used - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true, - }, - keyEncoding: { - type: String, - enum: [ - ENCODING_SCHEME_UTF8, - ENCODING_SCHEME_BASE64, - ], - required: true, - }, - salt: { - type: String, - select: false, - required: true, - }, - verifier: { - type: String, - select: false, - required: true, - }, - }, - { - timestamps: true, - } -); - -export const BackupPrivateKey = model( - "BackupPrivateKey", - backupPrivateKeySchema -); diff --git a/backend-mongo/src/models/bot.ts b/backend-mongo/src/models/bot.ts deleted file mode 100644 index 5a5c83b13..000000000 --- a/backend-mongo/src/models/bot.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, -} from "../variables"; - -export interface IBot { - _id: Types.ObjectId; - name: string; - workspace: Types.ObjectId; - isActive: boolean; - publicKey: string; - encryptedPrivateKey: string; - iv: string; - tag: string; - algorithm: "aes-256-gcm"; - keyEncoding: "base64" | "utf8"; -} - -const botSchema = new Schema( - { - name: { - type: String, - required: true, - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - }, - isActive: { - type: Boolean, - required: true, - default: false, - }, - publicKey: { - type: String, - required: true, - }, - encryptedPrivateKey: { - type: String, - required: true, - select: false, - }, - iv: { - type: String, - required: true, - select: false, - }, - tag: { - type: String, - required: true, - select: false, - }, - algorithm: { // the encryption algorithm used - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true, - select: false, - }, - keyEncoding: { - type: String, - enum: [ - ENCODING_SCHEME_UTF8, - ENCODING_SCHEME_BASE64, - ], - required: true, - select: false, - }, - }, - { - timestamps: true, - } -); - -export const Bot = model("Bot", botSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/botKey.ts b/backend-mongo/src/models/botKey.ts deleted file mode 100644 index 02a6d6ea9..000000000 --- a/backend-mongo/src/models/botKey.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface IBotKey { - _id: Types.ObjectId; - encryptedKey: string; - nonce: string; - sender: Types.ObjectId; - bot: Types.ObjectId; - workspace: Types.ObjectId; -} - -const botKeySchema = new Schema( - { - encryptedKey: { - type: String, - required: true, - }, - nonce: { - type: String, - required: true, - }, - sender: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - bot: { - type: Schema.Types.ObjectId, - ref: "Bot", - required: true, - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - }, - }, - { - timestamps: true, - } -); - -export const BotKey = model("BotKey", botKeySchema); \ No newline at end of file diff --git a/backend-mongo/src/models/botOrg.ts b/backend-mongo/src/models/botOrg.ts deleted file mode 100644 index 177294ef9..000000000 --- a/backend-mongo/src/models/botOrg.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, -} from "../variables"; - -export interface IBotOrg { - _id: Types.ObjectId; - name: string; - organization: Types.ObjectId; - publicKey: string; - encryptedSymmetricKey: string; - symmetricKeyIV: string; - symmetricKeyTag: string; - symmetricKeyAlgorithm: "aes-256-gcm"; - symmetricKeyKeyEncoding: "base64" | "utf8"; - encryptedPrivateKey: string; - privateKeyIV: string; - privateKeyTag: string; - privateKeyAlgorithm: "aes-256-gcm"; - privateKeyKeyEncoding: "base64" | "utf8"; -} - -const botOrgSchema = new Schema( - { - name: { - type: String, - required: true, - }, - organization: { - type: Schema.Types.ObjectId, - ref: "Organization", - required: true, - }, - publicKey: { - type: String, - required: true, - }, - encryptedSymmetricKey: { - type: String, - required: true - }, - symmetricKeyIV: { - type: String, - required: true - }, - symmetricKeyTag: { - type: String, - required: true - }, - symmetricKeyAlgorithm: { - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true - }, - symmetricKeyKeyEncoding: { - type: String, - enum: [ - ENCODING_SCHEME_UTF8, - ENCODING_SCHEME_BASE64, - ], - required: true - }, - encryptedPrivateKey: { - type: String, - required: true - }, - privateKeyIV: { - type: String, - required: true - }, - privateKeyTag: { - type: String, - required: true - }, - privateKeyAlgorithm: { - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true - }, - privateKeyKeyEncoding: { - type: String, - enum: [ - ENCODING_SCHEME_UTF8, - ENCODING_SCHEME_BASE64, - ], - required: true - }, - }, - { - timestamps: true, - } -); - -export const BotOrg = model("BotOrg", botOrgSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/folder.ts b/backend-mongo/src/models/folder.ts deleted file mode 100644 index b3016822d..000000000 --- a/backend-mongo/src/models/folder.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export type TFolderRootSchema = { - _id: Types.ObjectId; - workspace: Types.ObjectId; - environment: string; - nodes: TFolderSchema; -}; - -export type TFolderSchema = { - id: string; - name: string; - version: number; - children: TFolderSchema[]; -}; - -const folderSchema = new Schema({ - id: { - required: true, - type: String, - }, - version: { - required: true, - type: Number, - default: 1, - }, - name: { - required: true, - type: String, - default: "root", - }, -}); - -folderSchema.add({ children: [folderSchema] }); - -const folderRootSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - }, - environment: { - type: String, - required: true, - }, - nodes: folderSchema, - }, - { - timestamps: true, - } -); - -export const Folder = model("Folder", folderRootSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/identity.ts b/backend-mongo/src/models/identity.ts deleted file mode 100644 index ec4948e1b..000000000 --- a/backend-mongo/src/models/identity.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; -import { IPType } from "../ee/models"; - -export interface IIdentityTrustedIp { - ipAddress: string; - type: IPType; - prefix: number; -} - -export enum IdentityAuthMethod { - UNIVERSAL_AUTH = "universal-auth" -} - -export interface IIdentity extends Document { - _id: Types.ObjectId; - name: string; - authMethod?: IdentityAuthMethod; -} - -const identitySchema = new Schema( - { - name: { - type: String, - required: true - }, - authMethod: { - type: String, - enum: IdentityAuthMethod, - required: false, - }, - - }, - { - timestamps: true - } -); - -export const Identity = model("Identity", identitySchema); diff --git a/backend-mongo/src/models/identityAccessToken.ts b/backend-mongo/src/models/identityAccessToken.ts deleted file mode 100644 index 82b2e6778..000000000 --- a/backend-mongo/src/models/identityAccessToken.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; -import { IIdentityTrustedIp } from "./identity"; -import { IPType } from "../ee/models/trustedIp"; - -export interface IIdentityAccessToken extends Document { - _id: Types.ObjectId; - identity: Types.ObjectId; - identityUniversalAuthClientSecret?: Types.ObjectId; - accessTokenLastUsedAt?: Date; - accessTokenLastRenewedAt?: Date; - accessTokenNumUses: number; - accessTokenNumUsesLimit: number; - accessTokenTTL: number; - accessTokenMaxTTL: number; - accessTokenTrustedIps: Array; - isAccessTokenRevoked: boolean; - updatedAt: Date; - createdAt: Date; -} - -const identityAccessTokenSchema = new Schema( - { - identity: { - type: Schema.Types.ObjectId, - ref: "Identity", - required: false - }, - identityUniversalAuthClientSecret: { - type: Schema.Types.ObjectId, - ref: "IdentityUniversalAuthClientSecret", - required: false - }, - accessTokenLastUsedAt: { - type: Date, - required: false - }, - accessTokenLastRenewedAt: { - type: Date, - required: false - }, - accessTokenNumUses: { - // number of times access token has been used - type: Number, - default: 0, - required: true - }, - accessTokenNumUsesLimit: { - // number of times access token can be used for - type: Number, - default: 0, // default: used as many times as needed - required: true - }, - accessTokenTTL: { // seconds - // incremental lifetime - type: Number, - default: 2592000, // 30 days - required: true - }, - accessTokenMaxTTL: { // seconds - // max lifetime - type: Number, - default: 2592000, // 30 days - required: true - }, - accessTokenTrustedIps: { - type: [ - { - ipAddress: { - type: String, - required: true - }, - type: { - type: String, - enum: [ - IPType.IPV4, - IPType.IPV6 - ], - required: true - }, - prefix: { - type: Number, - required: false - } - } - ], - default: [{ - ipAddress: "0.0.0.0", - type: IPType.IPV4.toString(), - prefix: 0 - }], - required: true - }, - isAccessTokenRevoked: { - type: Boolean, - default: false, - required: true - }, - }, - { - timestamps: true - } -); - -export const IdentityAccessToken = model("IdentityAccessToken", identityAccessTokenSchema); diff --git a/backend-mongo/src/models/identityMembership.ts b/backend-mongo/src/models/identityMembership.ts deleted file mode 100644 index 4fedfe909..000000000 --- a/backend-mongo/src/models/identityMembership.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../variables"; - -export interface IIdentityMembership { - _id: Types.ObjectId; - identity: Types.ObjectId; - workspace: Types.ObjectId; - role: "admin" | "member" | "viewer" | "no-access" | "custom"; - customRole: Types.ObjectId; -} - -const identityMembershipSchema = new Schema( - { - identity: { - type: Schema.Types.ObjectId, - ref: "Identity" - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - index: true, - }, - role: { - type: String, - enum: [ADMIN, MEMBER, VIEWER, CUSTOM, NO_ACCESS], - required: true - }, - customRole: { - type: Schema.Types.ObjectId, - ref: "Role" - } - }, - { - timestamps: true - } -); - -export const IdentityMembership = model("IdentityMembership", identityMembershipSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/identityMembershipOrg.ts b/backend-mongo/src/models/identityMembershipOrg.ts deleted file mode 100644 index 8da8693c4..000000000 --- a/backend-mongo/src/models/identityMembershipOrg.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { ADMIN, CUSTOM, MEMBER, NO_ACCESS} from "../variables"; - -export interface IIdentityMembershipOrg { - _id: Types.ObjectId; - identity: Types.ObjectId; - organization: Types.ObjectId; - role: "admin" | "member" | "no-access" | "custom"; - customRole: Types.ObjectId; -} - -const identityMembershipOrgSchema = new Schema( - { - identity: { - type: Schema.Types.ObjectId, - ref: "Identity" - }, - organization: { - type: Schema.Types.ObjectId, - ref: "Organization" - }, - role: { - type: String, - enum: [ADMIN, MEMBER, NO_ACCESS, CUSTOM], - required: true - }, - customRole: { - type: Schema.Types.ObjectId, - ref: "Role" - } - }, - { - timestamps: true - } -); - -export const IdentityMembershipOrg = model("IdentityMembershipOrg", identityMembershipOrgSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/identityUniversalAuth.ts b/backend-mongo/src/models/identityUniversalAuth.ts deleted file mode 100644 index 89fb46a95..000000000 --- a/backend-mongo/src/models/identityUniversalAuth.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; -import { IPType } from "../ee/models"; -import { IIdentityTrustedIp } from "./identity"; - -export interface IIdentityUniversalAuth extends Document { - _id: Types.ObjectId; - identity: Types.ObjectId; - clientId: string; - clientSecretTrustedIps: Array; - accessTokenTTL: number; - accessTokenMaxTTL: number; - accessTokenNumUsesLimit: number; - accessTokenTrustedIps: Array; -} - -const identityUniversalAuthSchema = new Schema( - { - identity: { - type: Schema.Types.ObjectId, - ref: "Identity", - required: true - }, - clientId: { - type: String, - required: true - }, - clientSecretTrustedIps: { - type: [ - { - ipAddress: { - type: String, - required: true - }, - type: { - type: String, - enum: [ - IPType.IPV4, - IPType.IPV6 - ], - required: true - }, - prefix: { - type: Number, - required: false - } - } - ], - default: [{ - ipAddress: "0.0.0.0", - type: IPType.IPV4.toString(), - prefix: 0 - }], - required: true - }, - accessTokenTTL: { // seconds - // incremental lifetime - type: Number, - default: 7200, - required: true - }, - accessTokenMaxTTL: { // seconds - // max lifetime - type: Number, - default: 7200, - required: true - }, - accessTokenNumUsesLimit: { - // number of times access token can be used for - type: Number, - default: 0, // default: used as many times as needed - required: true - }, - accessTokenTrustedIps: { - type: [ - { - ipAddress: { - type: String, - required: true - }, - type: { - type: String, - enum: [ - IPType.IPV4, - IPType.IPV6 - ], - required: true - }, - prefix: { - type: Number, - required: false - } - } - ], - default: [{ - ipAddress: "0.0.0.0", - type: IPType.IPV4.toString(), - prefix: 0 - }], - required: true - } - }, - { - timestamps: true - } -); - -export const IdentityUniversalAuth = model("IdentityUniversalAuth", identityUniversalAuthSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/identityUniversalAuthClientSecret.ts b/backend-mongo/src/models/identityUniversalAuthClientSecret.ts deleted file mode 100644 index af9cc08a4..000000000 --- a/backend-mongo/src/models/identityUniversalAuthClientSecret.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; - -export interface IIdentityUniversalAuthClientSecret extends Document { - _id: Types.ObjectId; - identity: Types.ObjectId; - identityUniversalAuth : Types.ObjectId; - description: string; - clientSecretPrefix: string; - clientSecretHash: string; - clientSecretLastUsedAt?: Date; - clientSecretNumUses: number; - clientSecretNumUsesLimit: number; - clientSecretTTL: number; - updatedAt: Date; - createdAt: Date; - isClientSecretRevoked: boolean; -} - -const identityUniversalAuthClientSecretSchema = new Schema( - { - identity: { - type: Schema.Types.ObjectId, - ref: "Identity", - required: true - }, - identityUniversalAuth: { - type: Schema.Types.ObjectId, - ref: "IdentityUniversalAuth", - required: true - }, - description: { - type: String, - required: true - }, - clientSecretPrefix: { - type: String, - required: true - }, - clientSecretHash: { - type: String, - required: true - }, - clientSecretLastUsedAt: { - type: Date, - required: false - }, - clientSecretNumUses: { - // number of times client secret has been used - // in login operation - type: Number, - default: 0, - required: true - }, - clientSecretNumUsesLimit: { - // number of times client secret can be used for - // a login operation - type: Number, - default: 0, // default: used as many times as needed - required: true - }, - clientSecretTTL: { - type: Number, - default: 0, // default: does not expire - required: true - }, - isClientSecretRevoked: { - type: Boolean, - default: false, - required: true - } - }, - { - timestamps: true - } -); - -identityUniversalAuthClientSecretSchema.index( - { identityUniversalAuth: 1, isClientSecretRevoked: 1 } -); - -export const IdentityUniversalAuthClientSecret = model("IdentityUniversalAuthClientSecret", identityUniversalAuthClientSecretSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/incidentContactOrg.ts b/backend-mongo/src/models/incidentContactOrg.ts deleted file mode 100644 index 905b9263f..000000000 --- a/backend-mongo/src/models/incidentContactOrg.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface IIncidentContactOrg { - _id: Types.ObjectId; - email: string; - organization: Types.ObjectId; -} - -const incidentContactOrgSchema = new Schema( - { - email: { - type: String, - required: true, - }, - organization: { - type: Schema.Types.ObjectId, - ref: "Organization", - required: true, - }, - }, - { - timestamps: true, - } -); - -export const IncidentContactOrg = model( - "IncidentContactOrg", - incidentContactOrgSchema -); \ No newline at end of file diff --git a/backend-mongo/src/models/index.ts b/backend-mongo/src/models/index.ts deleted file mode 100644 index 9d20ea67a..000000000 --- a/backend-mongo/src/models/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -export * from "./backupPrivateKey"; -export * from "./bot"; -export * from "./botOrg"; -export * from "./botKey"; -export * from "./incidentContactOrg"; -export * from "./integration/integration"; -export * from "./integrationAuth"; -export * from "./key"; -export * from "./membership"; -export * from "./membershipOrg"; -export * from "./organization"; -export * from "./secret"; -export * from "./tag"; -export * from "./folder"; -export * from "./secretImports"; -export * from "./secretBlindIndexData"; -export * from "./serviceToken"; // TODO: deprecate -export * from "./tokenData"; -export * from "./user"; -export * from "./userAction"; -export * from "./workspace"; -export * from "./serviceTokenData"; // TODO: deprecate - -// new -export * from "./identity"; -export * from "./identityMembership"; -export * from "./identityMembershipOrg"; -export * from "./identityUniversalAuth"; -export * from "./identityUniversalAuthClientSecret"; -export * from "./identityAccessToken"; - -export * from "./apiKeyData"; // TODO: deprecate -export * from "./apiKeyDataV2"; -export * from "./loginSRPDetail"; -export * from "./tokenVersion"; -export * from "./webhooks"; diff --git a/backend-mongo/src/models/integration/index.ts b/backend-mongo/src/models/integration/index.ts deleted file mode 100644 index 2ed44cd28..000000000 --- a/backend-mongo/src/models/integration/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./integration"; \ No newline at end of file diff --git a/backend-mongo/src/models/integration/integration.ts b/backend-mongo/src/models/integration/integration.ts deleted file mode 100644 index eaaadec92..000000000 --- a/backend-mongo/src/models/integration/integration.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { - INTEGRATION_AWS_PARAMETER_STORE, - INTEGRATION_AWS_SECRET_MANAGER, - INTEGRATION_AZURE_KEY_VAULT, - INTEGRATION_BITBUCKET, - INTEGRATION_CHECKLY, - INTEGRATION_CIRCLECI, - INTEGRATION_CLOUDFLARE_PAGES, - INTEGRATION_CLOUDFLARE_WORKERS, - INTEGRATION_CLOUD_66, - INTEGRATION_CODEFRESH, - INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM, - INTEGRATION_FLYIO, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_GITHUB, - INTEGRATION_GITLAB, - INTEGRATION_HASHICORP_VAULT, - INTEGRATION_HASURA_CLOUD, - INTEGRATION_HEROKU, - INTEGRATION_LARAVELFORGE, - INTEGRATION_NETLIFY, - INTEGRATION_NORTHFLANK, - INTEGRATION_QOVERY, - INTEGRATION_RAILWAY, - INTEGRATION_RENDER, - INTEGRATION_SUPABASE, - INTEGRATION_TEAMCITY, - INTEGRATION_TERRAFORM_CLOUD, - INTEGRATION_TRAVISCI, - INTEGRATION_VERCEL, - INTEGRATION_WINDMILL -} from "../../variables"; -import { Schema, Types, model } from "mongoose"; -import { Metadata } from "./types"; - -export interface IIntegration { - _id: Types.ObjectId; - workspace: Types.ObjectId; - environment: string; - isActive: boolean; - url: string; - app: string; - appId: string; - owner: string; - targetEnvironment: string; - targetEnvironmentId: string; - targetService: string; - targetServiceId: string; - path: string; - region: string; - scope: string; - secretPath: string; - integration: - | "azure-key-vault" - | "aws-parameter-store" - | "aws-secret-manager" - | "heroku" - | "vercel" - | "netlify" - | "github" - | "gitlab" - | "render" - | "railway" - | "flyio" - | "circleci" - | "laravel-forge" - | "travisci" - | "supabase" - | "checkly" - | "qovery" - | "terraform-cloud" - | "teamcity" - | "hashicorp-vault" - | "cloudflare-pages" - | "cloudflare-workers" - | "bitbucket" - | "codefresh" - | "digital-ocean-app-platform" - | "cloud-66" - | "northflank" - | "windmill" - | "gcp-secret-manager" - | "hasura-cloud"; - integrationAuth: Types.ObjectId; - metadata: Metadata; -} - -const integrationSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - environment: { - type: String, - required: true - }, - isActive: { - type: Boolean, - required: true - }, - url: { - // for custom self-hosted integrations (e.g. self-hosted GitHub enterprise) - type: String, - default: null - }, - app: { - // name of app in provider - type: String, - default: null - }, - appId: { - // id of app in provider - type: String, - default: null - }, - targetEnvironment: { - // target environment - type: String, - default: null - }, - targetEnvironmentId: { - type: String, - default: null - }, - targetService: { - // railway-specific service - // qovery-specific project - type: String, - default: null - }, - targetServiceId: { - // railway-specific service - // qovery specific project - type: String, - default: null - }, - owner: { - // github-specific repo owner-login - type: String, - default: null - }, - path: { - // aws-parameter-store-specific path - // (also) vercel preview-branch - type: String, - default: null - }, - region: { - // aws-parameter-store-specific path - type: String, - default: null - }, - scope: { - // qovery-specific scope - type: String, - default: null - }, - integration: { - type: String, - enum: [ - INTEGRATION_AZURE_KEY_VAULT, - INTEGRATION_AWS_PARAMETER_STORE, - INTEGRATION_AWS_SECRET_MANAGER, - INTEGRATION_HEROKU, - INTEGRATION_VERCEL, - INTEGRATION_NETLIFY, - INTEGRATION_GITHUB, - INTEGRATION_GITLAB, - INTEGRATION_RENDER, - INTEGRATION_RAILWAY, - INTEGRATION_FLYIO, - INTEGRATION_CIRCLECI, - INTEGRATION_LARAVELFORGE, - INTEGRATION_TRAVISCI, - INTEGRATION_SUPABASE, - INTEGRATION_CHECKLY, - INTEGRATION_QOVERY, - INTEGRATION_TERRAFORM_CLOUD, - INTEGRATION_TEAMCITY, - INTEGRATION_HASHICORP_VAULT, - INTEGRATION_CLOUDFLARE_PAGES, - INTEGRATION_CLOUDFLARE_WORKERS, - INTEGRATION_CODEFRESH, - INTEGRATION_WINDMILL, - INTEGRATION_BITBUCKET, - INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM, - INTEGRATION_CLOUD_66, - INTEGRATION_NORTHFLANK, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_HASURA_CLOUD - ], - required: true - }, - integrationAuth: { - type: Schema.Types.ObjectId, - ref: "IntegrationAuth", - required: true - }, - secretPath: { - type: String, - required: true, - default: "/" - }, - metadata: { - type: Schema.Types.Mixed, - default: {} - } - }, - { - timestamps: true - } -); - -export const Integration = model("Integration", integrationSchema); diff --git a/backend-mongo/src/models/integration/types.ts b/backend-mongo/src/models/integration/types.ts deleted file mode 100644 index 5c4387bba..000000000 --- a/backend-mongo/src/models/integration/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type Metadata = { - secretPrefix?: string; - secretSuffix?: string; - secretGCPLabel?: { - labelName: string; - labelValue: string; - } -} \ No newline at end of file diff --git a/backend-mongo/src/models/integrationAuth/index.ts b/backend-mongo/src/models/integrationAuth/index.ts deleted file mode 100644 index 157095bd2..000000000 --- a/backend-mongo/src/models/integrationAuth/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./integrationAuth"; \ No newline at end of file diff --git a/backend-mongo/src/models/integrationAuth/integrationAuth.ts b/backend-mongo/src/models/integrationAuth/integrationAuth.ts deleted file mode 100644 index da1e57268..000000000 --- a/backend-mongo/src/models/integrationAuth/integrationAuth.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, - INTEGRATION_AWS_PARAMETER_STORE, - INTEGRATION_AWS_SECRET_MANAGER, - INTEGRATION_AZURE_KEY_VAULT, - INTEGRATION_BITBUCKET, - INTEGRATION_CIRCLECI, - INTEGRATION_CLOUDFLARE_PAGES, - INTEGRATION_CLOUDFLARE_WORKERS, - INTEGRATION_CLOUD_66, - INTEGRATION_CODEFRESH, - INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM, - INTEGRATION_FLYIO, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_GITHUB, - INTEGRATION_GITLAB, - INTEGRATION_HASHICORP_VAULT, - INTEGRATION_HASURA_CLOUD, - INTEGRATION_HEROKU, - INTEGRATION_LARAVELFORGE, - INTEGRATION_NETLIFY, - INTEGRATION_NORTHFLANK, - INTEGRATION_RAILWAY, - INTEGRATION_RENDER, - INTEGRATION_SUPABASE, - INTEGRATION_TEAMCITY, - INTEGRATION_TERRAFORM_CLOUD, - INTEGRATION_TRAVISCI, - INTEGRATION_VERCEL, - INTEGRATION_WINDMILL -} from "../../variables"; -import { Document, Schema, Types, model } from "mongoose"; -import { IntegrationAuthMetadata } from "./types"; - -export interface IIntegrationAuth extends Document { - _id: Types.ObjectId; - workspace: Types.ObjectId; - integration: - | "heroku" - | "vercel" - | "netlify" - | "github" - | "gitlab" - | "render" - | "railway" - | "flyio" - | "azure-key-vault" - | "laravel-forge" - | "circleci" - | "travisci" - | "supabase" - | "aws-parameter-store" - | "aws-secret-manager" - | "checkly" - | "qovery" - | "cloudflare-pages" - | "cloudflare-workers" - | "codefresh" - | "digital-ocean-app-platform" - | "bitbucket" - | "cloud-66" - | "terraform-cloud" - | "teamcity" - | "northflank" - | "windmill" - | "gcp-secret-manager" - | "hasura-cloud"; - teamId: string; - accountId: string; - url: string; - namespace: string; - refreshCiphertext?: string; - refreshIV?: string; - refreshTag?: string; - accessIdCiphertext?: string; - accessIdIV?: string; - accessIdTag?: string; - accessCiphertext?: string; - accessIV?: string; - accessTag?: string; - algorithm?: "aes-256-gcm"; - keyEncoding?: "utf8" | "base64"; - accessExpiresAt?: Date; - metadata?: IntegrationAuthMetadata; -} - -const integrationAuthSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - integration: { - type: String, - enum: [ - INTEGRATION_AZURE_KEY_VAULT, - INTEGRATION_AWS_PARAMETER_STORE, - INTEGRATION_AWS_SECRET_MANAGER, - INTEGRATION_HEROKU, - INTEGRATION_VERCEL, - INTEGRATION_NETLIFY, - INTEGRATION_GITHUB, - INTEGRATION_GITLAB, - INTEGRATION_RENDER, - INTEGRATION_RAILWAY, - INTEGRATION_FLYIO, - INTEGRATION_CIRCLECI, - INTEGRATION_LARAVELFORGE, - INTEGRATION_TRAVISCI, - INTEGRATION_TEAMCITY, - INTEGRATION_SUPABASE, - INTEGRATION_TERRAFORM_CLOUD, - INTEGRATION_HASHICORP_VAULT, - INTEGRATION_CLOUDFLARE_PAGES, - INTEGRATION_CLOUDFLARE_WORKERS, - INTEGRATION_CODEFRESH, - INTEGRATION_WINDMILL, - INTEGRATION_BITBUCKET, - INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM, - INTEGRATION_CLOUD_66, - INTEGRATION_NORTHFLANK, - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_HASURA_CLOUD - ], - required: true - }, - teamId: { - // vercel-specific integration param - type: String - }, - url: { - // for any self-hosted integrations (e.g. self-hosted hashicorp-vault) - type: String - }, - namespace: { - // hashicorp-vault-specific integration param - type: String - }, - accountId: { - // netlify-specific integration param - type: String - }, - refreshCiphertext: { - type: String, - select: false - }, - refreshIV: { - type: String, - select: false - }, - refreshTag: { - type: String, - select: false - }, - accessIdCiphertext: { - type: String, - select: false - }, - accessIdIV: { - type: String, - select: false - }, - accessIdTag: { - type: String, - select: false - }, - accessCiphertext: { - type: String, - select: false - }, - accessIV: { - type: String, - select: false - }, - accessTag: { - type: String, - select: false - }, - accessExpiresAt: { - type: Date, - select: false - }, - algorithm: { - // the encryption algorithm used - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true - }, - keyEncoding: { - type: String, - enum: [ENCODING_SCHEME_UTF8, ENCODING_SCHEME_BASE64], - required: true - }, - metadata: { - type: Schema.Types.Mixed - } - }, - { - timestamps: true - } -); - -export const IntegrationAuth = model("IntegrationAuth", integrationAuthSchema); diff --git a/backend-mongo/src/models/integrationAuth/types.ts b/backend-mongo/src/models/integrationAuth/types.ts deleted file mode 100644 index d29869e3b..000000000 --- a/backend-mongo/src/models/integrationAuth/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -interface GCPIntegrationAuthMetadata { - authMethod: "oauth2" | "serviceAccount" -} - -export type IntegrationAuthMetadata = GCPIntegrationAuthMetadata; \ No newline at end of file diff --git a/backend-mongo/src/models/key.ts b/backend-mongo/src/models/key.ts deleted file mode 100644 index fcc6e6f60..000000000 --- a/backend-mongo/src/models/key.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface IKey { - _id: Types.ObjectId; - encryptedKey: string; - nonce: string; - sender: Types.ObjectId; - receiver: Types.ObjectId; - workspace: Types.ObjectId; -} - -const keySchema = new Schema( - { - encryptedKey: { - type: String, - required: true, - }, - nonce: { - type: String, - required: true, - }, - sender: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - receiver: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - }, - }, - { - timestamps: true, - } -); - -export const Key = model("Key", keySchema); \ No newline at end of file diff --git a/backend-mongo/src/models/loginSRPDetail.ts b/backend-mongo/src/models/loginSRPDetail.ts deleted file mode 100644 index 26f897270..000000000 --- a/backend-mongo/src/models/loginSRPDetail.ts +++ /dev/null @@ -1,27 +0,0 @@ -import mongoose, { Schema, Types, model } from "mongoose"; - -export interface ILoginSRPDetail { - _id: Types.ObjectId; - clientPublicKey: string; - email: string; - serverBInt: mongoose.Schema.Types.Buffer; - userId: string; - expireAt: Date; -} - -const loginSRPDetailSchema = new Schema( - { - clientPublicKey: { - type: String, - required: true, - }, - email: { - type: String, - unique: true, - }, - serverBInt: { type: mongoose.Schema.Types.Buffer }, - expireAt: { type: Date }, - } -); - -export const LoginSRPDetail = model("LoginSRPDetail", loginSRPDetailSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/membership.ts b/backend-mongo/src/models/membership.ts deleted file mode 100644 index c09fa2779..000000000 --- a/backend-mongo/src/models/membership.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { ADMIN, CUSTOM, MEMBER, NO_ACCESS, VIEWER } from "../variables"; - -export interface IMembershipPermission { - environmentSlug: string; - ability: string; -} - -export interface IMembership { - _id: Types.ObjectId; - user: Types.ObjectId; - inviteEmail?: string; - workspace: Types.ObjectId; - role: "admin" | "member" | "viewer" | "no-access" | "custom"; - customRole: Types.ObjectId; - deniedPermissions: IMembershipPermission[]; -} - -const membershipSchema = new Schema( - { - user: { - type: Schema.Types.ObjectId, - ref: "User" - }, - inviteEmail: { - type: String - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - deniedPermissions: { - type: [ - { - environmentSlug: String, - ability: { - type: String, - enum: ["read", "write"] - } - } - ], - default: [] - }, - role: { - type: String, - enum: [ADMIN, MEMBER, VIEWER, NO_ACCESS, CUSTOM], - required: true - }, - customRole: { - type: Schema.Types.ObjectId, - ref: "Role" - } - }, - { - timestamps: true - } -); - -export const Membership = model("Membership", membershipSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/membershipOrg.ts b/backend-mongo/src/models/membershipOrg.ts deleted file mode 100644 index 0d4a2f6b7..000000000 --- a/backend-mongo/src/models/membershipOrg.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; -import { ACCEPTED, ADMIN, CUSTOM, INVITED, MEMBER, NO_ACCESS } from "../variables"; - -export interface IMembershipOrg extends Document { - _id: Types.ObjectId; - user: Types.ObjectId; - inviteEmail: string; - organization: Types.ObjectId; - role: "admin" | "member" | "no-access" | "custom"; - customRole: Types.ObjectId; - status: "invited" | "accepted"; -} - -const membershipOrgSchema = new Schema( - { - user: { - type: Schema.Types.ObjectId, - ref: "User" - }, - inviteEmail: { - type: String - }, - organization: { - type: Schema.Types.ObjectId, - ref: "Organization" - }, - role: { - type: String, - enum: [ADMIN, MEMBER, NO_ACCESS, CUSTOM], - required: true - }, - status: { - type: String, - enum: [INVITED, ACCEPTED], - required: true - }, - customRole: { - type: Schema.Types.ObjectId, - ref: "Role" - } - }, - { - timestamps: true - } -); - -export const MembershipOrg = model("MembershipOrg", membershipOrgSchema); diff --git a/backend-mongo/src/models/organization.ts b/backend-mongo/src/models/organization.ts deleted file mode 100644 index 1ae3bcb45..000000000 --- a/backend-mongo/src/models/organization.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface IOrganization { - _id: Types.ObjectId; - name: string; - customerId?: string; -} - -const organizationSchema = new Schema( - { - name: { - type: String, - required: true, - }, - customerId: { - type: String, - }, - }, - { - timestamps: true, - } -); - -export const Organization = model("Organization", organizationSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/secret.ts b/backend-mongo/src/models/secret.ts deleted file mode 100644 index 4c1400fa8..000000000 --- a/backend-mongo/src/models/secret.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { Schema, Types, model } from "mongoose"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, - SECRET_PERSONAL, - SECRET_SHARED -} from "../variables"; - -export interface ISecret { - _id: Types.ObjectId; - version: number; - workspace: Types.ObjectId; - type: string; - user?: Types.ObjectId; - environment: string; - secretBlindIndex?: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretKeyHash: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretValueHash: string; - secretCommentCiphertext?: string; - secretCommentIV?: string; - secretCommentTag?: string; - secretCommentHash?: string; - - // ? NOTE: This works great for workspace-level reminders. - // ? If we want to do it on a user-basis, we should ideally have a seperate model for reminders. - secretReminderRepeatDays?: number | null; - secretReminderNote?: string | null; - - skipMultilineEncoding?: boolean; - algorithm: "aes-256-gcm"; - keyEncoding: "utf8" | "base64"; - tags?: string[]; - folder?: string; - metadata?: { - [key: string]: string; - }; -} - -const secretSchema = new Schema( - { - version: { - type: Number, - required: true, - default: 1 - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - type: { - type: String, - enum: [SECRET_SHARED, SECRET_PERSONAL], - required: true - }, - user: { - // user associated with the personal secret - type: Schema.Types.ObjectId, - ref: "User" - }, - tags: { - ref: "Tag", - type: [Schema.Types.ObjectId], - default: [] - }, - environment: { - type: String, - required: true - }, - secretBlindIndex: { - type: String, - select: false - }, - secretKeyCiphertext: { - type: String, - required: true - }, - secretKeyIV: { - type: String, // symmetric - required: true - }, - secretKeyTag: { - type: String, // symmetric - required: true - }, - secretKeyHash: { - type: String - }, - secretValueCiphertext: { - type: String, - required: true - }, - secretValueIV: { - type: String, // symmetric - required: true - }, - secretValueTag: { - type: String, // symmetric - required: true - }, - secretValueHash: { - type: String - }, - secretCommentCiphertext: { - type: String, - required: false - }, - secretCommentIV: { - type: String, // symmetric - required: false - }, - secretCommentTag: { - type: String, // symmetric - required: false - }, - secretCommentHash: { - type: String, - required: false - }, - - secretReminderRepeatDays: { - type: Number, - required: false, - default: null - }, - secretReminderNote: { - type: String, - required: false, - default: null - }, - - skipMultilineEncoding: { - type: Boolean, - required: false - }, - - algorithm: { - // the encryption algorithm used - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true, - default: ALGORITHM_AES_256_GCM - }, - keyEncoding: { - type: String, - enum: [ENCODING_SCHEME_UTF8, ENCODING_SCHEME_BASE64], - required: true, - default: ENCODING_SCHEME_UTF8 - }, - folder: { - type: String, - default: "root" - }, - metadata: { - type: Schema.Types.Mixed - } - }, - { - timestamps: true - } -); - -secretSchema.index({ tags: 1 }, { background: true }); - -export const Secret = model("Secret", secretSchema); diff --git a/backend-mongo/src/models/secretBlindIndexData.ts b/backend-mongo/src/models/secretBlindIndexData.ts deleted file mode 100644 index da397d2c1..000000000 --- a/backend-mongo/src/models/secretBlindIndexData.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, -} from "../variables"; - -export interface ISecretBlindIndexData extends Document { - _id: Types.ObjectId; - workspace: Types.ObjectId; - encryptedSaltCiphertext: string; - saltIV: string; - saltTag: string; - algorithm: "aes-256-gcm"; - keyEncoding: "base64" | "utf8" -} - -const secretBlindIndexDataSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - }, - encryptedSaltCiphertext: { // TODO: make these select: false - type: String, - required: true, - }, - saltIV: { - type: String, - required: true, - }, - saltTag: { - type: String, - required: true, - }, - algorithm: { - type: String, - enum: [ALGORITHM_AES_256_GCM], - required: true, - select: false, - }, - keyEncoding: { - type: String, - enum: [ - ENCODING_SCHEME_UTF8, - ENCODING_SCHEME_BASE64, - ], - required: true, - select: false, - }, - - } -); - -secretBlindIndexDataSchema.index({ workspace: 1 }); - -export const SecretBlindIndexData = model("SecretBlindIndexData", secretBlindIndexDataSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/secretImports.ts b/backend-mongo/src/models/secretImports.ts deleted file mode 100644 index 79046a489..000000000 --- a/backend-mongo/src/models/secretImports.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface ISecretImports { - _id: Types.ObjectId; - workspace: Types.ObjectId; - environment: string; - folderId: string; - imports: Array<{ - environment: string; - secretPath: string; - }>; -} - -const secretImportSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - environment: { - type: String, - required: true - }, - folderId: { - type: String, - required: true, - default: "root" - }, - imports: { - type: [ - { - environment: { - type: String, - required: true - }, - secretPath: { - type: String, - required: true - } - } - ], - default: [] - } - }, - { - timestamps: true - } -); - -export const SecretImport = model("SecretImports", secretImportSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/serverConfig.ts b/backend-mongo/src/models/serverConfig.ts deleted file mode 100644 index 13e469bd5..000000000 --- a/backend-mongo/src/models/serverConfig.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface IServerConfig { - _id: Types.ObjectId; - initialized: boolean; - allowSignUp: boolean; -} - -const serverConfigSchema = new Schema( - { - initialized: { - type: Boolean, - default: false - }, - allowSignUp: { - type: Boolean, - default: true - } - }, - { - timestamps: true - } -); - -export const ServerConfig = model("ServerConfig", serverConfigSchema); diff --git a/backend-mongo/src/models/serviceToken.ts b/backend-mongo/src/models/serviceToken.ts deleted file mode 100644 index 0e943b177..000000000 --- a/backend-mongo/src/models/serviceToken.ts +++ /dev/null @@ -1,60 +0,0 @@ -// TODO: deprecate -import { Schema, Types, model } from "mongoose"; -export interface IServiceToken { - _id: Types.ObjectId; - name: string; - user: Types.ObjectId; - workspace: Types.ObjectId; - environment: string; - expiresAt: Date; - publicKey: string; - encryptedKey: string; - nonce: string; -} - -const serviceTokenSchema = new Schema( - { - name: { - type: String, - required: true, - }, - user: { - // token issuer - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - }, - environment: { - type: String, - required: true, - }, - expiresAt: { - type: Date, - }, - publicKey: { - type: String, - required: true, - select: true, - }, - encryptedKey: { - type: String, - required: true, - select: true, - }, - nonce: { - type: String, - required: true, - select: true, - }, - }, - { - timestamps: true, - } -); - -export const ServiceToken = model("ServiceToken", serviceTokenSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/serviceTokenData.ts b/backend-mongo/src/models/serviceTokenData.ts deleted file mode 100644 index 735131703..000000000 --- a/backend-mongo/src/models/serviceTokenData.ts +++ /dev/null @@ -1,93 +0,0 @@ -// TODO: deprecate -import { Document, Schema, Types, model } from "mongoose"; - -export interface IServiceTokenData extends Document { - _id: Types.ObjectId; - name: string; - workspace: Types.ObjectId; - scopes: Array<{ - environment: string; - secretPath: string; - }>; - user: Types.ObjectId; - serviceAccount: Types.ObjectId; - lastUsed: Date; - expiresAt: Date; - secretHash: string; - encryptedKey: string; - iv: string; - tag: string; - permissions: string[]; -} - -const serviceTokenDataSchema = new Schema( - { - name: { - type: String, - required: true - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - scopes: { - type: [ - { - environment: { - type: String, - required: true - }, - secretPath: { - type: String, - default: "/", - required: true - } - } - ], - required: true - }, - user: { - type: Schema.Types.ObjectId, - ref: "User", - required: true - }, - serviceAccount: { - type: Schema.Types.ObjectId, - ref: "ServiceAccount" - }, - lastUsed: { - type: Date - }, - expiresAt: { - type: Date - }, - secretHash: { - type: String, - required: true, - select: false - }, - encryptedKey: { - type: String, - select: false - }, - iv: { - type: String, - select: false - }, - tag: { - type: String, - select: false - }, - permissions: { - type: [String], - enum: ["read", "write"], - default: ["read"] - } - }, - { - timestamps: true - } -); - -export const ServiceTokenData = model("ServiceTokenData", serviceTokenDataSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/tag.ts b/backend-mongo/src/models/tag.ts deleted file mode 100644 index a5f0bd307..000000000 --- a/backend-mongo/src/models/tag.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface ITag { - _id: Types.ObjectId; - name: string; - tagColor: string; - slug: string; - user: Types.ObjectId; - workspace: Types.ObjectId; -} - -const tagSchema = new Schema( - { - name: { - type: String, - required: true, - trim: true, - }, - tagColor: { - type: String, - required: false, - trim: true, - }, - slug: { - type: String, - required: true, - trim: true, - lowercase: true, - validate: [ - function (value: any) { - return value.indexOf(" ") === -1; - }, - "slug cannot contain spaces", - ], - }, - user: { - type: Schema.Types.ObjectId, - ref: "User", - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - }, - }, - { - timestamps: true, - } -); - -tagSchema.index({ slug: 1, workspace: 1 }, { unique: true }) -tagSchema.index({ workspace: 1 }) - -export const Tag = model("Tag", tagSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/token.ts b/backend-mongo/src/models/token.ts deleted file mode 100644 index 62d342b0a..000000000 --- a/backend-mongo/src/models/token.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Schema, model } from "mongoose"; - -export interface IToken { - email: string; - token: string; - createdAt: Date; - ttl: number; -} - -const tokenSchema = new Schema({ - email: { - type: String, - required: true, - }, - token: { - type: String, - required: true, - }, - createdAt: { - type: Date, - default: Date.now, - }, - ttl: { - type: Number, - }, -}); - -tokenSchema.index({ email: 1 }); - -export const Token = model("Token", tokenSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/tokenData.ts b/backend-mongo/src/models/tokenData.ts deleted file mode 100644 index 2544c05f1..000000000 --- a/backend-mongo/src/models/tokenData.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface ITokenData { - type: string; - email?: string; - phoneNumber?: string; - organization?: Types.ObjectId; - tokenHash: string; - triesLeft?: number; - expiresAt: Date; - createdAt: Date; - updatedAt: Date; -} - -const tokenDataSchema = new Schema({ - type: { - type: String, - enum: [ - "emailConfirmation", - "emailMfa", - "organizationInvitation", - "passwordReset", - ], - required: true, - }, - email: { - type: String, - }, - phoneNumber: { - type: String, - }, - organization: { // organizationInvitation-specific field - type: Schema.Types.ObjectId, - ref: "Organization", - }, - tokenHash: { - type: String, - select: false, - required: true, - }, - triesLeft: { - type: Number, - }, - expiresAt: { - type: Date, - expires: 0, - required: true, - }, -}, { - timestamps: true, -}); - -export const TokenData = model("TokenData", tokenDataSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/tokenVersion.ts b/backend-mongo/src/models/tokenVersion.ts deleted file mode 100644 index b162e019e..000000000 --- a/backend-mongo/src/models/tokenVersion.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; - -export interface ITokenVersion extends Document { - user: Types.ObjectId; - ip: string; - userAgent: string; - refreshVersion: number; - accessVersion: number; - lastUsed: Date; -} - -const tokenVersionSchema = new Schema( - { - user: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - ip: { - type: String, - required: true, - }, - userAgent: { - type: String, - required: true, - }, - refreshVersion: { - type: Number, - required: true, - }, - accessVersion: { - type: Number, - required: true, - }, - lastUsed: { - type: Date, - required: true, - }, - }, - { - timestamps: true, - } -); - -export const TokenVersion = model("TokenVersion", tokenVersionSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/user.ts b/backend-mongo/src/models/user.ts deleted file mode 100644 index a3d73b8a2..000000000 --- a/backend-mongo/src/models/user.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; - -export enum AuthMethod { - EMAIL = "email", - GOOGLE = "google", - GITHUB = "github", - GITLAB = "gitlab", - OKTA_SAML = "okta-saml", - AZURE_SAML = "azure-saml", - JUMPCLOUD_SAML = "jumpcloud-saml" -} - -export interface IUser extends Document { - _id: Types.ObjectId; - authProvider?: AuthMethod; - authMethods: AuthMethod[]; - email: string; - superAdmin?: boolean; - firstName?: string; - lastName?: string; - encryptionVersion: number; - protectedKey: string; - protectedKeyIV: string; - protectedKeyTag: string; - publicKey?: string; - encryptedPrivateKey?: string; - iv?: string; - tag?: string; - salt?: string; - verifier?: string; - isMfaEnabled: boolean; - mfaMethods: boolean; - devices: { - ip: string; - userAgent: string; - }[]; -} - -const userSchema = new Schema( - { - authProvider: { - // TODO field: deprecate - type: String, - enum: AuthMethod - }, - authMethods: { - type: [ - { - type: String, - enum: AuthMethod - } - ], - default: [AuthMethod.EMAIL], - required: true - }, - email: { - type: String, - required: true, - unique: true - }, - firstName: { - type: String - }, - lastName: { - type: String - }, - encryptionVersion: { - type: Number, - select: false, - default: 1 // to resolve backward-compatibility issues - }, - protectedKey: { - // introduced as part of encryption version 2 - type: String, - select: false - }, - protectedKeyIV: { - // introduced as part of encryption version 2 - type: String, - select: false - }, - protectedKeyTag: { - // introduced as part of encryption version 2 - type: String, - select: false - }, - publicKey: { - type: String, - select: false - }, - encryptedPrivateKey: { - type: String, - select: false - }, - superAdmin: { - type: Boolean - }, - iv: { - // iv of [encryptedPrivateKey] - type: String, - select: false - }, - tag: { - // tag of [encryptedPrivateKey] - type: String, - select: false - }, - salt: { - type: String, - select: false - }, - verifier: { - type: String, - select: false - }, - isMfaEnabled: { - type: Boolean, - default: false - }, - mfaMethods: [ - { - type: String - } - ], - devices: { - type: [ - { - ip: String, - userAgent: String - } - ], - default: [], - select: false - } - }, - { - timestamps: true - } -); - -export const User = model("User", userSchema); diff --git a/backend-mongo/src/models/userAction.ts b/backend-mongo/src/models/userAction.ts deleted file mode 100644 index 68fae22be..000000000 --- a/backend-mongo/src/models/userAction.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface IUserAction { - _id: Types.ObjectId; - user: Types.ObjectId; - action: string; -} - -const userActionSchema = new Schema( - { - user: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - action: { - type: String, - required: true, - }, - }, - { - timestamps: true, - } -); - -export const UserAction = model("UserAction", userActionSchema); \ No newline at end of file diff --git a/backend-mongo/src/models/webhooks.ts b/backend-mongo/src/models/webhooks.ts deleted file mode 100644 index bef5e795a..000000000 --- a/backend-mongo/src/models/webhooks.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; -import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_BASE64, ENCODING_SCHEME_UTF8 } from "../variables"; - -export interface IWebhook extends Document { - _id: Types.ObjectId; - workspace: Types.ObjectId; - environment: string; - secretPath: string; - url: string; - lastStatus: "success" | "failed"; - lastRunErrorMessage?: string; - isDisabled: boolean; - encryptedSecretKey: string; - iv: string; - tag: string; - algorithm: "aes-256-gcm"; - keyEncoding: "base64" | "utf8"; -} - -const WebhookSchema = new Schema( - { - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true - }, - environment: { - type: String, - required: true - }, - secretPath: { - type: String, - required: true, - default: "/" - }, - url: { - type: String, - required: true - }, - lastStatus: { - type: String, - enum: ["success", "failed"] - }, - lastRunErrorMessage: { - type: String - }, - isDisabled: { - type: Boolean, - default: false - }, - // used for webhook signature - encryptedSecretKey: { - type: String, - select: false - }, - iv: { - type: String, - select: false - }, - tag: { - type: String, - select: false - }, - algorithm: { - // the encryption algorithm used - type: String, - enum: [ALGORITHM_AES_256_GCM], - select: false - }, - keyEncoding: { - type: String, - enum: [ENCODING_SCHEME_UTF8, ENCODING_SCHEME_BASE64], - select: false - } - }, - { - timestamps: true - } -); - -export const Webhook = model("Webhook", WebhookSchema); diff --git a/backend-mongo/src/models/workspace.ts b/backend-mongo/src/models/workspace.ts deleted file mode 100644 index 9d7a19fcc..000000000 --- a/backend-mongo/src/models/workspace.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Schema, Types, model } from "mongoose"; - -export interface IWorkspace { - _id: Types.ObjectId; - name: string; - organization: Types.ObjectId; - environments: Array<{ - name: string; - slug: string; - }>; - autoCapitalization: boolean; -} - -const workspaceSchema = new Schema({ - name: { - type: String, - required: true, - }, - autoCapitalization: { - type: Boolean, - default: true, - }, - organization: { - type: Schema.Types.ObjectId, - ref: "Organization", - required: true, - }, - environments: { - type: [ - { - name: String, - slug: String, - }, - ], - default: [ - { - name: "Development", - slug: "dev", - }, - { - name: "Staging", - slug: "staging", - }, - { - name: "Production", - slug: "prod", - }, - ], - }, -}); - -export const Workspace = model("Workspace", workspaceSchema); \ No newline at end of file diff --git a/backend-mongo/src/queues/integrations/syncSecretsToThirdPartyServices.ts b/backend-mongo/src/queues/integrations/syncSecretsToThirdPartyServices.ts deleted file mode 100644 index 490b31c10..000000000 --- a/backend-mongo/src/queues/integrations/syncSecretsToThirdPartyServices.ts +++ /dev/null @@ -1,86 +0,0 @@ -import Queue, { Job } from "bull"; -import { Integration, IntegrationAuth } from "../../models"; -import { BotService } from "../../services"; -import { getIntegrationAuthAccessHelper } from "../../helpers"; -import { syncSecrets } from "../../integrations/sync" - - -type TSyncSecretsToThirdPartyServices = { - workspaceId: string - environment?: string -} - -export const syncSecretsToThirdPartyServices = new Queue("sync-secrets-to-third-party-services", process.env.REDIS_URL as string); - -syncSecretsToThirdPartyServices.process(async (job: Job) => { - const { workspaceId, environment }: TSyncSecretsToThirdPartyServices = job.data - const integrations = await Integration.find({ - workspace: workspaceId, - ...(environment - ? { - environment - } - : {}), - isActive: true, - }); - - // for each workspace integration, sync/push secrets - // to that integration - for (const integration of integrations) { - // get workspace, environment (shared) secrets - const secrets = await BotService.getSecrets({ - workspaceId: integration.workspace, - environment: integration.environment, - secretPath: integration.secretPath - }); - - const suffixedSecrets: any = {}; - if (integration.metadata) { - for (const key in secrets) { - const prefix = (integration.metadata?.secretPrefix || ""); - const suffix = (integration.metadata?.secretSuffix || ""); - const newKey = prefix + key + suffix; - - suffixedSecrets[newKey] = secrets[key]; - } - } - - const integrationAuth = await IntegrationAuth.findById(integration.integrationAuth); - - if (!integrationAuth) throw new Error("Failed to find integration auth"); - - // get integration auth access token - const access = await getIntegrationAuthAccessHelper({ - integrationAuthId: integration.integrationAuth - }); - - // sync secrets to integration - await syncSecrets({ - integration, - integrationAuth, - secrets: Object.keys(suffixedSecrets).length !== 0 ? suffixedSecrets : secrets, - accessId: access.accessId === undefined ? null : access.accessId, - accessToken: access.accessToken, - appendices: { prefix: integration.metadata?.secretPrefix || "", suffix: integration.metadata?.secretSuffix || "" } - }); - } -}) - -syncSecretsToThirdPartyServices.on("error", (error) => { - // console.log("QUEUE ERROR:", error) // eslint-disable-line -}) - -export const syncSecretsToActiveIntegrationsQueue = (jobDetails: TSyncSecretsToThirdPartyServices) => { - syncSecretsToThirdPartyServices.add(jobDetails, { - attempts: 5, - backoff: { - type: "exponential", - delay: 3000 - }, - removeOnComplete: true, - removeOnFail: { - count: 20 // keep the most recent 20 jobs - } - }) -} - diff --git a/backend-mongo/src/queues/reminders/sendSecretReminders.ts b/backend-mongo/src/queues/reminders/sendSecretReminders.ts deleted file mode 100644 index 0cf4a79a4..000000000 --- a/backend-mongo/src/queues/reminders/sendSecretReminders.ts +++ /dev/null @@ -1,83 +0,0 @@ -import Queue, { Job } from "bull"; -import { IUser, Membership, Organization, Workspace } from "../../models"; -import { Types } from "mongoose"; -import { sendMail } from "../../helpers"; - -type TSendSecretReminders = { - workspaceId: string; - secretId: string; - repeatDays: number; - note: string | undefined | null; -}; - -type TDeleteSecretReminder = { - secretId: string; - repeatDays: number; -}; - -const DAY_IN_MS = 86400000; - -export const sendSecretReminders = new Queue( - "send-secret-reminders", - process.env.REDIS_URL as string -); - -sendSecretReminders.process(async (job: Job) => { - const { workspaceId }: TSendSecretReminders = job.data; - - const workspace = await Workspace.findById(new Types.ObjectId(workspaceId)); - const organization = await Organization.findById(new Types.ObjectId(workspace?.organization)); - - if (!workspace) { - throw new Error("Workspace for reminder not found"); - } - if (!organization) { - throw new Error("Organization for reminder not found"); - } - - const memberships = await Membership.find({ - workspace: workspaceId - }).populate<{ user: IUser }>("user"); - - await sendMail({ - template: "secretReminder.handlebars", - subjectLine: "Infisical secret reminder", - recipients: [...memberships.map((membership) => membership.user.email)], - substitutions: { - reminderNote: job.data.note, // May not be present. - workspaceName: workspace.name, - organizationName: organization.name - } - }); -}); - -export const createRecurringSecretReminder = (jobDetails: TSendSecretReminders) => { - const repeat = jobDetails.repeatDays * DAY_IN_MS; - - return sendSecretReminders.add(jobDetails, { - delay: repeat, - repeat: { - every: repeat - }, - jobId: `reminder-${jobDetails.secretId}`, - removeOnComplete: true, - removeOnFail: { - count: 20 - } - }); -}; - -export const deleteRecurringSecretReminder = (jobDetails: TDeleteSecretReminder) => { - const repeat = jobDetails.repeatDays * DAY_IN_MS; - - return sendSecretReminders.removeRepeatable({ - every: repeat, - jobId: `reminder-${jobDetails.secretId}` - }); -}; - -export const updateRecurringSecretReminder = async (jobDetails: TSendSecretReminders) => { - // We need to delete the potentially existing reminder job first, or the new one won't be created. - await deleteRecurringSecretReminder(jobDetails); - await createRecurringSecretReminder(jobDetails); -}; diff --git a/backend-mongo/src/queues/secret-scanning/githubScanFullRepository.ts b/backend-mongo/src/queues/secret-scanning/githubScanFullRepository.ts deleted file mode 100644 index ece43bd97..000000000 --- a/backend-mongo/src/queues/secret-scanning/githubScanFullRepository.ts +++ /dev/null @@ -1,101 +0,0 @@ -import Queue, { Job } from "bull"; -import { ProbotOctokit } from "probot" -import TelemetryService from "../../services/TelemetryService"; -import { sendMail } from "../../helpers"; -import { GitRisks } from "../../ee/models"; -import { MembershipOrg, User } from "../../models"; -import { ADMIN } from "../../variables"; -import { convertKeysToLowercase, scanFullRepoContentAndGetFindings } from "../../ee/services/GithubSecretScanning/helper"; -import { getSecretScanningGitAppId, getSecretScanningPrivateKey } from "../../config"; -import { SecretMatch } from "../../ee/services/GithubSecretScanning/types"; - -export const githubFullRepositorySecretScan = new Queue("github-full-repository-secret-scanning", "redis://redis:6379"); - -type TScanPushEventQueueDetails = { - organizationId: string, - installationId: string, - repository: { - id: number, - fullName: string, - }, -} - -githubFullRepositorySecretScan.process(async (job: Job, done: Queue.DoneCallback) => { - const { organizationId, repository, installationId }: TScanPushEventQueueDetails = job.data - try { - const octokit = new ProbotOctokit({ - auth: { - appId: await getSecretScanningGitAppId(), - privateKey: await getSecretScanningPrivateKey(), - installationId: installationId - }, - }); - - const findings: SecretMatch[] = await scanFullRepoContentAndGetFindings(octokit, installationId as any, repository.fullName) - for (const finding of findings) { - await GitRisks.findOneAndUpdate({ fingerprint: finding.Fingerprint }, - { - ...convertKeysToLowercase(finding), - installationId: installationId, - organization: organizationId, - repositoryFullName: repository.fullName, - repositoryId: repository.id - }, { - upsert: true - }).lean() - } - - // get emails of admins - const adminsOfWork = await MembershipOrg.find({ - organization: organizationId, - role: ADMIN, - }).lean() - - const userEmails = await User.find({ - _id: { - $in: [adminsOfWork.map(orgMembership => orgMembership.user)] - } - }).select("email").lean() - - const usersToNotify = userEmails.map(userObject => userObject.email) - - if (findings.length) { - await sendMail({ - template: "historicalSecretLeakIncident.handlebars", - subjectLine: `Incident alert: leaked secrets found in Github repository ${repository.fullName}`, - recipients: usersToNotify, - substitutions: { - numberOfSecrets: findings.length, - } - }); - } - - const postHogClient = await TelemetryService.getPostHogClient(); - if (postHogClient) { - postHogClient.capture({ - event: "historical cloud secret scan", - distinctId: repository.fullName, - properties: { - numberOfRisksFound: findings.length, - } - }); - } - done(null, findings) - } catch (error) { - done(new Error(`gitHubHistoricalScanning.process: an error occurred ${error}`), null) - } -}) - -export const scanGithubFullRepoForSecretLeaks = (pushEventPayload: TScanPushEventQueueDetails) => { - githubFullRepositorySecretScan.add(pushEventPayload, { - attempts: 3, - backoff: { - type: "exponential", - delay: 5000 - }, - removeOnComplete: true, - removeOnFail: { - count: 20 // keep the most recent 20 jobs - } - }) -} \ No newline at end of file diff --git a/backend-mongo/src/queues/secret-scanning/githubScanPushEvent.ts b/backend-mongo/src/queues/secret-scanning/githubScanPushEvent.ts deleted file mode 100644 index af2d88f1f..000000000 --- a/backend-mongo/src/queues/secret-scanning/githubScanPushEvent.ts +++ /dev/null @@ -1,145 +0,0 @@ -import Queue, { Job } from "bull"; -import { ProbotOctokit } from "probot" -import { Commit } from "@octokit/webhooks-types"; -import TelemetryService from "../../services/TelemetryService"; -import { sendMail } from "../../helpers"; -import { GitRisks } from "../../ee/models"; -import { MembershipOrg, User } from "../../models"; -import { ADMIN } from "../../variables"; -import { convertKeysToLowercase, scanContentAndGetFindings } from "../../ee/services/GithubSecretScanning/helper"; -import { getSecretScanningGitAppId, getSecretScanningPrivateKey } from "../../config"; -import { SecretMatch } from "../../ee/services/GithubSecretScanning/types"; - -export const githubPushEventSecretScan = new Queue("github-push-event-secret-scanning", "redis://redis:6379"); - -type TScanPushEventQueueDetails = { - organizationId: string, - commits: Commit[] - pusher: { - name: string, - email: string | null - }, - repository: { - id: number, - fullName: string, - }, - installationId: number -} - -githubPushEventSecretScan.process(async (job: Job, done: Queue.DoneCallback) => { - const { organizationId, commits, pusher, repository, installationId }: TScanPushEventQueueDetails = job.data - const [owner, repo] = repository.fullName.split("/"); - const octokit = new ProbotOctokit({ - auth: { - appId: await getSecretScanningGitAppId(), - privateKey: await getSecretScanningPrivateKey(), - installationId: installationId - }, - }); - - const allFindingsByFingerprint: { [key: string]: SecretMatch; } = {} - - for (const commit of commits) { - for (const filepath of [...commit.added, ...commit.modified]) { - try { - const fileContentsResponse = await octokit.repos.getContent({ - owner, - repo, - path: filepath, - }); - - const data: any = fileContentsResponse.data; - const fileContent = Buffer.from(data.content, "base64").toString(); - - const findings = await scanContentAndGetFindings(`\n${fileContent}`) // extra line to count lines correctly - - for (const finding of findings) { - const fingerPrintWithCommitId = `${commit.id}:${filepath}:${finding.RuleID}:${finding.StartLine}` - const fingerPrintWithoutCommitId = `${filepath}:${finding.RuleID}:${finding.StartLine}` - finding.Fingerprint = fingerPrintWithCommitId - finding.FingerPrintWithoutCommitId = fingerPrintWithoutCommitId - finding.Commit = commit.id - finding.File = filepath - finding.Author = commit.author.name - finding.Email = commit?.author?.email ? commit?.author?.email : "" - - allFindingsByFingerprint[fingerPrintWithCommitId] = finding - } - - } catch (error) { - done(new Error(`gitHubHistoricalScanning.process: unable to fetch content for [filepath=${filepath}] because [error=${error}]`), null) - } - } - } - - // change to update - for (const key in allFindingsByFingerprint) { - await GitRisks.findOneAndUpdate({ fingerprint: allFindingsByFingerprint[key].Fingerprint }, - { - ...convertKeysToLowercase(allFindingsByFingerprint[key]), - installationId: installationId, - organization: organizationId, - repositoryFullName: repository.fullName, - repositoryId: repository.id - }, { - upsert: true - }).lean() - } - // get emails of admins - const adminsOfWork = await MembershipOrg.find({ - organization: organizationId, - role: ADMIN - }).lean() - - const userEmails = await User.find({ - _id: { - $in: [adminsOfWork.map(orgMembership => orgMembership.user)] - } - }).select("email").lean() - - const adminOrOwnerEmails = userEmails.map(userObject => userObject.email) - - const usersToNotify = pusher?.email ? [pusher.email, ...adminOrOwnerEmails] : [...adminOrOwnerEmails] - if (Object.keys(allFindingsByFingerprint).length) { - await sendMail({ - template: "secretLeakIncident.handlebars", - subjectLine: `Incident alert: leaked secrets found in Github repository ${repository.fullName}`, - recipients: usersToNotify, - substitutions: { - numberOfSecrets: Object.keys(allFindingsByFingerprint).length, - pusher_email: pusher.email, - pusher_name: pusher.name - } - }); - } - - const postHogClient = await TelemetryService.getPostHogClient(); - if (postHogClient) { - postHogClient.capture({ - event: "cloud secret scan", - distinctId: pusher.email, - properties: { - numberOfCommitsScanned: commits.length, - numberOfRisksFound: Object.keys(allFindingsByFingerprint).length, - } - }); - } - - done(null, allFindingsByFingerprint) - -}) - -export const scanGithubPushEventForSecretLeaks = (pushEventPayload: TScanPushEventQueueDetails) => { - githubPushEventSecretScan.add(pushEventPayload, { - attempts: 3, - backoff: { - type: "exponential", - delay: 5000 - }, - removeOnComplete: true, - removeOnFail: { - count: 20 // keep the most recent 20 jobs - } - }) -} - diff --git a/backend-mongo/src/routes/status/index.ts b/backend-mongo/src/routes/status/index.ts deleted file mode 100644 index 6f3be6271..000000000 --- a/backend-mongo/src/routes/status/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import healthCheck from "./status"; - -export { - healthCheck, -} \ No newline at end of file diff --git a/backend-mongo/src/routes/status/status.ts b/backend-mongo/src/routes/status/status.ts deleted file mode 100644 index fc35f7edb..000000000 --- a/backend-mongo/src/routes/status/status.ts +++ /dev/null @@ -1,28 +0,0 @@ -import express, { Request, Response } from "express"; -import { getInviteOnlySignup, getRedisUrl, getSecretScanningGitAppId, getSecretScanningPrivateKey, getSecretScanningWebhookSecret, getSmtpConfigured } from "../../config"; - -const router = express.Router(); - -router.get( - "/status", - async (req: Request, res: Response) => { - const gitAppId = await getSecretScanningGitAppId() - const gitSecretScanningWebhookSecret = await getSecretScanningWebhookSecret() - const gitSecretScanningPrivateKey = await getSecretScanningPrivateKey() - let secretScanningConfigured = false - if (gitAppId && gitSecretScanningPrivateKey && gitSecretScanningWebhookSecret) { - secretScanningConfigured = true - } - - res.status(200).json({ - date: new Date(), - message: "Ok", - emailConfigured: await getSmtpConfigured(), - inviteOnlySignup: await getInviteOnlySignup(), - redisConfigured: await getRedisUrl() !== "" && await getRedisUrl() !== undefined, - secretScanningConfigured: secretScanningConfigured, - }) - } -); - -export default router \ No newline at end of file diff --git a/backend-mongo/src/routes/v1/admin.ts b/backend-mongo/src/routes/v1/admin.ts deleted file mode 100644 index 5e7989ce0..000000000 --- a/backend-mongo/src/routes/v1/admin.ts +++ /dev/null @@ -1,20 +0,0 @@ -import express from "express"; -import { adminController } from "../../controllers/v1"; -const router = express.Router(); -import { requireAuth, requireSuperAdminAccess } from "../../middleware"; -import { AuthMode } from "../../variables"; - -router.get("/config", adminController.getServerConfigInfo); - -router.post("/signup", adminController.adminSignUp); - -router.patch( - "/config", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - requireSuperAdminAccess, - adminController.updateServerConfig -); - -export default router; diff --git a/backend-mongo/src/routes/v1/auth.ts b/backend-mongo/src/routes/v1/auth.ts deleted file mode 100644 index a3037f311..000000000 --- a/backend-mongo/src/routes/v1/auth.ts +++ /dev/null @@ -1,51 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth, validateRequest } from "../../middleware"; -import { authController } from "../../controllers/v1"; -import { authLimiter } from "../../helpers/rateLimiter"; -import { AuthMode } from "../../variables"; - -router.post("/token", validateRequest, authController.getNewToken); - -router.post( - // TODO endpoint: deprecate (moved to api/v3/auth/login1) - "/login1", - authLimiter, - authController.login1 -); - -router.post( - // TODO endpoint: deprecate (moved to api/v3/auth/login2) - "/login2", - authLimiter, - authController.login2 -); - -router.post( - "/logout", - authLimiter, - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - authController.logout -); - -router.post( - "/checkAuth", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - authController.checkAuth -); - -router.delete( - // TODO endpoint: deprecate (moved to DELETE v2/users/me/sessions) - "/sessions", - authLimiter, - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - authController.revokeAllSessions -); - -export default router; diff --git a/backend-mongo/src/routes/v1/bot.ts b/backend-mongo/src/routes/v1/bot.ts deleted file mode 100644 index 5e76288cd..000000000 --- a/backend-mongo/src/routes/v1/bot.ts +++ /dev/null @@ -1,25 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { - requireAuth -} from "../../middleware"; -import { botController } from "../../controllers/v1"; -import { AuthMode } from "../../variables"; - -router.get( - "/:workspaceId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - botController.getBotByWorkspaceId -); - -router.patch( - "/:botId/active", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - botController.setBotActiveState -); - -export default router; diff --git a/backend-mongo/src/routes/v1/index.ts b/backend-mongo/src/routes/v1/index.ts deleted file mode 100644 index 8bc4642af..000000000 --- a/backend-mongo/src/routes/v1/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import signup from "./signup"; -import bot from "./bot"; -import auth from "./auth"; -import universalAuth from "./universalAuth"; -import user from "./user"; -import userAction from "./userAction"; -import organization from "./organization"; -import workspace from "./workspace"; -import membershipOrg from "./membershipOrg"; -import membership from "./membership"; -import key from "./key"; -import inviteOrg from "./inviteOrg"; -import secret from "./secret"; -import serviceToken from "./serviceToken"; -import sso from "./sso"; -import password from "./password"; -import integration from "./integration"; -import integrationAuth from "./integrationAuth"; -import secretsFolder from "./secretsFolder"; -import webhooks from "./webhook"; -import secretImps from "./secretImps"; -import admin from "./admin"; - -export { - signup, - auth, - universalAuth, - bot, - user, - userAction, - organization, - workspace, - membershipOrg, - membership, - key, - inviteOrg, - secret, - serviceToken, - password, - integration, - integrationAuth, - secretsFolder, - webhooks, - secretImps, - sso, - admin -}; diff --git a/backend-mongo/src/routes/v1/integration.ts b/backend-mongo/src/routes/v1/integration.ts deleted file mode 100644 index 6dda7527b..000000000 --- a/backend-mongo/src/routes/v1/integration.ts +++ /dev/null @@ -1,39 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; -import { integrationController } from "../../controllers/v1"; - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - integrationController.createIntegration -); - -router.patch( - "/:integrationId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationController.updateIntegration -); - -router.delete( - "/:integrationId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationController.deleteIntegration -); - -router.post( - "/manual-sync", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationController.manualSync -); - -export default router; \ No newline at end of file diff --git a/backend-mongo/src/routes/v1/integrationAuth.ts b/backend-mongo/src/routes/v1/integrationAuth.ts deleted file mode 100644 index 9e4236dbc..000000000 --- a/backend-mongo/src/routes/v1/integrationAuth.ts +++ /dev/null @@ -1,175 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; -import { integrationAuthController } from "../../controllers/v1"; - -router.get( - "/integration-options", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationOptions -); - -router.get( - "/:integrationAuthId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuth -); - -router.post( - "/oauth-token", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.oAuthExchange -); - -router.post( - "/access-token", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - integrationAuthController.saveIntegrationToken -); - -router.get( - "/:integrationAuthId/apps", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthApps -); - -router.get( - "/:integrationAuthId/teams", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthTeams -); - -router.get( - "/:integrationAuthId/vercel/branches", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthVercelBranches -); - -router.get( - "/:integrationAuthId/checkly/groups", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthChecklyGroups -); - -router.get( - "/:integrationAuthId/qovery/orgs", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthQoveryOrgs -); - -router.get( - "/:integrationAuthId/qovery/projects", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthQoveryProjects -); - -router.get( - "/:integrationAuthId/qovery/environments", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthQoveryEnvironments -); - -router.get( - "/:integrationAuthId/qovery/apps", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthQoveryApps -); - -router.get( - "/:integrationAuthId/qovery/containers", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthQoveryContainers -); - -router.get( - "/:integrationAuthId/qovery/jobs", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthQoveryJobs -); - -router.get( - "/:integrationAuthId/railway/environments", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthRailwayEnvironments -); - -router.get( - "/:integrationAuthId/railway/services", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthRailwayServices -); - -router.get( - "/:integrationAuthId/bitbucket/workspaces", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthBitBucketWorkspaces -); - -router.get( - "/:integrationAuthId/northflank/secret-groups", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthNorthflankSecretGroups -); - -router.get( - "/:integrationAuthId/teamcity/build-configs", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.getIntegrationAuthTeamCityBuildConfigs -); - -router.delete( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.deleteIntegrationAuths -); - -router.delete( - "/:integrationAuthId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - integrationAuthController.deleteIntegrationAuthById -); - -export default router; diff --git a/backend-mongo/src/routes/v1/inviteOrg.ts b/backend-mongo/src/routes/v1/inviteOrg.ts deleted file mode 100644 index 0fa4ffbfe..000000000 --- a/backend-mongo/src/routes/v1/inviteOrg.ts +++ /dev/null @@ -1,27 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { body } from "express-validator"; -import { requireAuth, validateRequest } from "../../middleware"; -import { membershipOrgController } from "../../controllers/v1"; -import { AuthMode } from "../../variables"; - -// TODO endpoint: consider moving these endpoints to be under /organization to be more RESTful - -router.post( - "/signup", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - membershipOrgController.inviteUserToOrganization -); - -router.post( - "/verify", - body("email").exists().trim().notEmpty(), - body("organizationId").exists().trim().notEmpty(), - body("code").exists().trim().notEmpty(), - validateRequest, - membershipOrgController.verifyUserToOrganization -); - -export default router; diff --git a/backend-mongo/src/routes/v1/key.ts b/backend-mongo/src/routes/v1/key.ts deleted file mode 100644 index be2c8d929..000000000 --- a/backend-mongo/src/routes/v1/key.ts +++ /dev/null @@ -1,26 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; -import { keyController } from "../../controllers/v1"; - -// TODO endpoint: consider moving these endpoints to be under /workspaces to be more RESTful - -router.post( - "/:workspaceId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - keyController.uploadKey -); - -router.get( - // TODO endpoint: deprecate (note: move frontend to v2/workspace/key or something) - "/:workspaceId/latest", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - keyController.getLatestKey -); - -export default router; diff --git a/backend-mongo/src/routes/v1/membership.ts b/backend-mongo/src/routes/v1/membership.ts deleted file mode 100644 index 54b3b7c9e..000000000 --- a/backend-mongo/src/routes/v1/membership.ts +++ /dev/null @@ -1,37 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { membershipController } from "../../controllers/v1"; -import { AuthMode } from "../../variables"; - -// note: ALL DEPRECIATED (moved to api/v2/workspace/:workspaceId/memberships/:membershipId) -// TODO endpoint: consider moving these endpoints to be under /workspace to be more RESTful - -router.get( - // TODO endpoint: deprecate - used for old CLI (deprecate) - "/:workspaceId/connect", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - membershipController.validateMembership -); - -router.delete( - // TODO endpoint: check dashboard - "/:membershipId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - membershipController.deleteMembership -); - -router.post( - // TODO endpoint: check dashboard - "/:membershipId/change-role", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - membershipController.changeMembershipRole -); - -export default router; diff --git a/backend-mongo/src/routes/v1/membershipOrg.ts b/backend-mongo/src/routes/v1/membershipOrg.ts deleted file mode 100644 index d841f0c4b..000000000 --- a/backend-mongo/src/routes/v1/membershipOrg.ts +++ /dev/null @@ -1,29 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { param } from "express-validator"; -import { requireAuth, validateRequest } from "../../middleware"; -import { membershipOrgController } from "../../controllers/v1"; -import { AuthMode } from "../../variables"; - -// depreciated completely -// ignored for new codebase -router.post( - // TODO endpoint: check dashboard - "/membershipOrg/:membershipOrgId/change-role", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - param("membershipOrgId"), - validateRequest, - membershipOrgController.changeMembershipOrgRole -); - -router.delete( - "/:membershipOrgId", // TODO endpoint: check dashboard - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - membershipOrgController.deleteMembershipOrg -); - -export default router; diff --git a/backend-mongo/src/routes/v1/organization.ts b/backend-mongo/src/routes/v1/organization.ts deleted file mode 100644 index 3dfe2689b..000000000 --- a/backend-mongo/src/routes/v1/organization.ts +++ /dev/null @@ -1,90 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; -import { organizationController } from "../../controllers/v1"; - -router.get( - // TODO endpoint: deprecate (moved to api/v2/users/me/organizations) - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.getOrganizations -); - -router.get( - "/:organizationId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.getOrganization -); - -router.get( - // TODO endpoint: deprecate (moved to api/v2/organizations/:organizationId/memberships) - "/:organizationId/users", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.getOrganizationMembers -); - -router.get( - // TODO endpoint: move to /v2/users/me/organizations/:organizationId/workspaces - "/:organizationId/my-workspaces", // deprecated (moved to api/v2/organizations/:organizationId/workspaces) - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.getOrganizationWorkspaces -); - -router.patch( - "/:organizationId/name", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.changeOrganizationName -); - -router.get( - "/:organizationId/incidentContactOrg", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.getOrganizationIncidentContacts -); - -router.post( - "/:organizationId/incidentContactOrg", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.addOrganizationIncidentContact -); - -router.delete( - "/:organizationId/incidentContactOrg", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.deleteOrganizationIncidentContact -); - -router.post( - "/:organizationId/customer-portal-session", // TODO endpoint: move to EE - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.createOrganizationPortalSession -); - -router.get( - "/:organizationId/workspace-memberships", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationController.getOrganizationMembersAndTheirWorkspaces -); - -export default router; diff --git a/backend-mongo/src/routes/v1/password.ts b/backend-mongo/src/routes/v1/password.ts deleted file mode 100644 index aec995cee..000000000 --- a/backend-mongo/src/routes/v1/password.ts +++ /dev/null @@ -1,51 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth, requireSignupAuth } from "../../middleware"; -import { passwordController } from "../../controllers/v1"; -import { passwordLimiter } from "../../helpers/rateLimiter"; -import { AuthMode } from "../../variables"; - -router.post( - "/srp1", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - passwordController.srp1 -); - -router.post( - "/change-password", - passwordLimiter, - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - passwordController.changePassword -); - -router.post("/email/password-reset", passwordLimiter, passwordController.emailPasswordReset); - -router.post( - "/email/password-reset-verify", - passwordLimiter, - passwordController.emailPasswordResetVerify -); - -router.get( - "/backup-private-key", - passwordLimiter, - requireSignupAuth, - passwordController.getBackupPrivateKey -); - -router.post( - "/backup-private-key", - passwordLimiter, - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - passwordController.createBackupPrivateKey -); - -router.post("/password-reset", requireSignupAuth, passwordController.resetPassword); - -export default router; diff --git a/backend-mongo/src/routes/v1/secret.ts b/backend-mongo/src/routes/v1/secret.ts deleted file mode 100644 index e2b63e9ef..000000000 --- a/backend-mongo/src/routes/v1/secret.ts +++ /dev/null @@ -1,63 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { - requireAuth, - requireServiceTokenAuth, - requireWorkspaceAuth, - validateRequest, -} from "../../middleware"; -import { body, param, query } from "express-validator"; -import { secretController } from "../../controllers/v1"; -import { - ADMIN, - AuthMode, - MEMBER -} from "../../variables"; - -// note: endpoints deprecated in favor of v3/secrets - -router.post( // TODO endpoint: deprecate (moved to POST api/v3/secrets) - "/:workspaceId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params", - }), - body("secrets").exists(), - body("keys").exists(), - body("environment").exists().trim().notEmpty(), - body("channel"), - param("workspaceId").exists().trim(), - validateRequest, - secretController.pushSecrets -); - -router.get( // TODO endpoint: deprecate (moved to GET api/v3/secrets) - "/:workspaceId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params", - }), - query("environment").exists().trim(), - query("channel"), - param("workspaceId").exists().trim(), - validateRequest, - secretController.pullSecrets -); - -router.get( // TODO endpoint: deprecate (moved to GET api/v3/secrets) - "/:workspaceId/service-token", - requireServiceTokenAuth, - query("environment").exists().trim(), - query("channel"), - param("workspaceId").exists().trim(), - validateRequest, - secretController.pullSecretsServiceToken -); - -export default router; diff --git a/backend-mongo/src/routes/v1/secretImps.ts b/backend-mongo/src/routes/v1/secretImps.ts deleted file mode 100644 index 478714e54..000000000 --- a/backend-mongo/src/routes/v1/secretImps.ts +++ /dev/null @@ -1,47 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { secretImpsController } from "../../controllers/v1"; -import { AuthMode } from "../../variables"; - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - secretImpsController.createSecretImp -); - -router.put( - "/:id", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - secretImpsController.updateSecretImport -); - -router.delete( - "/:id", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - secretImpsController.deleteSecretImport -); - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - secretImpsController.getSecretImports -); - -router.get( - "/secrets", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY] - }), - secretImpsController.getAllSecretsFromImport -); - -export default router; diff --git a/backend-mongo/src/routes/v1/secretsFolder.ts b/backend-mongo/src/routes/v1/secretsFolder.ts deleted file mode 100644 index e7bfc8987..000000000 --- a/backend-mongo/src/routes/v1/secretsFolder.ts +++ /dev/null @@ -1,44 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { - createFolder, - deleteFolder, - getFolders, - updateFolderById -} from "../../controllers/v1/secretsFolderController"; -import { AuthMode } from "../../variables"; - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - createFolder -); - -router.patch( - "/:folderName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - updateFolderById -); - -router.delete( - "/:folderName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - deleteFolder -); - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - getFolders -); - -export default router; diff --git a/backend-mongo/src/routes/v1/serviceToken.ts b/backend-mongo/src/routes/v1/serviceToken.ts deleted file mode 100644 index e79d24ffa..000000000 --- a/backend-mongo/src/routes/v1/serviceToken.ts +++ /dev/null @@ -1,45 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { - requireAuth, - requireServiceTokenAuth, - requireWorkspaceAuth, - validateRequest, -} from "../../middleware"; -import { body } from "express-validator"; -import { - ADMIN, - AuthMode, - MEMBER -} from "../../variables"; -import { serviceTokenController } from "../../controllers/v1"; - -// note: deprecate service-token routes in favor of service-token data routes/structure - -router.get( // TODO endpoint: deprecate - "/", - requireServiceTokenAuth, - serviceTokenController.getServiceToken -); - -router.post( // TODO endpoint: deprecate - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "body", - }), - body("name").exists().trim().notEmpty(), - body("workspaceId").exists().trim().notEmpty(), - body("environment").exists().trim().notEmpty(), - body("expiresIn"), // measured in ms - body("publicKey").exists().trim().notEmpty(), - body("encryptedKey").exists().trim().notEmpty(), - body("nonce").exists().trim().notEmpty(), - validateRequest, - serviceTokenController.createServiceToken -); - -export default router; diff --git a/backend-mongo/src/routes/v1/signup.ts b/backend-mongo/src/routes/v1/signup.ts deleted file mode 100644 index e7b92f643..000000000 --- a/backend-mongo/src/routes/v1/signup.ts +++ /dev/null @@ -1,24 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { signupController } from "../../controllers/v1"; -import { authLimiter } from "../../helpers/rateLimiter"; -import { disableSignUpByServerCfg } from "../../middleware"; - -// TODO: consider moving to users/v3/signup - -router.post( - // TODO endpoint: consider moving to v3/users/signup/mail - "/email/signup", - disableSignUpByServerCfg, - authLimiter, - signupController.beginEmailSignup -); - -router.post( - "/email/verify", // TODO endpoint: consider moving to v3/users/signup/verify - disableSignUpByServerCfg, - authLimiter, - signupController.verifyEmailSignup -); - -export default router; diff --git a/backend-mongo/src/routes/v1/sso.ts b/backend-mongo/src/routes/v1/sso.ts deleted file mode 100644 index b06ba9986..000000000 --- a/backend-mongo/src/routes/v1/sso.ts +++ /dev/null @@ -1,72 +0,0 @@ -import express from "express"; -const router = express.Router(); -import passport from "passport"; -import { authLimiter } from "../../helpers/rateLimiter"; -import { ssoController } from "../../ee/controllers/v1"; - -router.get("/redirect/google", authLimiter, (req, res, next) => { - passport.authenticate("google", { - scope: ["profile", "email"], - session: false, - ...(req.query.callback_port - ? { - state: req.query.callback_port as string - } - : {}) - })(req, res, next); -}); - -router.get( - "/google", - passport.authenticate("google", { - failureRedirect: "/login/provider/error", - session: false - }), - ssoController.redirectSSO -); - -router.get("/redirect/github", authLimiter, (req, res, next) => { - passport.authenticate("github", { - session: false, - ...(req.query.callback_port - ? { - state: req.query.callback_port as string - } - : {}) - })(req, res, next); -}); - -router.get( - "/github", - authLimiter, - passport.authenticate("github", { - failureRedirect: "/login/provider/error", - session: false - }), - ssoController.redirectSSO -); - -router.get( - "/redirect/gitlab", - authLimiter, - (req, res, next) => { - passport.authenticate("gitlab", { - session: false, - ...(req.query.callback_port ? { - state: req.query.callback_port as string - } : {}) - })(req, res, next); - } -); - -router.get( - "/gitlab", - authLimiter, - passport.authenticate("gitlab", { - failureRedirect: "/login/provider/error", - session: false - }), - ssoController.redirectSSO -); - -export default router; \ No newline at end of file diff --git a/backend-mongo/src/routes/v1/universalAuth.ts b/backend-mongo/src/routes/v1/universalAuth.ts deleted file mode 100644 index b9d180040..000000000 --- a/backend-mongo/src/routes/v1/universalAuth.ts +++ /dev/null @@ -1,66 +0,0 @@ - -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { universalAuthController } from "../../controllers/v1"; -import { AuthMode } from "../../variables"; - -router.post( - "/token/renew", - universalAuthController.renewAccessToken -); - -router.post( - "/universal-auth/login", - universalAuthController.loginIdentityUniversalAuth -); - -router.post( - "/universal-auth/identities/:identityId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - universalAuthController.attachIdentityUniversalAuth -); - -router.patch( - "/universal-auth/identities/:identityId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - universalAuthController.updateIdentityUniversalAuth -); - -router.get( - "/universal-auth/identities/:identityId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - universalAuthController.getIdentityUniversalAuth -); - -router.post( - "/universal-auth/identities/:identityId/client-secrets", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - universalAuthController.createUniversalAuthClientSecret -); - -router.get( - "/universal-auth/identities/:identityId/client-secrets", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - universalAuthController.getUniversalAuthClientSecretsDetails -); - -router.post( - "/universal-auth/identities/:identityId/client-secrets/:clientSecretId/revoke", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - universalAuthController.revokeUniversalAuthClientSecret -); - -export default router; \ No newline at end of file diff --git a/backend-mongo/src/routes/v1/user.ts b/backend-mongo/src/routes/v1/user.ts deleted file mode 100644 index 85333db9b..000000000 --- a/backend-mongo/src/routes/v1/user.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { userController } from "../../controllers/v1"; -import { AuthMode } from "../../variables"; - -router.get( // TODO endpoint: deprecate (moved to v2/users/me) - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - userController.getUser -); - -export default router; diff --git a/backend-mongo/src/routes/v1/userAction.ts b/backend-mongo/src/routes/v1/userAction.ts deleted file mode 100644 index 762e1cde3..000000000 --- a/backend-mongo/src/routes/v1/userAction.ts +++ /dev/null @@ -1,25 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { userActionController } from "../../controllers/v1"; -import { AuthMode } from "../../variables"; - -// note: [userAction] will be deprecated in /v2 in favor of [action] -router.post( - // TODO endpoint: move this into /users/me - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - userActionController.addUserAction -); - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - userActionController.getUserAction -); - -export default router; diff --git a/backend-mongo/src/routes/v1/webhook.ts b/backend-mongo/src/routes/v1/webhook.ts deleted file mode 100644 index 30c59a15b..000000000 --- a/backend-mongo/src/routes/v1/webhook.ts +++ /dev/null @@ -1,47 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; -import { webhookController } from "../../controllers/v1"; - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - webhookController.createWebhook -); - -router.patch( - "/:webhookId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - webhookController.updateWebhook -); - -router.post( - "/:webhookId/test", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - webhookController.testWebhook -); - -router.delete( - "/:webhookId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - webhookController.deleteWebhook -); - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - webhookController.listWebhooks -); - -export default router; diff --git a/backend-mongo/src/routes/v1/workspace.ts b/backend-mongo/src/routes/v1/workspace.ts deleted file mode 100644 index 4dbc121b9..000000000 --- a/backend-mongo/src/routes/v1/workspace.ts +++ /dev/null @@ -1,95 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; -import { membershipController, workspaceController } from "../../controllers/v1"; - -router.get( - "/:workspaceId/keys", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.getWorkspacePublicKeys -); - -router.get( - "/:workspaceId/users", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.getWorkspaceMemberships -); - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - workspaceController.getWorkspaces -); - -router.get( - "/:workspaceId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.getWorkspace -); - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.createWorkspace -); - -router.delete( - "/:workspaceId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.deleteWorkspace -); - -router.post( - "/:workspaceId/name", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.changeWorkspaceName -); - -router.post( - "/:workspaceId/invite-signup", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - membershipController.inviteUserToWorkspace -); - -router.get( - "/:workspaceId/integrations", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.getWorkspaceIntegrations -); - -router.get( - "/:workspaceId/authorizations", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.getWorkspaceIntegrationAuthorizations -); - -router.get( - "/:workspaceId/service-tokens", // TODO endpoint: deprecate - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.getWorkspaceServiceTokens -); - -export default router; diff --git a/backend-mongo/src/routes/v2/auth.ts b/backend-mongo/src/routes/v2/auth.ts deleted file mode 100644 index c24324e3f..000000000 --- a/backend-mongo/src/routes/v2/auth.ts +++ /dev/null @@ -1,33 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { body } from "express-validator"; -import { requireMfaAuth, validateRequest } from "../../middleware"; -import { authController } from "../../controllers/v2"; -import { authLimiter } from "../../helpers/rateLimiter"; - -router.post( - // TODO: deprecate (moved to api/v3/auth/login1) - "/login1", - authLimiter, - body("email").isString().trim().notEmpty().toLowerCase(), - body("clientPublicKey").isString().trim().notEmpty(), - validateRequest, - authController.login1 -); - -router.post( - // TODO: deprecate (moved to api/v3/auth/login1) - "/login2", - authLimiter, - body("email").isString().trim().notEmpty().toLowerCase(), - body("clientProof").isString().trim().notEmpty(), - validateRequest, - authController.login2 -); - -//remove above ones after depreciation -router.post("/mfa/send", authLimiter, requireMfaAuth, authController.sendMfaToken); - -router.post("/mfa/verify", authLimiter, requireMfaAuth, authController.verifyMfaToken); - -export default router; diff --git a/backend-mongo/src/routes/v2/environment.ts b/backend-mongo/src/routes/v2/environment.ts deleted file mode 100644 index e5143e6fe..000000000 --- a/backend-mongo/src/routes/v2/environment.ts +++ /dev/null @@ -1,39 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { environmentController } from "../../controllers/v2"; -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; - -router.post( - "/:workspaceId/environments", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - environmentController.createWorkspaceEnvironment -); - -router.put( - "/:workspaceId/environments", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - environmentController.renameWorkspaceEnvironment -); - -router.patch( - "/:workspaceId/environments", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - environmentController.reorderWorkspaceEnvironments -); - -router.delete( - "/:workspaceId/environments", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - environmentController.deleteWorkspaceEnvironment -); - -export default router; diff --git a/backend-mongo/src/routes/v2/index.ts b/backend-mongo/src/routes/v2/index.ts deleted file mode 100644 index 99ad9c01a..000000000 --- a/backend-mongo/src/routes/v2/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -import auth from "./auth"; -import environment from "./environment"; -import membership from "./membership"; -import organizations from "./organizations"; -import secret from "./secret"; // deprecated -import secrets from "./secrets"; -import serviceTokenData from "./serviceTokenData"; -import signup from "./signup"; -import tags from "./tags"; -import users from "./users"; -import workspace from "./workspace"; - -export { - auth, - signup, - users, - organizations, - workspace, - secret, - secrets, - serviceTokenData, - environment, - tags, - membership -}; diff --git a/backend-mongo/src/routes/v2/membership.ts b/backend-mongo/src/routes/v2/membership.ts deleted file mode 100644 index a91af00f2..000000000 --- a/backend-mongo/src/routes/v2/membership.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { membershipController } from "../../controllers/v2"; -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; - -router.post( - "/:workspaceId/memberships", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - membershipController.addUserToWorkspace -); - -export default router; diff --git a/backend-mongo/src/routes/v2/organizations.ts b/backend-mongo/src/routes/v2/organizations.ts deleted file mode 100644 index c66223750..000000000 --- a/backend-mongo/src/routes/v2/organizations.ts +++ /dev/null @@ -1,65 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; -import { organizationsController } from "../../controllers/v2"; - -// TODO: /POST to create membership - -router.get( - "/:organizationId/memberships", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - organizationsController.getOrganizationMemberships -); - -router.patch( - "/:organizationId/memberships/:membershipId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - organizationsController.updateOrganizationMembership -); - -router.delete( - "/:organizationId/memberships/:membershipId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - organizationsController.deleteOrganizationMembership -); - -router.get( - "/:organizationId/workspaces", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - organizationsController.getOrganizationWorkspaces -); - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.createOrganization -); - -router.delete( - "/:organizationId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - organizationsController.deleteOrganizationById -); - -router.get( - "/:organizationId/identity-memberships", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - organizationsController.getOrganizationIdentityMemberships -); - -export default router; diff --git a/backend-mongo/src/routes/v2/secret.ts b/backend-mongo/src/routes/v2/secret.ts deleted file mode 100644 index 1707a927f..000000000 --- a/backend-mongo/src/routes/v2/secret.ts +++ /dev/null @@ -1,147 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { - requireAuth, - requireSecretAuth, - requireWorkspaceAuth, - validateRequest, -} from "../../middleware"; -import { body, param, query } from "express-validator"; -import { - ADMIN, - AuthMode, - MEMBER, - PERMISSION_READ_SECRETS, - PERMISSION_WRITE_SECRETS, -} from "../../variables"; -import { CreateSecretRequestBody, ModifySecretRequestBody } from "../../types/secret"; -import { secretController } from "../../controllers/v2"; - -// note: endpoints deprecated in favor of v3/secrets - -router.post( // TODO endpoint: deprecate (moved to POST api/v3/secrets) - "/batch-create/workspace/:workspaceId/environment/:environment", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params", - }), - param("workspaceId").exists().isMongoId().trim(), - param("environment").exists().trim(), - body("secrets").exists().isArray().custom((value) => value.every((item: CreateSecretRequestBody) => typeof item === "object")), - body("channel"), - validateRequest, - secretController.createSecrets -); - -router.post( - "/workspace/:workspaceId/environment/:environment", // TODO endpoint: deprecate (moved to POST api/v3/secrets) - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params", - }), - param("workspaceId").exists().isMongoId().trim(), - param("environment").exists().trim(), - body("secret").exists().isObject(), - body("channel"), - validateRequest, - secretController.createSecret -); - -router.get( // TODO endpoint: deprecate (moved to GET api/v3/secrets) - "/workspace/:workspaceId", - param("workspaceId").exists().trim(), - query("environment").exists(), - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN], - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params", - }), - query("channel"), - validateRequest, - secretController.getSecrets -); - -router.get( // TODO endpoint: deprecate (moved to POST api/v3/secrets) - "/:secretId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN], - }), - requireSecretAuth({ - acceptedRoles: [ADMIN, MEMBER], - requiredPermissions: [PERMISSION_READ_SECRETS], - }), - validateRequest, - secretController.getSecret -); - -router.delete( // TODO endpoint: deprecate (moved to DELETE api/v3/secrets) - "/batch/workspace/:workspaceId/environment/:environmentName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - param("workspaceId").exists().isMongoId().trim(), - param("environmentName").exists().trim(), - body("secretIds").exists().isArray().custom(array => array.length > 0), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params", - }), - validateRequest, - secretController.deleteSecrets -); - -router.delete( // TODO endpoint: deprecate (moved to DELETE api/v3/secrets) - "/:secretId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - requireSecretAuth({ - acceptedRoles: [ADMIN, MEMBER], - requiredPermissions: [PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS], - }), - param("secretId").isMongoId(), - validateRequest, - secretController.deleteSecret -); - -router.patch( // TODO endpoint: deprecate (moved to PATCH api/v3/secrets) - "/batch-modify/workspace/:workspaceId/environment/:environmentName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - body("secrets").exists().isArray().custom((secrets: ModifySecretRequestBody[]) => secrets.length > 0), - param("workspaceId").exists().isMongoId().trim(), - param("environmentName").exists().trim(), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params", - }), - validateRequest, - secretController.updateSecrets -); - -router.patch( // TODO endpoint: deprecate (moved to PATCH api/v3/secrets) - "/workspace/:workspaceId/environment/:environmentName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT], - }), - body("secret").isObject(), - param("workspaceId").exists().isMongoId().trim(), - param("environmentName").exists().trim(), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params", - }), - validateRequest, - secretController.updateSecret -); - -export default router; diff --git a/backend-mongo/src/routes/v2/secrets.ts b/backend-mongo/src/routes/v2/secrets.ts deleted file mode 100644 index c175335ff..000000000 --- a/backend-mongo/src/routes/v2/secrets.ts +++ /dev/null @@ -1,173 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { - requireAuth, - requireSecretsAuth, - requireWorkspaceAuth, - validateRequest -} from "../../middleware"; -import { body } from "express-validator"; -import { secretsController } from "../../controllers/v2"; -import { - ADMIN, - AuthMode, - MEMBER, - PERMISSION_READ_SECRETS, - PERMISSION_WRITE_SECRETS, - SECRET_PERSONAL, - SECRET_SHARED -} from "../../variables"; - -router.post( - // TODO endpoint: strongly consider deprecation in favor of a single operation experience on dashboard - "/batch", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN] - }), - secretsController.batchSecrets -); - -router.post( - // TODO endpoint: deprecate (moved to POST api/v3/secrets) - "/", - body("workspaceId").exists().isString().trim(), - body("environment").exists().isString().trim(), - body("folderId").default("root").isString().trim(), - body("secretPath").optional().isString().trim(), - body("secrets") - .exists() - .custom((value) => { - if (Array.isArray(value)) { - // case: create multiple secrets - if (value.length === 0) throw new Error("secrets cannot be an empty array"); - for (const secret of value) { - if ( - !secret.type || - !(secret.type === SECRET_PERSONAL || secret.type === SECRET_SHARED) || - !secret.secretKeyCiphertext || - !secret.secretKeyIV || - !secret.secretKeyTag || - typeof secret.secretValueCiphertext !== "string" || - !secret.secretValueIV || - !secret.secretValueTag - ) { - throw new Error( - "secrets array must contain objects that have required secret properties" - ); - } - } - } else if (typeof value === "object") { - // case: update 1 secret - if ( - !value.type || - !(value.type === SECRET_PERSONAL || value.type === SECRET_SHARED) || - !value.secretKeyCiphertext || - !value.secretKeyIV || - !value.secretKeyTag || - !value.secretValueCiphertext || - !value.secretValueIV || - !value.secretValueTag - ) { - throw new Error("secrets object is missing required secret properties"); - } - } else { - throw new Error("secrets must be an object or an array of objects"); - } - - return true; - }), - validateRequest, - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN] - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "body", - locationEnvironment: "body", - requiredPermissions: [PERMISSION_WRITE_SECRETS] - }), - secretsController.createSecrets -); - -router.get( - // TODO endpoint: deprecate (moved to GET api/v3/secrets) - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN] - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "query", - locationEnvironment: "query", - requiredPermissions: [PERMISSION_READ_SECRETS] - }), - secretsController.getSecrets -); - -router.patch( - // TODO endpoint: deprecate (moved to PATCH api/v3/secrets) - "/", - body("secrets") - .exists() - .custom((value) => { - if (Array.isArray(value)) { - // case: update multiple secrets - if (value.length === 0) throw new Error("secrets cannot be an empty array"); - for (const secret of value) { - if (!secret.id) { - throw new Error("Each secret must contain a ID property"); - } - } - } else if (typeof value === "object") { - // case: update 1 secret - if (!value.id) { - throw new Error("secret must contain a ID property"); - } - } else { - throw new Error("secrets must be an object or an array of objects"); - } - - return true; - }), - validateRequest, - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN] - }), - requireSecretsAuth({ - acceptedRoles: [ADMIN, MEMBER], - requiredPermissions: [PERMISSION_WRITE_SECRETS] - }), - secretsController.updateSecrets -); - -router.delete( - // TODO endpoint: deprecate (moved to DELETE api/v3/secrets) - "/", - body("secretIds") - .exists() - .custom((value) => { - // case: delete 1 secret - if (typeof value === "string") return true; - - if (Array.isArray(value)) { - // case: delete multiple secrets - if (value.length === 0) throw new Error("secrets cannot be an empty array"); - return value.every((id: string) => typeof id === "string"); - } - - throw new Error("secretIds must be a string or an array of strings"); - }) - .not() - .isEmpty(), - validateRequest, - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN] - }), - requireSecretsAuth({ - acceptedRoles: [ADMIN, MEMBER], - requiredPermissions: [PERMISSION_WRITE_SECRETS] - }), - secretsController.deleteSecrets -); - -export default router; diff --git a/backend-mongo/src/routes/v2/serviceTokenData.ts b/backend-mongo/src/routes/v2/serviceTokenData.ts deleted file mode 100644 index 2a0760f50..000000000 --- a/backend-mongo/src/routes/v2/serviceTokenData.ts +++ /dev/null @@ -1,33 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { - requireAuth -} from "../../middleware"; -import { AuthMode } from "../../variables"; -import { serviceTokenDataController } from "../../controllers/v2"; - -router.get( // TODO: deprecate (moving to identity) - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.SERVICE_TOKEN] - }), - serviceTokenDataController.getServiceTokenData -); - -router.post( // TODO: deprecate (moving to identity) - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - serviceTokenDataController.createServiceTokenData -); - -router.delete( // TODO: deprecate (moving to identity) - "/:serviceTokenDataId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - serviceTokenDataController.deleteServiceTokenData -); - -export default router; \ No newline at end of file diff --git a/backend-mongo/src/routes/v2/signup.ts b/backend-mongo/src/routes/v2/signup.ts deleted file mode 100644 index 8a404cfc2..000000000 --- a/backend-mongo/src/routes/v2/signup.ts +++ /dev/null @@ -1,51 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { body } from "express-validator"; -import { disableSignUpByServerCfg, requireSignupAuth, validateRequest } from "../../middleware"; -import { signupController } from "../../controllers/v2"; -import { authLimiter } from "../../helpers/rateLimiter"; - -router.post( - "/complete-account/signup", // TODO endpoint: deprecate (moved to v3/signup/complete/account-signup), - disableSignUpByServerCfg, - authLimiter, - requireSignupAuth, - body("email").exists().isString().trim().notEmpty().isEmail(), - body("firstName").exists().isString().trim().notEmpty(), - body("lastName").exists().isString().trim().notEmpty(), - body("protectedKey").exists().isString().trim().notEmpty(), - body("protectedKeyIV").exists().isString().trim().notEmpty(), - body("protectedKeyTag").exists().isString().trim().notEmpty(), - body("publicKey").exists().isString().trim().notEmpty(), - body("encryptedPrivateKey").exists().isString().trim().notEmpty(), - body("encryptedPrivateKeyIV").exists().isString().trim().notEmpty(), - body("encryptedPrivateKeyTag").exists().isString().trim().notEmpty(), - body("salt").exists().isString().trim().notEmpty(), - body("verifier").exists().isString().trim().notEmpty(), - body("organizationName").exists().isString().trim().notEmpty(), - validateRequest, - signupController.completeAccountSignup -); - -router.post( - "/complete-account/invite", // TODO: consider moving to v3/users/new/complete-account/invite - disableSignUpByServerCfg, - authLimiter, - requireSignupAuth, - body("email").exists().isString().trim().notEmpty().isEmail(), - body("firstName").exists().isString().trim().notEmpty(), - body("lastName").exists().isString().trim().notEmpty(), - body("protectedKey").exists().isString().trim().notEmpty(), - body("protectedKeyIV").exists().isString().trim().notEmpty(), - body("protectedKeyTag").exists().isString().trim().notEmpty(), - body("publicKey").exists().trim().notEmpty(), - body("encryptedPrivateKey").exists().isString().trim().notEmpty(), - body("encryptedPrivateKeyIV").exists().isString().trim().notEmpty(), - body("encryptedPrivateKeyTag").exists().isString().trim().notEmpty(), - body("salt").exists().isString().trim().notEmpty(), - body("verifier").exists().isString().trim().notEmpty(), - validateRequest, - signupController.completeAccountInvite -); - -export default router; diff --git a/backend-mongo/src/routes/v2/tags.ts b/backend-mongo/src/routes/v2/tags.ts deleted file mode 100644 index 7926e9452..000000000 --- a/backend-mongo/src/routes/v2/tags.ts +++ /dev/null @@ -1,31 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { tagController } from "../../controllers/v2"; -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; - -router.get( - "/:workspaceId/tags", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - tagController.getWorkspaceTags -); - -router.delete( - "/tags/:tagId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - tagController.deleteWorkspaceTag -); - -router.post( - "/:workspaceId/tags", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - tagController.createWorkspaceTag -); - -export default router; diff --git a/backend-mongo/src/routes/v2/users.ts b/backend-mongo/src/routes/v2/users.ts deleted file mode 100644 index 54c16898f..000000000 --- a/backend-mongo/src/routes/v2/users.ts +++ /dev/null @@ -1,95 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { usersController } from "../../controllers/v2"; -import { AuthMode } from "../../variables"; - -router.patch( - "/me/mfa", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - usersController.updateMyMfaEnabled -); - -router.patch( - "/me/name", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - usersController.updateName -); - -router.put( - "/me/auth-methods", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY], - }), - usersController.updateAuthMethods, -); - -router.get( - "/me/organizations", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - usersController.getMyOrganizations -); - -router.get( // TODO: deprecate (moving to API Key V2) - "/me/api-keys", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - usersController.getMyAPIKeys -); - -router.post( - "/me/api-keys", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - usersController.createAPIKey -); - -router.delete( - "/me/api-keys/:apiKeyDataId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - usersController.deleteAPIKey -); - -router.get( - "/me/sessions", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - usersController.getMySessions -); - -router.delete( - "/me/sessions", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - usersController.deleteMySessions -); - -router.get( - "/me", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - usersController.getMe -); - -router.delete( - "/me", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - usersController.deleteMe -); - -export default router; diff --git a/backend-mongo/src/routes/v2/workspace.ts b/backend-mongo/src/routes/v2/workspace.ts deleted file mode 100644 index f45c38f0c..000000000 --- a/backend-mongo/src/routes/v2/workspace.ts +++ /dev/null @@ -1,129 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { body, param, query } from "express-validator"; -import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware"; -import { ADMIN, AuthMode, MEMBER } from "../../variables"; -import { workspaceController } from "../../controllers/v2"; - -router.post( - // TODO endpoint: deprecate (moved to POST v3/secrets) - "/:workspaceId/secrets", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params" - }), - body("secrets").exists(), - body("keys").exists(), - body("environment").exists().trim().notEmpty(), - body("channel"), - param("workspaceId").exists().trim(), - validateRequest, - workspaceController.pushWorkspaceSecrets -); - -router.get( - // TODO endpoint: deprecate (moved to GET v3/secrets) - "/:workspaceId/secrets", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN] - }), - requireWorkspaceAuth({ - acceptedRoles: [ADMIN, MEMBER], - locationWorkspaceId: "params" - }), - query("environment").exists().trim(), - query("channel"), - param("workspaceId").exists().trim(), - validateRequest, - workspaceController.pullSecrets -); - -router.get( - // TODO endpoint: consider moving to v3/users/me/workspaces/:workspaceId/key - "/:workspaceId/encrypted-key", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] - }), - workspaceController.getWorkspaceKey -); - -router.get( - "/:workspaceId/service-token-data", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.getWorkspaceServiceTokenData -); - -router.get( - // new - TODO: rewire dashboard to this route - "/:workspaceId/memberships", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.getWorkspaceMemberships -); - -router.patch( - // TODO - rewire dashboard to this route - "/:workspaceId/memberships/:membershipId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.updateWorkspaceMembership -); - -router.delete( - // TODO - rewire dashboard to this route - "/:workspaceId/memberships/:membershipId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.deleteWorkspaceMembership -); - -router.patch( - "/:workspaceId/auto-capitalization", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspaceController.toggleAutoCapitalization -); - -router.post( - "/:workspaceId/identity-memberships/:identityId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.addIdentityToWorkspace -); - -router.patch( - "/:workspaceId/identity-memberships/:identityId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.updateIdentityWorkspaceRole -); - -router.delete( - "/:workspaceId/identity-memberships/:identityId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.deleteIdentityFromWorkspace -); - -router.get( - "/:workspaceId/identity-memberships", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - workspaceController.getWorkspaceIdentityMemberships -); - - -export default router; diff --git a/backend-mongo/src/routes/v3/auth.ts b/backend-mongo/src/routes/v3/auth.ts deleted file mode 100644 index 44fdaef4b..000000000 --- a/backend-mongo/src/routes/v3/auth.ts +++ /dev/null @@ -1,11 +0,0 @@ -import express from "express"; -import { authController } from "../../controllers/v3"; -import { authLimiter } from "../../helpers/rateLimiter"; - -const router = express.Router(); - -router.post("/login1", authLimiter, authController.login1); - -router.post("/login2", authLimiter, authController.login2); - -export default router; diff --git a/backend-mongo/src/routes/v3/index.ts b/backend-mongo/src/routes/v3/index.ts deleted file mode 100644 index a2b64294c..000000000 --- a/backend-mongo/src/routes/v3/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import auth from "./auth"; -import users from "./users"; -import secrets from "./secrets"; -import workspaces from "./workspaces"; -import signup from "./signup"; - -export { - auth, - users, - secrets, - signup, - workspaces -} diff --git a/backend-mongo/src/routes/v3/secrets.ts b/backend-mongo/src/routes/v3/secrets.ts deleted file mode 100644 index be6daba2c..000000000 --- a/backend-mongo/src/routes/v3/secrets.ts +++ /dev/null @@ -1,160 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth, requireBlindIndicesEnabled, requireE2EEOff } from "../../middleware"; -import { secretsController } from "../../controllers/v3"; -import { AuthMode } from "../../variables"; - -router.get( - "/raw", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - secretsController.getSecretsRaw -); - -router.get( - "/raw/:secretName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "query" - }), - requireE2EEOff({ - locationWorkspaceId: "query" - }), - secretsController.getSecretByNameRaw -); - -router.post( - "/raw/:secretName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "body" - }), - requireE2EEOff({ - locationWorkspaceId: "body" - }), - secretsController.createSecretRaw -); - -router.patch( - "/raw/:secretName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "body" - }), - requireE2EEOff({ - locationWorkspaceId: "body" - }), - secretsController.updateSecretByNameRaw -); - -router.delete( - "/raw/:secretName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "body" - }), - requireE2EEOff({ - locationWorkspaceId: "body" - }), - secretsController.deleteSecretByNameRaw -); - -router.get( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "query" - }), - secretsController.getSecrets -); - -// akhilmhdh: dont put batch router below the individual operation as those have arbitory name as params -router.post( - "/batch", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "body" - }), - secretsController.createSecretByNameBatch -); - -router.patch( - "/batch", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "body" - }), - secretsController.updateSecretByNameBatch -); - -router.delete( - "/batch", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "body" - }), - secretsController.deleteSecretByNameBatch -); - -router.post( - "/:secretName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "body" - }), - secretsController.createSecret -); - -router.get( - "/:secretName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "query" - }), - secretsController.getSecretByName -); - -router.patch( - "/:secretName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "body" - }), - secretsController.updateSecretByName -); - -router.delete( - "/:secretName", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.API_KEY_V2, AuthMode.SERVICE_TOKEN] - }), - requireBlindIndicesEnabled({ - locationWorkspaceId: "body" - }), - secretsController.deleteSecretByName -); - -export default router; diff --git a/backend-mongo/src/routes/v3/signup.ts b/backend-mongo/src/routes/v3/signup.ts deleted file mode 100644 index e2b13a0a2..000000000 --- a/backend-mongo/src/routes/v3/signup.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { signupController } from "../../controllers/v3"; -import { authLimiter } from "../../helpers/rateLimiter"; -import { disableSignUpByServerCfg, validateRequest } from "../../middleware"; - -router.post( - "/complete-account/signup", // TODO: consider moving endpoint to v3/users/new/complete-account/signup - disableSignUpByServerCfg, - authLimiter, - validateRequest, - signupController.completeAccountSignup -); - -export default router; diff --git a/backend-mongo/src/routes/v3/users.ts b/backend-mongo/src/routes/v3/users.ts deleted file mode 100644 index f465791f8..000000000 --- a/backend-mongo/src/routes/v3/users.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { AuthMode } from "../../variables"; -import { usersController } from "../../controllers/v3"; - -router.get( - "/me/api-keys", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - usersController.getMyAPIKeys -); - -export default router; \ No newline at end of file diff --git a/backend-mongo/src/routes/v3/workspaces.ts b/backend-mongo/src/routes/v3/workspaces.ts deleted file mode 100644 index 834d54cd1..000000000 --- a/backend-mongo/src/routes/v3/workspaces.ts +++ /dev/null @@ -1,37 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../middleware"; -import { workspacesController } from "../../controllers/v3"; -import { AuthMode } from "../../variables"; - -// -- migration to blind indices endpoints - -router.get( - "/:workspaceId/secrets/blind-index-status", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspacesController.getWorkspaceBlindIndexStatus -); - -router.get( - // allow admins to get all workspace secrets (part of blind indices migration) - "/:workspaceId/secrets", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspacesController.getWorkspaceSecrets -); - -router.post( - // allow admins to name all workspace secrets (part of blind indices migration) - "/:workspaceId/secrets/names", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - workspacesController.nameWorkspaceSecrets -); - -// -- - -export default router; diff --git a/backend-mongo/src/services/BotOrgService.ts b/backend-mongo/src/services/BotOrgService.ts deleted file mode 100644 index 070a44058..000000000 --- a/backend-mongo/src/services/BotOrgService.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Types } from "mongoose"; -import { getSymmetricKeyHelper } from "../helpers/botOrg"; - -// TODO: DOCstrings - -class BotOrgService { - static async getSymmetricKey(organizationId: Types.ObjectId) { - return await getSymmetricKeyHelper(organizationId); - } -} - -export default BotOrgService; \ No newline at end of file diff --git a/backend-mongo/src/services/BotService.ts b/backend-mongo/src/services/BotService.ts deleted file mode 100644 index ca75985d2..000000000 --- a/backend-mongo/src/services/BotService.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { Types } from "mongoose"; -import { - decryptSymmetricHelper, - encryptSymmetricHelper, - getIsWorkspaceE2EEHelper, - getKey, - getSecretsBotHelper, - getSecretsCommentBotHelper, -} from "../helpers/bot"; - -/** - * Class to handle bot actions - */ -class BotService { - /** - * Return whether or not workspace with id [workspaceId] is end-to-end encrypted - * @param workspaceId - id of workspace - * @returns {Boolean} - */ - static async getIsWorkspaceE2EE(workspaceId: Types.ObjectId) { - return await getIsWorkspaceE2EEHelper(workspaceId); - } - - /** - * Get workspace key for workspace with id [workspaceId] shared to bot. - * @param {Object} obj - * @param {Types.ObjectId} obj.workspaceId - id of workspace to get workspace key for - * @returns - */ - static async getWorkspaceKeyWithBot({ - workspaceId, - }: { - workspaceId: Types.ObjectId; - }) { - return await getKey({ - workspaceId, - }); - } - - /** - * Return decrypted secrets for workspace with id [workspaceId] and - * environment [environmen] shared to bot. - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace of secrets - * @param {String} obj.environment - environment for secrets - * @returns {Object} secretObj - object where keys are secret keys and values are secret values - */ - static async getSecrets({ - workspaceId, - environment, - secretPath, - }: { - workspaceId: Types.ObjectId; - environment: string; - secretPath: string; - }) { - return await getSecretsBotHelper({ - workspaceId, - environment, - secretPath, - }); - } - - /** - * Return symmetrically encrypted [plaintext] using the - * bot's copy of the workspace key for workspace with id [workspaceId] - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace - * @param {String} obj.plaintext - plaintext to encrypt - */ - static async encryptSymmetric({ - workspaceId, - plaintext, - }: { - workspaceId: Types.ObjectId; - plaintext: string; - }) { - return await encryptSymmetricHelper({ - workspaceId, - plaintext, - }); - } - - /** - * Return symmetrically decrypted [ciphertext] using the - * bot's copy of the workspace key for workspace with id [workspaceId] - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace - * @param {String} obj.ciphertext - ciphertext to decrypt - * @param {String} obj.iv - iv - * @param {String} obj.tag - tag - */ - static async decryptSymmetric({ - workspaceId, - ciphertext, - iv, - tag, - }: { - workspaceId: Types.ObjectId; - ciphertext: string; - iv: string; - tag: string; - }) { - return await decryptSymmetricHelper({ - workspaceId, - ciphertext, - iv, - tag, - }); - } - - /** - * Return decrypted secret comments for workspace with id [worskpaceId] and - * environment [environment] shared to bot. - * @param {Object} obj - * @param {String} obj.workspaceId - id of workspace of secrets - * @param {String} obj.environment - environment for secrets - * @returns {Object} secretObj - object where keys are secret keys and values are comments - */ - static async getSecretComments({ - workspaceId, - environment, - secretPath - }: { - workspaceId: Types.ObjectId; - environment: string; - secretPath: string; - }) { - return await getSecretsCommentBotHelper({ - workspaceId, - environment, - secretPath - }); - } -} - -export default BotService; diff --git a/backend-mongo/src/services/DatabaseService.ts b/backend-mongo/src/services/DatabaseService.ts deleted file mode 100644 index 4b40863d0..000000000 --- a/backend-mongo/src/services/DatabaseService.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - closeDatabaseHelper, - initDatabaseHelper, -} from "../helpers/database"; - -/** - * Class to handle database actions - */ -class DatabaseService { - /** - * Initialize database connection - * @param {Object} obj - * @param {String} obj.mongoURL - mongo connection string - * @returns - */ - static async initDatabase(MONGO_URL: string) { - return await initDatabaseHelper({ - mongoURL: MONGO_URL, - }); - } - - /** - * Close database conection - */ - static async closeDatabase() { - return await closeDatabaseHelper(); - } -} - -export default DatabaseService; \ No newline at end of file diff --git a/backend-mongo/src/services/EventService.ts b/backend-mongo/src/services/EventService.ts deleted file mode 100644 index 7abc7c1b1..000000000 --- a/backend-mongo/src/services/EventService.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Types } from "mongoose"; -import { handleEventHelper } from "../helpers/event"; - -interface Event { - name: string; - workspaceId: Types.ObjectId; - environment?: string; - payload: any; -} - -/** - * Class to handle events. - */ -class EventService { - /** - * Handle event [event] - * @param {Object} obj - * @param {Event} obj.event - an event - * @param {String} obj.event.name - name of event - * @param {String} obj.event.workspaceId - id of workspace that event is part of - * @param {Object} obj.event.payload - payload of event (depends on event) - */ - static async handleEvent({ event }: { event: Event }): Promise { - await handleEventHelper({ - event, - }); - } -} - -export default EventService; \ No newline at end of file diff --git a/backend-mongo/src/services/FolderService.ts b/backend-mongo/src/services/FolderService.ts deleted file mode 100644 index 65aa5b0f3..000000000 --- a/backend-mongo/src/services/FolderService.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { nanoid } from "nanoid"; -import { Types } from "mongoose"; -import { Folder, TFolderSchema } from "../models"; -import path from "path"; - -type TAppendFolderDTO = { - folderName: string; - directory: string; -}; - -type TRenameFolderDTO = { - folderName: string; - folderId: string; -}; - -export const validateFolderName = (folderName: string) => { - const validNameRegex = /^[a-zA-Z0-9-_]+$/; - return validNameRegex.test(folderName); -}; - -export const generateFolderId = (): string => nanoid(12); - -// simple bfs search -export const searchByFolderId = ( - root: TFolderSchema, - folderId: string -): TFolderSchema | undefined => { - const queue = [root]; - while (queue.length) { - const folder = queue.pop() as TFolderSchema; - if (folder.id === folderId) { - return folder; - } - queue.push(...folder.children); - } -}; - -export const folderBfsTraversal = async ( - root: TFolderSchema, - callback: (data: TFolderSchema) => void | Promise -) => { - const queue = [root]; - while (queue.length) { - const folder = queue.pop() as TFolderSchema; - await callback(folder); - queue.push(...folder.children); - } -}; - -// bfs and then append to the folder -const appendChild = (folders: TFolderSchema, folderName: string) => { - const folder = folders.children.find(({ name }) => name === folderName); - if (folder) return { folder, hasCreated: false }; - - const id = generateFolderId(); - folders.version += 1; - folders.children.push({ - id, - name: folderName, - children: [], - version: 1 - }); - // last element that is the new one - return { folder: folders.children[folders.children.length - 1], hasCreated: true }; -}; - -// root of append child wrapper -export const appendFolder = ( - folders: TFolderSchema, - { folderName, directory }: TAppendFolderDTO -): { parent: TFolderSchema; child: TFolderSchema; hasCreated?: boolean } => { - if (directory === "/") { - const newFolder = appendChild(folders, folderName); - return { parent: folders, child: newFolder.folder, hasCreated: newFolder.hasCreated }; - } - - const segments = directory.split("/").filter(Boolean); - const segment = segments.shift(); - if (segment) { - const nestedFolders = appendChild(folders, segment); - return appendFolder(nestedFolders.folder, { - folderName, - directory: path.join("/", ...segments) - }); - } - - const newFolder = appendChild(folders, folderName); - return { parent: folders, child: newFolder.folder, hasCreated: newFolder.hasCreated }; -}; - -export const renameFolder = ( - folders: TFolderSchema, - { folderName, folderId }: TRenameFolderDTO -) => { - const folder = searchByFolderId(folders, folderId); - if (!folder) { - throw new Error("Folder doesn't exist"); - } - - folder.name = folderName; -}; - -// bfs but stops on parent folder -// Then unmount the required child and then return both -export const deleteFolderById = (folders: TFolderSchema, folderId: string) => { - const queue = [folders]; - while (queue.length) { - const folder = queue.pop() as TFolderSchema; - const index = folder.children.findIndex(({ id }) => folderId === id); - if (index !== -1) { - const deletedFolder = folder.children.splice(index, 1); - return { deletedNode: deletedFolder[0], parent: folder }; - } - queue.push(...folder.children); - } -}; - -// bfs but return parent of the folderID -export const getParentFromFolderId = (folders: TFolderSchema, folderId: string) => { - const queue = [folders]; - while (queue.length) { - const folder = queue.pop() as TFolderSchema; - const index = folder.children.findIndex(({ id }) => folderId === id); - if (index !== -1) return folder; - - queue.push(...folder.children); - } -}; - -// to get all folders ids from everything from below nodes -export const getAllFolderIds = (folders: TFolderSchema) => { - const folderIds: Array<{ id: string; name: string }> = []; - const queue = [folders]; - while (queue.length) { - const folder = queue.pop() as TFolderSchema; - folderIds.push({ id: folder.id, name: folder.name }); - queue.push(...folder.children); - } - return folderIds; -}; - -// To get the path of a folder from the root. Used for breadcrumbs -// LOGIC: We do dfs instead if bfs -// Each time we go down we record the current node -// We then record the number of childs of each root node -// When we reach leaf node or when all childs of a root node are visited -// We remove it from path recorded by using the total child record -export const searchByFolderIdWithDir = (folders: TFolderSchema, folderId: string) => { - const stack = [folders]; - const dir: Array<{ id: string; name: string }> = []; - const hits: Record = {}; - - while (stack.length) { - const folder = stack.shift() as TFolderSchema; - // score the hit - hits[folder.id] = folder.children.length; - const parent = dir[dir.length - 1]; - if (parent) hits[parent.id] -= 1; - - if (folder.id === folderId) { - dir.push({ name: folder.name, id: folder.id }); - return { folder, dir }; - } - - if (folder.children.length) { - dir.push({ name: folder.name, id: folder.id }); - stack.unshift(...folder.children); - } else { - if (!hits[parent.id]) { - dir.pop(); - } - } - } - return; -}; - -// used for get folder path from id -export const getFolderWithPathFromId = (folders: TFolderSchema, parentFolderId: string) => { - const search = searchByFolderIdWithDir(folders, parentFolderId); - if (!search) { - throw { message: "Folder permission denied" }; - } - const { folder, dir } = search; - const folderPath = path.join( - "/", - ...dir.filter(({ name }) => name !== "root").map(({ name }) => name) - ); - return { folder, folderPath, dir }; -}; - -// to get folder of a path given -// Like /frontend/folder#1 -export const getFolderByPath = (folders: TFolderSchema, searchPath: string) => { - // corner case when its just / return root - if (searchPath === "/") { - return folders.id === "root" ? folders : undefined; - } - - const path = searchPath.split("/").filter(Boolean); - const queue = [folders]; - let segment: TFolderSchema | undefined; - while (queue.length && path.length) { - const folder = queue.pop(); - const segmentPath = path.shift(); - segment = folder?.children.find(({ name }) => name === segmentPath); - if (!segment) return; - - queue.push(segment); - } - return segment; -}; - -export const getFolderIdFromServiceToken = async ( - workspaceId: Types.ObjectId | string, - environment: string, - secretPath: string -) => { - const folders = await Folder.findOne({ - workspace: workspaceId, - environment - }); - - if (!folders) { - if (secretPath !== "/") throw new Error("Invalid path. Folders not found"); - } else { - const folder = getFolderByPath(folders.nodes, secretPath); - if (!folder) { - throw new Error("Folder not found"); - } - return folder.id; - } - return "root"; -}; diff --git a/backend-mongo/src/services/IntegrationService.ts b/backend-mongo/src/services/IntegrationService.ts deleted file mode 100644 index 06d0426f3..000000000 --- a/backend-mongo/src/services/IntegrationService.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { Types } from "mongoose"; -import { - getIntegrationAuthAccessHelper, - getIntegrationAuthRefreshHelper, - handleOAuthExchangeHelper, - setIntegrationAuthAccessHelper, - setIntegrationAuthRefreshHelper, -} from "../helpers/integration"; -import { syncSecretsToActiveIntegrationsQueue } from "../queues/integrations/syncSecretsToThirdPartyServices"; -import { IIntegrationAuth } from "../models"; - -/** - * Class to handle integrations - */ -class IntegrationService { - - /** - * Perform OAuth2 code-token exchange for workspace with id [workspaceId] and integration - * named [integration] - * - Store integration access and refresh tokens returned from the OAuth2 code-token exchange - * - Add placeholder inactive integration - * - Create bot sequence for integration - * @param {Object} obj1 - * @param {String} obj1.workspaceId - id of workspace - * @param {String} obj1.environment - workspace environment - * @param {String} obj1.integration - name of integration - * @param {String} obj1.code - code - * @returns {IntegrationAuth} integrationAuth - integration authorization after OAuth2 code-token exchange - */ - static async handleOAuthExchange({ - workspaceId, - integration, - code, - environment, - url - }: { - workspaceId: string; - integration: string; - code: string; - environment: string; - url?: string; - }) { - return await handleOAuthExchangeHelper({ - workspaceId, - integration, - code, - environment, - url - }); - } - - /** - * Sync/push environment variables in workspace with id [workspaceId] to - * all associated integrations - * @param {Object} obj - * @param {Object} obj.workspaceId - id of workspace - */ - static syncIntegrations({ - workspaceId, - environment, - }: { - workspaceId: Types.ObjectId; - environment?: string; - }) { - syncSecretsToActiveIntegrationsQueue({ workspaceId: workspaceId.toString(), environment: environment }) - } - - /** - * Return decrypted refresh token for integration auth - * with id [integrationAuthId] - * @param {Object} obj - * @param {String} obj.integrationAuthId - id of integration auth - * @param {String} refreshToken - decrypted refresh token - */ - static async getIntegrationAuthRefresh({ integrationAuthId }: { integrationAuthId: Types.ObjectId }) { - return await getIntegrationAuthRefreshHelper({ - integrationAuthId, - }); - } - - /** - * Return decrypted access token for integration auth - * with id [integrationAuthId] - * @param {Object} obj - * @param {String} obj.integrationAuthId - id of integration auth - * @param {String} accessToken - decrypted access token - */ - static async getIntegrationAuthAccess({ integrationAuthId }: { integrationAuthId: Types.ObjectId }) { - return await getIntegrationAuthAccessHelper({ - integrationAuthId, - }); - } - - /** - * Encrypt refresh token [refreshToken] using the bot's copy - * of the workspace key for workspace belonging to integration auth - * with id [integrationAuthId] - * @param {Object} obj - * @param {String} obj.integrationAuthId - id of integration auth - * @param {String} obj.refreshToken - refresh token - * @returns {IntegrationAuth} integrationAuth - updated integration auth - */ - static async setIntegrationAuthRefresh({ - integrationAuthId, - refreshToken, - }: { - integrationAuthId: string; - refreshToken: string; - }): Promise { - return await setIntegrationAuthRefreshHelper({ - integrationAuthId, - refreshToken, - }); - } - - /** - * Encrypt access token [accessToken] and (optionally) access id using the - * bot's copy of the workspace key for workspace belonging to integration auth - * with id [integrationAuthId] - * @param {Object} obj - * @param {String} obj.integrationAuthId - id of integration auth - * @param {String} obj.accessId - access id - * @param {String} obj.accessToken - access token - * @param {Date} obj.accessExpiresAt - expiration date of access token - * @returns {IntegrationAuth} - updated integration auth - */ - static async setIntegrationAuthAccess({ - integrationAuthId, - accessId, - accessToken, - accessExpiresAt, - }: { - integrationAuthId: string; - accessId?: string; - accessToken?: string; - accessExpiresAt: Date | undefined; - }) { - return await setIntegrationAuthAccessHelper({ - integrationAuthId, - accessId, - accessToken, - accessExpiresAt, - }); - } -} - -export default IntegrationService; \ No newline at end of file diff --git a/backend-mongo/src/services/RedisService.ts b/backend-mongo/src/services/RedisService.ts deleted file mode 100644 index e439ffb40..000000000 --- a/backend-mongo/src/services/RedisService.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Redis, { Redis as TRedis } from "ioredis"; -import { logger } from "../utils/logging"; - -let redisClient: TRedis | null; - -export const initRedis = async () => { - if (process.env.REDIS_URL) { - redisClient = new Redis(process.env.REDIS_URL as string); - } else { - logger.warn("Redis URL not set, skipping Redis initialization."); - redisClient = null; - } -} - - -export { redisClient }; diff --git a/backend-mongo/src/services/SecretImportService.ts b/backend-mongo/src/services/SecretImportService.ts deleted file mode 100644 index b8d622d30..000000000 --- a/backend-mongo/src/services/SecretImportService.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { Types } from "mongoose"; -import { generateSecretBlindIndexHelper } from "../helpers"; -import { SecretVersion } from "../ee/models"; -import { Folder, ISecret, Secret, SecretImport } from "../models"; -import { getFolderByPath } from "./FolderService"; - -type TSecretImportFid = { environment: string; folderId: string; secretPath: string }; - -export const getAnImportedSecret = async ( - secretName: string, - workspaceId: string, - environment: string, - folderId = "root", - version?: number -) => { - const secretBlindIndex = await generateSecretBlindIndexHelper({ - secretName, - workspaceId: new Types.ObjectId(workspaceId) - }); - - const secImports = await SecretImport.findOne({ - workspace: workspaceId, - environment, - folderId - }); - if (!secImports) return; - if (secImports.imports.length === 0) return; - const folders = await Folder.find({ - workspace: workspaceId, - environment: { $in: secImports.imports.map((el) => el.environment) } - }); - - const importedSecByFid: TSecretImportFid[] = []; - secImports.imports.forEach((el) => { - const folder = folders.find((fl) => fl.environment === el.environment); - if (folder) { - const secPathFolder = getFolderByPath(folder.nodes, el.secretPath); - if (secPathFolder) - importedSecByFid.push({ - environment: el.environment, - folderId: secPathFolder.id, - secretPath: el.secretPath - }); - } else { - if (el.secretPath === "/") { - // this happens when importing with a fresh env without any folders - importedSecByFid.push({ environment: el.environment, folderId: "root", secretPath: "/" }); - } - } - }); - if (importedSecByFid.length === 0) return; - - let secret; - if (version === undefined) { - secret = await Secret.findOne({ - workspace: workspaceId, - secretBlindIndex - }).or(importedSecByFid.map(({ environment, folderId }) => ({ environment, folder: folderId }))).lean() - } else { - const secretVersion = await SecretVersion.findOne({ - workspace: workspaceId, - secretBlindIndex, - version - }).or(importedSecByFid.map(({ environment, folderId }) => ({ environment, folder: folderId }))).lean(); - - if (secretVersion) { - secret = await new Secret({ - ...secretVersion, - _id: secretVersion.secret, - }); - } - } - - return secret; -}; - -export const getAllImportedSecrets = async ( - workspaceId: string, - environment: string, - folderId = "root", - permissionCheckCB: (env: string, secPath: string) => boolean -) => { - const secImports = await SecretImport.findOne({ - workspace: workspaceId, - environment, - folderId - }); - if (!secImports) return []; - if (secImports.imports.length === 0) return []; - - const importedEnv: Record = {}; // to get folders from all environment - const allowedSecretImports = secImports.imports.filter((el) => - permissionCheckCB(el.environment, el.secretPath) - ); - allowedSecretImports.forEach((el) => (importedEnv[el.environment] = true)); - - const folders = await Folder.find({ - workspace: workspaceId, - environment: { $in: Object.keys(importedEnv) } - }); - - const importedSecByFid: TSecretImportFid[] = []; - allowedSecretImports.forEach((el) => { - const folder = folders.find((fl) => fl.environment === el.environment); - if (folder) { - const secPathFolder = getFolderByPath(folder.nodes, el.secretPath); - if (secPathFolder) - importedSecByFid.push({ - environment: el.environment, - folderId: secPathFolder.id, - secretPath: el.secretPath - }); - } else { - if (el.secretPath === "/") { - // this happens when importing with a fresh env without any folders - importedSecByFid.push({ environment: el.environment, folderId: "root", secretPath: "/" }); - } - } - }); - if (importedSecByFid.length === 0) return []; - - const secsGroupedByRef = await Secret.aggregate([ - { - $match: { - workspace: new Types.ObjectId(workspaceId), - type: "shared" - } - }, - { - $group: { - _id: { - environment: "$environment", - folderId: "$folder" - }, - secrets: { $push: "$$ROOT" } - } - }, - { - $match: { - $or: importedSecByFid.map(({ environment, folderId: fid }) => ({ - "_id.environment": environment, - "_id.folderId": fid - })) - } - } - ]); - - // now let stitch together secrets. - const importedSecrets: Array = []; - importedSecByFid.forEach(({ environment, folderId, secretPath }) => { - const secretsGrouped = secsGroupedByRef.find( - (el) => el._id.environment === environment && el._id.folderId === folderId - ); - if (secretsGrouped) { - importedSecrets.push({ secretPath, folderId, environment, secrets: secretsGrouped.secrets }); - } - }); - return importedSecrets; -}; diff --git a/backend-mongo/src/services/SecretService.ts b/backend-mongo/src/services/SecretService.ts deleted file mode 100644 index 109fe1507..000000000 --- a/backend-mongo/src/services/SecretService.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { Types } from "mongoose"; -import { - CreateSecretBatchParams, - CreateSecretParams, - DeleteSecretBatchParams, - DeleteSecretParams, - GetSecretParams, - GetSecretsParams, - UpdateSecretBatchParams, - UpdateSecretParams -} from "../interfaces/services/SecretService"; -import { - createSecretBatchHelper, - createSecretBlindIndexDataHelper, - createSecretHelper, - deleteSecretBatchHelper, - deleteSecretHelper, - generateSecretBlindIndexHelper, - generateSecretBlindIndexWithSaltHelper, - getSecretBlindIndexSaltHelper, - getSecretHelper, - getSecretsHelper, - updateSecretBatchHelper, - updateSecretHelper -} from "../helpers/secrets"; - -class SecretService { - /** - * Create secret blind index data containing encrypted blind index salt - * for workspace with id [workspaceId] - * @param {Object} obj - * @param {Buffer} obj.salt - 16-byte random salt - * @param {Types.ObjectId} obj.workspaceId - */ - static async createSecretBlindIndexData({ workspaceId }: { workspaceId: Types.ObjectId }) { - return await createSecretBlindIndexDataHelper({ - workspaceId - }); - } - - /** - * Get secret blind index salt for workspace with id [workspaceId] - * @param {Object} obj - * @param {Types.ObjectId} obj.workspaceId - id of workspace to get salt for - * @returns - */ - static async getSecretBlindIndexSalt({ workspaceId }: { workspaceId: Types.ObjectId }) { - return await getSecretBlindIndexSaltHelper({ - workspaceId - }); - } - - /** - * Generate blind index for secret with name [secretName] - * and salt [salt] - * @param {Object} obj - * @param {Object} obj.secretName - name of secret to generate blind index for - * @param {String} obj.salt - base64-salt - */ - static async generateSecretBlindIndexWithSalt({ - secretName, - salt - }: { - secretName: string; - salt: string; - }) { - return await generateSecretBlindIndexWithSaltHelper({ - secretName, - salt - }); - } - - /** - * Create and return blind index for secret with - * name [secretName] part of workspace with id [workspaceId] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to generate blind index for - * @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to - */ - static async generateSecretBlindIndex({ - secretName, - workspaceId - }: { - secretName: string; - workspaceId: Types.ObjectId; - }) { - return await generateSecretBlindIndexHelper({ - secretName, - workspaceId - }); - } - - /** - * Create secret with name [secretName] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to create - * @param {Types.ObjectId} obj.workspaceId - id of workspace to create secret for - * @param {String} obj.environment - environment in workspace to create secret for - * @param {'shared' | 'personal'} obj.type - type of secret - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ - static async createSecret(createSecretParams: CreateSecretParams) { - return await createSecretHelper(createSecretParams); - } - - /** - * Get secrets for workspace with id [workspaceId] and environment [environment] - * @param {Object} obj - * @param {Types.ObjectId} obj.workspaceId - id of workspace - * @param {String} obj.environment - environment in workspace - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ - static async getSecrets(getSecretsParams: GetSecretsParams) { - return await getSecretsHelper(getSecretsParams); - } - - /** - * Get secret with name [secretName] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to get - * @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to - * @param {String} obj.environment - environment in workspace that secret belongs to - * @param {'shared' | 'personal'} obj.type - type of secret - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ - static async getSecret(getSecretParams: GetSecretParams) { - // TODO(akhilmhdh) The one above is diff. Change this to some other name - return await getSecretHelper(getSecretParams); - } - - /** - * Update secret with name [secretName] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to update - * @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to - * @param {String} obj.environment - environment in workspace that secret belongs to - * @param {'shared' | 'personal'} obj.type - type of secret - * @param {String} obj.secretValueCiphertext - ciphertext of secret value - * @param {String} obj.secretValueIV - IV of secret value - * @param {String} obj.secretValueTag - tag of secret value - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ - static async updateSecret(updateSecretParams: UpdateSecretParams) { - return await updateSecretHelper(updateSecretParams); - } - - /** - * Delete secret with name [secretName] - * @param {Object} obj - * @param {String} obj.secretName - name of secret to delete - * @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to - * @param {String} obj.environment - environment in workspace that secret belongs to - * @param {'shared' | 'personal'} obj.type - type of secret - * @param {AuthData} obj.authData - authentication data on request - * @returns - */ - static async deleteSecret(deleteSecretParams: DeleteSecretParams) { - return await deleteSecretHelper(deleteSecretParams); - } - - static async createSecretBatch(createSecretParams: CreateSecretBatchParams) { - return await createSecretBatchHelper(createSecretParams); - } - - static async updateSecretBatch(updateSecretParams: UpdateSecretBatchParams) { - return await updateSecretBatchHelper(updateSecretParams); - } - - static async deleteSecretBatch(deleteSecretParams: DeleteSecretBatchParams) { - return await deleteSecretBatchHelper(deleteSecretParams); - } -} - -export default SecretService; diff --git a/backend-mongo/src/services/TelemetryService.ts b/backend-mongo/src/services/TelemetryService.ts deleted file mode 100644 index 60cb93216..000000000 --- a/backend-mongo/src/services/TelemetryService.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { PostHog } from "posthog-node"; -import { logger } from "../utils/logging"; -import { AuthData } from "../interfaces/middleware"; -import { - getNodeEnv, - getPostHogHost, - getPostHogProjectApiKey, - getTelemetryEnabled, -} from "../config"; -import { - Identity, - ServiceTokenData, - User -} from "../models"; -import { - AccountNotFoundError, -} from "../utils/errors"; - -class Telemetry { - /** - * Logs telemetry enable/disable notice. - */ - static logTelemetryMessage = async () => { - - if (!(await getTelemetryEnabled())) { - [ - "To improve, Infisical collects telemetry data about general usage.", - "This helps us understand how the product is doing and guide our product development to create the best possible platform; it also helps us demonstrate growth as we support Infisical as open-source software.", - "To opt into telemetry, you can set `TELEMETRY_ENABLED=true` within the environment variables.", - ].forEach(line => logger.info(line)); - } - } - - /** - * Return an instance of the PostHog client initialized. - * @returns - */ - static getPostHogClient = async () => { - let postHogClient: any; - if ((await getNodeEnv()) === "production" && (await getTelemetryEnabled())) { - // case: enable opt-out telemetry in production - postHogClient = new PostHog(await getPostHogProjectApiKey(), { - host: await getPostHogHost(), - }); - } - - return postHogClient; - } - - static getDistinctId = async ({ - authData, - }: { - authData: AuthData; - }) => { - - let distinctId = ""; - if (authData.authPayload instanceof User) { - distinctId = authData.authPayload.email; - } else if (authData.authPayload instanceof ServiceTokenData) { - if (authData.authPayload.user) { - const user = await User.findById(authData.authPayload.user, "email"); - if (!user) throw AccountNotFoundError(); - distinctId = user.email; - } - } else if (authData.authPayload instanceof Identity) { - distinctId = `identity-${authData.authPayload._id.toString()}` - } else { - distinctId = "unknown-auth-data" - } - - return distinctId; - } -} - -export default Telemetry; \ No newline at end of file diff --git a/backend-mongo/src/services/TokenService.ts b/backend-mongo/src/services/TokenService.ts deleted file mode 100644 index 7d0ff881d..000000000 --- a/backend-mongo/src/services/TokenService.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Types } from "mongoose"; -import { createTokenHelper, validateTokenHelper } from "../helpers/token"; - -/** - * Class to handle token actions - * TODO: elaborate more on this class - */ -class TokenService { - /** - * Create a token [token] for type [type] with associated details - * @param {Object} obj - * @param {String} obj.type - type or context of token (e.g. emailConfirmation) - * @param {String} obj.email - email associated with the token - * @param {String} obj.phoneNumber - phone number associated with the token - * @param {Types.ObjectId} obj.organizationId - id of organization associated with the token - * @returns {String} token - the token to create - */ - static async createToken({ - type, - email, - phoneNumber, - organizationId, - }: { - type: "emailConfirmation" | "emailMfa" | "organizationInvitation" | "passwordReset"; - email?: string; - phoneNumber?: string; - organizationId?: Types.ObjectId; - }) { - return await createTokenHelper({ - type, - email, - phoneNumber, - organizationId, - }); - } - - /** - * Validate whether or not token [token] and its associated details match a token in the DB - * @param {Object} obj - * @param {String} obj.type - type or context of token (e.g. emailConfirmation) - * @param {String} obj.email - email associated with the token - * @param {String} obj.phoneNumber - phone number associated with the token - * @param {Types.ObjectId} obj.organizationId - id of organization associated with the token - * @param {String} obj.token - the token to validate - */ - static async validateToken({ - type, - email, - phoneNumber, - organizationId, - token, - }: { - type: "emailConfirmation" | "emailMfa" | "organizationInvitation" | "passwordReset"; - email?: string; - phoneNumber?: string; - organizationId?: Types.ObjectId; - token: string; - }) { - return await validateTokenHelper({ - type, - email, - phoneNumber, - organizationId, - token, - }); - } -} - -export default TokenService; \ No newline at end of file diff --git a/backend-mongo/src/services/WebhookService.ts b/backend-mongo/src/services/WebhookService.ts deleted file mode 100644 index cc2106a1c..000000000 --- a/backend-mongo/src/services/WebhookService.ts +++ /dev/null @@ -1,109 +0,0 @@ -import axios from "axios"; -import crypto from "crypto"; -import { Types } from "mongoose"; -import picomatch from "picomatch"; -import { client, getEncryptionKey, getRootEncryptionKey } from "../config"; -import { IWebhook, Webhook } from "../models"; -import { decryptSymmetric128BitHexKeyUTF8 } from "../utils/crypto"; -import { ENCODING_SCHEME_BASE64, ENCODING_SCHEME_UTF8 } from "../variables"; - -export const triggerWebhookRequest = async ( - { url, encryptedSecretKey, iv, tag, keyEncoding }: IWebhook, - payload: Record -) => { - const headers: Record = {}; - payload["timestamp"] = Date.now(); - - if (encryptedSecretKey) { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - let secretKey; - if (rootEncryptionKey && keyEncoding === ENCODING_SCHEME_BASE64) { - // case: encoding scheme is base64 - secretKey = client.decryptSymmetric(encryptedSecretKey, rootEncryptionKey, iv, tag); - } else if (encryptionKey && keyEncoding === ENCODING_SCHEME_UTF8) { - // case: encoding scheme is utf8 - secretKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: encryptedSecretKey, - iv: iv, - tag: tag, - key: encryptionKey - }); - } - if (secretKey) { - const webhookSign = crypto - .createHmac("sha256", secretKey) - .update(JSON.stringify(payload)) - .digest("hex"); - headers["x-infisical-signature"] = `t=${payload["timestamp"]};${webhookSign}`; - } - } - const req = await axios.post(url, payload, { headers }); - return req; -}; - -export const getWebhookPayload = ( - eventName: string, - workspaceId: string, - environment: string, - secretPath?: string -) => ({ - event: eventName, - project: { - workspaceId, - environment, - secretPath - } -}); - -export const triggerWebhook = async ( - workspaceId: string, - environment: string, - secretPath: string -) => { - const webhooks = await Webhook.find({ workspace: workspaceId, environment, isDisabled: false }); - // TODO(akhilmhdh): implement retry policy later, for that a cron job based approach is needed - // for exponential backoff - const toBeTriggeredHooks = webhooks.filter(({ secretPath: hookSecretPath }) => - picomatch.isMatch(secretPath, hookSecretPath, { strictSlashes: false }) - ); - const webhooksTriggered = await Promise.allSettled( - toBeTriggeredHooks.map((hook) => - triggerWebhookRequest( - hook, - getWebhookPayload("secrets.modified", workspaceId, environment, secretPath) - ) - ) - ); - const successWebhooks: Types.ObjectId[] = []; - const failedWebhooks: Array<{ id: Types.ObjectId; error: string }> = []; - webhooksTriggered.forEach((data, index) => { - if (data.status === "rejected") { - failedWebhooks.push({ id: toBeTriggeredHooks[index]._id, error: data.reason.message }); - return; - } - successWebhooks.push(toBeTriggeredHooks[index]._id); - }); - // dont remove the workspaceid and environment filter. its used to reduce the dataset before $in check - await Webhook.bulkWrite([ - { - updateMany: { - filter: { workspace: workspaceId, environment, _id: { $in: successWebhooks } }, - update: { lastStatus: "success", lastRunErrorMessage: null } - } - }, - ...failedWebhooks.map(({ id, error }) => ({ - updateOne: { - filter: { - workspace: workspaceId, - environment, - _id: id - }, - update: { - lastStatus: "failed", - lastRunErrorMessage: error - } - } - })) - ]); -}; diff --git a/backend-mongo/src/services/health.ts b/backend-mongo/src/services/health.ts deleted file mode 100644 index b9cb90fa2..000000000 --- a/backend-mongo/src/services/health.ts +++ /dev/null @@ -1,32 +0,0 @@ -import mongoose from "mongoose"; -import { createTerminus } from "@godaddy/terminus"; -import { logger } from "../utils/logging"; - -export const setUpHealthEndpoint = (server: T) => { - const onSignal = async () => { - logger.info("Server is starting clean-up"); - return Promise.all([ - new Promise((resolve) => { - if (mongoose.connection && mongoose.connection.readyState == 1) { - mongoose.connection.close() - .then(() => resolve("Database connection closed")); - } else { - resolve("Database connection already closed"); - } - }), - ]); - }; - - const healthCheck = () => { - // `state.isShuttingDown` (boolean) shows whether the server is shutting down or not - // optionally include a resolve value to be included as info in the health check response - return Promise.resolve(); - }; - - createTerminus(server, { - healthChecks: { - "/healthcheck": healthCheck, - onSignal, - }, - }); -}; diff --git a/backend-mongo/src/services/index.ts b/backend-mongo/src/services/index.ts deleted file mode 100644 index 781fb435c..000000000 --- a/backend-mongo/src/services/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import DatabaseService from "./DatabaseService"; -// import { logTelemetryMessage, getPostHogClient } from './TelemetryService'; -import TelemetryService from "./TelemetryService"; -import BotService from "./BotService"; -import BotOrgService from "./BotOrgService"; -import EventService from "./EventService"; -import IntegrationService from "./IntegrationService"; -import TokenService from "./TokenService"; -import SecretService from "./SecretService"; - -export { - TelemetryService, - DatabaseService, - BotService, - BotOrgService, - EventService, - IntegrationService, - TokenService, - SecretService, -} diff --git a/backend-mongo/src/services/smtp.ts b/backend-mongo/src/services/smtp.ts deleted file mode 100644 index 027b81295..000000000 --- a/backend-mongo/src/services/smtp.ts +++ /dev/null @@ -1,82 +0,0 @@ -import nodemailer from "nodemailer"; -import { - SMTP_HOST_GMAIL, - SMTP_HOST_MAILGUN, - SMTP_HOST_OFFICE365, - SMTP_HOST_SENDGRID, - SMTP_HOST_SOCKETLABS, - SMTP_HOST_ZOHOMAIL -} from "../variables"; -import SMTPConnection from "nodemailer/lib/smtp-connection"; -import { - getSmtpHost, - getSmtpPassword, - getSmtpPort, - getSmtpSecure, - getSmtpUsername -} from "../config"; - -export const initSmtp = async () => { - const mailOpts: SMTPConnection.Options = { - host: await getSmtpHost(), - port: await getSmtpPort() - }; - - if ((await getSmtpUsername()) && (await getSmtpPassword())) { - mailOpts.auth = { - user: await getSmtpUsername(), - pass: await getSmtpPassword() - }; - } - - if ((await getSmtpSecure()) ? await getSmtpSecure() : false) { - switch (await getSmtpHost()) { - case SMTP_HOST_SENDGRID: - mailOpts.requireTLS = true; - break; - case SMTP_HOST_MAILGUN: - mailOpts.requireTLS = true; - mailOpts.tls = { - ciphers: "TLSv1.2" - }; - break; - case SMTP_HOST_SOCKETLABS: - mailOpts.requireTLS = true; - mailOpts.tls = { - ciphers: "TLSv1.2" - }; - break; - case SMTP_HOST_ZOHOMAIL: - mailOpts.requireTLS = true; - mailOpts.tls = { - ciphers: "TLSv1.2" - }; - break; - case SMTP_HOST_GMAIL: - mailOpts.requireTLS = true; - mailOpts.tls = { - ciphers: "TLSv1.2" - }; - break; - case SMTP_HOST_OFFICE365: - mailOpts.requireTLS = true; - mailOpts.tls = { - ciphers: "TLSv1.2" - }; - break; - default: - if ((await getSmtpHost()).includes("amazonaws.com")) { - mailOpts.tls = { - ciphers: "TLSv1.2" - }; - } else { - mailOpts.secure = true; - } - break; - } - } - - const transporter = nodemailer.createTransport(mailOpts); - - return transporter; -}; diff --git a/backend-mongo/src/templates/emailMfa.handlebars b/backend-mongo/src/templates/emailMfa.handlebars deleted file mode 100644 index 489c9dd30..000000000 --- a/backend-mongo/src/templates/emailMfa.handlebars +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - MFA Code - - - -

Infisical

-

Sign in attempt requires further verification

-

Your MFA code is below โ€” enter it where you started signing in to Infisical.

-

{{code}}

-

The MFA code will be valid for 2 minutes.

-

Not you? Contact Infisical or your administrator immediately.

- - - \ No newline at end of file diff --git a/backend-mongo/src/templates/emailVerification.handlebars b/backend-mongo/src/templates/emailVerification.handlebars deleted file mode 100644 index fc738d202..000000000 --- a/backend-mongo/src/templates/emailVerification.handlebars +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - Code - - - -

Confirm your email address

-

Your confirmation code is below โ€” enter it in the browser window where you've started signing up for Infisical.

-

{{code}}

-

Questions about setting up Infisical? Email us at support@infisical.com

- - - \ No newline at end of file diff --git a/backend-mongo/src/templates/historicalSecretLeakIncident.handlebars b/backend-mongo/src/templates/historicalSecretLeakIncident.handlebars deleted file mode 100644 index 3cb517a57..000000000 --- a/backend-mongo/src/templates/historicalSecretLeakIncident.handlebars +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - Incident alert: secrets potentially leaked - - - -

Infisical has uncovered {{numberOfSecrets}} secret(s) from historical commits to your repo

-

View leaked secrets

- -

If these are production secrets, please rotate them immediately.

- -

Once you have taken action, be sure to update the status of the risk in your Infisical - dashboard.

- - - \ No newline at end of file diff --git a/backend-mongo/src/templates/newDevice.handlebars b/backend-mongo/src/templates/newDevice.handlebars deleted file mode 100644 index 654bb1ba3..000000000 --- a/backend-mongo/src/templates/newDevice.handlebars +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - Successful login for {{email}} from new device - - - -

Infisical

-

We're verifying a recent login for {{email}}:

-

Timestamp: {{timestamp}}

-

IP address: {{ip}}

-

User agent: {{userAgent}}

-

If you believe that this login is suspicious, please contact Infisical or reset your password immediately.

- - - \ No newline at end of file diff --git a/backend-mongo/src/templates/organizationInvitation.handlebars b/backend-mongo/src/templates/organizationInvitation.handlebars deleted file mode 100644 index b281786f4..000000000 --- a/backend-mongo/src/templates/organizationInvitation.handlebars +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Organization Invitation - - -

Join your organization on Infisical

-

{{inviterFirstName}} ({{inviterEmail}}) has invited you to their Infisical organization โ€” {{organizationName}}

- Join now -

What is Infisical?

-

Infisical is an easy-to-use end-to-end encrypted tool that enables developers to sync and manage their secrets and configs.

- - \ No newline at end of file diff --git a/backend-mongo/src/templates/passwordReset.handlebars b/backend-mongo/src/templates/passwordReset.handlebars deleted file mode 100644 index 3b136e859..000000000 --- a/backend-mongo/src/templates/passwordReset.handlebars +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Account Recovery - - -

Reset your password

-

Someone requested a password reset.

- Reset password -

If you didn't initiate this request, please contact us immediately at team@infisical.com

- - \ No newline at end of file diff --git a/backend-mongo/src/templates/secretLeakIncident.handlebars b/backend-mongo/src/templates/secretLeakIncident.handlebars deleted file mode 100644 index 1bf2d3175..000000000 --- a/backend-mongo/src/templates/secretLeakIncident.handlebars +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - Incident alert: secret leaked - - - -

Infisical has uncovered {{numberOfSecrets}} secret(s) from your recent push

-

View leaked secrets

-

You are receiving this notification because one or more secret leaks have been detected in a recent commit pushed - by {{pusher_name}} ({{pusher_email}}). If - these are test secrets, please add `infisical-scan:ignore` at the end of the line containing the secret as comment - in the given programming. This will prevent future notifications from being sent out for those secret(s).

- -

If these are production secrets, please rotate them immediately.

- -

Once you have taken action, be sure to update the status of the risk in your Infisical - dashboard.

- - - \ No newline at end of file diff --git a/backend-mongo/src/templates/secretReminder.handlebars b/backend-mongo/src/templates/secretReminder.handlebars deleted file mode 100644 index 58f738534..000000000 --- a/backend-mongo/src/templates/secretReminder.handlebars +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - Secret Reminder - - - -

Infisical

-

You have a new secret reminder!

-

You have a new secret reminder from workspace "{{workspaceName}}", in {{organizationName}}

- {{#if reminderNote}} -

Here's the note included with the reminder: {{reminderNote}}

- {{/if}} - - - \ No newline at end of file diff --git a/backend-mongo/src/templates/workspaceInvitation.handlebars b/backend-mongo/src/templates/workspaceInvitation.handlebars deleted file mode 100644 index 60556555c..000000000 --- a/backend-mongo/src/templates/workspaceInvitation.handlebars +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Project Invitation - - -

Join your team on Infisical

-

{{inviterFirstName}} ({{inviterEmail}}) has invited you to their Infisical project โ€” {{workspaceName}}

- Join now -

What is Infisical?

-

Infisical is an easy-to-use end-to-end encrypted tool that enables developers to sync and manage their secrets and configs.

- - \ No newline at end of file diff --git a/backend-mongo/src/types/express/index.d.ts b/backend-mongo/src/types/express/index.d.ts deleted file mode 100644 index 654a24f1d..000000000 --- a/backend-mongo/src/types/express/index.d.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Types } from "mongoose"; -import { - AuthData -} from "../../interfaces/middleware"; - -declare module "express" { - interface Request { - user?: any; - } -} - -// TODO: fix (any) types -declare global { - namespace Express { - interface Request { - clientIp: any; - user: any; - workspace: any; - membership: any; - targetMembership: any; - isUserCompleted: boolean; - providerAuthToken: any; - organization: any; - membershipOrg: any; - integration: any; - integrationAuth: any; - bot: any; - _secret: any; - secrets: any; - secretSnapshot: any; - serviceToken: any; - accessToken: any; - accessId: any; - serviceTokenData: any; - apiKeyData: any; - query?: any; - tokenVersionId?: Types.ObjectId; - authData: AuthData; - realIP: string; - requestData: { - [key: string]: string - }; - } - } -} diff --git a/backend-mongo/src/types/secret/index.d.ts b/backend-mongo/src/types/secret/index.d.ts deleted file mode 100644 index 05016b38e..000000000 --- a/backend-mongo/src/types/secret/index.d.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Assign, Omit } from "utility-types"; -import { ISecret } from "../../models"; - -// Everything is required, except the omitted types -export type CreateSecretRequestBody = Omit< - ISecret, - "user" | "version" | "environment" | "workspace" ->; - -// Omit the listed properties, then make everything optional and then make _id required -export type ModifySecretRequestBody = Assign< - Partial>, - { _id: string } ->; - -// Used for modeling sanitized secrets before uplaod. To be used for converting user input for uploading -export type SanitizedSecretModify = Partial< - Omit ->; - -// Everything is required, except the omitted types -export type SanitizedSecretForCreate = Omit; - -export interface BatchSecretRequest { - id: string; - method: "POST" | "PATCH" | "DELETE"; - secret: Secret; -} - -export interface BatchSecret { - version?: number; - _id?: string; - user?: string; - environment: string; - workspace?: string; - algorithm?: string; - keyEncoding?: string; - type: "shared" | "personal"; - secretName: string; - secretBlindIndex: string; - secretKeyCiphertext: string; - secretKeyIV: string; - secretKeyTag: string; - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - secretCommentCiphertext: string; - secretCommentIV: string; - secretCommentTag: string; - tags: string[]; - folder: string; -} diff --git a/backend-mongo/src/utils/addDevelopmentUser.ts b/backend-mongo/src/utils/addDevelopmentUser.ts deleted file mode 100644 index b5b0f3495..000000000 --- a/backend-mongo/src/utils/addDevelopmentUser.ts +++ /dev/null @@ -1,147 +0,0 @@ -/************************************************************************************************ -* -* Attention: The credentials below are only for development purposes, it should never be used for production -* -************************************************************************************************/ - -import { Key, Membership, MembershipOrg, Organization, User, Workspace } from "../models"; -import { SecretService } from "../services"; -import { Types } from "mongoose"; -import { getNodeEnv } from "../config"; - -export const testUserEmail = "test@localhost.local" -export const testUserPassword = "testInfisical1" -export const testUserId = "63cefa6ec8d3175601cfa980" -export const testWorkspaceId = "63cefb15c8d3175601cfa989" -export const testOrgId = "63cefb15c8d3175601cfa985" -export const testMembershipId = "63cefb159185d9aa3ef0cf35" -export const testMembershipOrgId = "63cefb159185d9aa3ef0cf31" -export const testWorkspaceKeyId = "63cf48f0225e6955acec5eff" -export const plainTextWorkspaceKey = "543fef8224813a46230b0a50a46c5fb2" - -export const createTestUserForDevelopment = async () => { - if ((await getNodeEnv()) === "development" || (await getNodeEnv()) === "test") { - const testUser = { - _id: testUserId, - email: testUserEmail, - refreshVersion: 0, - encryptedPrivateKey: "ITMdDXtLoxib4+53U/qzvIV/T/UalRwimogFCXv/UsulzEoiKM+aK2aqOb0=", - firstName: "Jake", - iv: "9fp0dZHI+UuHeKkWMDvD6w==", - lastName: "Moni", - publicKey: "cf44BhkybbBfsE0fZHe2jvqtCj6KLXvSq4hVjV0svzk=", - salt: "d8099dc70958090346910fb9639262b83cf526fc9b4555a171b36a9e1bcd0240", - tag: "bQ/UTghqcQHRoSMpLQD33g==", - verifier: "12271fcd50937ca4512e1e3166adaf9d9fc7a5cd0e4c4cb3eda89f35572ede4d9eef23f64aef9220367abff9437b0b6fa55792c442f177201d87051cf77dadade254ff667170440327355fb7d6ac4745d4db302f4843632c2ed5919ebdcff343287a4cd552255d9e3ce81177edefe089617b7616683901475d393405f554634b9bf9230c041ac85624f37a60401be20b78044932580ae0868323be3749fbf856df1518153ba375fec628275f0c445f237446ea4aa7f12c1aa1d6b5fd74b7f2e88d062845a19819ec63f2d2ed9e9f37c055149649461d997d2ae1482f53b04f9de7493efbb9686fb19b2d559b9aa2b502c22dec83f9fc43290dfea89a1dc6f03580b3642b3824513853e81a441be9a0b2fde2231bac60f3287872617a36884697805eeea673cf1a351697834484ada0f282e4745015c9c2928d61e6d092f1b9c3a27eda8413175d23bb2edae62f82ccaf52bf5a6a90344a766c7e4ebf65dae9ae90b2ad4ae65dbf16e3a6948e429771cc50307ae86d454f71a746939ed061f080dd3ae369c1a0739819aca17af46a085bac1f2a5d936d198e7951a8ac3bb38b893665fe7312835abd3f61811f81efa2a8761af5070085f9b6adcca80bf9b0d81899c3d41487fba90728bb24eceb98bd69770360a232624133700ceb4d153f2ad702e0a5b7dfaf97d20bc8aa71dc8c20024a58c06a8fecdad18cb5a2f89c51eaf7", - } - - const testWorkspaceKey = { - _id: new Types.ObjectId(testWorkspaceKeyId), - workspace: testWorkspaceId, - encryptedKey: "96ZIRSU21CjVzIQ4Yp994FGWQvDdyK3gq+z+NCaJLK0ByTlvUePmf+AYGFJjkAdz", - nonce: "1jhCGqg9Wx3n0OtVxbDgiYYGq4S3EdgO", - sender: "63cefa6ec8d3175601cfa980", - receiver: "63cefa6ec8d3175601cfa980", - } - - const testWorkspace = { - _id: new Types.ObjectId(testWorkspaceId), - name: "Example Project", - organization: testOrgId, - environments: [ - { - _id: "63cefb15c8d3175601cfa98a", - name: "Development", - slug: "dev", - }, - { - _id: "63cefb15c8d3175601cfa98b", - name: "Test", - slug: "test", - }, - { - _id: "63cefb15c8d3175601cfa98c", - name: "Staging", - slug: "staging", - }, - { - _id: "63cefb15c8d3175601cfa98d", - name: "Production", - slug: "prod", - }, - ], - } - - const testOrg = { - _id: testOrgId, - name: "Jake's organization", - } - - const testMembershipOrg = { - _id: testMembershipOrgId, - organization: testOrgId, - role: "admin", - status: "accepted", - user: testUserId, - } - - const testMembership = { - _id: testMembershipId, - role: "admin", - user: testUserId, - workspace: testWorkspaceId, - } - - try { - // create user if not exist - const userInDB = await User.findById(testUserId) - if (!userInDB) { - await User.create(testUser) - } - - // create org if not exist - const orgInDB = await Organization.findById(testOrgId) - if (!orgInDB) { - await Organization.create(testOrg) - } - - // create membership org if not exist - const membershipOrgInDB = await MembershipOrg.findById(testMembershipOrgId) - if (!membershipOrgInDB) { - await MembershipOrg.create(testMembershipOrg) - } - - // create membership - const membershipInDB = await Membership.findById(testMembershipId) - if (!membershipInDB) { - await Membership.create(testMembership) - } - - // create workspace if not exist - const workspaceInDB = await Workspace.findById(testWorkspaceId) - if (!workspaceInDB) { - const workspace = await Workspace.create(testWorkspace) - - // initialize blind index salt for workspace - await SecretService.createSecretBlindIndexData({ - workspaceId: workspace._id, - }); - } - - // create workspace key if not exist - const workspaceKeyInDB = await Key.findById(testWorkspaceKeyId) - if (!workspaceKeyInDB) { - await Key.create(testWorkspaceKey) - } - - /* eslint-disable no-console */ - console.info(`DEVELOPMENT MODE DETECTED: You may login with test user with email: ${testUserEmail} and password: ${testUserPassword}`) - /* eslint-enable no-console */ - - } catch (e) { - /* eslint-disable no-console */ - console.error(`Unable to create test user while booting up [err=${e}]`) - /* eslint-enable no-console */ - } - } -} \ No newline at end of file diff --git a/backend-mongo/src/utils/aes-gcm.ts b/backend-mongo/src/utils/aes-gcm.ts deleted file mode 100644 index 21734611b..000000000 --- a/backend-mongo/src/utils/aes-gcm.ts +++ /dev/null @@ -1,41 +0,0 @@ -import crypto = require("crypto"); - -const ALGORITHM = "aes-256-gcm"; -const BLOCK_SIZE_BYTES = 16; - -export default class AesGCM { - static encrypt( - text: string, - secret: string - ): { ciphertext: string; iv: string; tag: string } { - const iv = crypto.randomBytes(BLOCK_SIZE_BYTES); - const cipher = crypto.createCipheriv(ALGORITHM, secret, iv); - - let ciphertext = cipher.update(text, "utf8", "base64"); - ciphertext += cipher.final("base64"); - return { - ciphertext, - iv: iv.toString("base64"), - tag: cipher.getAuthTag().toString("base64"), - }; - } - - static decrypt( - ciphertext: string, - iv: string, - tag: string, - secret: string - ): string { - const decipher = crypto.createDecipheriv( - ALGORITHM, - secret, - Buffer.from(iv, "base64") - ); - decipher.setAuthTag(Buffer.from(tag, "base64")); - - let cleartext = decipher.update(ciphertext, "base64", "utf8"); - cleartext += decipher.final("utf8"); - - return cleartext; - } -} diff --git a/backend-mongo/src/utils/authn/authModeValidators/apiKey.ts b/backend-mongo/src/utils/authn/authModeValidators/apiKey.ts deleted file mode 100644 index a1bfd28b8..000000000 --- a/backend-mongo/src/utils/authn/authModeValidators/apiKey.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Types } from "mongoose"; -import { - APIKeyData, - IUser, - User -} from "../../../models"; -import { AccountNotFoundError, UnauthorizedRequestError } from "../../errors"; -import bcrypt from "bcrypt"; - -interface ValidateAPIKeyParams { - authTokenValue: string; -} - -export const validateAPIKey = async ({ - authTokenValue -}: ValidateAPIKeyParams) => { - - const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3); - - let apiKeyData = await APIKeyData - .findById(TOKEN_IDENTIFIER, "+secretHash +expiresAt") - .populate<{ user: IUser }>("user", "+publicKey"); - - if (!apiKeyData) { - throw UnauthorizedRequestError(); - } else if (apiKeyData?.expiresAt && new Date(apiKeyData.expiresAt) < new Date()) { - // case: API key expired - await APIKeyData.findByIdAndDelete(apiKeyData._id); - throw UnauthorizedRequestError(); - } - - const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKeyData.secretHash); - if (!isMatch) throw UnauthorizedRequestError(); - - apiKeyData = await APIKeyData.findOneAndUpdate({ - _id: new Types.ObjectId(TOKEN_IDENTIFIER), - }, { - lastUsed: new Date(), - }, { - new: true, - }); - - if (!apiKeyData) throw UnauthorizedRequestError(); - - const user = await User.findById(apiKeyData.user).select("+publicKey"); - - if (!user) throw AccountNotFoundError(); - - return user; -} \ No newline at end of file diff --git a/backend-mongo/src/utils/authn/authModeValidators/apiKeyV2.ts b/backend-mongo/src/utils/authn/authModeValidators/apiKeyV2.ts deleted file mode 100644 index 2b57542b9..000000000 --- a/backend-mongo/src/utils/authn/authModeValidators/apiKeyV2.ts +++ /dev/null @@ -1,39 +0,0 @@ -import jwt from "jsonwebtoken"; -import { APIKeyDataV2, User } from "../../../models"; -import { getAuthSecret } from "../../../config"; -import { AuthTokenType } from "../../../variables"; -import { AccountNotFoundError, UnauthorizedRequestError } from "../../errors"; - -interface ValidateAPIKeyV2Params { - authTokenValue: string; -} - -export const validateAPIKeyV2 = async ({ - authTokenValue -}: ValidateAPIKeyV2Params) => { - - const decodedToken = ( - jwt.verify(authTokenValue, await getAuthSecret()) - ); - - if (decodedToken.authTokenType !== AuthTokenType.API_KEY) throw UnauthorizedRequestError(); - - const apiKeyData = await APIKeyDataV2.findByIdAndUpdate( - decodedToken.apiKeyDataId, - { - lastUsed: new Date(), - $inc: { usageCount: 1 } - }, - { - new: true - } - ); - - if (!apiKeyData) throw UnauthorizedRequestError(); - - const user = await User.findById(apiKeyData.user).select("+publicKey"); - - if (!user) throw AccountNotFoundError(); - - return user; -} diff --git a/backend-mongo/src/utils/authn/authModeValidators/identity.ts b/backend-mongo/src/utils/authn/authModeValidators/identity.ts deleted file mode 100644 index c85227e32..000000000 --- a/backend-mongo/src/utils/authn/authModeValidators/identity.ts +++ /dev/null @@ -1,104 +0,0 @@ -import jwt from "jsonwebtoken"; -import { IIdentity, IdentityAccessToken } from "../../../models"; -import { getAuthSecret } from "../../../config"; -import { AuthTokenType } from "../../../variables"; -import { UnauthorizedRequestError } from "../../errors"; -import { checkIPAgainstBlocklist } from "../../../utils/ip"; - -interface ValidateIdentityParams { - authTokenValue: string; - ipAddress: string; -} - -export const validateIdentity = async ({ - authTokenValue, - ipAddress -}: ValidateIdentityParams) => { - const decodedToken = ( - jwt.verify(authTokenValue, await getAuthSecret()) - ); - - if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) throw UnauthorizedRequestError(); - - const identityAccessToken = await IdentityAccessToken - .findOne({ - _id: decodedToken.identityAccessTokenId, - isAccessTokenRevoked: false - }) - .populate<{ identity: IIdentity }>("identity"); - - if (!identityAccessToken || !identityAccessToken?.identity) throw UnauthorizedRequestError(); - - const { - accessTokenNumUsesLimit, - accessTokenNumUses, - accessTokenTTL, - accessTokenLastRenewedAt, - accessTokenMaxTTL, - createdAt: accessTokenCreatedAt - } = identityAccessToken; - - checkIPAgainstBlocklist({ - ipAddress, - trustedIps: identityAccessToken.accessTokenTrustedIps - }); - - // ttl check - if (accessTokenTTL > 0) { - const currentDate = new Date(); - if (accessTokenLastRenewedAt) { - // access token has been renewed - const accessTokenRenewed = new Date(accessTokenLastRenewedAt); - const ttlInMilliseconds = accessTokenTTL * 1000; - const expirationDate = new Date(accessTokenRenewed.getTime() + ttlInMilliseconds); - - if (currentDate > expirationDate) throw UnauthorizedRequestError({ - message: "Failed to authenticate identity access token due to TTL expiration" - }); - } else { - // access token has never been renewed - const accessTokenCreated = new Date(accessTokenCreatedAt); - const ttlInMilliseconds = accessTokenTTL * 1000; - const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds); - - if (currentDate > expirationDate) throw UnauthorizedRequestError({ - message: "Failed to authenticate identity access token due to TTL expiration" - }); - } - } - - // max ttl check - if (accessTokenMaxTTL > 0) { - const accessTokenCreated = new Date(accessTokenCreatedAt); - const ttlInMilliseconds = accessTokenMaxTTL * 1000; - const currentDate = new Date(); - const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds); - - if (currentDate > expirationDate) throw UnauthorizedRequestError({ - message: "Failed to authenticate identity access token due to Max TTL expiration" - }); - } - - // num uses check - if ( - accessTokenNumUsesLimit > 0 - && accessTokenNumUses === accessTokenNumUsesLimit - ) { - throw UnauthorizedRequestError({ - message: "Failed to authenticate MI access token due to access token number of uses limit reached" - }); - } - - await IdentityAccessToken.findByIdAndUpdate( - identityAccessToken._id, - { - accessTokenLastUsedAt: new Date(), - $inc: { accessTokenNumUses: 1 } - }, - { - new: true - } - ); - - return identityAccessToken.identity; -} \ No newline at end of file diff --git a/backend-mongo/src/utils/authn/authModeValidators/index.ts b/backend-mongo/src/utils/authn/authModeValidators/index.ts deleted file mode 100644 index 170a8ce59..000000000 --- a/backend-mongo/src/utils/authn/authModeValidators/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./apiKey"; -export * from "./apiKeyV2"; -export * from "./jwt"; -export * from "./serviceTokenV2"; -export * from "./identity"; \ No newline at end of file diff --git a/backend-mongo/src/utils/authn/authModeValidators/jwt.ts b/backend-mongo/src/utils/authn/authModeValidators/jwt.ts deleted file mode 100644 index f9f0971fc..000000000 --- a/backend-mongo/src/utils/authn/authModeValidators/jwt.ts +++ /dev/null @@ -1,41 +0,0 @@ -import jwt from "jsonwebtoken"; -import { Types } from "mongoose"; -import { TokenVersion, User } from "../../../models"; -import { getAuthSecret } from "../../../config"; -import { AuthTokenType } from "../../../variables"; -import { AccountNotFoundError, UnauthorizedRequestError } from "../../errors"; - -interface ValidateJWTParams { - authTokenValue: string; -} - -export const validateJWT = async ({ - authTokenValue -}: ValidateJWTParams) => { - - const decodedToken = ( - jwt.verify(authTokenValue, await getAuthSecret()) - ); - - if (decodedToken.authTokenType !== AuthTokenType.ACCESS_TOKEN) throw UnauthorizedRequestError(); - - const tokenVersion = await TokenVersion.findOneAndUpdate({ - _id: new Types.ObjectId(decodedToken.tokenVersionId), - user: decodedToken.userId - }, { - lastUsed: new Date(), - }); - - if (!tokenVersion) throw UnauthorizedRequestError(); - if (decodedToken.accessVersion !== tokenVersion.accessVersion) throw UnauthorizedRequestError(); - - const user = await User.findOne({ - _id: new Types.ObjectId(decodedToken.userId), - }).select("+publicKey"); - - if (!user) throw AccountNotFoundError({ message: "Failed to find user" }); - - if (!user?.publicKey) throw UnauthorizedRequestError({ message: "Failed to authenticate user with partially set up account" }); - - return user; -} diff --git a/backend-mongo/src/utils/authn/authModeValidators/serviceTokenV2.ts b/backend-mongo/src/utils/authn/authModeValidators/serviceTokenV2.ts deleted file mode 100644 index 0ebed5963..000000000 --- a/backend-mongo/src/utils/authn/authModeValidators/serviceTokenV2.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Types } from "mongoose"; -import { ServiceTokenData } from "../../../models"; -import { ResourceNotFoundError, UnauthorizedRequestError } from "../../errors"; -import bcrypt from "bcrypt"; - -interface ValidateServiceTokenV2Params { - authTokenValue: string; -} - -export const validateServiceTokenV2 = async ({ - authTokenValue -}: ValidateServiceTokenV2Params) => { - const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3); - - const serviceTokenData = await ServiceTokenData - .findById(TOKEN_IDENTIFIER, "+secretHash +expiresAt") - - if (!serviceTokenData) { - throw UnauthorizedRequestError(); - } else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) { - // case: service token expired - await ServiceTokenData.findByIdAndDelete(serviceTokenData._id); - throw UnauthorizedRequestError({ - message: "Failed to authenticate expired service token", - }); - } - - const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash); - if (!isMatch) throw UnauthorizedRequestError(); - - const serviceTokenDataToReturn = await ServiceTokenData - .findOneAndUpdate({ - _id: new Types.ObjectId(TOKEN_IDENTIFIER), - }, { - lastUsed: new Date(), - }, { - new: true, - }) - .select("+encryptedKey +iv +tag") - - if (!serviceTokenDataToReturn) throw ResourceNotFoundError(); - - return serviceTokenDataToReturn; -} \ No newline at end of file diff --git a/backend-mongo/src/utils/authn/helpers/authDataExtractors.ts b/backend-mongo/src/utils/authn/helpers/authDataExtractors.ts deleted file mode 100644 index e928108c3..000000000 --- a/backend-mongo/src/utils/authn/helpers/authDataExtractors.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { AuthData } from "../../../interfaces/middleware"; -import { - Identity, - ServiceTokenData, - User -} from "../../../models"; - -/** - * Returns an object containing the id of the authentication data payload - * @param {AuthData} authData - authentication data object - * @returns - */ - export const getAuthDataPayloadIdObj = (authData: AuthData) => { - if (authData.authPayload instanceof User) { - return { userId: authData.authPayload._id }; - } - - if (authData.authPayload instanceof ServiceTokenData) { - return { serviceTokenDataId: authData.authPayload._id }; - } - - if (authData.authPayload instanceof Identity) { - return { serviceTokenDataId: authData.authPayload._id }; - } -}; - -/** - * Returns an object containing the user associated with the authentication data payload - * @param {AuthData} authData - authentication data object - * @returns - */ -export const getAuthDataPayloadUserObj = (authData: AuthData) => { - if (authData.authPayload instanceof User) { - return { user: authData.authPayload._id }; - } - - if (authData.authPayload instanceof ServiceTokenData) { - return { user: authData.authPayload.user }; - } - - if (authData.authPayload instanceof Identity) { - return {}; - } -} \ No newline at end of file diff --git a/backend-mongo/src/utils/authn/helpers/index.ts b/backend-mongo/src/utils/authn/helpers/index.ts deleted file mode 100644 index d3df5ffa5..000000000 --- a/backend-mongo/src/utils/authn/helpers/index.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { AuthData } from "../../../interfaces/middleware"; -import jwt from "jsonwebtoken"; -import { getAuthSecret } from "../../../config"; -import { ActorType } from "../../../ee/models"; -import { AuthMode, AuthTokenType } from "../../../variables"; -import { UnauthorizedRequestError } from "../../errors"; -import { - validateAPIKey, - validateAPIKeyV2, - validateIdentity, - validateJWT, - validateServiceTokenV2 -} from "../authModeValidators"; -import { getUserAgentType } from "../../posthog"; - -export * from "./authDataExtractors"; - -interface ExtractAuthModeParams { - headers: { [key: string]: string | string[] | undefined }; -} - -interface ExtractAuthModeReturn { - authMode: AuthMode; - authTokenValue: string; -} - -interface GetAuthDataParams { - authMode: AuthMode; - authTokenValue: string; - ipAddress: string; - userAgent: string; -} - -/** - * Returns the recognized authentication mode based on token in [headers]; accepted token types include: - * - SERVICE_TOKEN - * - API_KEY - * - JWT - * - IDENTITY_ACCESS_TOKEN (from identity) - * - API_KEY_V2 - * @param {Object} params - * @param {Object.} params.headers - The HTTP request headers, usually from Express's `req.headers`. - * @returns {Promise} The derived authentication mode based on the headers. - * @throws {UnauthorizedError} Throws an error if no applicable authMode is found. - */ -export const extractAuthMode = async ({ - headers -}: ExtractAuthModeParams): Promise => { - const apiKey = headers["x-api-key"] as string; - const authHeader = headers["authorization"] as string; - - if (apiKey) { - return { authMode: AuthMode.API_KEY, authTokenValue: apiKey }; - } - - if (!authHeader) - throw UnauthorizedRequestError({ - message: "Failed to authenticate unknown authentication method" - }); - - if (!authHeader.startsWith("Bearer ")) - throw UnauthorizedRequestError({ - message: "Failed to authenticate unknown authentication method" - }); - - const authTokenValue = authHeader.slice(7); - - if (authTokenValue.startsWith("st.")) { - return { authMode: AuthMode.SERVICE_TOKEN, authTokenValue }; - } - - const decodedToken = jwt.verify(authTokenValue, await getAuthSecret()); - - switch (decodedToken.authTokenType) { - case AuthTokenType.ACCESS_TOKEN: - return { authMode: AuthMode.JWT, authTokenValue }; - case AuthTokenType.API_KEY: - return { authMode: AuthMode.API_KEY_V2, authTokenValue }; - case AuthTokenType.IDENTITY_ACCESS_TOKEN: - return { authMode: AuthMode.IDENTITY_ACCESS_TOKEN, authTokenValue }; - default: - throw UnauthorizedRequestError({ - message: "Failed to authenticate unknown authentication method" - }); - } -}; - -export const getAuthData = async ({ - authMode, - authTokenValue, - ipAddress, - userAgent -}: GetAuthDataParams): Promise => { - const userAgentType = getUserAgentType(userAgent); - - switch (authMode) { - case AuthMode.SERVICE_TOKEN: { - const serviceTokenData = await validateServiceTokenV2({ - authTokenValue - }); - - return { - actor: { - type: ActorType.SERVICE, - metadata: { - serviceId: serviceTokenData._id.toString(), - name: serviceTokenData.name - } - }, - authPayload: serviceTokenData, - ipAddress, - userAgent, - userAgentType - }; - } - case AuthMode.IDENTITY_ACCESS_TOKEN: { - const identity = await validateIdentity({ - authTokenValue, - ipAddress - }); - - return { - actor: { - type: ActorType.IDENTITY, - metadata: { - identityId: identity._id.toString(), - name: identity.name - } - }, - authPayload: identity, - ipAddress, - userAgent, - userAgentType - }; - } - case AuthMode.API_KEY: { - const user = await validateAPIKey({ - authTokenValue - }); - - return { - actor: { - type: ActorType.USER, - metadata: { - userId: user._id.toString(), - email: user.email - } - }, - authPayload: user, - ipAddress, - userAgent, - userAgentType - }; - } - case AuthMode.API_KEY_V2: { - const user = await validateAPIKeyV2({ - authTokenValue - }); - - return { - actor: { - type: ActorType.USER, - metadata: { - userId: user._id.toString(), - email: user.email - } - }, - authPayload: user, - ipAddress, - userAgent, - userAgentType - }; - } - case AuthMode.JWT: { - const user = await validateJWT({ - authTokenValue - }); - - return { - actor: { - type: ActorType.USER, - metadata: { - userId: user._id.toString(), - email: user.email - } - }, - authPayload: user, - ipAddress, - userAgent, - userAgentType - }; - } - } -}; diff --git a/backend-mongo/src/utils/authn/passport/github.ts b/backend-mongo/src/utils/authn/passport/github.ts deleted file mode 100644 index 2f0a9b1ab..000000000 --- a/backend-mongo/src/utils/authn/passport/github.ts +++ /dev/null @@ -1,60 +0,0 @@ -import express from "express"; -import passport from "passport"; -import { - getClientIdGitHubLogin, - getClientSecretGitHubLogin, -} from "../../../config"; -import { standardRequest } from "../../../config/request"; -import { AuthMethod } from "../../../models"; -import { INTEGRATION_GITHUB_API_URL } from "../../../variables"; -import { handleSSOUserTokenFlow } from "./helpers"; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const GitHubStrategy = require("passport-github").Strategy; - -export const initializeGitHubStrategy = async () => { - const clientIdGitHubLogin = await getClientIdGitHubLogin(); - const clientSecretGitHubLogin = await getClientSecretGitHubLogin(); - if (clientIdGitHubLogin && clientSecretGitHubLogin) { - passport.use( - new GitHubStrategy({ - passReqToCallback: true, - clientID: clientIdGitHubLogin, - clientSecret: clientSecretGitHubLogin, - callbackURL: "/api/v1/sso/github", - scope: ["user:email"] - }, async (req : express.Request, accessToken : any, refreshToken : any, profile : any, done : any) => { - interface GitHubEmail { - email: string; - primary: boolean; - verified: boolean; - visibility: null | string; - } - - const { data }: { data: GitHubEmail[] } = await standardRequest.get( - `${INTEGRATION_GITHUB_API_URL}/user/emails`, - { - headers: { - Authorization: `Bearer ${accessToken}` - } - } - ); - - const primaryEmail = data.filter((gitHubEmail: GitHubEmail) => gitHubEmail.primary)[0]; - const email = primaryEmail.email; - - const { isUserCompleted, providerAuthToken } = await handleSSOUserTokenFlow({ - email, - firstName: profile.displayName, - lastName: "", - authMethod: AuthMethod.GITHUB, - callbackPort: req.query.state as string - }); - - req.isUserCompleted = isUserCompleted; - req.providerAuthToken = providerAuthToken; - return done(null, profile); - }) - ); - } -} \ No newline at end of file diff --git a/backend-mongo/src/utils/authn/passport/gitlab.ts b/backend-mongo/src/utils/authn/passport/gitlab.ts deleted file mode 100644 index 22851a450..000000000 --- a/backend-mongo/src/utils/authn/passport/gitlab.ts +++ /dev/null @@ -1,44 +0,0 @@ -import express from "express"; -import passport from "passport"; -import { - getClientIdGitLabLogin, - getClientSecretGitLabLogin, - getUrlGitLabLogin -} from "../../../config"; -import { AuthMethod } from "../../../models"; -import { handleSSOUserTokenFlow } from "./helpers"; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const GitLabStrategy = require("passport-gitlab2").Strategy; - -export const initializeGitLabStrategy = async () => { - const urlGitLab = await getUrlGitLabLogin(); - const clientIdGitLabLogin = await getClientIdGitLabLogin(); - const clientSecretGitLabLogin = await getClientSecretGitLabLogin(); - - if (urlGitLab && clientIdGitLabLogin && clientSecretGitLabLogin) { - passport.use( - new GitLabStrategy({ - passReqToCallback: true, - clientID: clientIdGitLabLogin, - clientSecret: clientSecretGitLabLogin, - callbackURL: "/api/v1/sso/gitlab", - baseURL: urlGitLab - }, async (req : express.Request, accessToken : any, refreshToken : any, profile : any, done : any) => { - const email = profile.emails[0].value; - - const { isUserCompleted, providerAuthToken } = await handleSSOUserTokenFlow({ - email, - firstName: profile.displayName, - lastName: "", - authMethod: AuthMethod.GITLAB, - callbackPort: req.query.state as string - }); - - req.isUserCompleted = isUserCompleted; - req.providerAuthToken = providerAuthToken; - return done(null, profile); - }) - ); - } -} \ No newline at end of file diff --git a/backend-mongo/src/utils/authn/passport/google.ts b/backend-mongo/src/utils/authn/passport/google.ts deleted file mode 100644 index 126f2f9fb..000000000 --- a/backend-mongo/src/utils/authn/passport/google.ts +++ /dev/null @@ -1,48 +0,0 @@ -import express from "express"; -import passport from "passport"; -import { getClientIdGoogleLogin, getClientSecretGoogleLogin } from "../../../config"; -import { AuthMethod } from "../../../models"; - -import { handleSSOUserTokenFlow } from "./helpers"; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const GoogleStrategy = require("passport-google-oauth20").Strategy; - -export const initializeGoogleStrategy = async () => { - const clientIdGoogleLogin = await getClientIdGoogleLogin(); - const clientSecretGoogleLogin = await getClientSecretGoogleLogin(); - - if (clientIdGoogleLogin && clientSecretGoogleLogin) { - passport.use(new GoogleStrategy({ - passReqToCallback: true, - clientID: clientIdGoogleLogin, - clientSecret: clientSecretGoogleLogin, - callbackURL: "/api/v1/sso/google", - scope: ["profile", " email"], - }, async ( - req: express.Request, - accessToken: string, - refreshToken: string, - profile: any, - done: any - ) => { - try { - const email = profile.emails[0].value; - - const { isUserCompleted, providerAuthToken } = await handleSSOUserTokenFlow({ - email, - firstName: profile.name.givenName, - lastName: profile.name.familyName, - authMethod: AuthMethod.GOOGLE, - callbackPort: req.query.state as string - }); - - req.isUserCompleted = isUserCompleted; - req.providerAuthToken = providerAuthToken; - done(null, profile); - } catch (err) { - done(null, false); - } - })); - } -} \ No newline at end of file diff --git a/backend-mongo/src/utils/authn/passport/helpers.ts b/backend-mongo/src/utils/authn/passport/helpers.ts deleted file mode 100644 index b6e672caa..000000000 --- a/backend-mongo/src/utils/authn/passport/helpers.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { AuthMethod, User } from "../../../models"; -import { createToken } from "../../../helpers/auth"; -import { AuthTokenType } from "../../../variables"; -import { getAuthSecret, getJwtProviderAuthLifetime } from "../../../config"; -import { getServerConfig } from "../../../config/serverConfig"; - -interface SSOUserTokenFlowParams { - email: string; - firstName: string; - lastName: string; - authMethod: AuthMethod; - callbackPort?: string; -} - -export const handleSSOUserTokenFlow = async ({ - email, - firstName, - lastName, - authMethod, - callbackPort -}: SSOUserTokenFlowParams) => { - let user = await User.findOne({ - email - }).select("+publicKey"); - - const serverCfg = getServerConfig(); - if (!user && !serverCfg.allowSignUp) throw new Error("User signup disabled"); - - if (!user) { - user = await new User({ - email, - authMethods: [authMethod], - firstName, - lastName - }).save(); - } - - let isLinkingRequired = false; - if (!user.authMethods.includes(authMethod)) { - isLinkingRequired = true; - } - - const isUserCompleted = !!user.publicKey; - const providerAuthToken = createToken({ - payload: { - authTokenType: AuthTokenType.PROVIDER_TOKEN, - userId: user._id.toString(), - email: user.email, - firstName: user.firstName, - lastName: user.lastName, - authMethod, - isUserCompleted, - isLinkingRequired, - ...(callbackPort - ? { - callbackPort - } - : {}) - }, - expiresIn: await getJwtProviderAuthLifetime(), - secret: await getAuthSecret() - }); - - return { isUserCompleted, providerAuthToken }; -}; diff --git a/backend-mongo/src/utils/authn/passport/index.ts b/backend-mongo/src/utils/authn/passport/index.ts deleted file mode 100644 index 4346c7d67..000000000 --- a/backend-mongo/src/utils/authn/passport/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { initializeGoogleStrategy } from "./google"; -export { initializeGitHubStrategy } from "./github"; -export { initializeGitLabStrategy } from "./gitlab"; -export { initializeSamlStrategy } from "./saml"; diff --git a/backend-mongo/src/utils/authn/passport/saml.ts b/backend-mongo/src/utils/authn/passport/saml.ts deleted file mode 100644 index 74a2242e2..000000000 --- a/backend-mongo/src/utils/authn/passport/saml.ts +++ /dev/null @@ -1,174 +0,0 @@ -import passport from "passport"; -import { - getAuthSecret, - getJwtProviderAuthLifetime, - getSiteURL -} from "../../../config"; -import { - AuthMethod, - MembershipOrg, - Organization, - User -} from "../../../models"; -import { - createToken -} from "../../../helpers/auth"; -import { - ACCEPTED, - AuthTokenType, - INVITED, - MEMBER -} from "../../../variables"; -import { Types } from "mongoose"; -import { getSSOConfigHelper } from "../../../ee/helpers/organizations"; -import { InternalServerError, OrganizationNotFoundError } from "../../errors"; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const { MultiSamlStrategy } = require("@node-saml/passport-saml"); - -export const initializeSamlStrategy = async () => { - passport.use("saml", new MultiSamlStrategy( - { - passReqToCallback: true, - getSamlOptions: async (req: any, done: any) => { - const { ssoIdentifier } = req.params; - - const ssoConfig = await getSSOConfigHelper({ - ssoConfigId: new Types.ObjectId(ssoIdentifier) - }); - - interface ISAMLConfig { - callbackUrl: string; - entryPoint: string; - issuer: string; - cert: string; - audience: string; - wantAuthnResponseSigned?: boolean; - } - - const samlConfig: ISAMLConfig = ({ - callbackUrl: `${await getSiteURL()}/api/v1/sso/saml2/${ssoIdentifier}`, - entryPoint: ssoConfig.entryPoint, - issuer: ssoConfig.issuer, - cert: ssoConfig.cert, - audience: await getSiteURL() - }); - - if (ssoConfig.authProvider.toString() === AuthMethod.JUMPCLOUD_SAML.toString()) { - samlConfig.wantAuthnResponseSigned = false; - } - - if (ssoConfig.authProvider.toString() === AuthMethod.AZURE_SAML.toString()) { - if (req.body.RelayState && JSON.parse(req.body.RelayState).spInitiated) { - samlConfig.audience = `spn:${ssoConfig.issuer}`; - } - } - - req.ssoConfig = ssoConfig; - - done(null, samlConfig); - }, - }, - async (req: any, profile: any, done: any) => { - if (!req.ssoConfig.isActive) return done(InternalServerError()); - - const organization = await Organization.findById(req.ssoConfig.organization); - - if (!organization) return done(OrganizationNotFoundError()); - - const email = profile?.email ?? profile?.emailAddress // emailRippling is added because in Rippling the field `email` reserved - const firstName = profile.firstName; - const lastName = profile.lastName; - - let user = await User.findOne({ - email - }).select("+publicKey"); - - if (user) { - // if user does not have SAML enabled then update - const hasSamlEnabled = user.authMethods - .some( - (authMethod: AuthMethod) => [ - AuthMethod.OKTA_SAML, - AuthMethod.AZURE_SAML, - AuthMethod.JUMPCLOUD_SAML - ].includes(authMethod) - ); - - if (!hasSamlEnabled) { - await User.findByIdAndUpdate( - user._id, - { - authMethods: [req.ssoConfig.authProvider] - }, - { - new: true - } - ); - } - - let membershipOrg = await MembershipOrg.findOne( - { - user: user._id, - organization: organization._id - } - ); - - if (!membershipOrg) { - membershipOrg = await new MembershipOrg({ - inviteEmail: email, - user: user._id, - organization: organization._id, - role: MEMBER, - status: ACCEPTED - }).save(); - } - - if (membershipOrg.status === INVITED) { - membershipOrg.status = ACCEPTED; - await membershipOrg.save(); - } - } else { - user = await new User({ - email, - authMethods: [req.ssoConfig.authProvider], - firstName, - lastName - }).save(); - - await new MembershipOrg({ - inviteEmail: email, - user: user._id, - organization: organization._id, - role: MEMBER, - status: INVITED - }).save(); - } - - const isUserCompleted = !!user.publicKey; - const providerAuthToken = createToken({ - payload: { - authTokenType: AuthTokenType.PROVIDER_TOKEN, - userId: user._id.toString(), - email: user.email, - firstName, - lastName, - organizationName: organization?.name, - organizationId: organization?._id, - authMethod: req.ssoConfig.authProvider, - isUserCompleted, - ...(req.body.RelayState ? { - callbackPort: JSON.parse(req.body.RelayState).callbackPort as string - } : {}) - }, - expiresIn: await getJwtProviderAuthLifetime(), - secret: await getAuthSecret(), - }); - - req.isUserCompleted = isUserCompleted; - req.providerAuthToken = providerAuthToken; - - done(null, profile); - } - )); -} \ No newline at end of file diff --git a/backend-mongo/src/utils/crypto/index.ts b/backend-mongo/src/utils/crypto/index.ts deleted file mode 100644 index 9194bd7e8..000000000 --- a/backend-mongo/src/utils/crypto/index.ts +++ /dev/null @@ -1,165 +0,0 @@ -import crypto from "crypto"; -import nacl from "tweetnacl"; -import util from "tweetnacl-util"; -import { - IDecryptAsymmetricInput, - IDecryptSymmetricInput, - IEncryptAsymmetricInput, - IEncryptAsymmetricOutput, - IEncryptSymmetricInput, - IGenerateKeyPairOutput, -} from "../../interfaces/utils"; -import { BadRequestError } from "../errors"; -import { - ALGORITHM_AES_256_GCM, - BLOCK_SIZE_BYTES_16, -} from "../../variables"; - -/** - * Return new base64, NaCl, public-private key pair. - * @returns {Object} obj - * @returns {String} obj.publicKey - (base64) NaCl, public key - * @returns {String} obj.privateKey - (base64), NaCl, private key - */ -const generateKeyPair = (): IGenerateKeyPairOutput => { - const pair = nacl.box.keyPair(); - - return ({ - publicKey: util.encodeBase64(pair.publicKey), - privateKey: util.encodeBase64(pair.secretKey), - }); -} - -/** - * Return assymmetrically encrypted [plaintext] using [publicKey] where - * [publicKey] likely belongs to the recipient. - * @param {Object} obj - * @param {String} obj.plaintext - plaintext to encrypt - * @param {String} obj.publicKey - (base64) Nacl public key of the recipient - * @param {String} obj.privateKey - (base64) Nacl private key of the sender (current user) - * @returns {Object} obj - * @returns {String} obj.ciphertext - (base64) ciphertext - * @returns {String} obj.nonce - (base64) nonce - */ -const encryptAsymmetric = ({ - plaintext, - publicKey, - privateKey, -}: IEncryptAsymmetricInput): IEncryptAsymmetricOutput => { - const nonce = nacl.randomBytes(24); - const ciphertext = nacl.box( - util.decodeUTF8(plaintext), - nonce, - util.decodeBase64(publicKey), - util.decodeBase64(privateKey) - ); - - return { - ciphertext: util.encodeBase64(ciphertext), - nonce: util.encodeBase64(nonce), - }; -}; - -/** - * Return assymmetrically decrypted [ciphertext] using [privateKey] where - * [privateKey] likely belongs to the recipient. - * @param {Object} obj - * @param {String} obj.ciphertext - ciphertext to decrypt - * @param {String} obj.nonce - (base64) nonce - * @param {String} obj.publicKey - (base64) public key of the sender - * @param {String} obj.privateKey - (base64) private key of the receiver (current user) - * @returns {String} plaintext - (utf8) plaintext - */ -const decryptAsymmetric = ({ - ciphertext, - nonce, - publicKey, - privateKey, -}: IDecryptAsymmetricInput): string => { - const plaintext: Uint8Array | null = nacl.box.open( - util.decodeBase64(ciphertext), - util.decodeBase64(nonce), - util.decodeBase64(publicKey), - util.decodeBase64(privateKey) - ); - - if (plaintext == null) throw BadRequestError({ - message: "Invalid ciphertext or keys", - }); - - return util.encodeUTF8(plaintext); -}; - -/** - * Return symmetrically encrypted [plaintext] using [key]. - * - * NOTE: THIS FUNCTION SHOULD NOT BE USED FOR ALL FUTURE - * ENCRYPTION OPERATIONS UNLESS IT TOUCHES OLD FUNCTIONALITY - * THAT USES IT. USE encryptSymmetric() instead - * - * @param {Object} obj - * @param {String} obj.plaintext - (utf8) plaintext to encrypt - * @param {String} obj.key - (hex) 128-bit key - * @returns {Object} obj - * @returns {String} obj.ciphertext (base64) ciphertext - * @returns {String} obj.iv (base64) iv - * @returns {String} obj.tag (base64) tag - */ -const encryptSymmetric128BitHexKeyUTF8 = ({ - plaintext, - key, -}: IEncryptSymmetricInput) => { - const iv = crypto.randomBytes(BLOCK_SIZE_BYTES_16); - const cipher = crypto.createCipheriv(ALGORITHM_AES_256_GCM, key, iv); - - let ciphertext = cipher.update(plaintext, "utf8", "base64"); - ciphertext += cipher.final("base64"); - - return { - ciphertext, - iv: iv.toString("base64"), - tag: cipher.getAuthTag().toString("base64"), - }; -} -/** - * Return symmetrically decrypted [ciphertext] using [iv], [tag], - * and [key]. - * - * NOTE: THIS FUNCTION SHOULD NOT BE USED FOR ALL FUTURE - * DECRYPTION OPERATIONS UNLESS IT TOUCHES OLD FUNCTIONALITY - * THAT USES IT. USE decryptSymmetric() instead - * - * @param {Object} obj - * @param {String} obj.ciphertext - ciphertext to decrypt - * @param {String} obj.iv - (base64) 256-bit iv - * @param {String} obj.tag - (base64) tag - * @param {String} obj.key - (hex) 128-bit key - * @returns {String} cleartext - the deciphered ciphertext - */ -const decryptSymmetric128BitHexKeyUTF8 = ({ - ciphertext, - iv, - tag, - key, -}: IDecryptSymmetricInput) => { - const decipher = crypto.createDecipheriv( - ALGORITHM_AES_256_GCM, - key, - Buffer.from(iv, "base64") - ); - - decipher.setAuthTag(Buffer.from(tag, "base64")); - - let cleartext = decipher.update(ciphertext, "base64", "utf8"); - cleartext += decipher.final("utf8"); - - return cleartext; -} - -export { - generateKeyPair, - encryptAsymmetric, - decryptAsymmetric, - encryptSymmetric128BitHexKeyUTF8, - decryptSymmetric128BitHexKeyUTF8, -}; diff --git a/backend-mongo/src/utils/errors.ts b/backend-mongo/src/utils/errors.ts deleted file mode 100644 index 105069c2a..000000000 --- a/backend-mongo/src/utils/errors.ts +++ /dev/null @@ -1,214 +0,0 @@ -import RequestError, { LogLevel, RequestErrorContext } from "./requestError" - -//* ----->[GENERAL HTTP ERRORS]<----- -export const RouteNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.INFO, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "route_not_found", - message: error?.message ?? "The requested source was not found", - context: error?.context, - stack: error?.stack, -}); - -export const MethodNotAllowedError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.INFO, - statusCode: error?.statusCode ?? 405, - type: error?.type ?? "method_not_allowed", - message: error?.message ?? "The requested method is not allowed for the resource", - context: error?.context, - stack: error?.stack, -}); - -export const UnauthorizedRequestError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.INFO, - statusCode: error?.statusCode ?? 401, - type: error?.type ?? "unauthorized", - message: error?.message ?? "You are not authorized to access this resource", - context: error?.context, - stack: error?.stack, -}); - -export const ForbiddenRequestError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.WARN, - statusCode: error?.statusCode ?? 403, - type: error?.type ?? "forbidden", - message: error?.message ?? "You are not allowed to access this resource", - context: error?.context, - stack: error?.stack, -}); - -export const BadRequestError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.INFO, - statusCode: error?.statusCode ?? 400, - type: error?.type ?? "bad_request", - message: error?.message ?? "The request is invalid or cannot be served", - context: error?.context, - stack: error?.stack, -}); - -export const ResourceNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.INFO, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "resource_not_found", - message: error?.message ?? "The requested resource is not found", - context: error?.context, - stack: error?.stack, -}); - -export const InternalServerError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 500, - type: error?.type ?? "internal_server_error", - message: error?.message ?? "The server encountered an error while processing the request", - context: error?.context, - stack: error?.stack, -}); - -export const ServiceUnavailableError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 503, - type: error?.type ?? "service_unavailable", - message: error?.message ?? "The service is currently unavailable. Please try again later.", - context: error?.context, - stack: error?.stack, -}); - -export const ValidationError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 400, - type: error?.type ?? "validation_error", - message: error?.message ?? "The request failed validation", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[INTEGRATION AUTH ERRORS]<----- -export const IntegrationAuthNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "integration_auth_not_found_error", - message: error?.message ?? "The requested integration authorization was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[INTEGRATION ERRORS]<----- -export const IntegrationNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "integration_not_found_error", - message: error?.message ?? "The requested integration was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[WORKSPACE ERRORS]<----- -export const WorkspaceNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "workspace_not_found_error", - message: error?.message ?? "The requested workspace was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[WORKSPACE MEMBERSHIP ERRORS]<----- -export const MembershipNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "workspace_membership_not_found_error", - message: error?.message ?? "The requested membership was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[ORGANIZATION ERRORS]<----- -export const OrganizationNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "organization_not_found_error", - message: error?.message ?? "The requested organization was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[MEMBERSHIP ORGANIZATION ERRORS]<----- -export const MembershipOrgNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "organization_membership_not_found_error", - message: error?.message ?? "The requested organization membership was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[ACCOUNT ERRORS]<----- -export const AccountNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "account_not_found_error", - message: error?.message ?? "The requested account was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[SECRET ERRORS]<----- -export const SecretNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "secret_not_found_error", - message: error?.message ?? "The requested secret was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[SECRET BLIND INDEX DATA ERRORS]<----- -export const SecretBlindIndexDataNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "secret_blind_index_data_not_found_error", - message: error?.message ?? "The requested secret was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[SECRET SNAPSHOT ERRORS]<----- -export const SecretSnapshotNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "secret_snapshot_not_found_error", - message: error?.message ?? "The requested secret snapshot was not found", - context: error?.context, - stack: error?.stack, -}); - -//* ----->[SERVICE TOKEN DATA ERRORS]<----- -export const ServiceTokenDataNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "service_token_data_not_found_error", - message: error?.message ?? "The requested service token data was not found", - context: error?.context, - stack: error?.stack, -}) - -//* ----->[API KEY DATA ERRORS]<----- -export const APIKeyDataNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "api_key_data_not_found_error", - message: error?.message ?? "The requested service token data was not found", - context: error?.context, - stack: error?.stack, -}); - -export const BotNotFoundError = (error?: Partial) => new RequestError({ - logLevel: error?.logLevel ?? LogLevel.ERROR, - statusCode: error?.statusCode ?? 404, - type: error?.type ?? "bot_not_found_error", - message: error?.message ?? "The requested bot was not found", - context: error?.context, - stack: error?.stack, -}) - -//* ----->[MISC ERRORS]<----- diff --git a/backend-mongo/src/utils/folder.ts b/backend-mongo/src/utils/folder.ts deleted file mode 100644 index 45a8b94d6..000000000 --- a/backend-mongo/src/utils/folder.ts +++ /dev/null @@ -1,87 +0,0 @@ -// import Folder from "../models/folder"; - -// export const ROOT_FOLDER_PATH = "/" - -// export const getFolderPath = async (folderId: string) => { -// let currentFolder = await Folder.findById(folderId); -// const pathSegments = []; - -// while (currentFolder) { -// pathSegments.unshift(currentFolder.name); -// currentFolder = currentFolder.parent ? await Folder.findById(currentFolder.parent) : null; -// } - -// return '/' + pathSegments.join('/'); -// }; - -// /** -// Returns the folder ID associated with the specified secret path in the given workspace and environment. -// @param workspaceId - The ID of the workspace to search in. -// @param environment - The environment to search in. -// @param secretPath - The secret path to search for. -// @returns The folder ID associated with the specified secret path, or undefined if the path is at the root folder level. -// @throws Error if the specified secret path is not found. -// */ -// export const getFolderIdFromPath = async (workspaceId: string, environment: string, secretPath: string) => { -// const secretPathParts = secretPath.split("/").filter(path => path != "") -// if (secretPathParts.length <= 1) { -// return undefined // root folder, so no folder id -// } - -// const folderId = await Folder.find({ path: secretPath, workspace: workspaceId, environment: environment }) -// if (!folderId) { -// throw Error("Secret path not found") -// } - -// return folderId -// } - -// /** -// * Cleans up a path by removing empty parts, duplicate slashes, -// * and ensuring it starts with ROOT_FOLDER_PATH. -// * @param path - The input path to clean up. -// * @returns The cleaned-up path string. -// */ -// export const normalizePath = (path: string) => { -// if (path == undefined || path == "" || path == ROOT_FOLDER_PATH) { -// return ROOT_FOLDER_PATH -// } - -// const pathParts = path.split("/").filter(part => part != "") -// const cleanPathString = ROOT_FOLDER_PATH + pathParts.join("/") - -// return cleanPathString -// } - -// export const getFoldersInDirectory = async (workspaceId: string, environment: string, pathString: string) => { -// const normalizedPath = normalizePath(pathString) -// const foldersInDirectory = await Folder.find({ -// workspace: workspaceId, -// environment: environment, -// parentPath: normalizedPath, -// }); - -// return foldersInDirectory; -// } - -// /** -// * Returns the parent path of the given path. -// * @param path - The input path. -// * @returns The parent path string. -// */ -// export const getParentPath = (path: string) => { -// const normalizedPath = normalizePath(path); -// const folderParts = normalizedPath.split('/').filter(part => part !== ''); - -// let folderParent = ROOT_FOLDER_PATH; -// if (folderParts.length > 1) { -// folderParent = ROOT_FOLDER_PATH + folderParts.slice(0, folderParts.length - 1).join('/'); -// } - -// return folderParent; -// } - -// export const validateFolderName = (folderName: string) => { -// const validNameRegex = /^[a-zA-Z0-9-_]+$/; -// return validNameRegex.test(folderName); -// } diff --git a/backend-mongo/src/utils/ip/index.ts b/backend-mongo/src/utils/ip/index.ts deleted file mode 100644 index 17c8ce5a6..000000000 --- a/backend-mongo/src/utils/ip/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./ip"; \ No newline at end of file diff --git a/backend-mongo/src/utils/ip/ip.ts b/backend-mongo/src/utils/ip/ip.ts deleted file mode 100644 index bc314fa9a..000000000 --- a/backend-mongo/src/utils/ip/ip.ts +++ /dev/null @@ -1,136 +0,0 @@ -import net from "net"; -import { IPType } from "../../ee/models"; -import { InternalServerError, UnauthorizedRequestError } from "../errors"; - -/** - * Return details of IP [ip]: - * - If [ip] is a specific IP address then return the IPv4/IPv6 address - * - If [ip] is a subnet then return the network IPv4/IPv6 address and prefix - * @param {String} ip - ip whose details to return - * @returns - */ -export const extractIPDetails = (ip: string) => { - if (net.isIPv4(ip)) return ({ - ipAddress: ip, - type: IPType.IPV4 - }); - - if (net.isIPv6(ip)) return ({ - ipAddress: ip, - type: IPType.IPV6 - }); - - const [ipNet, prefix] = ip.split("/"); - - let type; - switch (net.isIP(ipNet)) { - case 4: - type = IPType.IPV4; - break; - case 6: - type = IPType.IPV6; - break; - default: - throw InternalServerError({ - message: "Failed to extract IP details" - }); - } - - return ({ - ipAddress: ipNet, - type, - prefix: parseInt(prefix, 10) - }); -} - -/** - * Checks if a given string is a valid CIDR block. - * - * The function checks if the input string is a valid IPv4 or IPv6 address in CIDR notation. - * - * CIDR notation includes a network address followed by a slash ('/') and a prefix length. - * For IPv4, the prefix length must be between 0 and 32. For IPv6, it must be between 0 and 128. - * If the input string is not a valid CIDR block, the function returns `false`. - * - * @param {string} cidr - string in CIDR notation - * @returns {boolean} Returns `true` if the string is a valid CIDR block, `false` otherwise. - * -*/ -export const isValidCidr = (cidr: string): boolean => { - const [ip, prefix] = cidr.split("/"); - - const prefixNum = parseInt(prefix, 10); - - // ensure prefix exists and is a number within the appropriate range for each IP version - if (!prefix || isNaN(prefixNum) || - (net.isIPv4(ip) && (prefixNum < 0 || prefixNum > 32)) || - (net.isIPv6(ip) && (prefixNum < 0 || prefixNum > 128))) { - return false; - } - - // ensure the IP portion of the CIDR block is a valid IPv4 or IPv6 address - if (!net.isIPv4(ip) && !net.isIPv6(ip)) { - return false; - } - - return true; -} - -/** - * Checks if a given string is a valid IPv4/IPv6 address or a valid CIDR block. - * - * If the string contains a slash ('/'), it treats the input as a CIDR block and checks its validity. - * Otherwise, it treats the string as a standalone IP address (either IPv4 or IPv6) and checks its validity. - * - * @param {string} input - The string to be checked. It could be an IP address or a CIDR block. - * @returns {boolean} Returns `true` if the string is a valid IP address (either IPv4 or IPv6) or a valid CIDR block, `false` otherwise. - * -*/ -export const isValidIpOrCidr = (ip: string): boolean => { - // if the string contains a slash, treat it as a CIDR block - if (ip.includes("/")) { - return isValidCidr(ip); - } - - // otherwise, treat it as a standalone IP address - if (net.isIPv4(ip) || net.isIPv6(ip)) { - return true; - } - - return false; -} - -/** - * Validates the IP address [ipAddress] against the trusted IPs [trustedIps]. - * @param {Object} obj - * @param {String} obj.ipAddress - IP address to check - * @param {Object[]} obj.trustedIps - IPs to trust in blocklist - */ -export const checkIPAgainstBlocklist = ({ - ipAddress, - trustedIps -}: { - ipAddress: string; - trustedIps: { - ipAddress: string; - type: IPType; - prefix: number; - }[] -}) => { - const blockList = new net.BlockList(); - - for (const trustedIp of trustedIps) { - if (trustedIp.prefix !== undefined) { - blockList.addSubnet(trustedIp.ipAddress, trustedIp.prefix, trustedIp.type); - } else { - blockList.addAddress(trustedIp.ipAddress, trustedIp.type); - } - } - - const { type } = extractIPDetails(ipAddress); - const check = blockList.check(ipAddress, type); - - if (!check) throw UnauthorizedRequestError({ - message: "Failed to authenticate" - }); -} diff --git a/backend-mongo/src/utils/logging/index.ts b/backend-mongo/src/utils/logging/index.ts deleted file mode 100644 index 5d5654efc..000000000 --- a/backend-mongo/src/utils/logging/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { logger, initLogger } from "./logger"; diff --git a/backend-mongo/src/utils/logging/logger.ts b/backend-mongo/src/utils/logging/logger.ts deleted file mode 100644 index 70ba77570..000000000 --- a/backend-mongo/src/utils/logging/logger.ts +++ /dev/null @@ -1,69 +0,0 @@ -import pino, { Logger } from "pino"; -import { getAwsCloudWatchLog, getNodeEnv } from "../../config"; - -export let logger: Logger; - -// https://github.com/pinojs/pino/blob/master/lib/levels.js#L13-L20 -const logLevelToSeverityLookup: Record = { - "10": "TRACE", - "20": "DEBUG", - "30": "INFO", - "40": "WARNING", - "50": "ERROR", - "60": "CRITICAL" -} - -export const initLogger = async () => { - const awsCloudWatchLogCfg = await getAwsCloudWatchLog(); - const nodeEnv = await getNodeEnv(); - const isProduction = nodeEnv === "production"; - const targets: pino.TransportMultiOptions["targets"][number][] = [ - isProduction - ? { level: "info", target: "pino/file", options: {} } - : { - level: "info", - target: "pino-pretty", // must be installed separately - options: { - colorize: true - } - } - ]; - - if (awsCloudWatchLogCfg) { - targets.push({ - target: "@serdnam/pino-cloudwatch-transport", - level: "info", - options: { - logGroupName: awsCloudWatchLogCfg.logGroupName, - logStreamName: awsCloudWatchLogCfg.logGroupName, - awsRegion: awsCloudWatchLogCfg.region, - awsAccessKeyId: awsCloudWatchLogCfg.accessKeyId, - awsSecretAccessKey: awsCloudWatchLogCfg.accessKeySecret, - interval: awsCloudWatchLogCfg.interval - } - }); - } - - const transport = pino.transport({ - targets - }); - - logger = pino( - { - mixin(_context, level) { - return { "severity": logLevelToSeverityLookup[level] || logLevelToSeverityLookup["30"] } - }, - level: process.env.PINO_LOG_LEVEL || "info", - formatters: { - bindings: (bindings) => { - return { - pid: bindings.pid, - hostname: bindings.hostname - // node_version: process.version - }; - } - } - }, - transport - ); -}; diff --git a/backend-mongo/src/utils/posthog.ts b/backend-mongo/src/utils/posthog.ts deleted file mode 100644 index 5ee2eef13..000000000 --- a/backend-mongo/src/utils/posthog.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { UserAgentType } from "../ee/models" - -export const getUserAgentType = function (userAgent: string | undefined) { - if (userAgent == undefined) { - return UserAgentType.OTHER; - } else if (userAgent == UserAgentType.CLI) { - return UserAgentType.CLI; - } else if (userAgent == UserAgentType.K8_OPERATOR) { - return UserAgentType.K8_OPERATOR; - } else if (userAgent == UserAgentType.TERRAFORM) { - return UserAgentType.TERRAFORM; - } else if (userAgent.toLowerCase().includes("mozilla")) { - return UserAgentType.WEB; - } else if (userAgent.includes(UserAgentType.NODE_SDK)) { - return UserAgentType.NODE_SDK; - } else if (userAgent.includes(UserAgentType.PYTHON_SDK)) { - return UserAgentType.PYTHON_SDK; - } else { - return UserAgentType.OTHER; - } -} \ No newline at end of file diff --git a/backend-mongo/src/utils/requestError.ts b/backend-mongo/src/utils/requestError.ts deleted file mode 100644 index 7e4625e30..000000000 --- a/backend-mongo/src/utils/requestError.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { Request } from "express" -import { getVerboseErrorOutput } from "../config"; - -export enum LogLevel { - TRACE = 10, - DEBUG = 20, - INFO = 30, - WARN = 40, - ERROR = 50, - FATAL = 60 -} - -type PinoLogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal"; - -export const mapToPinoLogLevel = (customLogLevel: LogLevel): PinoLogLevel => { - switch (customLogLevel) { - case LogLevel.TRACE: - return "trace"; - case LogLevel.DEBUG: - return "debug"; - case LogLevel.INFO: - return "info"; - case LogLevel.WARN: - return "warn"; - case LogLevel.ERROR: - return "error"; - case LogLevel.FATAL: - return "fatal"; - } -} - -export type RequestErrorContext = { - logLevel?: LogLevel, - statusCode: number, - type: string, - message: string, - context?: Record, - stack?: string|undefined -} - -export default class RequestError extends Error { - - private _logLevel: LogLevel - private _logName: string; - statusCode: number - type: string - context: Record - extra: Record[] - private stacktrace: string|undefined|string[] - - constructor( - {logLevel, statusCode, type, message, context, stack} : RequestErrorContext - ){ - - super(message) - this._logLevel = logLevel || LogLevel.INFO; - this._logName = LogLevel[this._logLevel]; - this.statusCode = statusCode; - this.message = message; - this.type = type - this.context = context || {} - this.extra = [] - - if(stack) this.stack = stack - else Error.captureStackTrace(this, this.constructor) - this.stacktrace = this.stack?.split("\n") - } - - static convertFrom(error: Error) { - //This error was not handled by error handler. Please report this incident to the staff. - return new RequestError({ - logLevel: LogLevel.ERROR, - statusCode: 500, - type: "internal_server_error", - message: "This error was not handled by error handler. Please report this incident to the staff", - context: { - message: error.message, - name: error.name, - }, - stack: error.stack, - }) - } - - get level(){ - return this._logLevel - } - get levelName(){ - return this._logName - } - - withTags(...tags: string[]|number[]){ - this.context["tags"] = Object.assign(tags, this.context["tags"]) - return this - } - - withExtras(...extras: Record[]){ - this.extra = Object.assign(extras, this.extra) - return this - } - - private _omit(obj: any, keys: string[]): typeof obj{ - const exclude = new Set(keys) - obj = Object.fromEntries(Object.entries(obj).filter(e => !exclude.has(e[0]))) - return obj - } - - public async format(req: Request){ - let _context = Object.assign({ - stacktrace: this.stacktrace, - }, this.context) - - //* Omit sensitive information from context that can leak internal workings of this program if user is not developer - const verboseErrorOutput = await getVerboseErrorOutput(); - if (verboseErrorOutput !== undefined) { - _context = this._omit(_context, [ - "stacktrace", - "exception", - ]) - } - - const formatObject = { - type: this.type, - message: this.message, - context: _context, - level: this.level, - level_name: this.levelName, - status_code: this.statusCode, - datetime_iso: new Date().toISOString(), - application: process.env.npm_package_name || "unknown", - request_id: req.headers["Request-Id"], - extra: this.extra, - } - - return formatObject - - } -} diff --git a/backend-mongo/src/utils/setup/backfillData.ts b/backend-mongo/src/utils/setup/backfillData.ts deleted file mode 100644 index 21b2a95df..000000000 --- a/backend-mongo/src/utils/setup/backfillData.ts +++ /dev/null @@ -1,879 +0,0 @@ -import crypto from "crypto"; -import { Types } from "mongoose"; -import { encryptSymmetric128BitHexKeyUTF8 } from "../crypto"; -import { EESecretService } from "../../ee/services"; -import { redisClient } from "../../services/RedisService"; -import { - IPType, - ISecretVersion, - Role, - SecretSnapshot, - SecretVersion, - TrustedIP -} from "../../ee/models"; -import { - AuthMethod, - BackupPrivateKey, - Bot, - BotOrg, - ISecret, - IWorkspace, - Integration, - IntegrationAuth, - Membership, - MembershipOrg, - Organization, - Secret, - SecretBlindIndexData, - ServiceTokenData, - User, - Workspace -} from "../../models"; -import { generateKeyPair } from "../../utils/crypto"; -import { client, getEncryptionKey, getIsInfisicalCloud, getRootEncryptionKey } from "../../config"; -import { - ADMIN, - ALGORITHM_AES_256_GCM, - CUSTOM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, - MEMBER, - OWNER -} from "../../variables"; -import { InternalServerError } from "../errors"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - memberProjectPermissions -} from "../../ee/services/ProjectRoleService"; -import { logger } from "../logging"; -import { getServerConfig, updateServerConfig } from "../../config/serverConfig"; - -/** - * Backfill secrets to ensure that they're all versioned and have - * corresponding secret versions - */ -export const backfillSecretVersions = async () => { - await Secret.updateMany({ version: { $exists: false } }, { $set: { version: 1 } }); - - const unversionedSecrets: ISecret[] = await Secret.aggregate([ - { - $lookup: { - from: "secretversions", - localField: "_id", - foreignField: "secret", - as: "versions" - } - }, - { - $match: { - versions: { $size: 0 } - } - } - ]); - - if (unversionedSecrets.length > 0) { - await EESecretService.addSecretVersions({ - secretVersions: unversionedSecrets.map( - (s, idx) => - new SecretVersion({ - ...s, - secret: s._id, - version: s.version ? s.version : 1, - isDeleted: false, - workspace: s.workspace, - environment: s.environment, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }) - ) - }); - } - logger.info("Migration: Secret version migration v1 complete"); -}; - -/** - * Backfill workspace bots to ensure that every workspace has a bot - */ -export const backfillBots = async () => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - const workspaceIdsWithBot = await Bot.distinct("workspace"); - const workspaceIdsToAddBot = await Workspace.distinct("_id", { - _id: { - $nin: workspaceIdsWithBot - } - }); - - if (workspaceIdsToAddBot.length === 0) return; - - const botsToInsert = await Promise.all( - workspaceIdsToAddBot.map(async (workspaceToAddBot) => { - const { publicKey, privateKey } = generateKeyPair(); - - if (rootEncryptionKey) { - const { - ciphertext: encryptedPrivateKey, - iv, - tag - } = client.encryptSymmetric(privateKey, rootEncryptionKey); - - return new Bot({ - name: "Infisical Bot", - workspace: workspaceToAddBot, - isActive: false, - publicKey, - encryptedPrivateKey, - iv, - tag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_BASE64 - }); - } else if (encryptionKey) { - const { - ciphertext: encryptedPrivateKey, - iv, - tag - } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: privateKey, - key: encryptionKey - }); - - return new Bot({ - name: "Infisical Bot", - workspace: workspaceToAddBot, - isActive: false, - publicKey, - encryptedPrivateKey, - iv, - tag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }); - } - - throw InternalServerError({ - message: "Failed to backfill workspace bots due to missing encryption key" - }); - }) - ); - - await Bot.insertMany(botsToInsert); -}; - -/** - * Backfill organization bots to ensure that every organization has a bot - */ -export const backfillBotOrgs = async () => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - const organizationIdsWithBot = await BotOrg.distinct("organization"); - const organizationIdsToAddBot = await Organization.distinct("_id", { - _id: { - $nin: organizationIdsWithBot - } - }); - - if (organizationIdsToAddBot.length === 0) return; - - const botsToInsert = await Promise.all( - organizationIdsToAddBot.map(async (organizationToAddBot) => { - const { publicKey, privateKey } = generateKeyPair(); - - const key = client.createSymmetricKey(); - - if (rootEncryptionKey) { - const { - ciphertext: encryptedPrivateKey, - iv: privateKeyIV, - tag: privateKeyTag - } = client.encryptSymmetric(privateKey, rootEncryptionKey); - - const { - ciphertext: encryptedSymmetricKey, - iv: symmetricKeyIV, - tag: symmetricKeyTag - } = client.encryptSymmetric(key, rootEncryptionKey); - - return new BotOrg({ - name: "Infisical Bot", - organization: organizationToAddBot, - publicKey, - encryptedSymmetricKey, - symmetricKeyIV, - symmetricKeyTag, - symmetricKeyAlgorithm: ALGORITHM_AES_256_GCM, - symmetricKeyKeyEncoding: ENCODING_SCHEME_BASE64, - encryptedPrivateKey, - privateKeyIV, - privateKeyTag, - privateKeyAlgorithm: ALGORITHM_AES_256_GCM, - privateKeyKeyEncoding: ENCODING_SCHEME_BASE64 - }); - } else if (encryptionKey) { - const { - ciphertext: encryptedPrivateKey, - iv: privateKeyIV, - tag: privateKeyTag - } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: privateKey, - key: encryptionKey - }); - - const { - ciphertext: encryptedSymmetricKey, - iv: symmetricKeyIV, - tag: symmetricKeyTag - } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: key, - key: encryptionKey - }); - - return new BotOrg({ - name: "Infisical Bot", - organization: organizationToAddBot, - publicKey, - encryptedSymmetricKey, - symmetricKeyIV, - symmetricKeyTag, - symmetricKeyAlgorithm: ALGORITHM_AES_256_GCM, - symmetricKeyKeyEncoding: ENCODING_SCHEME_UTF8, - encryptedPrivateKey, - privateKeyIV, - privateKeyTag, - privateKeyAlgorithm: ALGORITHM_AES_256_GCM, - privateKeyKeyEncoding: ENCODING_SCHEME_UTF8 - }); - } - - throw InternalServerError({ - message: "Failed to backfill organization bots due to missing encryption key" - }); - }) - ); - - await BotOrg.insertMany(botsToInsert); -}; - -/** - * Backfill secret blind index data to ensure that every workspace - * has a secret blind index data - */ -export const backfillSecretBlindIndexData = async () => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - const workspaceIdsBlindIndexed = await SecretBlindIndexData.distinct("workspace"); - const workspaceIdsToBlindIndex = await Workspace.distinct("_id", { - _id: { - $nin: workspaceIdsBlindIndexed - } - }); - - if (workspaceIdsToBlindIndex.length === 0) return; - - const secretBlindIndexDataToInsert = await Promise.all( - workspaceIdsToBlindIndex.map(async (workspaceToBlindIndex) => { - const salt = crypto.randomBytes(16).toString("base64"); - - if (rootEncryptionKey) { - const { - ciphertext: encryptedSaltCiphertext, - iv: saltIV, - tag: saltTag - } = client.encryptSymmetric(salt, rootEncryptionKey); - - return new SecretBlindIndexData({ - workspace: workspaceToBlindIndex, - encryptedSaltCiphertext, - saltIV, - saltTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_BASE64 - }); - } else if (encryptionKey) { - const { - ciphertext: encryptedSaltCiphertext, - iv: saltIV, - tag: saltTag - } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: salt, - key: encryptionKey - }); - - return new SecretBlindIndexData({ - workspace: workspaceToBlindIndex, - encryptedSaltCiphertext, - saltIV, - saltTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - }); - } - - throw InternalServerError({ - message: "Failed to backfill secret blind index data due to missing encryption key" - }); - }) - ); - - SecretBlindIndexData.insertMany(secretBlindIndexDataToInsert); -}; - -/** - * Backfill Secret, SecretVersion, SecretBlindIndexData, Bot, - * BackupPrivateKey, IntegrationAuth collections to ensure that - * they all have encryption metadata documented - */ -export const backfillEncryptionMetadata = async () => { - // backfill secret encryption metadata - await Secret.updateMany( - { - algorithm: { - $exists: false - }, - keyEncoding: { - $exists: false - } - }, - { - $set: { - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - } - } - ); - - // backfill secret version encryption metadata - await SecretVersion.updateMany( - { - algorithm: { - $exists: false - }, - keyEncoding: { - $exists: false - } - }, - { - $set: { - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - } - } - ); - - // backfill secret blind index encryption metadata - await SecretBlindIndexData.updateMany( - { - algorithm: { - $exists: false - }, - keyEncoding: { - $exists: false - } - }, - { - $set: { - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - } - } - ); - - // backfill bot encryption metadata - await Bot.updateMany( - { - algorithm: { - $exists: false - }, - keyEncoding: { - $exists: false - } - }, - { - $set: { - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - } - } - ); - - // backfill backup private key encryption metadata - await BackupPrivateKey.updateMany( - { - algorithm: { - $exists: false - }, - keyEncoding: { - $exists: false - } - }, - { - $set: { - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - } - } - ); - - // backfill integration auth encryption metadata - await IntegrationAuth.updateMany( - { - algorithm: { - $exists: false - }, - keyEncoding: { - $exists: false - } - }, - { - $set: { - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8 - } - } - ); -}; - -export const backfillSecretFolders = async () => { - await Secret.updateMany( - { - folder: { - $exists: false - } - }, - { - $set: { - folder: "root" - } - } - ); - - await SecretVersion.updateMany( - { - folder: { - $exists: false - } - }, - { - $set: { - folder: "root" - } - } - ); - - // Back fill because tags were missing in secret versions - await SecretVersion.updateMany( - { - tags: { - $exists: false - } - }, - { - $set: { - tags: [] - } - } - ); - - let secretSnapshots = await SecretSnapshot.find({ - environment: { - $exists: false - } - }) - .populate<{ secretVersions: ISecretVersion[] }>("secretVersions") - .limit(50); - - while (secretSnapshots.length > 0) { - for (const secSnapshot of secretSnapshots) { - const groupSnapByEnv: Record> = {}; - secSnapshot.secretVersions.forEach((secVer) => { - if (!groupSnapByEnv?.[secVer.environment]) groupSnapByEnv[secVer.environment] = []; - groupSnapByEnv[secVer.environment].push(secVer); - }); - - const newSnapshots = Object.keys(groupSnapByEnv).map((snapEnv) => { - const secretIdsOfEnvGroup = groupSnapByEnv[snapEnv] - ? groupSnapByEnv[snapEnv].map((secretVersion) => secretVersion._id) - : []; - return { - ...secSnapshot.toObject({ virtuals: false }), - _id: new Types.ObjectId(), - environment: snapEnv, - secretVersions: secretIdsOfEnvGroup - }; - }); - - await SecretSnapshot.insertMany(newSnapshots); - await secSnapshot.deleteOne(); - } - - secretSnapshots = await SecretSnapshot.find({ - environment: { - $exists: false - } - }) - .populate<{ secretVersions: ISecretVersion[] }>("secretVersions") - .limit(50); - } - - logger.info("Migration: Folder migration v1 complete"); -}; - -export const backfillServiceToken = async () => { - await ServiceTokenData.updateMany( - { - secretPath: { - $exists: false - } - }, - { - $set: { - secretPath: "/" - } - } - ); - logger.info("Migration: Service token migration v1 complete"); -}; - -export const backfillIntegration = async () => { - await Integration.updateMany( - { - secretPath: { - $exists: false - } - }, - { - $set: { - secretPath: "/" - } - } - ); - logger.info("Migration: Integration migration v1 complete"); -}; - -export const backfillServiceTokenMultiScope = async () => { - const documentsToUpdate = await ServiceTokenData.find({ scopes: { $exists: false } }); - - for (const doc of documentsToUpdate) { - // Cast doc to any to bypass TypeScript's type checks - const anyDoc = doc as any; - - const environment = anyDoc.environment; - const secretPath = anyDoc.secretPath; - - if (environment && secretPath) { - const updatedScopes = [ - { - environment: environment, - secretPath: secretPath - } - ]; - - await ServiceTokenData.updateOne({ _id: doc._id }, { $set: { scopes: updatedScopes } }); - } - } - - logger.info("Migration: Service token migration v2 complete"); -}; - -/** - * Backfill each workspace without any registered trusted IPs to - * have default trusted ip of 0.0.0.0/0 - */ -export const backfillTrustedIps = async () => { - const workspaceIdsWithTrustedIps = await TrustedIP.distinct("workspace"); - const workspaceIdsToAddTrustedIp = await Workspace.distinct("_id", { - _id: { - $nin: workspaceIdsWithTrustedIps - } - }); - - if (workspaceIdsToAddTrustedIp.length > 0) { - const operations: { - updateOne: { - filter: { - workspace: Types.ObjectId; - ipAddress: string; - }; - update: { - workspace: Types.ObjectId; - ipAddress: string; - type: string; - prefix: number; - isActive: boolean; - comment: string; - }; - upsert: boolean; - }; - }[] = []; - - workspaceIdsToAddTrustedIp.forEach((workspaceId) => { - // default IPv4 trusted CIDR - operations.push({ - updateOne: { - filter: { - workspace: workspaceId, - ipAddress: "0.0.0.0" - }, - update: { - workspace: workspaceId, - ipAddress: "0.0.0.0", - type: IPType.IPV4.toString(), - prefix: 0, - isActive: true, - comment: "" - }, - upsert: true - } - }); - - // default IPv6 trusted CIDR - operations.push({ - updateOne: { - filter: { - workspace: workspaceId, - ipAddress: "::" - }, - update: { - workspace: workspaceId, - ipAddress: "::", - type: IPType.IPV6.toString(), - prefix: 0, - isActive: true, - comment: "" - }, - upsert: true - } - }); - }); - - await TrustedIP.bulkWrite(operations); - logger.info("Backfill: Trusted IPs complete"); - } -}; - -export const backfillUserAuthMethods = async () => { - await User.updateMany( - { - authProvider: { - $exists: false - }, - authMethods: { - $exists: false - } - }, - { - authMethods: [AuthMethod.EMAIL] - } - ); - - const documentsToUpdate = await User.find({ - authProvider: { $exists: true }, - authMethods: { $exists: false } - }); - - for (const doc of documentsToUpdate) { - // Cast doc to any to bypass TypeScript's type checks - const anyDoc = doc as any; - - const authProvider = anyDoc.authProvider; - const authMethods = [authProvider]; - - await User.updateOne( - { _id: doc._id }, - { - $set: { authMethods: authMethods }, - $unset: { authProvider: 1, authId: 1 } - } - ); - } -}; - -export const backfillPermission = async () => { - const lockKey = "backfill_permission_lock"; - const timeout = 900000; // 15 min lock timeout in milliseconds - const lock = await redisClient?.set(lockKey, 1, "PX", timeout, "NX"); - - if (lock) { - try { - logger.info("Lock acquired for script [backfillPermission]"); - - const memberships = await Membership.find({ - deniedPermissions: { - $exists: true, - $ne: [] - }, - role: MEMBER - }) - .populate<{ workspace: IWorkspace }>("workspace") - .lean(); - - // group memberships that need the same permission set - const roleMap = new Map< - string, - { membershipIds: string[]; permissions: any[]; organizationId: string; workspaceId: string } - >(); - - for (const membership of memberships) { - // get permissions of members except secret permission - const customPermissions = memberProjectPermissions.rules.filter( - ({ subject }) => subject !== ProjectPermissionSub.Secrets - ); - const secretAccessRule: Record = {}; - - // iterate and record true and false ones - membership.deniedPermissions.forEach(({ ability, environmentSlug }) => { - if (!secretAccessRule?.[environmentSlug]) - secretAccessRule[environmentSlug] = { read: true, write: true }; - if (ability === "write") secretAccessRule[environmentSlug].write = false; - if (ability === "read") secretAccessRule[environmentSlug].read = false; - }); - - // environments that are not listed in deniedPermissions should be set to allowed for both read & and write - membership.workspace.environments.forEach((env) => { - if (!secretAccessRule?.[env.slug]) { - secretAccessRule[env.slug] = { read: true, write: true }; - } - }); - - const secretPermissions: any = []; - Object.entries(secretAccessRule).forEach(([envSlug, { read, write }]) => { - if (read) { - secretPermissions.push({ - subject: ProjectPermissionSub.Secrets, - action: ProjectPermissionActions.Read, - conditions: { environment: envSlug } - }); - } - if (write) { - secretPermissions.push( - { - subject: ProjectPermissionSub.Secrets, - action: ProjectPermissionActions.Edit, - conditions: { environment: envSlug } - }, - { - subject: ProjectPermissionSub.Secrets, - action: ProjectPermissionActions.Delete, - conditions: { environment: envSlug } - }, - { - subject: ProjectPermissionSub.Secrets, - action: ProjectPermissionActions.Create, - conditions: { environment: envSlug } - } - ); - } - }); - - const key = `${JSON.stringify(secretPermissions)}-${membership.workspace._id.toString()}`; // group roles that have same permission with in the same workspace - const value = roleMap.get(key); - if (value) { - value.membershipIds.push(membership._id.toString()); - value.organizationId = membership.workspace.organization.toString(); - value.workspaceId = membership.workspace._id.toString(); - } else { - roleMap.set(key, { - membershipIds: [membership._id.toString()], - permissions: [...customPermissions, ...secretPermissions], - organizationId: membership.workspace.organization.toString(), - workspaceId: membership.workspace._id.toString() - }); - } - } - - for (const [key, value] of roleMap.entries()) { - const { membershipIds, permissions, workspaceId, organizationId } = value; - const membership_identity = crypto.randomBytes(3).toString("hex"); - const role = new Role({ - name: `Limited [${membership_identity.toUpperCase()}]`, - organization: organizationId, - workspace: workspaceId, - description: - "This role was auto generated by Infisical in effort to migrate your project members to our new permission system", - isOrgRole: false, - slug: `custom-role-${membership_identity}`, - permissions: permissions - }); - - await role.save(); - - for (const id of membershipIds) { - await Membership.findByIdAndUpdate(id, { - // document db doesn't support update many so we must loop - $set: { - role: CUSTOM, - customRole: role - } - }); - } - } - - logger.info("Backfill: Finished converting old denied permission in workspace to viewers"); - - await MembershipOrg.updateMany( - { - role: OWNER - }, - { - $set: { - role: ADMIN - } - } - ); - - logger.info("Backfill: Finished converting owner role to member"); - } catch (error) { - logger.error(error, "An error occurred when running script [backfillPermission]"); - } - } else { - logger.info("Could not acquire lock for script [backfillPermission], skipping"); - } -}; - -export const migrateRoleFromOwnerToAdmin = async () => { - await MembershipOrg.updateMany( - { - role: OWNER - }, - { - $set: { - role: ADMIN - } - } - ); - - logger.info("Backfill: Finished converting owner role to member"); -}; - -export const migrationAssignSuperadmin = async () => { - const users = await User.find({}).sort({ createdAt: 1 }).limit(2); - const serverCfg = getServerConfig(); - if (serverCfg.initialized) return; - - if (await getIsInfisicalCloud()) { - await updateServerConfig({ initialized: true }); - logger.info("Backfill: Infisical Cloud(initialized)"); - return; - } - - if (users.length) { - let superAdminUserId = ""; - const firstAccount = users?.[0]; - if (firstAccount.email === "test@localhost.local" && users.length === 2) { - superAdminUserId = users?.[1]?._id.toString(); - } else { - superAdminUserId = firstAccount._id.toString(); - } - - if (superAdminUserId) { - const user = await User.findByIdAndUpdate(superAdminUserId, { superAdmin: true }); - await updateServerConfig({ initialized: true }); - logger.info(`Migrated ${user?.email} to superuser`); - } - logger.info("Backfill: Migrated first infisical user to super admin"); - } -}; diff --git a/backend-mongo/src/utils/setup/index.ts b/backend-mongo/src/utils/setup/index.ts deleted file mode 100644 index e6bdd88a0..000000000 --- a/backend-mongo/src/utils/setup/index.ts +++ /dev/null @@ -1,116 +0,0 @@ -import * as Sentry from "@sentry/node"; -import { TelemetryService } from "../../services"; -import { setTransporter } from "../../helpers/nodemailer"; -import { EELicenseService } from "../../ee/services"; -import { initSmtp } from "../../services/smtp"; -// eslint-disable-next-line @typescript-eslint/no-var-requires -import { validateEncryptionKeysConfig } from "./validateConfig"; -import { - backfillBotOrgs, - backfillBots, - backfillEncryptionMetadata, - backfillIntegration, - backfillSecretBlindIndexData, - backfillSecretFolders, - backfillSecretVersions, - backfillServiceToken, - backfillServiceTokenMultiScope, - backfillTrustedIps, - backfillUserAuthMethods, - migrateRoleFromOwnerToAdmin, - migrationAssignSuperadmin -} from "./backfillData"; -import { - reencryptBotOrgKeys, - reencryptBotPrivateKeys, - reencryptSecretBlindIndexDataSalts -} from "./reencryptData"; -import { getNodeEnv, getRedisUrl, getSentryDSN } from "../../config"; -import { - initializeGitHubStrategy, - initializeGitLabStrategy, - initializeGoogleStrategy, - initializeSamlStrategy -} from "../authn/passport"; -import { logger } from "../logging"; -import { bootstrap } from "../../bootstrap"; - -/** - * Prepare Infisical upon startup. This includes tasks like: - * - Log initial telemetry message - * - Initializing SMTP configuration - * - Initializing the instance global feature set (if applicable) - * - Initializing the database connection - * - Initializing Sentry - * - Backfilling data - * - Re-encrypting data - */ -export const setup = async () => { - if ((await getRedisUrl()) === undefined || (await getRedisUrl()) === "") { - logger.error( - "WARNING: Redis is not yet configured. Infisical may not function as expected without it." - ); - } - - await validateEncryptionKeysConfig(); - await TelemetryService.logTelemetryMessage(); - - // initializing SMTP configuration - const transporter = await initSmtp(); - setTransporter(transporter); - - // initializing global feature set - await EELicenseService.initGlobalFeatureSet(); - - // initializing auth strategies - await initializeGoogleStrategy(); - await initializeGitHubStrategy(); - await initializeGitLabStrategy(); - await initializeSamlStrategy(); - - // re-encrypt any data previously encrypted under server hex 128-bit ENCRYPTION_KEY - // to base64 256-bit ROOT_ENCRYPTION_KEY - // await reencryptBotPrivateKeys(); - // await reencryptSecretBlindIndexDataSalts(); - - await bootstrap({ transporter }); - - /** - * NOTE: the order in this setup function is critical. - * It is important to backfill data before performing any re-encryption functionality. - */ - - // backfilling data to catch up with new collections and updated fields - await backfillSecretVersions(); - await backfillBots(); - await backfillBotOrgs(); - await backfillSecretBlindIndexData(); - await backfillEncryptionMetadata(); - await backfillSecretFolders(); - await backfillServiceToken(); - await backfillIntegration(); - await backfillServiceTokenMultiScope(); - await backfillTrustedIps(); - await backfillUserAuthMethods(); - // await backfillPermission(); - await migrateRoleFromOwnerToAdmin(); - await migrationAssignSuperadmin(); - - // re-encrypt any data previously encrypted under server hex 128-bit ENCRYPTION_KEY - // to base64 256-bit ROOT_ENCRYPTION_KEY - await reencryptBotPrivateKeys(); - await reencryptBotOrgKeys(); - await reencryptSecretBlindIndexDataSalts(); - - // initializing Sentry - Sentry.init({ - dsn: await getSentryDSN(), - tracesSampleRate: 1.0, - debug: (await getNodeEnv()) === "production" ? false : true, - environment: await getNodeEnv() - }); - - // akhilmhdh: removed dev account as we have now admin account onboarding flow - // That will be user's first account going forward - // await createTestUserForDevelopment(); -}; diff --git a/backend-mongo/src/utils/setup/reencryptData.ts b/backend-mongo/src/utils/setup/reencryptData.ts deleted file mode 100644 index 1a782ab8f..000000000 --- a/backend-mongo/src/utils/setup/reencryptData.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { - Bot, - BotOrg, - IBot, - IBotOrg, - ISecretBlindIndexData, - SecretBlindIndexData, -} from "../../models"; -import { decryptSymmetric128BitHexKeyUTF8 } from "../../utils/crypto"; -import { - client, - getEncryptionKey, - getRootEncryptionKey, -} from "../../config"; -import { - ALGORITHM_AES_256_GCM, - ENCODING_SCHEME_BASE64, - ENCODING_SCHEME_UTF8, -} from "../../variables"; - -/** - * Re-encrypt bot private keys from under hex 128-bit ENCRYPTION_KEY - * to base64 256-bit ROOT_ENCRYPTION_KEY - */ -export const reencryptBotPrivateKeys = async () => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - if (encryptionKey && rootEncryptionKey) { - // 1: re-encrypt bot private keys under ROOT_ENCRYPTION_KEY - const bots = await Bot.find({ - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - }).select("+encryptedPrivateKey iv tag algorithm keyEncoding"); - - if (bots.length === 0) return; - - const operationsBot = await Promise.all( - bots.map(async (bot: IBot) => { - - const privateKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: bot.encryptedPrivateKey, - iv: bot.iv, - tag: bot.tag, - key: encryptionKey, - }); - - const { - ciphertext: encryptedPrivateKey, - iv, - tag, - } = client.encryptSymmetric(privateKey, rootEncryptionKey); - - return ({ - updateOne: { - filter: { - _id: bot._id, - }, - update: { - encryptedPrivateKey, - iv, - tag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_BASE64, - }, - }, - }) - }) - ); - - await Bot.bulkWrite(operationsBot); - } -} - -/** - * Re-encrypt organization bot keys (symmetric and private) from under hex 128-bit ENCRYPTION_KEY - * to base64 256-bit ROOT_ENCRYPTION_KEY - */ -export const reencryptBotOrgKeys = async () => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - if (encryptionKey && rootEncryptionKey) { - // 1: re-encrypt organization bot keys under ROOT_ENCRYPTION_KEY - const botOrgs = await BotOrg.find({ - symmetricKeyAlgorithm: ALGORITHM_AES_256_GCM, - symmetricKeyKeyEncoding: ENCODING_SCHEME_UTF8, - privateKeyAlgorithm: ALGORITHM_AES_256_GCM, - privateKeyKeyEncoding: ENCODING_SCHEME_UTF8 - }).select("+encryptedPrivateKey iv tag algorithm keyEncoding"); - - if (botOrgs.length === 0) return; - - const operationsBotOrg = await Promise.all( - botOrgs.map(async (botOrg: IBotOrg) => { - const privateKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: botOrg.encryptedPrivateKey, - iv: botOrg.privateKeyIV, - tag: botOrg.privateKeyTag, - key: encryptionKey - }); - - const { - ciphertext: encryptedPrivateKey, - iv: privateKeyIV, - tag: privateKeyTag, - } = client.encryptSymmetric(privateKey, rootEncryptionKey); - - const symmetricKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: botOrg.encryptedSymmetricKey, - iv: botOrg.symmetricKeyIV, - tag: botOrg.symmetricKeyTag, - key: encryptionKey - }); - - const { - ciphertext: encryptedSymmetricKey, - iv: symmetricKeyIV, - tag: symmetricKeyTag, - } = client.encryptSymmetric(symmetricKey, rootEncryptionKey); - - return ({ - updateOne: { - filter: { - _id: botOrg._id, - }, - update: { - encryptedSymmetricKey, - symmetricKeyIV, - symmetricKeyTag, - symmetricKeyAlgorithm: ALGORITHM_AES_256_GCM, - symmetricKeyKeyEncoding: ENCODING_SCHEME_BASE64, - encryptedPrivateKey, - privateKeyIV, - privateKeyTag, - privateKeyAlgorithm: ALGORITHM_AES_256_GCM, - privateKeyKeyEncoding: ENCODING_SCHEME_BASE64, - }, - }, - }) - }) - ); - - await BotOrg.bulkWrite(operationsBotOrg); - } -} - -/** - * Re-encrypt secret blind index data salts from hex 128-bit ENCRYPTION_KEY - * to base64 256-bit ROOT_ENCRYPTION_KEY - */ -export const reencryptSecretBlindIndexDataSalts = async () => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - if (encryptionKey && rootEncryptionKey) { - const secretBlindIndexData = await SecretBlindIndexData.find({ - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_UTF8, - }).select("+encryptedSaltCiphertext +saltIV +saltTag +algorithm +keyEncoding"); - - if (secretBlindIndexData.length == 0) return; - - const operationsSecretBlindIndexData = await Promise.all( - secretBlindIndexData.map(async (secretBlindIndexDatum: ISecretBlindIndexData) => { - - const salt = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secretBlindIndexDatum.encryptedSaltCiphertext, - iv: secretBlindIndexDatum.saltIV, - tag: secretBlindIndexDatum.saltTag, - key: encryptionKey, - }); - - const { - ciphertext: encryptedSaltCiphertext, - iv: saltIV, - tag: saltTag, - } = client.encryptSymmetric(salt, rootEncryptionKey); - - return ({ - updateOne: { - filter: { - _id: secretBlindIndexDatum._id, - }, - update: { - encryptedSaltCiphertext, - saltIV, - saltTag, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_BASE64, - }, - }, - }) - }) - ); - - await SecretBlindIndexData.bulkWrite(operationsSecretBlindIndexData); - } -} \ No newline at end of file diff --git a/backend-mongo/src/utils/setup/validateConfig.ts b/backend-mongo/src/utils/setup/validateConfig.ts deleted file mode 100644 index 3a3974791..000000000 --- a/backend-mongo/src/utils/setup/validateConfig.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - getEncryptionKey, - getRootEncryptionKey, -} from "../../config"; -import { - InternalServerError, -} from "../../utils/errors"; - -/** - * Validate ENCRYPTION_KEY and ROOT_ENCRYPTION_KEY. Specifically: - * - ENCRYPTION_KEY is a hex, 128-bit string - * - ROOT_ENCRYPTION_KEY is a base64, 128-bit string - * - Either ENCRYPTION_KEY or ROOT_ENCRYPTION_KEY are present - * - * - Encrypted data is consistent with the passed in encryption keys - * - * NOTE 1: ENCRYPTION_KEY is being transitioned to ROOT_ENCRYPTION_KEY - * NOTE 2: In the future, we will have a superior validation function - * built into the SDK. - */ -export const validateEncryptionKeysConfig = async () => { - const encryptionKey = await getEncryptionKey(); - const rootEncryptionKey = await getRootEncryptionKey(); - - if ( - (encryptionKey === undefined || encryptionKey === "") && - (rootEncryptionKey === undefined || rootEncryptionKey === "") - ) throw InternalServerError({ - message: "Failed to find required root encryption key environment variable. Please make sure that you're passing in a ROOT_ENCRYPTION_KEY environment variable.", - }); - - // if (encryptionKey && encryptionKey !== '') { - // // validate [encryptionKey] - - // const keyBuffer = Buffer.from(encryptionKey, 'hex'); - // const decoded = keyBuffer.toString('hex'); - - // if (decoded !== encryptionKey) throw InternalServerError({ - // message: 'Failed to validate that the encryption key is correctly encoded in hex.' - // }); - - // if (keyBuffer.length !== 16) throw InternalServerError({ - // message: 'Failed to validate that the encryption key is a 128-bit hex string.' - // }); - // } - - if (rootEncryptionKey && rootEncryptionKey !== "") { - // validate [rootEncryptionKey] - - const keyBuffer = Buffer.from(rootEncryptionKey, "base64") - const decoded = keyBuffer.toString("base64"); - - if (decoded !== rootEncryptionKey) throw InternalServerError({ - message: "Failed to validate that the root encryption key is correctly encoded in base64", - }); - - if (keyBuffer.length !== 32) throw InternalServerError({ - message: "Failed to validate that the encryption key is a 256-bit base64 string", - }); - } -} \ No newline at end of file diff --git a/backend-mongo/src/validation/action.ts b/backend-mongo/src/validation/action.ts deleted file mode 100644 index 7c76a5365..000000000 --- a/backend-mongo/src/validation/action.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { z } from "zod"; - -export const GetActionV1 = z.object({ - params: z.object({ - actionId: z.string().trim() - }) -}); - -export const AddUserActionV1 = z.object({ - body: z.object({ - action: z.string().trim() - }) -}); - -export const GetUserActionV1 = z.object({ - query: z.object({ - action: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/admin.ts b/backend-mongo/src/validation/admin.ts deleted file mode 100644 index aee6c88cc..000000000 --- a/backend-mongo/src/validation/admin.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { z } from "zod"; - -export const UpdateServerConfigV1 = z.object({ - body: z.object({ - allowSignUp: z.boolean().optional() - }) -}); - -export const SignupV1 = z.object({ - body: z.object({ - email: z.string().email().trim(), - firstName: z.string().trim(), - lastName: z.string().trim().optional(), - protectedKey: z.string().trim(), - protectedKeyIV: z.string().trim(), - protectedKeyTag: z.string().trim(), - publicKey: z.string().trim(), - encryptedPrivateKey: z.string().trim(), - encryptedPrivateKeyIV: z.string().trim(), - encryptedPrivateKeyTag: z.string().trim(), - salt: z.string().trim(), - verifier: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/apiKeyDataV3.ts b/backend-mongo/src/validation/apiKeyDataV3.ts deleted file mode 100644 index c92ce468c..000000000 --- a/backend-mongo/src/validation/apiKeyDataV3.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { z } from "zod"; - -export const CreateAPIKeyV3 = z.object({ - body: z.object({ - name: z.string().trim() - }) -}); - -export const UpdateAPIKeyV3 = z.object({ - params: z.object({ - apiKeyDataId: z.string().trim() - }), - body: z.object({ - name: z.string().trim() - }) -}); - -export const DeleteAPIKeyV3 = z.object({ - params: z.object({ - apiKeyDataId: z.string().trim() - }) -}); \ No newline at end of file diff --git a/backend-mongo/src/validation/auth.ts b/backend-mongo/src/validation/auth.ts deleted file mode 100644 index 6e494a66b..000000000 --- a/backend-mongo/src/validation/auth.ts +++ /dev/null @@ -1,226 +0,0 @@ -import { z } from "zod"; - -export const BeginEmailSignUpV1 = z.object({ - body: z.object({ - email: z.string().email().trim() - }) -}); - -export const VerifyEmailSignUpV1 = z.object({ - body: z.object({ - email: z.string().email().trim(), - code: z.string().trim() - }) -}); - -export const Login1V1 = z.object({ - body: z.object({ - email: z.string().email().trim(), - clientPublicKey: z.string().trim() - }) -}); - -export const Login2V1 = z.object({ - body: z.object({ - email: z.string().email().trim(), - clientProof: z.string().trim() - }) -}); - -export const Srp1V1 = z.object({ - body: z.object({ - clientPublicKey: z.string().trim() - }) -}); - -export const ChangePasswordV1 = z.object({ - body: z.object({ - clientProof: z.string().trim(), - protectedKey: z.string().trim(), - protectedKeyIV: z.string().trim(), - protectedKeyTag: z.string().trim(), - encryptedPrivateKey: z.string().trim(), - encryptedPrivateKeyIV: z.string().trim(), - encryptedPrivateKeyTag: z.string().trim(), - salt: z.string().trim(), - verifier: z.string().trim() - }) -}); - -export const EmailPasswordResetV1 = z.object({ - body: z.object({ - email: z.string().email().trim() - }) -}); - -export const EmailPasswordResetVerifyV1 = z.object({ - body: z.object({ - email: z.string().email().trim(), - code: z.string().trim() - }) -}); - -export const CreateBackupPrivateKeyV1 = z.object({ - body: z.object({ - clientProof: z.string().trim(), - encryptedPrivateKey: z.string().trim(), - iv: z.string().trim(), - tag: z.string().trim(), - salt: z.string().trim(), - verifier: z.string().trim() - }) -}); - -export const ResetPasswordV1 = z.object({ - body: z.object({ - protectedKey: z.string().trim(), - protectedKeyIV: z.string().trim(), - protectedKeyTag: z.string().trim(), - encryptedPrivateKey: z.string().trim(), - encryptedPrivateKeyIV: z.string().trim(), - encryptedPrivateKeyTag: z.string().trim(), - salt: z.string().trim(), - verifier: z.string().trim() - }) -}); - -export const RenewAccessTokenV1 = z.object({ - body: z.object({ - accessToken: z.string().trim(), - }) -}); - -export const LoginUniversalAuthV1 = z.object({ - body: z.object({ - clientId: z.string().trim(), - clientSecret: z.string().trim() - }) -}); - -export const AddUniversalAuthToIdentityV1 = z.object({ - params: z.object({ - identityId: z.string().trim() - }), - body: z.object({ - clientSecretTrustedIps: z - .object({ - ipAddress: z.string().trim(), - }) - .array() - .min(1) - .default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]), - accessTokenTrustedIps: z - .object({ - ipAddress: z.string().trim(), - }) - .array() - .min(1) - .default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]), - accessTokenTTL: z.number().int().min(1).refine(value => value !== 0, { - message: "accessTokenTTL must have a non zero number", - }).default(2592000), - accessTokenMaxTTL: z.number().int().refine(value => value !== 0, { - message: "accessTokenMaxTTL must have a non zero number", - }).default(2592000), // 30 days - accessTokenNumUsesLimit: z.number().int().min(0).default(0) - }) -}); - -export const UpdateUniversalAuthToIdentityV1 = z.object({ - params: z.object({ - identityId: z.string() - }), - body: z.object({ - clientSecretTrustedIps: z - .object({ - ipAddress: z.string().trim() - }) - .array() - .min(1) - .optional(), - accessTokenTrustedIps: z - .object({ - ipAddress: z.string().trim(), - }) - .array() - .min(1) - .optional(), - accessTokenTTL: z.number().int().min(0).optional(), - accessTokenNumUsesLimit: z.number().int().min(0).optional(), - accessTokenMaxTTL: z.number().int().refine(value => value !== 0, { - message: "accessTokenMaxTTL must have a non zero number", - }).optional(), - }), -}); - -export const GetUniversalAuthForIdentityV1 = z.object({ - params: z.object({ - identityId: z.string().trim() - }) -}); - -export const CreateUniversalAuthClientSecretV1 = z.object({ - params: z.object({ - identityId: z.string() - }), - body: z.object({ - description: z.string().trim().default(""), - numUsesLimit: z.number().min(0).default(0), - ttl: z.number().min(0).default(0), - }), -}); - -export const GetUniversalAuthClientSecretsV1 = z.object({ - params: z.object({ - identityId: z.string() - }) -}); - -export const RevokeUniversalAuthClientSecretV1 = z.object({ - params: z.object({ - identityId: z.string(), - clientSecretId: z.string() - }) -}); - -export const VerifyMfaTokenV2 = z.object({ - body: z.object({ - mfaToken: z.string().trim() - }) -}); - -export const Login1V3 = z.object({ - body: z.object({ - email: z.string().email().trim(), - providerAuthToken: z.string().trim().optional(), - clientPublicKey: z.string().trim() - }) -}); - -export const Login2V3 = z.object({ - body: z.object({ - email: z.string().email().trim(), - providerAuthToken: z.string().trim().optional(), - clientProof: z.string().trim() - }) -}); - -export const CompletedAccountSignupV3 = z.object({ - body: z.object({ - email: z.string().email().trim(), - firstName: z.string().trim(), - lastName: z.string().trim().optional(), - protectedKey: z.string().trim(), - protectedKeyIV: z.string().trim(), - protectedKeyTag: z.string().trim(), - publicKey: z.string().trim(), - encryptedPrivateKey: z.string().trim(), - encryptedPrivateKeyIV: z.string().trim(), - encryptedPrivateKeyTag: z.string().trim(), - salt: z.string().trim(), - verifier: z.string().trim(), - organizationName: z.string().trim(), - providerAuthToken: z.string().trim().optional().nullish(), - attributionSource: z.string().trim().optional() - }) -}); diff --git a/backend-mongo/src/validation/bot.ts b/backend-mongo/src/validation/bot.ts deleted file mode 100644 index a5fa5b52f..000000000 --- a/backend-mongo/src/validation/bot.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { z } from "zod"; - -export const GetBotByWorkspaceIdV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const SetBotActiveStateV1 = z.object({ - body: z.object({ - isActive: z.boolean(), - botKey: z - .object({ - nonce: z.string().trim().optional(), - encryptedKey: z.string().trim().optional() - }) - .optional() - }), - params: z.object({ - botId: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/cloudProducts.ts b/backend-mongo/src/validation/cloudProducts.ts deleted file mode 100644 index 1cfd361b3..000000000 --- a/backend-mongo/src/validation/cloudProducts.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from "zod"; - -export const GetCloudProductsV1 = z.object({ - query: z.object({ - "billing-cycle": z.enum(["monthly", "yearly"]) - }) -}); diff --git a/backend-mongo/src/validation/environments.ts b/backend-mongo/src/validation/environments.ts deleted file mode 100644 index 6cf7cf68a..000000000 --- a/backend-mongo/src/validation/environments.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { z } from "zod"; - -export const CreateWorkspaceEnvironmentV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - environmentSlug: z.string().trim(), - environmentName: z.string().trim() - }) -}); - -export const UpdateWorkspaceEnvironmentV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - environmentSlug: z.string().trim(), - environmentName: z.string().trim(), - oldEnvironmentSlug: z.string().trim() - }) -}); - -export const DeleteWorkspaceEnvironmentV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - environmentSlug: z.string().trim() - }) -}); - -export const GetAllAccessibileEnvironmentsOfWorkspaceV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const ReorderWorkspaceEnvironmentsV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - environmentSlug: z.string().trim(), - environmentName: z.string().trim(), - otherEnvironmentSlug: z.string().trim(), - otherEnvironmentName: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/folders.ts b/backend-mongo/src/validation/folders.ts deleted file mode 100644 index deda48b03..000000000 --- a/backend-mongo/src/validation/folders.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { z } from "zod"; - -export const CreateFolderV1 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - folderName: z.string().trim(), - directory: z.string().trim().default("/") - }) -}); - -export const UpdateFolderV1 = z.object({ - params: z.object({ - folderName: z.string().trim() - }), - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - name: z.string().trim(), - directory: z.string().trim().default("/") - }) -}); - -export const DeleteFolderV1 = z.object({ - params: z.object({ - folderName: z.string().trim() - }), - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - directory: z.string().trim().default("/") - }) -}); - -export const GetFoldersV1 = z.object({ - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - directory: z.string().trim().default("/") - }) -}); diff --git a/backend-mongo/src/validation/hasuraCloudIntegration.ts b/backend-mongo/src/validation/hasuraCloudIntegration.ts deleted file mode 100644 index 63b037370..000000000 --- a/backend-mongo/src/validation/hasuraCloudIntegration.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as z from "zod"; - -export const ZGetTenantEnv = z.object({ - data: z.object({ - getTenantEnv: z.object({ - hash: z.string(), - envVars: z.object({ - environment: z.record(z.any()).optional() - }) - }) - }) -}); - -export const ZUpdateTenantEnv = z.object({ - data: z.object({ - updateTenantEnv: z.object({ - hash: z.string(), - envVars: z.record(z.any()) - }) - }) -}); diff --git a/backend-mongo/src/validation/identities.ts b/backend-mongo/src/validation/identities.ts deleted file mode 100644 index fa22fde34..000000000 --- a/backend-mongo/src/validation/identities.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { z } from "zod"; -import { NO_ACCESS } from "../variables"; - -export const CreateIdentityV1 = z.object({ - body: z.object({ - name: z.string().trim(), - organizationId: z.string().trim(), - role: z.string().trim().min(1).default(NO_ACCESS) - }) -}); - -export const UpdateIdentityV1 = z.object({ - params: z.object({ - identityId: z.string() - }), - body: z.object({ - name: z.string().trim().optional(), - role: z.string().trim().min(1).optional() - }), -}); - -export const DeleteIdentityV1 = z.object({ - params: z.object({ - identityId: z.string() - }), -}); diff --git a/backend-mongo/src/validation/index.ts b/backend-mongo/src/validation/index.ts deleted file mode 100644 index e2027d41b..000000000 --- a/backend-mongo/src/validation/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -export * from "./user"; -export * from "./workspace"; -export * from "./bot"; -export * from "./integration"; -export * from "./integrationAuth"; -export * from "./membership"; -export * from "./membershipOrg"; -export * from "./organization"; -export * from "./secrets"; -export * from "./serviceTokenData"; -export * from "./identities"; -export * from "./apiKeyDataV3"; diff --git a/backend-mongo/src/validation/integration.ts b/backend-mongo/src/validation/integration.ts deleted file mode 100644 index b5a02b164..000000000 --- a/backend-mongo/src/validation/integration.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { z } from "zod"; - -export const CreateIntegrationV1 = z.object({ - body: z.object({ - integrationAuthId: z.string().trim(), - app: z.string().trim().optional(), - isActive: z.boolean(), - appId: z.string().trim().optional(), - secretPath: z.string().trim().default("/"), - sourceEnvironment: z.string().trim(), - targetEnvironment: z.string().trim().optional(), - targetEnvironmentId: z.string().trim().optional(), - targetService: z.string().trim().optional(), - targetServiceId: z.string().trim().optional(), - owner: z.string().trim().optional(), - path: z.string().trim().optional(), - region: z.string().trim().optional(), - scope: z.string().trim().optional(), - metadata: z.object({ - secretPrefix: z.string().optional(), - secretSuffix: z.string().optional(), - secretGCPLabel: z.object({ - labelName: z.string(), - labelValue: z.string() - }).optional(), - }).optional() - }) -}); - -export const UpdateIntegrationV1 = z.object({ - params: z.object({ - integrationId: z.string().trim() - }), - body: z.object({ - app: z.string().trim(), - appId: z.string().trim(), - isActive: z.boolean(), - secretPath: z.string().trim().default("/"), - targetEnvironment: z.string().trim(), - owner: z.string().trim(), - environment: z.string().trim() - }) -}); - -export const DeleteIntegrationV1 = z.object({ - params: z.object({ - integrationId: z.string().trim() - }) -}); - -export const ManualSyncV1 = z.object({ - body: z.object({ - environment: z.string().trim(), - workspaceId: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/integrationAuth.ts b/backend-mongo/src/validation/integrationAuth.ts deleted file mode 100644 index 928e7f233..000000000 --- a/backend-mongo/src/validation/integrationAuth.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { Types } from "mongoose"; -import { IUser, IWorkspace, IntegrationAuth } from "../models"; -import { IntegrationAuthNotFoundError, UnauthorizedRequestError } from "../utils/errors"; -import { IntegrationService } from "../services"; -import { validateUserClientForWorkspace } from "./user"; -import { AuthData } from "../interfaces/middleware"; -import { ActorType } from "../ee/models"; -import { z } from "zod"; - -/** - * Validate authenticated clients for integration authorization with id [integrationAuthId] based - * on any known permissions. - * @param {Object} obj - * @param {Object} obj.authData - authenticated client details - * @param {Types.ObjectId} obj.integrationAuthId - id of integration authorization to validate against - * @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles - * @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint - */ -const validateClientForIntegrationAuth = async ({ - authData, - integrationAuthId, - acceptedRoles, - attachAccessToken -}: { - authData: AuthData; - integrationAuthId: Types.ObjectId; - acceptedRoles: Array<"admin" | "member">; - attachAccessToken?: boolean; -}) => { - const integrationAuth = await IntegrationAuth.findById(integrationAuthId) - .populate<{ workspace: IWorkspace }>("workspace") - .select( - "+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt metadata" - ); - - if (!integrationAuth) throw IntegrationAuthNotFoundError(); - - let accessToken, accessId; - if (attachAccessToken) { - const access = await IntegrationService.getIntegrationAuthAccess({ - integrationAuthId: integrationAuth._id - }); - - accessToken = access.accessToken; - accessId = access.accessId; - } - - switch (authData.actor.type) { - case ActorType.USER: - await validateUserClientForWorkspace({ - user: authData.authPayload as IUser, - workspaceId: integrationAuth.workspace._id, - acceptedRoles - }); - - return { integrationAuth, accessToken, accessId }; - case ActorType.SERVICE: - throw UnauthorizedRequestError({ - message: "Failed service token authorization for integration authorization" - }); - case ActorType.IDENTITY: - throw UnauthorizedRequestError({ - message: "Failed identity authorization for integration authorization" - }); - } -}; - -export const GetIntegrationAuthV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }) -}); - -export const OauthExchangeV1 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - code: z.string().trim(), - integration: z.string().trim(), - url: z.string().trim().url().optional(), - }) -}); - -export const SaveIntegrationAccessTokenV1 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - integration: z.string().trim(), - accessId: z.string().trim().optional(), - accessToken: z.string().trim().optional(), - url: z.string().url().trim().optional(), - namespace: z.string().trim().optional(), - refreshToken:z.string().trim().optional() - }) -}); - -export const GetIntegrationAuthAppsV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }), - query: z.object({ - teamId: z.string().trim().optional(), - workspaceSlug: z.string().trim().optional() - }) -}); - -export const GetIntegrationAuthTeamsV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }) -}); - -export const GetIntegrationAuthVercelBranchesV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }), - query: z.object({ - appId: z.string().trim() - }) -}); - -export const GetIntegrationAuthChecklyGroupsV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }), - query: z.object({ - accountId: z.string().trim() - }) -}); - -export const GetIntegrationAuthQoveryOrgsV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }) -}); - -export const GetIntegrationAuthQoveryProjectsV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }), - query: z.object({ - orgId: z.string().trim() - }) -}); - -export const GetIntegrationAuthQoveryEnvironmentsV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }), - query: z.object({ - projectId: z.string().trim() - }) -}); - -export const GetIntegrationAuthQoveryScopesV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }), - query: z.object({ - environmentId: z.string().trim() - }) -}); - -export const GetIntegrationAuthRailwayEnvironmentsV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }), - query: z.object({ - appId: z.string().trim() - }) -}); - -export const GetIntegrationAuthRailwayServicesV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }), - query: z.object({ - appId: z.string().trim() - }) -}); - -export const GetIntegrationAuthBitbucketWorkspacesV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }) -}); - -export const GetIntegrationAuthNorthflankSecretGroupsV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }), - query: z.object({ - appId: z.string().trim() - }) -}); - -export const DeleteIntegrationAuthsV1 = z.object({ - query: z.object({ - integration: z.string().trim(), - workspaceId: z.string().trim() - }) -}); - -export const DeleteIntegrationAuthV1 = z.object({ - params: z.object({ - integrationAuthId: z.string().trim() - }) -}); - -export const GetIntegrationAuthTeamCityBuildConfigsV1 = z.object({ - params: z.object({ - integrationAuthId:z.string().trim() - }), - query: z.object({ - appId:z.string().trim() - }) -}) - -export { validateClientForIntegrationAuth }; diff --git a/backend-mongo/src/validation/key.ts b/backend-mongo/src/validation/key.ts deleted file mode 100644 index a2d4ab92d..000000000 --- a/backend-mongo/src/validation/key.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { z } from "zod"; - -export const UploadKeyV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - key: z.object({ - encryptedKey: z.string().trim(), - nonce: z.string().trim(), - userId: z.string().trim() - }) - }) -}); - -export const GetLatestKeyV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/membership.ts b/backend-mongo/src/validation/membership.ts deleted file mode 100644 index 373a09aa0..000000000 --- a/backend-mongo/src/validation/membership.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Types } from "mongoose"; -import { IServiceTokenData, IUser, Membership } from "../models"; -import { validateUserClientForWorkspace } from "./user"; -import { validateServiceTokenDataClientForWorkspace } from "./serviceTokenData"; -import { MembershipNotFoundError } from "../utils/errors"; -import { AuthData } from "../interfaces/middleware"; -import { ActorType } from "../ee/models"; -import { z } from "zod"; - -/** - * Validate authenticated clients for membership with id [membershipId] based - * on any known permissions. - * @param {Object} obj - * @param {Object} obj.authData - authenticated client details - * @param {Types.ObjectId} obj.membershipId - id of membership to validate against - * @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspaceRoles - * @returns {Membership} - validated membership - */ -export const validateClientForMembership = async ({ - authData, - membershipId, - acceptedRoles -}: { - authData: AuthData; - membershipId: Types.ObjectId; - acceptedRoles: Array<"admin" | "member">; -}) => { - const membership = await Membership.findById(membershipId); - - if (!membership) - throw MembershipNotFoundError({ - message: "Failed to find membership" - }); - - switch (authData.actor.type) { - case ActorType.USER: - await validateUserClientForWorkspace({ - user: authData.authPayload as IUser, - workspaceId: membership.workspace, - acceptedRoles - }); - - return membership; - case ActorType.SERVICE: - await validateServiceTokenDataClientForWorkspace({ - serviceTokenData: authData.authPayload as IServiceTokenData, - workspaceId: new Types.ObjectId(membership.workspace) - }); - - return membership; - } -}; - -export const ValidateMembershipV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const DeleteMembershipV1 = z.object({ - params: z.object({ - membershipId: z.string().trim() - }) -}); - -export const ChangeMembershipRoleV1 = z.object({ - body: z.object({ - role: z.string().trim() - }), - params: z.object({ membershipId: z.string().trim() }) -}); - -export const DenyMembershipPermissionV1 = z.object({ - params: z.object({ - membershipId: z.string().trim() - }), - body: z.object({ - permissions: z.object({}).array() - }) -}); - -export const AddUserToWorkspaceV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - members: z - .object({ - orgMembershipId: z.string().trim(), - workspaceEncryptedKey: z.string().trim(), - workspaceEncryptedNonce: z.string().trim() - }) - .array() - .min(1) - }) -}); diff --git a/backend-mongo/src/validation/membershipOrg.ts b/backend-mongo/src/validation/membershipOrg.ts deleted file mode 100644 index 4656d42fe..000000000 --- a/backend-mongo/src/validation/membershipOrg.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { z } from "zod"; - -export const DelOrgMembershipv1 = z.object({ - params: z.object({ - membershipOrgId: z.string().trim() - }) -}); - -export const InviteUserToOrgv1 = z.object({ - body: z.object({ - inviteeEmail: z.string().trim().email(), - organizationId: z.string().trim() - }) -}); - -export const VerifyUserToOrgv1 = z.object({ - body: z.object({ - email: z.string().trim().email(), - organizationId: z.string().trim(), - code: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/organization.ts b/backend-mongo/src/validation/organization.ts deleted file mode 100644 index d0ab37057..000000000 --- a/backend-mongo/src/validation/organization.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { Types } from "mongoose"; -import { z } from "zod"; -import { IUser, Organization } from "../models"; -import { OrganizationNotFoundError, UnauthorizedRequestError } from "../utils/errors"; -import { validateUserClientForOrganization } from "./user"; -import { AuthData } from "../interfaces/middleware"; -import { ActorType } from "../ee/models"; - -/** - * Validate accepted clients for organization with id [organizationId] - * @param {Object} obj - * @param {Object} obj.authData - authenticated client details - * @param {Types.ObjectId} obj.organizationId - id of organization to validate against - */ -export const validateClientForOrganization = async ({ - authData, - organizationId, - acceptedRoles, - acceptedStatuses -}: { - authData: AuthData; - organizationId: Types.ObjectId; - acceptedRoles: Array<"owner" | "admin" | "member">; - acceptedStatuses: Array<"invited" | "accepted">; -}) => { - const organization = await Organization.findById(organizationId); - - if (!organization) { - throw OrganizationNotFoundError({ - message: "Failed to find organization" - }); - } - - let membershipOrg; - switch (authData.actor.type) { - case ActorType.USER: - membershipOrg = await validateUserClientForOrganization({ - user: authData.authPayload as IUser, - organization, - acceptedRoles, - acceptedStatuses - }); - - return { organization, membershipOrg }; - case ActorType.SERVICE: - throw UnauthorizedRequestError({ - message: "Failed service token authorization for organization" - }); - case ActorType.IDENTITY: - throw UnauthorizedRequestError({ - message: "Failed identity authorization for organization" - }); - } -}; - -export const GetOrgPlansTablev1 = z.object({ - query: z.object({ billingCycle: z.enum(["monthly", "yearly"]) }), - params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgPlanv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }), - query: z.object({ workspaceId: z.string().trim().optional() }) -}); - -export const StartOrgTrailv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }), - body: z.object({ success_url: z.string().trim() }) -}); - -export const GetOrgPlanBillingInfov1 = z.object({ - params: z.object({ organizationId: z.string().trim() }), - query: z.object({ workspaceId: z.string().trim().optional() }) -}); - -export const GetOrgPlanTablev1 = z.object({ - params: z.object({ organizationId: z.string().trim() }), - query: z.object({ workspaceId: z.string().trim().optional() }) -}); - -export const GetOrgBillingDetailsv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const UpdateOrgBillingDetailsv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }), - body: z.object({ - email: z.string().trim().email().optional(), - name: z.string().trim().optional() - }) -}); - -export const GetOrgPmtMethodsv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const CreateOrgPmtMethodv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }), - body: z.object({ - success_url: z.string().trim(), - cancel_url: z.string().trim() - }) -}); - -export const DelOrgPmtMethodv1 = z.object({ - params: z.object({ - organizationId: z.string().trim(), - pmtMethodId: z.string().trim() - }) -}); - -export const GetOrgTaxIdsv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const CreateOrgTaxId = z.object({ - params: z.object({ organizationId: z.string().trim() }), - body: z.object({ - type: z.string().trim(), - value: z.string().trim() - }) -}); - -export const DelOrgTaxIdv1 = z.object({ - params: z.object({ - organizationId: z.string().trim(), - taxId: z.string().trim() - }) -}); - -export const GetOrgInvoicesv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgLicencesv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgv1 = z.object({ - params: z.object({ - organizationId: z.string().trim() - }) -}); - -export const GetOrgMembersv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgWorkspacesv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const ChangeOrgNamev1 = z.object({ - params: z.object({ organizationId: z.string().trim() }), - body: z.object({ name: z.string().trim() }) -}); - -export const GetOrgIncidentContactv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const CreateOrgIncideContact = z.object({ - params: z.object({ organizationId: z.string().trim() }), - body: z.object({ email: z.string().email().trim() }) -}); - -export const DelOrgIncideContact = z.object({ - params: z.object({ organizationId: z.string().trim() }), - body: z.object({ email: z.string().email().trim() }) -}); - -export const CreateOrgPortalSessionv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgMembersAndWsv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgMembersv2 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const UpdateOrgMemberv2 = z.object({ - params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }), - body: z.object({ - role: z.string().trim() - }) -}); - -export const DeleteOrgMemberv2 = z.object({ - params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }) -}); - -export const GetOrgWorkspacesv2 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const VerfiyUserToOrganizationV1 = z.object({ - body: z.object({ - email: z.string().trim().email(), - organizationId: z.string().trim(), - code: z.string().trim() - }) -}); - -export const CreateOrgv2 = z.object({ - body: z.object({ - name: z.string().trim() - }) -}); - -export const DeleteOrgv2 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgServiceMembersV2 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgIdentityMembershipsV2 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); \ No newline at end of file diff --git a/backend-mongo/src/validation/secretImports.ts b/backend-mongo/src/validation/secretImports.ts deleted file mode 100644 index a899a8346..000000000 --- a/backend-mongo/src/validation/secretImports.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { z } from "zod"; - -export const CreateSecretImportV1 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - directory: z.string().trim().default("/"), - secretImport: z.object({ - environment: z.string().trim(), - secretPath: z.string().trim() - }) - }) -}); - -export const UpdateSecretImportV1 = z.object({ - params: z.object({ - id: z.string().trim() - }), - body: z.object({ - secretImports: z - .object({ - environment: z.string().trim(), - secretPath: z.string().trim() - }) - .array() - }) -}); - -export const DeleteSecretImportV1 = z.object({ - params: z.object({ - id: z.string().trim() - }), - body: z.object({ - secretImportPath: z.string().trim(), - secretImportEnv: z.string().trim() - }) -}); - -export const GetSecretImportsV1 = z.object({ - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - directory: z.string().trim().default("/") - }) -}); - -export const GetAllSecretsFromImportV1 = z.object({ - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - directory: z.string().trim().default("/") - }) -}); diff --git a/backend-mongo/src/validation/secretScanning.ts b/backend-mongo/src/validation/secretScanning.ts deleted file mode 100644 index 2a883f8f8..000000000 --- a/backend-mongo/src/validation/secretScanning.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { z } from "zod"; - -export const CreateInstalLSessionv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const LinkInstallationToOrgv1 = z.object({ - body: z.object({ - installationId: z.string(), - sessionId: z.string().trim() - }) -}); - -export const GetOrgInstallStatusv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgRisksv1 = z.object({ - params: z.object({ organizationId: z.string().trim() }) -}); - -export const UpdateRiskStatusv1 = z.object({ - params: z.object({ organizationId: z.string().trim(), riskId: z.string().trim() }), - body: z.object({ status: z.string().trim() }) -}); diff --git a/backend-mongo/src/validation/secretSnapshot.ts b/backend-mongo/src/validation/secretSnapshot.ts deleted file mode 100644 index b431547e8..000000000 --- a/backend-mongo/src/validation/secretSnapshot.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from "zod"; - -export const GetSecretSnapshotV1 = z.object({ - params: z.object({ - secretSnapshotId: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/secrets.ts b/backend-mongo/src/validation/secrets.ts deleted file mode 100644 index 016fe750f..000000000 --- a/backend-mongo/src/validation/secrets.ts +++ /dev/null @@ -1,475 +0,0 @@ -import { Types } from "mongoose"; -import { ISecret, IServiceTokenData, IUser, Secret } from "../models"; -import { validateUserClientForSecret, validateUserClientForSecrets } from "./user"; -import { - validateServiceTokenDataClientForSecrets, - validateServiceTokenDataClientForWorkspace -} from "./serviceTokenData"; -import { BadRequestError, SecretNotFoundError } from "../utils/errors"; -import { AuthData } from "../interfaces/middleware"; -import { ActorType } from "../ee/models"; -import { z } from "zod"; -import { SECRET_PERSONAL, SECRET_SHARED } from "../variables"; -/** - * Validate authenticated clients for secrets with id [secretId] based - * on any known permissions. - * @param {Object} obj - * @param {Object} obj.authData - authenticated client details - * @param {Types.ObjectId} obj.secretId - id of secret to validate against - * @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles - * @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint - */ -export const validateClientForSecret = async ({ - authData, - secretId, - acceptedRoles, - requiredPermissions -}: { - authData: AuthData; - secretId: Types.ObjectId; - acceptedRoles: Array<"admin" | "member">; - requiredPermissions: string[]; -}) => { - const secret = await Secret.findById(secretId); - - if (!secret) - throw SecretNotFoundError({ - message: "Failed to find secret" - }); - - switch (authData.actor.type) { - case ActorType.USER: - await validateUserClientForSecret({ - user: authData.authPayload as IUser, - secret, - acceptedRoles, - requiredPermissions - }); - - return secret; - case ActorType.SERVICE: - await validateServiceTokenDataClientForWorkspace({ - serviceTokenData: authData.authPayload as IServiceTokenData, - workspaceId: secret.workspace, - environment: secret.environment - }); - - return secret; - } -}; - -/** - * Validate authenticated clients for secrets with ids [secretIds] based - * on any known permissions. - * @param {Object} obj - * @param {Object} obj.authData - authenticated client details - * @param {Types.ObjectId[]} obj.secretIds - id of workspace to validate against - * @param {String} obj.environment - (optional) environment in workspace to validate against - * @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles - * @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint - */ -export const validateClientForSecrets = async ({ - authData, - secretIds, - requiredPermissions -}: { - authData: AuthData; - secretIds: Types.ObjectId[]; - requiredPermissions: string[]; -}) => { - let secrets: ISecret[] = []; - - secrets = await Secret.find({ - _id: { - $in: secretIds - } - }); - - if (secrets.length != secretIds.length) { - throw BadRequestError({ message: "Failed to validate non-existent secrets" }); - } - - switch (authData.actor.type) { - case ActorType.USER: - await validateUserClientForSecrets({ - user: authData.authPayload as IUser, - secrets, - requiredPermissions - }); - - return secrets; - case ActorType.SERVICE: - await validateServiceTokenDataClientForSecrets({ - serviceTokenData: authData.authPayload as IServiceTokenData, - secrets, - requiredPermissions - }); - - return secrets; - } -}; - -export const GetSecretVersionsV1 = z.object({ - params: z.object({ - secretId: z.string().trim() - }), - query: z.object({ - offset: z.coerce.number(), - limit: z.coerce.number() - }) -}); - -export const RollbackSecretVersionV1 = z.object({ - params: z.object({ - secretId: z.string().trim() - }), - body: z.object({ - version: z.number() - }) -}); - -export const PushSecretsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - secrets: z.object({}).array(), - keys: z.object({}).array(), - environment: z.string().trim(), - channel: z.string().trim() - }) -}); - -export const PullSecretsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - query: z.object({ - channel: z.string().optional(), - environment: z.string().trim() - }) -}); - -export const PullSecretsServiceTokenV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - query: z.object({ - channel: z.string().optional(), - environment: z.string().trim() - }) -}); - -const batchUpdateRequestV2 = z.object({ - _id: z.string(), - folderId: z.string().trim().optional(), - type: z.enum(["shared", "personal"]), - secretName: z.string().trim(), - secretKeyCiphertext: z.string().trim(), - secretKeyIV: z.string().trim(), - secretKeyTag: z.string().trim(), - secretValueCiphertext: z.string().trim(), - secretValueIV: z.string().trim(), - secretValueTag: z.string().trim(), - secretCommentCiphertext: z.string().trim().optional(), - secretCommentIV: z.string().trim().optional(), - secretCommentTag: z.string().trim().optional(), - tags: z - .object({ - _id: z.string().trim(), - name: z.string().trim(), - slug: z.string().trim() - }) - .array() -}); - -export const BatchSecretsV2 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - folderId: z.string().trim().default("root"), - environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - requests: z - .discriminatedUnion("method", [ - z.object({ - method: z.literal("POST"), - secret: batchUpdateRequestV2.omit({ _id: true }) - }), - z.object({ - method: z.literal("PATCH"), - secret: batchUpdateRequestV2 - }), - z.object({ - method: z.literal("DELETE"), - secret: z.object({ _id: z.string().trim(), secretName: z.string().trim() }) - }) - ]) - .array() - }) -}); - -export const GetSecretsV2 = z.object({ - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - tagSlugs: z.string().trim().optional(), - folderId: z.string().trim().default("root"), - secretPath: z.string().trim().optional(), - include_imports: z - .enum(["true", "false"]) - .default("false") - .transform((value) => value === "true") - }) -}); - -export const GetSecretsRawV3 = z.object({ - query: z.object({ - workspaceId: z.string().trim().optional(), - environment: z.string().trim().optional(), - secretPath: z.string().trim().default("/"), - include_imports: z - .enum(["true", "false"]) - .default("false") - .transform((value) => value === "true") - }) -}); - -export const GetSecretByNameRawV3 = z.object({ - params: z.object({ - secretName: z.string().trim() - }), - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).optional(), - include_imports: z - .enum(["true", "false"]) - .default("true") - .transform((value) => value === "true"), - version: z - .string() - .trim() - .optional() - .transform((value) => value === undefined ? undefined : parseInt(value, 10)) - .refine((value) => value === undefined || !isNaN(value), { - message: "Version must be a number", - }) - }) -}); - -export const CreateSecretRawV3 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - secretValue: z - .string() - .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), - secretComment: z.string().trim().optional().default(""), - - skipMultilineEncoding: z.boolean().optional(), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]) - }), - params: z.object({ - secretName: z.string().trim() - }) -}); - -export const UpdateSecretByNameRawV3 = z.object({ - params: z.object({ - secretName: z.string().trim() - }), - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - - secretValue: z - .string() - .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), - secretPath: z.string().trim().default("/"), - skipMultilineEncoding: z.boolean().optional(), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).default(SECRET_SHARED) - }) -}); - -export const DeleteSecretByNameRawV3 = z.object({ - params: z.object({ - secretName: z.string().trim() - }), - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).default(SECRET_SHARED) - }) -}); - -export const GetSecretsV3 = z.object({ - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - include_imports: z - .enum(["true", "false"]) - .default("false") - .transform((value) => value === "true") - }) -}); - -export const GetSecretByNameV3 = z.object({ - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).optional(), - include_imports: z - .enum(["true", "false"]) - .default("true") - .transform((value) => value === "true"), - version: z - .string() - .trim() - .optional() - .transform((value) => value === undefined ? undefined : parseInt(value, 10)) - .refine((value) => value === undefined || !isNaN(value), { - message: "Version must be a number", - }) - }), - params: z.object({ - secretName: z.string().trim() - }) -}); - -export const CreateSecretV3 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]), - secretPath: z.string().trim().default("/"), - secretKeyCiphertext: z.string().trim(), - secretKeyIV: z.string().trim(), - secretKeyTag: z.string().trim(), - secretValueCiphertext: z.string().trim(), - secretValueIV: z.string().trim(), - secretValueTag: z.string().trim(), - secretCommentCiphertext: z.string().trim().optional(), - secretCommentIV: z.string().trim().optional(), - secretCommentTag: z.string().trim().optional(), - metadata: z.record(z.string()).optional(), - skipMultilineEncoding: z.boolean().optional() - }), - params: z.object({ - secretName: z.string().trim() - }) -}); - -export const UpdateSecretByNameV3 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretId: z.string().trim().optional(), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]), - secretPath: z.string().trim().default("/"), - secretValueCiphertext: z.string().trim(), - secretValueIV: z.string().trim(), - secretValueTag: z.string().trim(), - secretCommentCiphertext: z.string().trim().optional(), - secretCommentIV: z.string().trim().optional(), - secretCommentTag: z.string().trim().optional(), - - secretReminderRepeatDays: z.number().min(1).max(365).optional().nullable(), - secretReminderNote: z.string().trim().nullable().optional(), - - tags: z.string().array().optional(), - skipMultilineEncoding: z.boolean().optional(), - // to update secret name - secretName: z.string().trim().optional(), - secretKeyIV: z.string().trim().optional(), - secretKeyTag: z.string().trim().optional(), - secretKeyCiphertext: z.string().trim().optional() - }), - params: z.object({ - secretName: z.string() - }) -}); - -export const DeleteSecretByNameV3 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]), - secretPath: z.string().trim().default("/"), - secretId: z.string().trim().optional() - }), - params: z.object({ - secretName: z.string() - }) -}); - -export const CreateSecretByNameBatchV3 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - secrets: z - .object({ - secretName: z.string().trim(), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]), - secretKeyCiphertext: z.string().trim(), - secretKeyIV: z.string().trim(), - secretKeyTag: z.string().trim(), - secretValueCiphertext: z.string().trim(), - secretValueIV: z.string().trim(), - secretValueTag: z.string().trim(), - secretCommentCiphertext: z.string().trim().optional(), - secretCommentIV: z.string().trim().optional(), - secretCommentTag: z.string().trim().optional(), - metadata: z.record(z.string()).optional(), - skipMultilineEncoding: z.boolean().optional() - }) - .array() - .min(1) - }) -}); - -export const UpdateSecretByNameBatchV3 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - secrets: z - .object({ - secretName: z.string().trim(), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]), - secretValueCiphertext: z.string().trim(), - secretValueIV: z.string().trim(), - secretValueTag: z.string().trim(), - secretKeyCiphertext: z.string().trim(), - secretKeyIV: z.string().trim(), - secretKeyTag: z.string().trim(), - secretCommentCiphertext: z.string().trim().optional(), - secretCommentIV: z.string().trim().optional(), - secretCommentTag: z.string().trim().optional(), - skipMultilineEncoding: z.boolean().optional(), - tags: z.string().array().optional() - }) - .array() - .min(1) - }) -}); - -export const DeleteSecretByNameBatchV3 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - secrets: z - .object({ - secretName: z.string().trim(), - type: z.enum([SECRET_SHARED, SECRET_PERSONAL]) - }) - .array() - .min(1) - }) -}); diff --git a/backend-mongo/src/validation/serviceTokenData.ts b/backend-mongo/src/validation/serviceTokenData.ts deleted file mode 100644 index 49ee6d23f..000000000 --- a/backend-mongo/src/validation/serviceTokenData.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { Types } from "mongoose"; -import { ISecret, IServiceTokenData, IUser, ServiceTokenData } from "../models"; -import { ServiceTokenDataNotFoundError, UnauthorizedRequestError } from "../utils/errors"; -import { validateUserClientForWorkspace } from "./user"; -import { ActorType } from "../ee/models"; -import { AuthData } from "../interfaces/middleware"; -import { z } from "zod"; -import { isValidScope } from "../helpers"; - -/** - * Validate authenticated clients for service token with id [serviceTokenId] based - * on any known permissions. - * @param {Object} obj - * @param {Object} obj.authData - authenticated client details - * @param {Types.ObjectId} obj.serviceTokenData - id of service token to validate against - * @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles - */ -export const validateClientForServiceTokenData = async ({ - authData, - serviceTokenDataId, - acceptedRoles -}: { - authData: AuthData; - serviceTokenDataId: Types.ObjectId; - acceptedRoles: Array<"admin" | "member">; -}) => { - const serviceTokenData = await ServiceTokenData.findById(serviceTokenDataId) - .select("+encryptedKey +iv +tag") - .populate<{ user: IUser }>("user"); - - if (!serviceTokenData) - throw ServiceTokenDataNotFoundError({ - message: "Failed to find service token data" - }); - - switch (authData.actor.type) { - case ActorType.USER: - await validateUserClientForWorkspace({ - user: authData.authPayload as IUser, - workspaceId: serviceTokenData.workspace, - acceptedRoles - }); - - return serviceTokenData; - case ActorType.SERVICE: - throw UnauthorizedRequestError({ - message: "Failed service token authorization for service token data" - }); - } -}; - -/** - * Validate that service token (client) can access workspace - * with id [workspaceId] and its environment [environment] with required permissions - * [requiredPermissions] - * @param {Object} obj - * @param {ServiceTokenData} obj.serviceTokenData - service token client - * @param {Types.ObjectId} obj.workspaceId - id of workspace to validate against - * @param {String} environment - (optional) environment in workspace to validate against - * @param {String[]} requiredPermissions - required permissions as part of the endpoint - */ -export const validateServiceTokenDataClientForWorkspace = async ({ - serviceTokenData, - workspaceId, - environment, - secretPath = "/", - requiredPermissions -}: { - serviceTokenData: IServiceTokenData; - workspaceId: Types.ObjectId; - environment?: string; - secretPath?: string; - requiredPermissions?: string[]; -}) => { - if (!serviceTokenData.workspace.equals(workspaceId)) { - // case: invalid workspaceId passed - throw UnauthorizedRequestError({ - message: "Failed service token authorization for the given workspace" - }); - } - - if (environment) { - // case: environment is specified - if (!serviceTokenData.scopes.find(({ environment: tkEnv }) => tkEnv === environment)) { - // case: invalid environment passed - throw UnauthorizedRequestError({ - message: "Failed service token authorization for the given workspace environment" - }); - } - - if (!isValidScope(serviceTokenData, environment, secretPath)) { - throw UnauthorizedRequestError({ message: "Folder Permission Denied" }); - } - - requiredPermissions?.forEach((permission) => { - if (!serviceTokenData.permissions.includes(permission)) { - throw UnauthorizedRequestError({ - message: `Failed service token authorization for the given workspace environment action: ${permission}` - }); - } - }); - } -}; - -/** - * Validate that service token (client) can access secrets - * with required permissions [requiredPermissions] - * @param {Object} obj - * @param {ServiceTokenData} obj.serviceTokenData - service token client - * @param {Secret[]} secrets - secrets to validate against - * @param {string[]} requiredPermissions - required permissions as part of the endpoint - */ -export const validateServiceTokenDataClientForSecrets = async ({ - serviceTokenData, - secrets, - requiredPermissions -}: { - serviceTokenData: IServiceTokenData; - secrets: ISecret[]; - requiredPermissions?: string[]; -}) => { - secrets.forEach((secret: ISecret) => { - if (!serviceTokenData.workspace.equals(secret.workspace)) { - // case: invalid workspaceId passed - throw UnauthorizedRequestError({ - message: "Failed service token authorization for the given workspace" - }); - } - - if (!serviceTokenData.scopes.find(({ environment: tkEnv }) => tkEnv === secret.environment)) { - // case: invalid environment passed - throw UnauthorizedRequestError({ - message: "Failed service token authorization for the given workspace environment" - }); - } - - requiredPermissions?.forEach((permission) => { - if (!serviceTokenData.permissions.includes(permission)) { - throw UnauthorizedRequestError({ - message: `Failed service token authorization for the given workspace environment action: ${permission}` - }); - } - }); - }); -}; - -export const CreateServiceTokenV2 = z.object({ - body: z.object({ - name: z.string().trim(), - workspaceId: z.string().trim(), - scopes: z - .object({ - environment: z.string().trim(), - secretPath: z.string().trim() - }) - .array() - .min(1), - encryptedKey: z.string().trim(), - iv: z.string().trim(), - tag: z.string().trim(), - expiresIn: z.number().nullable().optional(), - permissions: z.enum(["read", "write"]).array() - }) -}); - -export const DeleteServiceTokenV2 = z.object({ - params: z.object({ - serviceTokenDataId: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/sso.ts b/backend-mongo/src/validation/sso.ts deleted file mode 100644 index 275ae611e..000000000 --- a/backend-mongo/src/validation/sso.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { z } from "zod"; -import { AuthProvider } from "../ee/models"; - -export const GetSsoConfigv1 = z.object({ - query: z.object({ organizationId: z.string().trim() }) -}); - -export const CreateSsoConfigv1 = z.object({ - body: z.object({ - organizationId: z.string().trim(), - authProvider: z.nativeEnum(AuthProvider), - isActive: z.boolean(), - entryPoint: z.string().trim(), - issuer: z.string().trim(), - cert: z.string().trim() - }) -}); - -export const UpdateSsoConfigv1 = z.object({ - body: z.object({ - organizationId: z.string().trim(), - authProvider: z.nativeEnum(AuthProvider).optional(), - isActive: z.boolean().optional(), - entryPoint: z.string().trim().optional(), - issuer: z.string().trim().optional(), - cert: z.string().trim().optional() - }) -}); diff --git a/backend-mongo/src/validation/tags.ts b/backend-mongo/src/validation/tags.ts deleted file mode 100644 index 0631e9a9a..000000000 --- a/backend-mongo/src/validation/tags.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { z } from "zod"; - -export const GetWorkspaceTagsV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const DeleteWorkspaceTagsV2 = z.object({ - params: z.object({ - tagId: z.string().trim() - }) -}); - -export const CreateWorkspaceTagsV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - name: z.string().trim(), - slug: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/user.ts b/backend-mongo/src/validation/user.ts deleted file mode 100644 index 1f356ec11..000000000 --- a/backend-mongo/src/validation/user.ts +++ /dev/null @@ -1,232 +0,0 @@ -import fs from "fs"; -import path from "path"; -import { Types } from "mongoose"; -import { IOrganization, ISecret, IUser, Membership } from "../models"; -import { validateMembership } from "../helpers/membership"; -import _ from "lodash"; -import { BadRequestError, UnauthorizedRequestError, ValidationError } from "../utils/errors"; -import { validateMembershipOrg } from "../helpers/membershipOrg"; -import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../variables"; -import { AuthMethod } from "../models"; -import { z } from "zod"; - -/** - * Validate that email [email] is not disposable - * @param email - email to validate - */ -export const validateUserEmail = (email: string) => { - const emailDomain = email.split("@")[1]; - const disposableEmails = fs - .readFileSync(path.resolve(__dirname, "../data/" + "disposable_emails.txt"), "utf8") - .split("\n"); - - if (disposableEmails.includes(emailDomain)) - throw ValidationError({ - message: "Failed to validate email as non-disposable" - }); -}; - -/** - * Validate that user (client) can access workspace - * with id [workspaceId] and its environment [environment] with required permissions - * [requiredPermissions] - * @param {Object} obj - * @param {User} obj.user - user client - * @param {Types.ObjectId} obj.workspaceId - id of workspace to validate against - * @param {String} environment - (optional) environment in workspace to validate against - * @param {String[]} requiredPermissions - required permissions as part of the endpoint - */ -export const validateUserClientForWorkspace = async ({ - user, - workspaceId, - environment, - acceptedRoles, - requiredPermissions -}: { - user: IUser; - workspaceId: Types.ObjectId; - environment?: string; - acceptedRoles: Array<"admin" | "member">; - requiredPermissions?: string[]; -}) => { - // validate user membership in workspace - const membership = await validateMembership({ - userId: user._id, - workspaceId, - acceptedRoles - }); - - let runningIsDisallowed = false; - requiredPermissions?.forEach((requiredPermission: string) => { - switch (requiredPermission) { - case PERMISSION_READ_SECRETS: - runningIsDisallowed = _.some(membership.deniedPermissions, { - environmentSlug: environment, - ability: PERMISSION_READ_SECRETS - }); - break; - case PERMISSION_WRITE_SECRETS: - runningIsDisallowed = _.some(membership.deniedPermissions, { - environmentSlug: environment, - ability: PERMISSION_WRITE_SECRETS - }); - break; - default: - break; - } - - if (runningIsDisallowed) { - throw UnauthorizedRequestError({ - message: `Failed permissions authorization for workspace environment action : ${requiredPermission}` - }); - } - }); - - return membership; -}; - -/** - * Validate that user (client) can access secret [secret] - * with required permissions [requiredPermissions] - * @param {Object} obj - * @param {User} obj.user - user client - * @param {Secret[]} obj.secrets - secrets to validate against - * @param {String[]} requiredPermissions - required permissions as part of the endpoint - */ -export const validateUserClientForSecret = async ({ - user, - secret, - acceptedRoles, - requiredPermissions -}: { - user: IUser; - secret: ISecret; - acceptedRoles?: Array<"admin" | "member">; - requiredPermissions?: string[]; -}) => { - const membership = await validateMembership({ - userId: user._id, - workspaceId: secret.workspace, - acceptedRoles - }); - - if (requiredPermissions?.includes(PERMISSION_WRITE_SECRETS)) { - const isDisallowed = _.some(membership.deniedPermissions, { - environmentSlug: secret.environment, - ability: PERMISSION_WRITE_SECRETS - }); - - if (isDisallowed) { - throw UnauthorizedRequestError({ - message: "You do not have the required permissions to perform this action" - }); - } - } -}; - -/** - * Validate that user (client) can access secrets [secrets] - * with required permissions [requiredPermissions] - * @param {Object} obj - * @param {User} obj.user - user client - * @param {Secret[]} obj.secrets - secrets to validate against - * @param {String[]} requiredPermissions - required permissions as part of the endpoint - */ -export const validateUserClientForSecrets = async ({ - user, - secrets, - requiredPermissions -}: { - user: IUser; - secrets: ISecret[]; - requiredPermissions?: string[]; -}) => { - // TODO: add acceptedRoles? - - const userMemberships = await Membership.find({ user: user._id }); - const userMembershipById = _.keyBy(userMemberships, "workspace"); - const workspaceIdsSet = new Set(userMemberships.map((m) => m.workspace.toString())); - - // for each secret check if the secret belongs to a workspace the user is a member of - secrets.forEach((secret: ISecret) => { - if (!workspaceIdsSet.has(secret.workspace.toString())) { - throw BadRequestError({ - message: "Failed authorization for the secret" - }); - } - - if (requiredPermissions?.includes(PERMISSION_WRITE_SECRETS)) { - const deniedMembershipPermissions = - userMembershipById[secret.workspace.toString()].deniedPermissions; - const isDisallowed = _.some(deniedMembershipPermissions, { - environmentSlug: secret.environment, - ability: PERMISSION_WRITE_SECRETS - }); - - if (isDisallowed) { - throw UnauthorizedRequestError({ - message: "You do not have the required permissions to perform this action" - }); - } - } - }); -}; - -/** - * Validate that user (client) can access organization [organization] - * @param {Object} obj - * @param {User} obj.user - user client - * @param {Organization} obj.organization - organization to validate against - */ -export const validateUserClientForOrganization = async ({ - user, - organization, - acceptedRoles, - acceptedStatuses -}: { - user: IUser; - organization: IOrganization; - acceptedRoles: Array<"owner" | "admin" | "member">; - acceptedStatuses: Array<"invited" | "accepted">; -}) => { - const membershipOrg = await validateMembershipOrg({ - userId: user._id, - organizationId: organization._id, - acceptedRoles, - acceptedStatuses - }); - - return membershipOrg; -}; - -export const UpdateMyMfaEnabledV2 = z.object({ - body: z.object({ - isMfaEnabled: z.boolean() - }) -}); - -export const UpdateNameV2 = z.object({ - body: z.object({ - firstName: z.string().trim(), - lastName: z.string().trim() - }) -}); - -export const UpdateAuthMethodsV2 = z.object({ - body: z.object({ - authMethods: z.nativeEnum(AuthMethod).array().min(1) - }) -}); - -export const CreateApiKeyV2 = z.object({ - body: z.object({ - name: z.string().trim(), - expiresIn: z.number() - }) -}); - -export const DeleteApiKeyV2 = z.object({ - params: z.object({ - apiKeyDataId: z.string().trim() - }) -}); diff --git a/backend-mongo/src/validation/webhooks.ts b/backend-mongo/src/validation/webhooks.ts deleted file mode 100644 index 907f90633..000000000 --- a/backend-mongo/src/validation/webhooks.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { z } from "zod"; - -export const CreateWebhookV1 = z.object({ - body: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), - webhookUrl: z.string().url().trim(), - webhookSecretKey: z.string().trim().optional(), - secretPath: z.string().trim().default("/") - }) -}); - -export const UpdateWebhookV1 = z.object({ - params: z.object({ - webhookId: z.string().trim() - }), - body: z.object({ - isDisabled: z.boolean().default(false) - }) -}); - -export const TestWebhookV1 = z.object({ - params: z.object({ - webhookId: z.string().trim() - }) -}); - -export const DeleteWebhookV1 = z.object({ - params: z.object({ - webhookId: z.string().trim() - }) -}); - -export const ListWebhooksV1 = z.object({ - query: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim().optional(), - secretPath: z.string().trim().optional() - }) -}); diff --git a/backend-mongo/src/validation/workspace.ts b/backend-mongo/src/validation/workspace.ts deleted file mode 100644 index 4c9a2183d..000000000 --- a/backend-mongo/src/validation/workspace.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { Types } from "mongoose"; -import { IServiceTokenData, IUser, Workspace } from "../models"; -import { ActorType } from "../ee/models"; -import { validateUserClientForWorkspace } from "./user"; -import { validateServiceTokenDataClientForWorkspace } from "./serviceTokenData"; -import { WorkspaceNotFoundError } from "../utils/errors"; -import { AuthData } from "../interfaces/middleware"; -import { z } from "zod"; -import { EventType, UserAgentType } from "../ee/models"; -import { UnauthorizedRequestError } from "../utils/errors"; -import { NO_ACCESS } from "../variables"; - -/** - * Validate authenticated clients for workspace with id [workspaceId] based - * on any known permissions. - * @param {Object} obj - * @param {Object} obj.authData - authenticated client details - * @param {Types.ObjectId} obj.workspaceId - id of workspace to validate against - * @param {String} obj.environment - (optional) environment in workspace to validate against - * @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles - * @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint - */ -export const validateClientForWorkspace = async ({ - authData, - workspaceId, - environment, - acceptedRoles, - requiredPermissions -}: { - authData: AuthData; - workspaceId: Types.ObjectId; - environment?: string; - acceptedRoles: Array<"admin" | "member">; - requiredPermissions?: string[]; -}) => { - const workspace = await Workspace.findById(workspaceId); - - if (!workspace) - throw WorkspaceNotFoundError({ - message: "Failed to find workspace" - }); - - let membership; - switch (authData.actor.type) { - case ActorType.USER: - membership = await validateUserClientForWorkspace({ - user: authData.authPayload as IUser, - workspaceId, - environment, - acceptedRoles, - requiredPermissions - }); - - return { membership, workspace }; - case ActorType.SERVICE: - await validateServiceTokenDataClientForWorkspace({ - serviceTokenData: authData.authPayload as IServiceTokenData, - workspaceId, - environment, - requiredPermissions - }); - return { membership, workspace }; - case ActorType.IDENTITY: - throw UnauthorizedRequestError({ - message: "Failed identity authorization for organization" - }); - } -}; - -export const GetWorkspaceSecretSnapshotsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - query: z.object({ - environment: z.string().trim(), - directory: z.string().trim().default("/"), - offset: z.coerce.number(), - limit: z.coerce.number() - }) -}); - -export const GetWorkspaceSecretSnapshotsCountV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - query: z.object({ - environment: z.string().trim(), - directory: z.string().trim().default("/") - }) -}); - -export const RollbackWorkspaceSecretSnapshotV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - environment: z.string().trim(), - directory: z.string().trim().default("/"), - version: z.number() - }) -}); - -export const GetWorkspaceLogsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - query: z.object({ - offset: z.coerce.number(), - limit: z.coerce.number(), - sortBy: z.string().trim().optional(), - userId: z.string().trim().optional(), - actionNames: z.string().trim().optional() - }) -}); - -export const GetWorkspaceAuditLogsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - query: z.object({ - eventType: z.nativeEnum(EventType).nullable().optional(), - userAgentType: z.nativeEnum(UserAgentType).nullable().optional(), - startDate: z.string().datetime().nullable().optional(), - endDate: z.string().datetime().nullable().optional(), - offset: z.coerce.number().default(0), - limit: z.coerce.number().default(20), - actor: z.string().nullish().optional() - }) -}); - -export const GetWorkspaceAuditLogActorFilterOptsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetWorkspaceTrustedIpsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const AddWorkspaceTrustedIpV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - ipAddress: z.string().trim(), - comment: z.string().trim().default(""), - isActive: z.boolean() - }) -}); - -export const UpdateWorkspaceTrustedIpV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim(), - trustedIpId: z.string().trim() - }), - body: z.object({ - ipAddress: z.string().trim(), - comment: z.string().trim().default("") - }) -}); - -export const DeleteWorkspaceTrustedIpV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim(), - trustedIpId: z.string().trim() - }) -}); - -export const GetWorkspacePublicKeysV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetWorkspaceMembershipsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetWorkspaceV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const CreateWorkspaceV1 = z.object({ - body: z.object({ - workspaceName: z.string().trim(), - organizationId: z.string().trim() - }) -}); - -export const DeleteWorkspaceV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const ChangeWorkspaceNameV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - name: z.string().trim() - }) -}); - -export const InviteUserToWorkspaceV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - email: z.string().trim() - }) -}); - -export const GetWorkspaceIntegrationsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetWorkspaceIntegrationAuthorizationsV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetWorkspaceServiceTokensV1 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetWorkspaceServiceTokenDataV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetWorkspaceKeyV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetWorkspaceMembershipsV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const UpdateWorkspaceMembershipsV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim(), - membershipId: z.string().trim() - }), - body: z.object({ - role: z.string().trim() - }) -}); - -export const DeleteWorkspaceMembershipsV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim(), - membershipId: z.string().trim() - }) -}); - -export const ToggleAutoCapitalizationV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - autoCapitalization: z.boolean() - }) -}); - -export const AddIdentityToWorkspaceV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim(), - identityId: z.string().trim() - }), - body: z.object({ - role: z.string().trim().min(1).default(NO_ACCESS), - }) -}); - -export const UpdateIdentityWorkspaceRoleV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim(), - identityId: z.string().trim() - }), - body: z.object({ - role: z.string().trim().min(1).default(NO_ACCESS), - }) -}); - -export const DeleteIdentityFromWorkspaceV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim(), - identityId: z.string().trim() - }) -}); - -export const GetWorkspaceIdentityMembersV2 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), -}); - -export const GetWorkspaceBlinkIndexStatusV3 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const GetWorkspaceSecretsV3 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }) -}); - -export const NameWorkspaceSecretsV3 = z.object({ - params: z.object({ - workspaceId: z.string().trim() - }), - body: z.object({ - secretsToUpdate: z - .object({ - secretName: z.string().trim(), - _id: z.string().trim() - }) - .array() - }) -}); diff --git a/backend-mongo/src/variables/authentication.ts b/backend-mongo/src/variables/authentication.ts deleted file mode 100644 index 5eec0ba22..000000000 --- a/backend-mongo/src/variables/authentication.ts +++ /dev/null @@ -1,21 +0,0 @@ -// TODO: merge [AuthTokenType] and [AuthMode] - -export enum AuthTokenType { - ACCESS_TOKEN = "accessToken", - REFRESH_TOKEN = "refreshToken", - SIGNUP_TOKEN = "signupToken", // TODO: remove in favor of claim - MFA_TOKEN = "mfaToken", // TODO: remove in favor of claim - PROVIDER_TOKEN = "providerToken", // TODO: remove in favor of claim - API_KEY = "apiKey", - IDENTITY_ACCESS_TOKEN = "identityAccessToken", -} - -export enum AuthMode { - JWT = "jwt", - SERVICE_TOKEN = "serviceToken", - IDENTITY_ACCESS_TOKEN = "identityAccessToken", - API_KEY = "apiKey", - API_KEY_V2 = "apiKeyV2" -} - -export const K8_USER_AGENT_NAME = "k8-operator" \ No newline at end of file diff --git a/backend-mongo/src/variables/crypto.ts b/backend-mongo/src/variables/crypto.ts deleted file mode 100644 index 64dde2c22..000000000 --- a/backend-mongo/src/variables/crypto.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const ALGORITHM_AES_256_GCM = "aes-256-gcm"; -export const NONCE_BYTES_SIZE = 12; -export const BLOCK_SIZE_BYTES_16 = 16; - -export const ENCODING_SCHEME_UTF8 = "utf8"; -export const ENCODING_SCHEME_HEX = "hex"; -export const ENCODING_SCHEME_BASE64 = "base64"; \ No newline at end of file diff --git a/backend-mongo/src/variables/environment.ts b/backend-mongo/src/variables/environment.ts deleted file mode 100644 index d0c8220ef..000000000 --- a/backend-mongo/src/variables/environment.ts +++ /dev/null @@ -1,6 +0,0 @@ -// environments -export const ENV_DEV = "dev"; -export const ENV_TESTING = "test"; -export const ENV_STAGING = "staging"; -export const ENV_PROD = "prod"; -export const ENV_SET = new Set([ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD]); \ No newline at end of file diff --git a/backend-mongo/src/variables/event.ts b/backend-mongo/src/variables/event.ts deleted file mode 100644 index 126ede040..000000000 --- a/backend-mongo/src/variables/event.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const EVENT_PUSH_SECRETS = "pushSecrets"; -export const EVENT_PULL_SECRETS = "pullSecrets"; -export const EVENT_START_INTEGRATION = "startIntegration"; diff --git a/backend-mongo/src/variables/index.ts b/backend-mongo/src/variables/index.ts deleted file mode 100644 index ec9f14212..000000000 --- a/backend-mongo/src/variables/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from "./authentication"; -export * from "./crypto"; -export * from "./environment"; -export * from "./event"; -export * from "./integration"; -export * from "./organization"; -export * from "./permission"; -export * from "./secret"; -export * from "./smtp"; -export * from "./token"; -export * from "./user"; diff --git a/backend-mongo/src/variables/integration.ts b/backend-mongo/src/variables/integration.ts deleted file mode 100644 index 28848dbb6..000000000 --- a/backend-mongo/src/variables/integration.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { - getClientIdAzure, - getClientIdBitBucket, - getClientIdGCPSecretManager, - getClientIdGitHub, - getClientIdGitLab, - getClientIdHeroku, - getClientIdNetlify, - getClientSlugVercel -} from "../config"; - -// integrations -export const INTEGRATION_AZURE_KEY_VAULT = "azure-key-vault"; -export const INTEGRATION_AWS_PARAMETER_STORE = "aws-parameter-store"; -export const INTEGRATION_AWS_SECRET_MANAGER = "aws-secret-manager"; -export const INTEGRATION_GCP_SECRET_MANAGER = "gcp-secret-manager"; -export const INTEGRATION_HEROKU = "heroku"; -export const INTEGRATION_VERCEL = "vercel"; -export const INTEGRATION_NETLIFY = "netlify"; -export const INTEGRATION_GITHUB = "github"; -export const INTEGRATION_GITLAB = "gitlab"; -export const INTEGRATION_RENDER = "render"; -export const INTEGRATION_RAILWAY = "railway"; -export const INTEGRATION_FLYIO = "flyio"; -export const INTEGRATION_LARAVELFORGE = "laravel-forge"; -export const INTEGRATION_CIRCLECI = "circleci"; -export const INTEGRATION_TRAVISCI = "travisci"; -export const INTEGRATION_TEAMCITY = "teamcity"; -export const INTEGRATION_SUPABASE = "supabase"; -export const INTEGRATION_CHECKLY = "checkly"; -export const INTEGRATION_QOVERY = "qovery"; -export const INTEGRATION_TERRAFORM_CLOUD = "terraform-cloud"; -export const INTEGRATION_HASHICORP_VAULT = "hashicorp-vault"; -export const INTEGRATION_CLOUDFLARE_PAGES = "cloudflare-pages"; -export const INTEGRATION_CLOUDFLARE_WORKERS = "cloudflare-workers"; -export const INTEGRATION_BITBUCKET = "bitbucket"; -export const INTEGRATION_CODEFRESH = "codefresh"; -export const INTEGRATION_WINDMILL = "windmill"; -export const INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM = "digital-ocean-app-platform"; -export const INTEGRATION_CLOUD_66 = "cloud-66"; -export const INTEGRATION_NORTHFLANK = "northflank"; -export const INTEGRATION_HASURA_CLOUD = "hasura-cloud"; -export const INTEGRATION_SET = new Set([ - INTEGRATION_GCP_SECRET_MANAGER, - INTEGRATION_AZURE_KEY_VAULT, - INTEGRATION_HEROKU, - INTEGRATION_VERCEL, - INTEGRATION_NETLIFY, - INTEGRATION_GITHUB, - INTEGRATION_GITLAB, - INTEGRATION_RENDER, - INTEGRATION_FLYIO, - INTEGRATION_CIRCLECI, - INTEGRATION_LARAVELFORGE, - INTEGRATION_TRAVISCI, - INTEGRATION_TEAMCITY, - INTEGRATION_SUPABASE, - INTEGRATION_CHECKLY, - INTEGRATION_QOVERY, - INTEGRATION_TERRAFORM_CLOUD, - INTEGRATION_HASHICORP_VAULT, - INTEGRATION_CLOUDFLARE_PAGES, - INTEGRATION_CLOUDFLARE_WORKERS, - INTEGRATION_CODEFRESH, - INTEGRATION_WINDMILL, - INTEGRATION_BITBUCKET, - INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM, - INTEGRATION_CLOUD_66, - INTEGRATION_NORTHFLANK, - INTEGRATION_HASURA_CLOUD -]); - -// integration types -export const INTEGRATION_OAUTH2 = "oauth2"; - -// integration oauth endpoints -export const INTEGRATION_GCP_TOKEN_URL = "https://oauth2.googleapis.com/token"; -export const INTEGRATION_AZURE_TOKEN_URL = - "https://login.microsoftonline.com/common/oauth2/v2.0/token"; -export const INTEGRATION_HEROKU_TOKEN_URL = "https://id.heroku.com/oauth/token"; -export const INTEGRATION_VERCEL_TOKEN_URL = "https://api.vercel.com/v2/oauth/access_token"; -export const INTEGRATION_NETLIFY_TOKEN_URL = "https://api.netlify.com/oauth/token"; -export const INTEGRATION_GITHUB_TOKEN_URL = "https://github.com/login/oauth/access_token"; -export const INTEGRATION_GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token"; -export const INTEGRATION_BITBUCKET_TOKEN_URL = "https://bitbucket.org/site/oauth2/access_token"; - -// integration apps endpoints -export const INTEGRATION_GCP_API_URL = "https://cloudresourcemanager.googleapis.com"; -export const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com"; -export const GITLAB_URL = "https://gitlab.com"; -export const INTEGRATION_GITLAB_API_URL = `${GITLAB_URL}/api`; -export const INTEGRATION_GITHUB_API_URL = "https://api.github.com"; -export const INTEGRATION_VERCEL_API_URL = "https://api.vercel.com"; -export const INTEGRATION_NETLIFY_API_URL = "https://api.netlify.com"; -export const INTEGRATION_RENDER_API_URL = "https://api.render.com"; -export const INTEGRATION_RAILWAY_API_URL = "https://backboard.railway.app/graphql/v2"; -export const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql"; -export const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api"; -export const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com"; -export const INTEGRATION_SUPABASE_API_URL = "https://api.supabase.com"; -export const INTEGRATION_LARAVELFORGE_API_URL = "https://forge.laravel.com"; -export const INTEGRATION_CHECKLY_API_URL = "https://api.checklyhq.com"; -export const INTEGRATION_QOVERY_API_URL = "https://api.qovery.com"; -export const INTEGRATION_TERRAFORM_CLOUD_API_URL = "https://app.terraform.io"; -export const INTEGRATION_CLOUDFLARE_PAGES_API_URL = "https://api.cloudflare.com"; -export const INTEGRATION_CLOUDFLARE_WORKERS_API_URL = "https://api.cloudflare.com"; -export const INTEGRATION_BITBUCKET_API_URL = "https://api.bitbucket.org"; -export const INTEGRATION_CODEFRESH_API_URL = "https://g.codefresh.io/api"; -export const INTEGRATION_WINDMILL_API_URL = "https://app.windmill.dev/api"; -export const INTEGRATION_DIGITAL_OCEAN_API_URL = "https://api.digitalocean.com"; -export const INTEGRATION_CLOUD_66_API_URL = "https://app.cloud66.com/api"; -export const INTEGRATION_NORTHFLANK_API_URL = "https://api.northflank.com"; -export const INTEGRATION_HASURA_CLOUD_API_URL = "https://data.pro.hasura.io/v1/graphql"; - -export const INTEGRATION_GCP_SECRET_MANAGER_SERVICE_NAME = "secretmanager.googleapis.com"; -export const INTEGRATION_GCP_SECRET_MANAGER_URL = `https://${INTEGRATION_GCP_SECRET_MANAGER_SERVICE_NAME}`; -export const INTEGRATION_GCP_SERVICE_USAGE_URL = "https://serviceusage.googleapis.com"; -export const INTEGRATION_GCP_CLOUD_PLATFORM_SCOPE = - "https://www.googleapis.com/auth/cloud-platform"; - -export const getIntegrationOptions = async () => { - const INTEGRATION_OPTIONS = [ - { - name: "Heroku", - slug: "heroku", - image: "Heroku.png", - isAvailable: true, - type: "oauth", - clientId: await getClientIdHeroku(), - docsLink: "" - }, - { - name: "Vercel", - slug: "vercel", - image: "Vercel.png", - isAvailable: true, - type: "oauth", - clientId: "", - clientSlug: await getClientSlugVercel(), - docsLink: "" - }, - { - name: "Netlify", - slug: "netlify", - image: "Netlify.png", - isAvailable: true, - type: "oauth", - clientId: await getClientIdNetlify(), - docsLink: "" - }, - { - name: "GitHub", - slug: "github", - image: "GitHub.png", - isAvailable: true, - type: "oauth", - clientId: await getClientIdGitHub(), - docsLink: "" - }, - { - name: "Render", - slug: "render", - image: "Render.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Railway", - slug: "railway", - image: "Railway.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Fly.io", - slug: "flyio", - image: "Flyio.svg", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "AWS Parameter Store", - slug: "aws-parameter-store", - image: "Amazon Web Services.png", - isAvailable: true, - type: "custom", - clientId: "", - docsLink: "" - }, - { - name: "Laravel Forge", - slug: "laravel-forge", - image: "Laravel Forge.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "AWS Secrets Manager", - slug: "aws-secret-manager", - image: "Amazon Web Services.png", - isAvailable: true, - type: "custom", - clientId: "", - docsLink: "" - }, - { - name: "Azure Key Vault", - slug: "azure-key-vault", - image: "Microsoft Azure.png", - isAvailable: true, - type: "oauth", - clientId: await getClientIdAzure(), - docsLink: "" - }, - { - name: "Circle CI", - slug: "circleci", - image: "Circle CI.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "GitLab", - slug: "gitlab", - image: "GitLab.png", - isAvailable: true, - type: "custom", - clientId: await getClientIdGitLab(), - docsLink: "" - }, - { - name: "Terraform Cloud", - slug: "terraform-cloud", - image: "Terraform Cloud.png", - isAvailable: true, - type: "pat", - cliendId: "", - docsLink: "" - }, - { - name: "Travis CI", - slug: "travisci", - image: "Travis CI.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "TeamCity", - slug: "teamcity", - image: "TeamCity.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Supabase", - slug: "supabase", - image: "Supabase.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Checkly", - slug: "checkly", - image: "Checkly.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Qovery", - slug: "qovery", - image: "Qovery.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "HashiCorp Vault", - slug: "hashicorp-vault", - image: "Vault.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "GCP Secret Manager", - slug: "gcp-secret-manager", - image: "Google Cloud Platform.png", - isAvailable: true, - type: "oauth", - clientId: await getClientIdGCPSecretManager(), - docsLink: "" - }, - { - name: "Cloudflare Pages", - slug: "cloudflare-pages", - image: "Cloudflare.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Cloudflare Workers", - slug: "cloudflare-workers", - image: "Cloudflare.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "BitBucket", - slug: "bitbucket", - image: "BitBucket.png", - isAvailable: true, - type: "oauth", - clientId: await getClientIdBitBucket(), - docsLink: "" - }, - { - name: "Codefresh", - slug: "codefresh", - image: "Codefresh.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Windmill", - slug: "windmill", - image: "Windmill.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Digital Ocean App Platform", - slug: "digital-ocean-app-platform", - image: "Digital Ocean.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Cloud 66", - slug: "cloud-66", - image: "Cloud 66.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Northflank", - slug: "northflank", - image: "Northflank.png", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - }, - { - name: "Hasura Cloud", - slug: "hasura-cloud", - image: "Hasura.svg", - isAvailable: true, - type: "pat", - clientId: "", - docsLink: "" - } - ]; - - return INTEGRATION_OPTIONS; -}; diff --git a/backend-mongo/src/variables/organization.ts b/backend-mongo/src/variables/organization.ts deleted file mode 100644 index c8eb52863..000000000 --- a/backend-mongo/src/variables/organization.ts +++ /dev/null @@ -1,13 +0,0 @@ -// membership roles -export const OWNER = "owner"; // depreciated -export const ADMIN = "admin"; -export const MEMBER = "member"; -export const VIEWER = "viewer"; -export const NO_ACCESS = "no-access"; -export const CUSTOM = "custom"; - -// membership statuses -export const INVITED = "invited"; - -// -- organization -export const ACCEPTED = "accepted"; diff --git a/backend-mongo/src/variables/permission.ts b/backend-mongo/src/variables/permission.ts deleted file mode 100644 index 9dc4c61a3..000000000 --- a/backend-mongo/src/variables/permission.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const PERMISSION_READ_SECRETS = "read"; -export const PERMISSION_WRITE_SECRETS = "write"; \ No newline at end of file diff --git a/backend-mongo/src/variables/secret.ts b/backend-mongo/src/variables/secret.ts deleted file mode 100644 index b24d8a101..000000000 --- a/backend-mongo/src/variables/secret.ts +++ /dev/null @@ -1,3 +0,0 @@ -// secrets -export const SECRET_SHARED = "shared"; -export const SECRET_PERSONAL = "personal"; diff --git a/backend-mongo/src/variables/smtp.ts b/backend-mongo/src/variables/smtp.ts deleted file mode 100644 index 4ad68c356..000000000 --- a/backend-mongo/src/variables/smtp.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const SMTP_HOST_SENDGRID = "smtp.sendgrid.net"; -export const SMTP_HOST_MAILGUN = "smtp.mailgun.org"; -export const SMTP_HOST_SOCKETLABS = "smtp.socketlabs.com"; -export const SMTP_HOST_ZOHOMAIL = "smtp.zoho.com"; -export const SMTP_HOST_GMAIL = "smtp.gmail.com"; -export const SMTP_HOST_OFFICE365 = "smtp.office365.com"; \ No newline at end of file diff --git a/backend-mongo/src/variables/token.ts b/backend-mongo/src/variables/token.ts deleted file mode 100644 index 187e2534b..000000000 --- a/backend-mongo/src/variables/token.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const TOKEN_EMAIL_CONFIRMATION = "emailConfirmation"; -export const TOKEN_EMAIL_MFA = "emailMfa"; -export const TOKEN_EMAIL_ORG_INVITATION = "organizationInvitation"; -export const TOKEN_EMAIL_PASSWORD_RESET = "passwordReset"; \ No newline at end of file diff --git a/backend-mongo/src/variables/user.ts b/backend-mongo/src/variables/user.ts deleted file mode 100644 index a688b115d..000000000 --- a/backend-mongo/src/variables/user.ts +++ /dev/null @@ -1 +0,0 @@ -export const MFA_METHOD_EMAIL = "email"; \ No newline at end of file diff --git a/backend-mongo/swagger/index.ts b/backend-mongo/swagger/index.ts deleted file mode 100644 index 6f3a426fb..000000000 --- a/backend-mongo/swagger/index.ts +++ /dev/null @@ -1,318 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -const swaggerAutogen = require("swagger-autogen")({ openapi: "3.0.0" }); -const fs = require("fs").promises; -const yaml = require("js-yaml"); - -/** - * Generates OpenAPI specs for all Infisical API endpoints: - * - spec.json in /backend for api-serving - * - spec.yaml in /docs for API reference - */ -const generateOpenAPISpec = async () => { - const doc = { - info: { - title: "Infisical API", - description: "List of all available APIs that can be consumed" - }, - host: ["https://infisical.com"], - servers: [ - { - url: "https://app.infisical.com", - description: "Production server" - }, - { - url: "http://localhost:8080", - description: "Local server" - } - ], - securityDefinitions: { - bearerAuth: { - type: "http", - scheme: "bearer", - bearerFormat: "JWT", - description: "An access token in Infisical" - }, - apiKeyAuth: { - type: "apiKey", - in: "header", - name: "X-API-Key", - description: "An API Key in Infisical" - } - }, - definitions: { - CurrentUser: { - _id: "", - email: "johndoe@gmail.com", - firstName: "John", - lastName: "Doe", - publicKey: "johns_nacl_public_key", - encryptedPrivateKey: "johns_enc_nacl_private_key", - iv: "iv_of_enc_nacl_private_key", - tag: "tag_of_enc_nacl_private_key", - updatedAt: "2023-01-13T14:16:12.210Z", - createdAt: "2023-01-13T14:16:12.210Z" - }, - Identity: { - _id: "", - name: "Machine 1", - authMethod: "universal-auth" - }, - IdentityUniversalAuth: { - _id: "", - identity: "", - clientId: "...", - clientSecretTrustedIps: [{ - ipAddress: "0.0.0.0", - type: "ipv4", - prefix: "0" - }], - accessTokenTTL: 7200, - accessTokenMaxTTL: 2592000, - accessTokenNumUsesLimit: 0, - accessTokenTrustedIps: [{ - ipAddress: "0.0.0.0", - type: "ipv4", - prefix: "0" - }] - }, - IdentityUniversalAuthClientSecretData: { - _id: "", - identityUniversalAuth: "", - isClientSecretRevoked: false, - description: "", - clientSecretPrefix: "abc", - clientSecretNumUses: 0, - clientSecretNumUsesLimit: 0, - clientSecretTTL: 0, - createdAt: "2023-01-13T14:16:12.210Z", - updatedAt: "2023-01-13T14:16:12.210Z" - }, - Membership: { - user: { - _id: "", - email: "johndoe@gmail.com", - firstName: "John", - lastName: "Doe", - publicKey: "johns_nacl_public_key", - updatedAt: "2023-01-13T14:16:12.210Z", - createdAt: "2023-01-13T14:16:12.210Z" - }, - workspace: "", - role: "admin" - }, - MembershipOrg: { - user: { - _id: "", - email: "johndoe@gmail.com", - firstName: "John", - lastName: "Doe", - publicKey: "johns_nacl_public_key", - updatedAt: "2023-01-13T14:16:12.210Z", - createdAt: "2023-01-13T14:16:12.210Z" - }, - organization: "", - role: "owner", - status: "accepted" - }, - IdentityMembership: { - identity: { - _id: "", - name: "Machine 1", - authMethod: "universal-auth" - }, - workspace: "", - role: "member" - }, - IdentityMembershipOrg: { - identity: { - _id: "", - name: "Machine 1", - authMethod: "universal-auth" - }, - organization: "", - role: "member", - status: "accepted" - }, - Organization: { - _id: "", - name: "Acme Corp.", - customerId: "" - }, - Project: { - name: "My Project", - organization: "", - environments: [ - { - name: "development", - slug: "dev" - } - ] - }, - ProjectKey: { - encryptedkey: "", - nonce: "", - sender: { - publicKey: "senders_nacl_public_key" - }, - receiver: "", - workspace: "" - }, - CreateSecret: { - type: "shared", - secretKeyCiphertext: "", - secretKeyIV: "", - secretKeyTag: "", - secretValueCiphertext: "", - secretValueIV: "", - secretValueTag: "", - secretCommentCiphertext: "", - secretCommentIV: "", - secretCommentTag: "" - }, - UpdateSecret: { - id: "", - secretKeyCiphertext: "", - secretKeyIV: "", - secretKeyTag: "", - secretValueCiphertext: "", - secretValueIV: "", - secretValueTag: "", - secretCommentCiphertext: "", - secretCommentIV: "", - secretCommentTag: "" - }, - Secret: { - _id: "", - version: 1, - workspace: "", - type: "shared", - user: null, - secretKeyCiphertext: "", - secretKeyIV: "", - secretKeyTag: "", - secretValueCiphertext: "", - secretValueIV: "", - secretValueTag: "", - secretCommentCiphertext: "", - secretCommentIV: "", - secretCommentTag: "", - updatedAt: "2023-01-13T14:16:12.210Z", - createdAt: "2023-01-13T14:16:12.210Z" - }, - RawSecret: { - _id: "abc123", - version: 1, - workspace: "abc123", - environment: "dev", - secretKey: "STRIPE_KEY", - secretValue: "abc123", - secretComment: "Lorem ipsum" - }, - SecretImport: { - _id: "", - workspace: "abc123", - environment: "dev", - folderId: "root", - imports: [], - updatedAt: "2023-01-13T14:16:12.210Z", - createdAt: "2023-01-13T14:16:12.210Z" - }, - Log: { - _id: "", - user: { - _id: "", - email: "johndoe@gmail.com", - firstName: "John", - lastName: "Doe" - }, - workspace: "", - actionNames: ["addSecrets"], - actions: [ - { - name: "addSecrets", - user: "", - workspace: "", - payload: [ - { - oldSecretVersion: "", - newSecretVersion: "" - } - ] - } - ], - channel: "cli", - ipAddress: "192.168.0.1", - updatedAt: "2023-01-13T14:16:12.210Z", - createdAt: "2023-01-13T14:16:12.210Z" - }, - SecretSnapshot: { - workspace: "", - version: 1, - secretVersions: [ - { - _id: "" - } - ] - }, - SecretVersion: { - _id: "", - secret: "", - version: 1, - workspace: "", - type: "shared", - user: "", - environment: "dev", - isDeleted: "", - secretKeyCiphertext: "", - secretKeyIV: "", - secretKeyTag: "", - secretValueCiphertext: "", - secretValueIV: "", - secretValueTag: "" - }, - ServiceTokenData: { - _id: "", - name: "", - workspace: "", - environment: "", - user: { - _id: "", - firstName: "", - lastName: "" - }, - expiresAt: "2023-01-13T14:16:12.210Z", - encryptedKey: "", - iv: "", - tag: "", - updatedAt: "2023-01-13T14:16:12.210Z", - createdAt: "2023-01-13T14:16:12.210Z" - }, - AuditLog: { - actor: { - type: "", - metadata: {} - }, - organization: "", - workspace: "", - ipAddress: "", - event: { - type: "", - metadata: {} - }, - userAgent: "", - userAgentType: "", - expiresAt: "" - } - } - }; - - const outputJSONFile = "../spec.json"; - const outputYAMLFile = "../docs/spec.yaml"; - const endpointsFiles = ["../src/index.ts"]; - - const spec = await swaggerAutogen(outputJSONFile, endpointsFiles, doc); - - await fs.writeFile(outputYAMLFile, yaml.dump(spec.data)); -}; - -generateOpenAPISpec(); diff --git a/backend-mongo/test-resources/docker-compose.test.yml b/backend-mongo/test-resources/docker-compose.test.yml deleted file mode 100644 index e9a8c519a..000000000 --- a/backend-mongo/test-resources/docker-compose.test.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: '3' - -services: - mongo-test: - image: mongo - container_name: infisical-test-mongo - restart: always - ports: - - 27018:27017 - environment: - - MONGO_INITDB_ROOT_USERNAME=test - - MONGO_INITDB_ROOT_PASSWORD=test1234 diff --git a/backend-mongo/test-resources/env-vars.js b/backend-mongo/test-resources/env-vars.js deleted file mode 100644 index 3149a27c9..000000000 --- a/backend-mongo/test-resources/env-vars.js +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint-disable no-undef */ -process.env.MONGO_URL = - 'mongodb://test:test1234@localhost:27018/?authSource=admin'; -process.env.MONGO_USERNAME = 'test'; -process.env.MONGO_PASSWORD = 'test1234'; -process.env.NODE_ENV = 'test'; -process.env.JWT_SIGNUP_SECRET= "38ea90fb7998b92176080f457d890392" -process.env.JWT_REFRESH_SECRET= "7764c7bbf3928ad501591a3e005eb364" -process.env.JWT_AUTH_SECRET= "5239fea3a4720c0e524f814a540e14a2" -process.env.JWT_SERVICE_SECRET= "8509fb8b90c9b53e9e61d1e35826dcb5" -process.env.ENCRYPTION_KEY="e05f54dffd58b5ab9b09e4c6fca7aff7" -process.env.ROOT_ENCRYPTION_KEY="MJA3DWJXjHiL6xjkUI2QCQuy/D+/SAbRNU1+rEo9gvQ=" diff --git a/backend-mongo/tests/data/batch-create-secrets-with-some-missing-params.json b/backend-mongo/tests/data/batch-create-secrets-with-some-missing-params.json deleted file mode 100644 index 40d07827c..000000000 --- a/backend-mongo/tests/data/batch-create-secrets-with-some-missing-params.json +++ /dev/null @@ -1,51 +0,0 @@ -[ - { - "method": "POST", - "secret": { - "workspace": "63cefb15c8d3175601cfa989", - "type": "shared", - "tags": [], - "environment": "dev", - "secretKeyCiphertext": "eaX9a2g=", - "secretKeyIV": "YJ4adgI/wEHifGdtT9reaA==", - "secretKeyTag": "dP73x3wrq7pqxzAHo+bfPA==", - "secretValueCiphertext": "cw==", - "secretValueIV": "7ksYWWZ3+9rzLG5NpEbEgg==", - "secretValueTag": "H0YQ8vrhiVJ0XSW4nBJdQA==", - "secretCommentCiphertext": "", - "secretCommentIV": "yXhMdLdA9q7Vaw4UUaeBYA==", - "secretCommentTag": "qMj7SHESM5Jn+C2qpbw2pA==" - } - }, - { - "method": "POST", - "secret": { - "workspace": "63cefb15c8d3175601cfa989", - "type": "shared", - "tags": [], - "environment": "dev", - "secretKeyIV": "YJ4adgI/wEHifGdtT9reaA==", - "secretKeyTag": "dP73x3wrq7pqxzAHo+bfPA==", - "secretValueIV": "7ksYWWZ3+9rzLG5NpEbEgg==", - "secretValueTag": "H0YQ8vrhiVJ0XSW4nBJdQA==", - "secretCommentIV": "yXhMdLdA9q7Vaw4UUaeBYA==", - "secretCommentTag": "qMj7SHESM5Jn+C2qpbw2pA==" - } - }, - { - "method": "POST", - "secret": { - "workspace": "63cefb15c8d3175601cfa989", - "type": "shared", - "tags": [], - "environment": "dev", - "secretKeyIV": "YJ4adgI/wEHifGdtT9reaA==", - "secretKeyTag": "dP73x3wrq7pqxzAHo+bfPA==", - "secretValueCiphertext": "cw==", - "secretValueTag": "H0YQ8vrhiVJ0XSW4nBJdQA==", - "secretCommentCiphertext": "", - "secretCommentIV": "yXhMdLdA9q7Vaw4UUaeBYA==", - "secretCommentTag": "qMj7SHESM5Jn+C2qpbw2pA==" - } - } -] \ No newline at end of file diff --git a/backend-mongo/tests/data/batch-secrets-no-override.json b/backend-mongo/tests/data/batch-secrets-no-override.json deleted file mode 100644 index 7236c907a..000000000 --- a/backend-mongo/tests/data/batch-secrets-no-override.json +++ /dev/null @@ -1,56 +0,0 @@ -[ - { - "method": "POST", - "secret": { - "workspace": "63cefb15c8d3175601cfa989", - "type": "shared", - "tags": [], - "environment": "dev", - "secretKeyCiphertext": "eaX9a2g=", - "secretKeyIV": "YJ4adgI/wEHifGdtT9reaA==", - "secretKeyTag": "dP73x3wrq7pqxzAHo+bfPA==", - "secretValueCiphertext": "cw==", - "secretValueIV": "7ksYWWZ3+9rzLG5NpEbEgg==", - "secretValueTag": "H0YQ8vrhiVJ0XSW4nBJdQA==", - "secretCommentCiphertext": "", - "secretCommentIV": "yXhMdLdA9q7Vaw4UUaeBYA==", - "secretCommentTag": "qMj7SHESM5Jn+C2qpbw2pA==" - } - }, - { - "method": "POST", - "secret": { - "workspace": "63cefb15c8d3175601cfa989", - "type": "shared", - "tags": [], - "environment": "dev", - "secretKeyCiphertext": "eaX9a2g=", - "secretKeyIV": "YJ4adgI/wEHifGdtT9reaA==", - "secretKeyTag": "dP73x3wrq7pqxzAHo+bfPA==", - "secretValueCiphertext": "cw==", - "secretValueIV": "7ksYWWZ3+9rzLG5NpEbEgg==", - "secretValueTag": "H0YQ8vrhiVJ0XSW4nBJdQA==", - "secretCommentCiphertext": "", - "secretCommentIV": "yXhMdLdA9q7Vaw4UUaeBYA==", - "secretCommentTag": "qMj7SHESM5Jn+C2qpbw2pA==" - } - }, - { - "method": "POST", - "secret": { - "workspace": "63cefb15c8d3175601cfa989", - "type": "shared", - "tags": [], - "environment": "dev", - "secretKeyCiphertext": "eaX9a2g=", - "secretKeyIV": "YJ4adgI/wEHifGdtT9reaA==", - "secretKeyTag": "dP73x3wrq7pqxzAHo+bfPA==", - "secretValueCiphertext": "cw==", - "secretValueIV": "7ksYWWZ3+9rzLG5NpEbEgg==", - "secretValueTag": "H0YQ8vrhiVJ0XSW4nBJdQA==", - "secretCommentCiphertext": "", - "secretCommentIV": "yXhMdLdA9q7Vaw4UUaeBYA==", - "secretCommentTag": "qMj7SHESM5Jn+C2qpbw2pA==" - } - } -] \ No newline at end of file diff --git a/backend-mongo/tests/data/batch-secrets-with-overrides.json b/backend-mongo/tests/data/batch-secrets-with-overrides.json deleted file mode 100644 index 6173289aa..000000000 --- a/backend-mongo/tests/data/batch-secrets-with-overrides.json +++ /dev/null @@ -1,38 +0,0 @@ -[ - { - "method": "POST", - "secret": { - "workspace": "63cefb15c8d3175601cfa989", - "type": "shared", - "environment": "dev", - "secretKeyCiphertext": "IVMtGWE=", - "secretKeyIV": "BDsG7/ylk7mT8MrIMn0e7w==", - "secretKeyTag": "1ujy08fctmZ1xTXMYr23UQ==", - "secretValueCiphertext": "I9psUg==", - "secretValueIV": "W+DJETpCerHkFv8AR9Fv4w==", - "secretValueTag": "yODOeN3HBr/usly4VSMt9w==", - "secretCommentCiphertext": "", - "secretCommentIV": "QET7oX2ZiuLDSzwrkeL2Ig==", - "secretCommentTag": "6P3xeA9eO+3Wp66ROHXgfg==" - } - }, - { - "method": "POST", - "secret": { - "workspace": "63cefb15c8d3175601cfa989", - "type": "personal", - "user": "63cefa6ec8d3175601cfa980", - "tags": [], - "environment": "dev", - "secretKeyCiphertext": "Q7lyRO8=", - "secretKeyIV": "yz8koc3d63ywJMiGXpCNSw==", - "secretKeyTag": "j2bMQ2d4sDZKA0OaKM5SXA==", - "secretValueCiphertext": "X4kaiShmtGZt", - "secretValueIV": "p/GdbksLVveNLsV3vz5GLA==", - "secretValueTag": "//dhRL+pagecavHJCtMPWg==", - "secretCommentCiphertext": "", - "secretCommentIV": "7eYJzuilvjQPutqrqbd2MQ==", - "secretCommentTag": "LpPv9K0Hhd5noE39Zu9U+w==" - } - } -] \ No newline at end of file diff --git a/backend-mongo/tests/helper/helper.ts b/backend-mongo/tests/helper/helper.ts deleted file mode 100644 index 510f5f353..000000000 --- a/backend-mongo/tests/helper/helper.ts +++ /dev/null @@ -1,98 +0,0 @@ -// Helper functions for integration tests - -import axiosInstance from "../../src/config/request"; -import { Secret } from "../../src/models"; -import { testUserEmail, testUserPassword } from "../../src/utils/addDevelopmentUser"; -// eslint-disable-next-line @typescript-eslint/no-var-requires -const crypto = require("crypto") -// eslint-disable-next-line @typescript-eslint/no-var-requires -const jsrp = require("jsrp"); -// eslint-disable-next-line @typescript-eslint/no-var-requires -const axios = require("axios"); -import { plainTextWorkspaceKey, testWorkspaceId } from "../../src/utils/addDevelopmentUser"; -import { - encryptSymmetric128BitHexKeyUTF8, -} from "../../src/utils/crypto"; - -interface TokenData { - token: string; - publicKey: string; - encryptedPrivateKey: string; - iv: string; - tag: string; -} - -export const getJWTFromTestUser = (): Promise => { - return new Promise((resolve, reject) => { - const client = new jsrp.client(); - const EMAIL = testUserEmail - const PASSWORD = testUserPassword - - client.init({ - username: EMAIL, - password: PASSWORD, - }, async () => { - const clientPublicKey = client.getPublicKey(); - - // POST: /login1 - const reqBody = { - email: EMAIL, - clientPublicKey, - } - - - const loginOneRes = await axiosInstance.post("http://localhost:4000/api/v1/auth/login1", reqBody); - const serverPublicKey = loginOneRes.data.serverPublicKey; - const salt = loginOneRes.data.salt; - - client.setSalt(salt); - client.setServerPublicKey(serverPublicKey); - const clientSharedKey = client.getSharedKey(); // shared Key - const clientProof = client.getProof(); // called M1 - - // POST: /login2 - const reqBody2 = { - email: EMAIL, - clientProof, - } - - const response2 = await axiosInstance.post("http://localhost:4000/api/v1/auth/login2", reqBody2); - - resolve(response2.data) - }) - }); -} - -export const getServiceTokenFromTestUser = async () => { - const loggedInUserDetails = await getJWTFromTestUser() - const randomBytes = crypto.randomBytes(16).toString("hex"); - const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({ - plaintext: plainTextWorkspaceKey, - key: randomBytes, - }); - - const newServiceToken = await axiosInstance.post("http://localhost:4000/api/v2/service-token/", { - "name": "test service token", - "workspaceId": testWorkspaceId, - "environment": "dev", - "encryptedKey": ciphertext, - "iv": iv, - "tag": tag, - "expiresIn": Date.now() + 90000, - "permissions": ["read"], - }, { - headers: { - "Authorization": `Bearer ${loggedInUserDetails.token}`, - }, - }); - - return `${newServiceToken.data.serviceToken}.${randomBytes}` -} - -export const deleteAllSecrets = async () => { - await Secret.deleteMany() -} - -export const getAllSecrets = async () => { - return await Secret.find() -} \ No newline at end of file diff --git a/backend-mongo/tests/integration-tests/routes/v2/secrets.test.ts b/backend-mongo/tests/integration-tests/routes/v2/secrets.test.ts deleted file mode 100644 index 700a6e8a2..000000000 --- a/backend-mongo/tests/integration-tests/routes/v2/secrets.test.ts +++ /dev/null @@ -1,408 +0,0 @@ -// import request from 'supertest' -// import main from '../../../../src/index' -// import { testWorkspaceId } from '../../../../src/utils/addDevelopmentUser'; -// import { deleteAllSecrets, getAllSecrets, getJWTFromTestUser, getServiceTokenFromTestUser } from '../../../helper/helper'; -// // eslint-disable-next-line @typescript-eslint/no-var-requires -// const batchSecretRequestWithNoOverride = require('../../../data/batch-secrets-no-override.json'); -// // eslint-disable-next-line @typescript-eslint/no-var-requires -// const batchSecretRequestWithOverrides = require('../../../data/batch-secrets-with-overrides.json'); - -// // eslint-disable-next-line @typescript-eslint/no-var-requires -// const batchSecretRequestWithBadRequest = require('../../../data/batch-create-secrets-with-some-missing-params.json'); - -// let server: any; -// beforeAll(async () => { -// server = await main; -// }); - -// afterAll(async () => { -// server.close(); -// }); - -// describe("GET /api/v2/secrets", () => { -// describe("Get secrets via JTW", () => { -// test("should create secrets and read secrets via jwt", async () => { -// try { -// // get login details -// const loginResponse = await getJWTFromTestUser() - -// // create creates -// const createSecretsResponse = await request(server) -// .post("/api/v2/secrets/batch") -// .set('Authorization', `Bearer ${loginResponse.token}`) -// .send({ -// workspaceId: testWorkspaceId, -// environment: "dev", -// requests: batchSecretRequestWithNoOverride -// }) - -// expect(createSecretsResponse.statusCode).toBe(200) - - -// const getSecrets = await request(server) -// .get("/api/v2/secrets") -// .set('Authorization', `Bearer ${loginResponse.token}`) -// .query({ -// workspaceId: testWorkspaceId, -// environment: "dev" -// }) - -// expect(getSecrets.statusCode).toBe(200) -// expect(getSecrets.body).toHaveProperty("secrets") -// expect(getSecrets.body.secrets).toHaveLength(3) -// expect(getSecrets.body.secrets).toBeInstanceOf(Array); - -// getSecrets.body.secrets.forEach((secret: any) => { -// expect(secret).toHaveProperty('_id'); -// expect(secret._id).toBeTruthy(); - -// expect(secret).toHaveProperty('version'); -// expect(secret.version).toBeTruthy(); - -// expect(secret).toHaveProperty('workspace'); -// expect(secret.workspace).toBeTruthy(); - -// expect(secret).toHaveProperty('type'); -// expect(secret.type).toBeTruthy(); - -// expect(secret).toHaveProperty('tags'); -// expect(secret.tags).toHaveLength(0); - -// expect(secret).toHaveProperty('environment'); -// expect(secret.environment).toEqual("dev"); - -// expect(secret).toHaveProperty('secretKeyCiphertext'); -// expect(secret.secretKeyCiphertext).toBeTruthy(); - -// expect(secret).toHaveProperty('secretKeyIV'); -// expect(secret.secretKeyIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretKeyTag'); -// expect(secret.secretKeyTag).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueCiphertext'); -// expect(secret.secretValueCiphertext).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueIV'); -// expect(secret.secretValueIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueTag'); -// expect(secret.secretValueTag).toBeTruthy(); - -// expect(secret).toHaveProperty('secretCommentCiphertext'); -// expect(secret.secretCommentCiphertext).toBeFalsy(); - -// expect(secret).toHaveProperty('secretCommentIV'); -// expect(secret.secretCommentIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretCommentTag'); -// expect(secret.secretCommentTag).toBeTruthy(); - -// expect(secret).toHaveProperty('createdAt'); -// expect(secret.createdAt).toBeTruthy(); - -// expect(secret).toHaveProperty('updatedAt'); -// expect(secret.updatedAt).toBeTruthy(); -// }); -// } finally { -// // clean up -// await deleteAllSecrets() -// } -// }) - -// test("Get secrets via jwt when personal overrides exist", async () => { -// try { -// // get login details -// const loginResponse = await getJWTFromTestUser() - -// // create creates -// const createSecretsResponse = await request(server) -// .post("/api/v2/secrets/batch") -// .set('Authorization', `Bearer ${loginResponse.token}`) -// .send({ -// workspaceId: testWorkspaceId, -// environment: "dev", -// requests: batchSecretRequestWithOverrides -// }) - -// expect(createSecretsResponse.statusCode).toBe(200) - -// const getSecrets = await request(server) -// .get("/api/v2/secrets") -// .set('Authorization', `Bearer ${loginResponse.token}`) -// .query({ -// workspaceId: testWorkspaceId, -// environment: "dev" -// }) - -// expect(getSecrets.statusCode).toBe(200) -// expect(getSecrets.body).toHaveProperty("secrets") -// expect(getSecrets.body.secrets).toHaveLength(2) -// expect(getSecrets.body.secrets).toBeInstanceOf(Array); - -// getSecrets.body.secrets.forEach((secret: any) => { -// expect(secret).toHaveProperty('_id'); -// expect(secret._id).toBeTruthy(); - -// expect(secret).toHaveProperty('version'); -// expect(secret.version).toBeTruthy(); - -// expect(secret).toHaveProperty('workspace'); -// expect(secret.workspace).toBeTruthy(); - -// expect(secret).toHaveProperty('type'); -// expect(secret.type).toBeTruthy(); - -// expect(secret).toHaveProperty('tags'); -// expect(secret.tags).toHaveLength(0); - -// expect(secret).toHaveProperty('environment'); -// expect(secret.environment).toEqual("dev"); - -// expect(secret).toHaveProperty('secretKeyCiphertext'); -// expect(secret.secretKeyCiphertext).toBeTruthy(); - -// expect(secret).toHaveProperty('secretKeyIV'); -// expect(secret.secretKeyIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretKeyTag'); -// expect(secret.secretKeyTag).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueCiphertext'); -// expect(secret.secretValueCiphertext).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueIV'); -// expect(secret.secretValueIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueTag'); -// expect(secret.secretValueTag).toBeTruthy(); - -// expect(secret).toHaveProperty('secretCommentCiphertext'); -// expect(secret.secretCommentCiphertext).toBeFalsy(); - -// expect(secret).toHaveProperty('secretCommentIV'); -// expect(secret.secretCommentIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretCommentTag'); -// expect(secret.secretCommentTag).toBeTruthy(); - -// expect(secret).toHaveProperty('createdAt'); -// expect(secret.createdAt).toBeTruthy(); - -// expect(secret).toHaveProperty('updatedAt'); -// expect(secret.updatedAt).toBeTruthy(); -// }); -// } finally { -// // clean up -// await deleteAllSecrets() -// } -// }) -// }) - -// describe("fetch secrets via service token", () => { -// test("Get secrets via jwt when personal overrides exist", async () => { -// try { -// // get login details -// const loginResponse = await getJWTFromTestUser() - -// // create creates -// const createSecretsResponse = await request(server) -// .post("/api/v2/secrets/batch") -// .set('Authorization', `Bearer ${loginResponse.token}`) -// .send({ -// workspaceId: testWorkspaceId, -// environment: "dev", -// requests: batchSecretRequestWithOverrides -// }) - -// expect(createSecretsResponse.statusCode).toBe(200) - -// // now use the service token to fetch secrets -// const serviceToken = await getServiceTokenFromTestUser() - -// const getSecrets = await request(server) -// .get("/api/v2/secrets") -// .set('Authorization', `Bearer ${serviceToken}`) -// .query({ -// workspaceId: testWorkspaceId, -// environment: "dev" -// }) - -// expect(getSecrets.statusCode).toBe(200) -// expect(getSecrets.body).toHaveProperty("secrets") -// expect(getSecrets.body.secrets).toHaveLength(2) -// expect(getSecrets.body.secrets).toBeInstanceOf(Array); - -// getSecrets.body.secrets.forEach((secret: any) => { -// expect(secret).toHaveProperty('_id'); -// expect(secret._id).toBeTruthy(); - -// expect(secret).toHaveProperty('version'); -// expect(secret.version).toBeTruthy(); - -// expect(secret).toHaveProperty('workspace'); -// expect(secret.workspace).toBeTruthy(); - -// expect(secret).toHaveProperty('type'); -// expect(secret.type).toBeTruthy(); - -// expect(secret).toHaveProperty('tags'); -// expect(secret.tags).toHaveLength(0); - -// expect(secret).toHaveProperty('environment'); -// expect(secret.environment).toEqual("dev"); - -// expect(secret).toHaveProperty('secretKeyCiphertext'); -// expect(secret.secretKeyCiphertext).toBeTruthy(); - -// expect(secret).toHaveProperty('secretKeyIV'); -// expect(secret.secretKeyIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretKeyTag'); -// expect(secret.secretKeyTag).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueCiphertext'); -// expect(secret.secretValueCiphertext).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueIV'); -// expect(secret.secretValueIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueTag'); -// expect(secret.secretValueTag).toBeTruthy(); - -// expect(secret).toHaveProperty('secretCommentCiphertext'); -// expect(secret.secretCommentCiphertext).toBeFalsy(); - -// expect(secret).toHaveProperty('secretCommentIV'); -// expect(secret.secretCommentIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretCommentTag'); -// expect(secret.secretCommentTag).toBeTruthy(); - -// expect(secret).toHaveProperty('createdAt'); -// expect(secret.createdAt).toBeTruthy(); - -// expect(secret).toHaveProperty('updatedAt'); -// expect(secret.updatedAt).toBeTruthy(); -// }); -// } finally { -// // clean up -// await deleteAllSecrets() -// } -// }) - -// test("should create secrets and read secrets via service token when no overrides", async () => { -// try { -// // get login details -// const loginResponse = await getJWTFromTestUser() - -// // create secrets -// const createSecretsResponse = await request(server) -// .post("/api/v2/secrets/batch") -// .set('Authorization', `Bearer ${loginResponse.token}`) -// .send({ -// workspaceId: testWorkspaceId, -// environment: "dev", -// requests: batchSecretRequestWithNoOverride -// }) - -// expect(createSecretsResponse.statusCode).toBe(200) - - -// // now use the service token to fetch secrets -// const serviceToken = await getServiceTokenFromTestUser() - -// const getSecrets = await request(server) -// .get("/api/v2/secrets") -// .set('Authorization', `Bearer ${serviceToken}`) -// .query({ -// workspaceId: testWorkspaceId, -// environment: "dev" -// }) - -// expect(getSecrets.statusCode).toBe(200) -// expect(getSecrets.body).toHaveProperty("secrets") -// expect(getSecrets.body.secrets).toHaveLength(3) -// expect(getSecrets.body.secrets).toBeInstanceOf(Array); - -// getSecrets.body.secrets.forEach((secret: any) => { -// expect(secret).toHaveProperty('_id'); -// expect(secret._id).toBeTruthy(); - -// expect(secret).toHaveProperty('version'); -// expect(secret.version).toBeTruthy(); - -// expect(secret).toHaveProperty('workspace'); -// expect(secret.workspace).toBeTruthy(); - -// expect(secret).toHaveProperty('type'); -// expect(secret.type).toBeTruthy(); - -// expect(secret).toHaveProperty('tags'); -// expect(secret.tags).toHaveLength(0); - -// expect(secret).toHaveProperty('environment'); -// expect(secret.environment).toEqual("dev"); - -// expect(secret).toHaveProperty('secretKeyCiphertext'); -// expect(secret.secretKeyCiphertext).toBeTruthy(); - -// expect(secret).toHaveProperty('secretKeyIV'); -// expect(secret.secretKeyIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretKeyTag'); -// expect(secret.secretKeyTag).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueCiphertext'); -// expect(secret.secretValueCiphertext).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueIV'); -// expect(secret.secretValueIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretValueTag'); -// expect(secret.secretValueTag).toBeTruthy(); - -// expect(secret).toHaveProperty('secretCommentCiphertext'); -// expect(secret.secretCommentCiphertext).toBeFalsy(); - -// expect(secret).toHaveProperty('secretCommentIV'); -// expect(secret.secretCommentIV).toBeTruthy(); - -// expect(secret).toHaveProperty('secretCommentTag'); -// expect(secret.secretCommentTag).toBeTruthy(); - -// expect(secret).toHaveProperty('createdAt'); -// expect(secret.createdAt).toBeTruthy(); - -// expect(secret).toHaveProperty('updatedAt'); -// expect(secret.updatedAt).toBeTruthy(); -// }); -// } finally { -// // clean up -// await deleteAllSecrets() -// } -// }) -// }) - -// describe("create secrets via JWT", () => { -// test("Create secrets via jwt when some requests have missing required parameters", async () => { -// // get login details -// const loginResponse = await getJWTFromTestUser() - -// // create creates -// const createSecretsResponse = await request(server) -// .post("/api/v2/secrets/batch") -// .set('Authorization', `Bearer ${loginResponse.token}`) -// .send({ -// workspaceId: testWorkspaceId, -// environment: "dev", -// requests: batchSecretRequestWithBadRequest -// }) - -// const allSecretsInDB = await getAllSecrets() - -// expect(createSecretsResponse.statusCode).toBe(500) // TODO should be set to 400 -// expect(allSecretsInDB).toHaveLength(0) -// }) -// }) -// }) \ No newline at end of file diff --git a/backend-mongo/tests/integration-tests/routes/v2/service-tokens.ts b/backend-mongo/tests/integration-tests/routes/v2/service-tokens.ts deleted file mode 100644 index 15d776bdd..000000000 --- a/backend-mongo/tests/integration-tests/routes/v2/service-tokens.ts +++ /dev/null @@ -1,58 +0,0 @@ -import request from "supertest" -import main from "../../../../src/index" -import { getServiceTokenFromTestUser } from "../../../helper/helper"; -let server: any; - -beforeAll(async () => { - server = await main; -}); - -afterAll(async () => { - server.close(); -}); - -describe("GET /api/v2/service-token", () => { - describe("Get service token details", () => { - test("should respond create and get the details of a service token", async () => { - // generate a service token - const serviceToken = await getServiceTokenFromTestUser() - - // get the service token details - const serviceTokenDetails = await request(server) - .get("/api/v2/service-token") - .set("Authorization", `Bearer ${serviceToken}`) - - expect(serviceTokenDetails.body).toMatchObject({ - _id: expect.any(String), - name: "test service token", - workspace: "63cefb15c8d3175601cfa989", - environment: "dev", - user: { - _id: "63cefa6ec8d3175601cfa980", - email: "test@localhost.local", - firstName: "Jake", - lastName: "Moni", - isMfaEnabled: false, - mfaMethods: expect.any(Array), - devices: [ - { - ip: expect.any(String), - userAgent: expect.any(String), - _id: expect.any(String), - }, - ], - createdAt: expect.any(String), - updatedAt: expect.any(String), - }, - lastUsed: expect.any(String), - expiresAt: expect.any(String), - encryptedKey: expect.any(String), - iv: expect.any(String), - tag: expect.any(String), - permissions: ["read"], - createdAt: expect.any(String), - updatedAt: expect.any(String), - }); - }) - }) -}) \ No newline at end of file diff --git a/backend-mongo/tests/setupTests.ts b/backend-mongo/tests/setupTests.ts deleted file mode 100644 index 8d259252e..000000000 --- a/backend-mongo/tests/setupTests.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Server } from "http"; -import main from "../src"; -import { afterAll, beforeAll, describe, expect, it } from "@jest/globals"; -import request from "supertest"; -import { githubPushEventSecretScan } from "../src/queues/secret-scanning/githubScanPushEvent"; -import { syncSecretsToThirdPartyServices } from "../src/queues/integrations/syncSecretsToThirdPartyServices"; - -let server: Server; - -beforeAll(async () => { - server = await main; -}); - -afterAll(async () => { - server.close(); - githubPushEventSecretScan.close() - syncSecretsToThirdPartyServices.close() -}); - -describe("Healthcheck endpoint", () => { - it("GET /healthcheck should return OK", async () => { - const res = await request(server).get("/healthcheck"); - expect(res.status).toEqual(200); - }); -}); diff --git a/backend-mongo/tests/unit-tests/utils/crypto.test.ts b/backend-mongo/tests/unit-tests/utils/crypto.test.ts deleted file mode 100644 index f9f2aba01..000000000 --- a/backend-mongo/tests/unit-tests/utils/crypto.test.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { describe, expect, test } from "@jest/globals"; -import { - decryptAsymmetric, - encryptAsymmetric, -} from "../../../src/utils/crypto"; - -describe("Crypto", () => { - describe("encryptAsymmetric", () => { - describe("given all valid publicKey, privateKey and plaintext", () => { - const publicKey = "6U5m6S5jlyazJ+R4z7Yf/Ah4th4JwKxDN8Wn7+upvzw="; - const privateKey = "Z8W53YV+2ddjJCrFwzptjK96y2QsQI9oXuvfcx+qxz0="; - const plaintext = "secret-message"; - - test("should encrypt plain text", () => { - const result = encryptAsymmetric({ plaintext, publicKey, privateKey }); - expect(result.ciphertext).toBeDefined(); - expect(result.nonce).toBeDefined(); - }); - }); - - describe("given empty/undefined publicKey", () => { - let publicKey: string; - const privateKey = "Z8W53YV+2ddjJCrFwzptjK96y2QsQI9oXuvfcx+qxz0="; - const plaintext = "secret-message"; - - test("should throw error if publicKey is undefined", () => { - expect(() => { - encryptAsymmetric({ plaintext, publicKey, privateKey }); - }).toThrowError("invalid encoding"); - }); - - test("should throw error if publicKey is empty string", () => { - publicKey = ""; - expect(() => { - encryptAsymmetric({ plaintext, publicKey, privateKey }); - }).toThrowError("bad public key size"); - }); - }); - - describe("given empty/undefined privateKey", () => { - const publicKey = "6U5m6S5jlyazJ+R4z7Yf/Ah4th4JwKxDN8Wn7+upvzw="; - let privateKey: string; - const plaintext = "secret-message"; - - test("should throw error if privateKey is undefined", () => { - expect(() => { - encryptAsymmetric({ plaintext, publicKey, privateKey }); - }).toThrowError("invalid encoding"); - }); - - test("should throw error if privateKey is empty string", () => { - privateKey = ""; - expect(() => { - encryptAsymmetric({ plaintext, publicKey, privateKey }); - }).toThrowError("bad secret key size"); - }); - }); - - describe("given undefined/invalid plaint text", () => { - const publicKey = "6U5m6S5jlyazJ+R4z7Yf/Ah4th4JwKxDN8Wn7+upvzw="; - const privateKey = "Z8W53YV+2ddjJCrFwzptjK96y2QsQI9oXuvfcx+qxz0="; - let plaintext: string; - - test("should throw error if plaintext is undefined", () => { - expect(() => { - encryptAsymmetric({ plaintext, publicKey, privateKey }); - }).toThrowError("expected string"); - }); - - test("should encrypt plaintext containing special characters", () => { - plaintext = "131@#$%235!@#&*(&123sadfkjadjf"; - const result = encryptAsymmetric({ - plaintext, - publicKey, - privateKey, - }); - expect(result.ciphertext).toBeDefined(); - expect(result.nonce).toBeDefined(); - }); - }); - }); - - describe("decryptAsymmetric", () => { - describe("given all valid publicKey, privateKey and plaintext", () => { - const publicKey = "6U5m6S5jlyazJ+R4z7Yf/Ah4th4JwKxDN8Wn7+upvzw="; - const privateKey = "Z8W53YV+2ddjJCrFwzptjK96y2QsQI9oXuvfcx+qxz0="; - const plaintext = "secret-message"; - - test("should decrypt the encrypted plaintext", () => { - const encryptedResult = encryptAsymmetric({ - plaintext, - publicKey, - privateKey, - }); - const ciphertext = encryptedResult.ciphertext; - const nonce = encryptedResult.nonce; - - const decryptedResult = decryptAsymmetric({ - ciphertext, - nonce, - publicKey, - privateKey, - }); - - expect(decryptedResult).toBeDefined(); - expect(decryptedResult).toEqual(plaintext); - }); - }); - - describe("given ciphertext or nonce is modified before decrypt", () => { - const publicKey = "6U5m6S5jlyazJ+R4z7Yf/Ah4th4JwKxDN8Wn7+upvzw="; - const privateKey = "Z8W53YV+2ddjJCrFwzptjK96y2QsQI9oXuvfcx+qxz0="; - const plaintext = "secret-message"; - - test("should throw error if ciphertext is modified", () => { - const encryptedResult = encryptAsymmetric({ - plaintext, - publicKey, - privateKey, - }); - const ciphertext = "=12adfJ@#52af1231=123"; // modified - const nonce = encryptedResult.nonce; - - expect(() => { - decryptAsymmetric({ - ciphertext, - nonce, - publicKey, - privateKey, - }); - }).toThrowError("invalid encoding"); - }); - - test("should throw error if nonce is modified", () => { - const encryptedResult = encryptAsymmetric({ - plaintext, - publicKey, - privateKey, - }); - const ciphertext = encryptedResult.ciphertext; - const nonce = "=12adfJ@#52af1231=123"; // modified - - expect(() => { - decryptAsymmetric({ - ciphertext, - nonce, - publicKey, - privateKey, - }); - }).toThrowError("invalid encoding"); - }); - }); - }); -}); diff --git a/backend-mongo/tests/unit-tests/utils/posthog.test.ts b/backend-mongo/tests/unit-tests/utils/posthog.test.ts deleted file mode 100644 index f449f39f2..000000000 --- a/backend-mongo/tests/unit-tests/utils/posthog.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, expect, test } from "@jest/globals"; -import { getUserAgentType } from "../../../src/utils/posthog"; - -describe("posthog getChannelFromUserAgent", () => { - test("should return 'web' when userAgent includes 'mozilla'", () => { - const userAgent = - "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.115 Mobile Safari/537.36"; - const channel = getUserAgentType(userAgent); - expect(channel).toBe("web"); - }); - - test("should return 'cli'", () => { - const userAgent = "cli"; - const channel = getUserAgentType(userAgent); - expect(channel).toBe("cli"); - }); - - test("should return 'k8-operator'", () => { - const userAgent = "k8-operator"; - const channel = getUserAgentType(userAgent); - expect(channel).toBe("k8-operator"); - }); - - test("should return undefined if no userAgent", () => { - const userAgent = undefined; - const channel = getUserAgentType(userAgent); - expect(channel).toBe("other"); - }); -}); diff --git a/backend-mongo/tsconfig.json b/backend-mongo/tsconfig.json deleted file mode 100644 index a908b72e9..000000000 --- a/backend-mongo/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "target": "es2016", - "lib": ["es6", "es2021"], - "module": "commonjs", - "rootDir": "src", - "resolveJsonModule": true, - "allowJs": true, - "outDir": "build", - "esModuleInterop": true, - "moduleResolution": "node", - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitAny": true, - "skipLibCheck": true, - "typeRoots": ["./src/types", "./node_modules/@types"] - }, - "ts-node": { - "swc": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules"] -} diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js index e9e98bffe..e99c48c09 100644 --- a/backend/.eslintrc.js +++ b/backend/.eslintrc.js @@ -1,27 +1,39 @@ +/* eslint-env node */ module.exports = { - root: true, env: { - browser: true, - es2021: true + es6: true, + node: true }, - extends: ["airbnb-base", "airbnb-typescript/base", "prettier"], - plugins: ["prettier", "simple-import-sort", "import"], + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-type-checked", + "airbnb-base", + "airbnb-typescript/base", + "plugin:prettier/recommended", + "prettier" + ], + plugins: ["@typescript-eslint", "simple-import-sort", "import"], + parser: "@typescript-eslint/parser", parserOptions: { - ecmaVersion: "latest", + project: true, sourceType: "module", - project: "./tsconfig.json", tsconfigRootDir: __dirname }, + root: true, rules: { - // "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "no-void": "off", "consistent-return": "off", // my style "import/order": "off", // for simple-import-order "import/prefer-default-export": "off", // why "no-restricted-syntax": "off", + // importing rules + "simple-import-sort/exports": "error", "import/first": "error", "import/newline-after-import": "error", "import/no-duplicates": "error", - "simple-import-sort/exports": "error", "simple-import-sort/imports": [ "warn", { @@ -45,12 +57,5 @@ module.exports = { ] } ] - }, - settings: { - "import/resolver": { - typescript: { - project: ["./tsconfig.json"] - } - } } }; diff --git a/backend/.prettierrc.json b/backend/.prettierrc.json index f9058accf..987f567d4 100644 --- a/backend/.prettierrc.json +++ b/backend/.prettierrc.json @@ -1,7 +1,7 @@ { "singleQuote": false, - "printWidth": 100, + "printWidth": 120, "trailingComma": "none", "tabWidth": 2, "semi": true -} \ No newline at end of file +} diff --git a/backend/Dockerfile b/backend/Dockerfile index 422ffef6a..2153ba33a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -28,6 +28,8 @@ RUN apk add --no-cache bash curl && curl -1sLf \ HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \ CMD node healthcheck.js +ENV HOST=0.0.0.0 + EXPOSE 4000 CMD ["npm", "start"] diff --git a/backend/e2e-test/mocks/queue.ts b/backend/e2e-test/mocks/queue.ts index f39e98738..c694979db 100644 --- a/backend/e2e-test/mocks/queue.ts +++ b/backend/e2e-test/mocks/queue.ts @@ -16,9 +16,11 @@ export const mockQueue = (): TQueueServiceFactory => { queues[name] = jobFn; workers[name] = jobFn; }, - listen: async (name, event) => { + listen: (name, event) => { events[name] = event; }, + clearQueue: async () => {}, + stopJobById: async () => {}, stopRepeatableJobByJobId: async () => true }; }; diff --git a/backend/package-lock.json b/backend/package-lock.json index a34b07589..1d4aeb2bd 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,6 +13,7 @@ "@casl/ability": "^6.5.0", "@fastify/cookie": "^9.2.0", "@fastify/cors": "^8.4.1", + "@fastify/etag": "^5.1.0", "@fastify/formbody": "^7.4.0", "@fastify/helmet": "^11.1.1", "@fastify/passport": "^2.4.0", @@ -23,6 +24,7 @@ "@node-saml/passport-saml": "^4.0.4", "@octokit/rest": "^20.0.2", "@octokit/webhooks-types": "^7.3.1", + "@serdnam/pino-cloudwatch-transport": "^1.0.4", "@sindresorhus/slugify": "^2.2.1", "@ucast/mongo2js": "^1.3.4", "ajv": "^8.12.0", @@ -33,7 +35,6 @@ "bcrypt": "^5.1.1", "bullmq": "^5.1.1", "dotenv": "^16.3.1", - "eslint-config-airbnb-typescript": "^17.1.0", "fastify": "^4.24.3", "fastify-plugin": "^4.5.1", "handlebars": "^4.7.8", @@ -47,7 +48,7 @@ "mysql2": "^3.6.5", "nanoid": "^5.0.4", "node-cache": "^5.1.2", - "nodemailer": "^6.9.7", + "nodemailer": "^6.9.9", "ora": "^7.0.1", "passport-github": "^1.1.0", "passport-gitlab2": "^5.0.0", @@ -78,11 +79,13 @@ "@types/pg": "^8.10.9", "@types/picomatch": "^2.3.3", "@types/prompt-sync": "^4.2.3", + "@types/resolve": "^1.20.6", "@types/uuid": "^9.0.7", - "@typescript-eslint/eslint-plugin": "^6.13.2", - "@typescript-eslint/parser": "^6.13.2", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "eslint": "^8.56.0", "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-airbnb-typescript": "^17.1.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", @@ -93,6 +96,7 @@ "prompt-sync": "^4.2.0", "rimraf": "^5.0.5", "ts-node": "^10.9.1", + "tsc-alias": "^1.8.8", "tsconfig-paths": "^4.2.0", "tsup": "^8.0.1", "tsx": "^4.4.0", @@ -105,6 +109,7 @@ "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -200,6 +205,477 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/@aws-sdk/client-cloudwatch-logs": { + "version": "3.501.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.501.0.tgz", + "integrity": "sha512-Lad4FHqTut9ZM8VKW48cnd8OBYekkVbYxWb6uWnf05NA9JMuy/zRBD4MGpv3MjhhRe4iX159G1406yK39N2GDg==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.501.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/credential-provider-node": "3.501.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-signing": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/eventstream-serde-browser": "^2.1.1", + "@smithy/eventstream-serde-config-resolver": "^2.1.1", + "@smithy/eventstream-serde-node": "^2.1.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sso": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.496.0.tgz", + "integrity": "sha512-fuaMuxKg7CMUsP9l3kxYWCOxFsBjdA0xj5nlikaDm1661/gB4KkAiGqRY8LsQkpNXvXU8Nj+f7oCFADFyGYzyw==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/client-sts": { + "version": "3.501.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.501.0.tgz", + "integrity": "sha512-Uwc/xuxsA46dZS5s+4U703LBNDrGpWF7RB4XYEEMD21BLfGuqntxLLQux8xxKt3Pcur0CsXNja5jXt3uLnE5MA==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/credential-provider-node": "3.501.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/core": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.496.0.tgz", + "integrity": "sha512-yT+ug7Cw/3eJi7x2es0+46x12+cIJm5Xv+GPWsrTFD1TKgqO/VPEgfDtHFagDNbFmjNQA65Ygc/kEdIX9ICX/A==", + "dependencies": { + "@smithy/core": "^1.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.496.0.tgz", + "integrity": "sha512-lukQMJ8SWWP5RqkRNOHi/H+WMhRvSWa3Fc5Jf/VP6xHiPLfF1XafcvthtV91e0VwPCiseI+HqChrcGq8pvnxHw==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.501.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.501.0.tgz", + "integrity": "sha512-6UXnwLtYIr298ljveumCVXsH+x7csGscK5ylY+veRFy514NqyloRdJt8JY26hhh5SF9MYnkW+JyWSJ2Ls3tOjQ==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.496.0", + "@aws-sdk/credential-provider-process": "3.496.0", + "@aws-sdk/credential-provider-sso": "3.501.0", + "@aws-sdk/credential-provider-web-identity": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.501.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.501.0.tgz", + "integrity": "sha512-NM62D8gYrQ1nyLYwW4k48B2/lMHDzHDcQccS1wJakr6bg5sdtG06CumwlVcY+LAa0o1xRnhHmh/yiwj/nN4avw==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.496.0", + "@aws-sdk/credential-provider-ini": "3.501.0", + "@aws-sdk/credential-provider-process": "3.496.0", + "@aws-sdk/credential-provider-sso": "3.501.0", + "@aws-sdk/credential-provider-web-identity": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.496.0.tgz", + "integrity": "sha512-/YZscCTGOKVmGr916Th4XF8Sz6JDtZ/n2loHG9exok9iy/qIbACsTRNLP9zexPxhPoue/oZqecY5xbVljfY34A==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.501.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.501.0.tgz", + "integrity": "sha512-y90dlvvZ55PwecODFdMx0NiNlJJfm7X6S61PKdLNCMRcu1YK+eWn0CmPHGHobBUQ4SEYhnFLcHSsf+VMim6BtQ==", + "dependencies": { + "@aws-sdk/client-sso": "3.496.0", + "@aws-sdk/token-providers": "3.501.0", + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.496.0.tgz", + "integrity": "sha512-IbP+qLlvJSpNPj+zW6TtFuLRTK5Tf0hW+2pom4vFyi5YSH4pn8UOC136UdewX8vhXGS9BJQ5zBDMasIyl5VeGQ==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.496.0.tgz", + "integrity": "sha512-jUdPpSJeqCYXf6hSjfwsfHway7peIV8Vz51w/BN91bF4vB/bYwAC5o9/iJiK/EoByp5asxA8fg9wFOyGjzdbLg==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-logger": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.496.0.tgz", + "integrity": "sha512-EwMVSY6iBMeGbVnvwdaFl/ClMS/YWtxCAo+bcEtgk8ltRuo7qgbJem8Km/fvWC1vdWvIbe4ArdJ8iGzq62ffAw==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.496.0.tgz", + "integrity": "sha512-+IuOcFsfqg2WAnaEzH6KhVbicqCxtOq9w3DH2jwTpddRlCx2Kqf6wCzg8luhHRGyjBZdsbIS+OXwyMevoppawA==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-signing": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.496.0.tgz", + "integrity": "sha512-Oq73Brs4IConvWnRlh8jM1V7LHoTw9SVQklu/QW2FPlNrB3B8fuTdWHHYIWv7ybw1bykXoCY99v865Mmq/Or/g==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.496.0.tgz", + "integrity": "sha512-+iMtRxFk0GmFWNUF4ilxylOQd9PZdR4ZC9jkcPIh1PZlvKtpCyFywKlk5RRZKklSoJ/CttcqwhMvOXTNbWm/0w==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.496.0.tgz", + "integrity": "sha512-URrNVOPHPgEDm6QFu6lDC2cUFs+Jx23mA3jEwCvoKlXiEY/ZoWjH8wlX3OMUlLrF1qoUTuD03jjrJzF6zoCgug==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/token-providers": { + "version": "3.501.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.501.0.tgz", + "integrity": "sha512-MvLPhNxlStmQqVm2crGLUqYWvK/AbMmI9j4FbEfJ15oG/I+730zjSJQEy2MvdiqbJRDPZ/tRCL89bUedOrmi0g==", + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.496.0", + "@aws-sdk/middleware-logger": "3.496.0", + "@aws-sdk/middleware-recursion-detection": "3.496.0", + "@aws-sdk/middleware-user-agent": "3.496.0", + "@aws-sdk/region-config-resolver": "3.496.0", + "@aws-sdk/types": "3.496.0", + "@aws-sdk/util-endpoints": "3.496.0", + "@aws-sdk/util-user-agent-browser": "3.496.0", + "@aws-sdk/util-user-agent-node": "3.496.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/types": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.496.0.tgz", + "integrity": "sha512-umkGadK4QuNQaMoDICMm7NKRI/mYSXiyPjcn3d53BhsuArYU/52CebGQKdt4At7SwwsiVJZw9RNBHyN5Mm0HVw==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-endpoints": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.496.0.tgz", + "integrity": "sha512-1QzOiWHi383ZwqSi/R2KgKCd7M+6DxkxI5acqLPm8mvDRDP2jRjrnVaC0g9/tlttWousGEemDUWStwrD2mVYSw==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "@smithy/util-endpoints": "^1.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.496.0.tgz", + "integrity": "sha512-4j2spN+h0I0qfSMsGvJXTfQBu1e18rPdekKvzsGJxhaAE1tNgUfUT4nbvc5uVn0sNjZmirskmJ3kfbzVOrqIFg==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/types": "^2.9.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.496.0.tgz", + "integrity": "sha512-h0Ax0jlDc7UIo3KoSI4C4tVLBFoiAdx3+DhTVfgLS7x93d41dMlziPoBX2RgdcFn37qnzw6AQKTVTMwDbRCGpg==", + "dependencies": { + "@aws-sdk/types": "3.496.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cloudwatch-logs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@aws-sdk/client-secrets-manager": { "version": "3.485.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.485.0.tgz", @@ -1064,6 +1540,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -1078,6 +1555,7 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -1086,6 +1564,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1108,6 +1587,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1123,6 +1603,7 @@ "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" }, @@ -1138,17 +1619,20 @@ "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/@eslint/eslintrc/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==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/@eslint/js": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -1199,6 +1683,14 @@ "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" }, + "node_modules/@fastify/etag": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fastify/etag/-/etag-5.1.0.tgz", + "integrity": "sha512-j/huE8baxgF22idzY35a579b6uP+9ykE9Jt02xY4ZApELNr2KGZmQOKTQsZS94TfKMLfPHwkoM8FfZRq8OZDXg==", + "dependencies": { + "fastify-plugin": "^4.0.0" + } + }, "node_modules/@fastify/fast-json-stringify-compiler": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", @@ -1319,6 +1811,7 @@ "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", @@ -1332,6 +1825,7 @@ "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" }, @@ -1347,12 +1841,14 @@ "node_modules/@humanwhocodes/config-array/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==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, "engines": { "node": ">=12.22" }, @@ -1364,7 +1860,8 @@ "node_modules/@humanwhocodes/object-schema": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true }, "node_modules/@ioredis/commands": { "version": "1.2.0", @@ -1665,6 +2162,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1677,6 +2175,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "engines": { "node": ">= 8" } @@ -1685,6 +2184,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -2421,6 +2921,28 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/@serdnam/pino-cloudwatch-transport": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@serdnam/pino-cloudwatch-transport/-/pino-cloudwatch-transport-1.0.4.tgz", + "integrity": "sha512-0wtILlFlO/qTFANM1oEMZLKa9REo+mluHN0VTDaOMh15H9Puc+qU4z4jAoZqggFz9Fw9EGG4c+UHpMduZ1EzeQ==", + "dependencies": { + "@aws-sdk/client-cloudwatch-logs": "^3.52.0", + "p-throttle": "^5.0.0", + "pino-abstract-transport": "^0.5.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@serdnam/pino-cloudwatch-transport/node_modules/pino-abstract-transport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", + "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", + "dependencies": { + "duplexify": "^4.1.2", + "split2": "^4.0.0" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2479,11 +3001,11 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.16.tgz", - "integrity": "sha512-4foO7738k8kM9flMHu3VLabqu7nPgvIj8TB909S0CnKx0YZz/dcDH3pZ/4JHdatfxlZdKF1JWOYCw9+v3HVVsw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.1.tgz", + "integrity": "sha512-1+qdrUqLhaALYL0iOcN43EP6yAXXQ2wWZ6taf4S2pNGowmOc5gx+iMQv+E42JizNJjB0+gEadOXeV1Bf7JWL1Q==", "dependencies": { - "@smithy/types": "^2.8.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2491,14 +3013,14 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "2.0.23", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.23.tgz", - "integrity": "sha512-XakUqgtP2YY8Mi+Nlif5BiqJgWdvfxJafSpOSQeCOMizu+PUhE4fBQSy6xFcR+eInrwVadaABNxoJyGUMn15ew==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.1.tgz", + "integrity": "sha512-lxfLDpZm+AWAHPFZps5JfDoO9Ux1764fOgvRUBpHIO8HWHcSN1dkgsago1qLRVgm1BZ8RCm8cgv99QvtaOWIhw==", "dependencies": { - "@smithy/node-config-provider": "^2.1.9", - "@smithy/types": "^2.8.0", - "@smithy/util-config-provider": "^2.1.0", - "@smithy/util-middleware": "^2.0.9", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2506,17 +3028,17 @@ } }, "node_modules/@smithy/core": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.2.2.tgz", - "integrity": "sha512-uLjrskLT+mWb0emTR5QaiAIxVEU7ndpptDaVDrTwwhD+RjvHhjIiGQ3YL5jKk1a5VSDQUA2RGkXvJ6XKRcz6Dg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.1.tgz", + "integrity": "sha512-tf+NIu9FkOh312b6M9G4D68is4Xr7qptzaZGZUREELF8ysE1yLKphqt7nsomjKZVwW7WE5pDDex9idowNGRQ/Q==", "dependencies": { - "@smithy/middleware-endpoint": "^2.3.0", - "@smithy/middleware-retry": "^2.0.26", - "@smithy/middleware-serde": "^2.0.16", - "@smithy/protocol-http": "^3.0.12", - "@smithy/smithy-client": "^2.2.1", - "@smithy/types": "^2.8.0", - "@smithy/util-middleware": "^2.0.9", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2524,14 +3046,14 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.1.5.tgz", - "integrity": "sha512-VfvE6Wg1MUWwpTZFBnUD7zxvPhLY8jlHCzu6bCjlIYoWgXCDzZAML76IlZUEf45nib3rjehnFgg0s1rgsuN/bg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.1.tgz", + "integrity": "sha512-7XHjZUxmZYnONheVQL7j5zvZXga+EWNgwEAP6OPZTi7l8J4JTeNh9aIOfE5fKHZ/ee2IeNOh54ZrSna+Vc6TFA==", "dependencies": { - "@smithy/node-config-provider": "^2.1.9", - "@smithy/property-provider": "^2.0.17", - "@smithy/types": "^2.8.0", - "@smithy/url-parser": "^2.0.16", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2539,36 +3061,87 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.16.tgz", - "integrity": "sha512-umYh5pdCE9GHgiMAH49zu9wXWZKNHHdKPm/lK22WYISTjqu29SepmpWNmPiBLy/yUu4HFEGJHIFrDWhbDlApaw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.1.tgz", + "integrity": "sha512-E8KYBxBIuU4c+zrpR22VsVrOPoEDzk35bQR3E+xm4k6Pa6JqzkDOdMyf9Atac5GPNKHJBdVaQ4JtjdWX2rl/nw==", "dependencies": { "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.8.0", - "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", "tslib": "^2.5.0" } }, - "node_modules/@smithy/fetch-http-handler": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.3.2.tgz", - "integrity": "sha512-O9R/OlnAOTsnysuSDjt0v2q6DcSvCz5cCFC/CFAWWcLyBwJDeFyGTCTszgpQTb19+Fi8uRwZE5/3ziAQBFeDMQ==", + "node_modules/@smithy/eventstream-serde-browser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.1.1.tgz", + "integrity": "sha512-JvEdCmGlZUay5VtlT8/kdR6FlvqTDUiJecMjXsBb0+k1H/qc9ME5n2XKPo8q/MZwEIA1GmGgYMokKGjVvMiDow==", "dependencies": { - "@smithy/protocol-http": "^3.0.12", - "@smithy/querystring-builder": "^2.0.16", - "@smithy/types": "^2.8.0", - "@smithy/util-base64": "^2.0.1", + "@smithy/eventstream-serde-universal": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.1.1.tgz", + "integrity": "sha512-EqNqXYp3+dk//NmW3NAgQr9bEQ7fsu/CcxQmTiq07JlaIcne/CBWpMZETyXm9w5LXkhduBsdXdlMscfDUDn2fA==", + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.1.1.tgz", + "integrity": "sha512-LF882q/aFidFNDX7uROAGxq3H0B7rjyPkV6QDn6/KDQ+CG7AFkRccjxRf1xqajq/Pe4bMGGr+VKAaoF6lELIQw==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.1.1.tgz", + "integrity": "sha512-LR0mMT+XIYTxk4k2fIxEA1BPtW3685QlqufUEUAX1AJcfFfxNDKEvuCRZbO8ntJb10DrIFVJR9vb0MhDCi0sAQ==", + "dependencies": { + "@smithy/eventstream-codec": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.1.tgz", + "integrity": "sha512-VYGLinPsFqH68lxfRhjQaSkjXM7JysUOJDTNjHBuN/ykyRb2f1gyavN9+VhhPTWCy32L4yZ2fdhpCs/nStEicg==", + "dependencies": { + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", "tslib": "^2.5.0" } }, "node_modules/@smithy/hash-node": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.18.tgz", - "integrity": "sha512-gN2JFvAgnZCyDN9rJgcejfpK0uPPJrSortVVVVWsru9whS7eQey6+gj2eM5ln2i6rHNntIXzal1Fm9XOPuoaKA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.1.tgz", + "integrity": "sha512-Qhoq0N8f2OtCnvUpCf+g1vSyhYQrZjhSwvJ9qvR8BUGOtTXiyv2x1OD2e6jVGmlpC4E4ax1USHoyGfV9JFsACg==", "dependencies": { - "@smithy/types": "^2.8.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", + "@smithy/types": "^2.9.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2576,18 +3149,18 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.16.tgz", - "integrity": "sha512-apEHakT/kmpNo1VFHP4W/cjfeP9U0x5qvfsLJubgp7UM/gq4qYp0GbqdE7QhsjUaYvEnrftRqs7+YrtWreV0wA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.1.tgz", + "integrity": "sha512-7WTgnKw+VPg8fxu2v9AlNOQ5yaz6RA54zOVB4f6vQuR0xFKd+RzlCpt0WidYTsye7F+FYDIaS/RnJW4pxjNInw==", "dependencies": { - "@smithy/types": "^2.8.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" } }, "node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz", + "integrity": "sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==", "dependencies": { "tslib": "^2.5.0" }, @@ -2596,12 +3169,12 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.18.tgz", - "integrity": "sha512-ZJ9uKPTfxYheTKSKYB+GCvcj+izw9WGzRLhjn8n254q0jWLojUzn7Vw0l4R/Gq7Wdpf/qmk/ptD+6CCXHNVCaw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.1.tgz", + "integrity": "sha512-rSr9ezUl9qMgiJR0UVtVOGEZElMdGFyl8FzWEF5iEKTlcWxGr2wTqGfDwtH3LAB7h+FPkxqv4ZU4cpuCN9Kf/g==", "dependencies": { - "@smithy/protocol-http": "^3.0.12", - "@smithy/types": "^2.8.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2609,16 +3182,16 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.3.0.tgz", - "integrity": "sha512-VsOAG2YQ8ykjSmKO+CIXdJBIWFo6AAvG6Iw95BakBTqk66/4BI7XyqLevoNSq/lZ6NgZv24sLmrcIN+fLDWBCg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.1.tgz", + "integrity": "sha512-XPZTb1E2Oav60Ven3n2PFx+rX9EDsU/jSTA8VDamt7FXks67ekjPY/XrmmPDQaFJOTUHJNKjd8+kZxVO5Ael4Q==", "dependencies": { - "@smithy/middleware-serde": "^2.0.16", - "@smithy/node-config-provider": "^2.1.9", - "@smithy/shared-ini-file-loader": "^2.2.8", - "@smithy/types": "^2.8.0", - "@smithy/url-parser": "^2.0.16", - "@smithy/util-middleware": "^2.0.9", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2626,17 +3199,17 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.26.tgz", - "integrity": "sha512-Qzpxo0U5jfNiq9iD38U3e2bheXwvTEX4eue9xruIvEgh+UKq6dKuGqcB66oBDV7TD/mfoJi9Q/VmaiqwWbEp7A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.1.tgz", + "integrity": "sha512-eMIHOBTXro6JZ+WWzZWd/8fS8ht5nS5KDQjzhNMHNRcG5FkNTqcKpYhw7TETMYzbLfhO5FYghHy1vqDWM4FLDA==", "dependencies": { - "@smithy/node-config-provider": "^2.1.9", - "@smithy/protocol-http": "^3.0.12", - "@smithy/service-error-classification": "^2.0.9", - "@smithy/smithy-client": "^2.2.1", - "@smithy/types": "^2.8.0", - "@smithy/util-middleware": "^2.0.9", - "@smithy/util-retry": "^2.0.9", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/service-error-classification": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", "tslib": "^2.5.0", "uuid": "^8.3.2" }, @@ -2653,11 +3226,11 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.16.tgz", - "integrity": "sha512-5EAd4t30pcc4M8TSSGq7q/x5IKrxfXR5+SrU4bgxNy7RPHQo2PSWBUco9C+D9Tfqp/JZvprRpK42dnupZafk2g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.1.1.tgz", + "integrity": "sha512-D8Gq0aQBeE1pxf3cjWVkRr2W54t+cdM2zx78tNrVhqrDykRA7asq8yVJij1u5NDtKzKqzBSPYh7iW0svUKg76g==", "dependencies": { - "@smithy/types": "^2.8.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2665,11 +3238,11 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.10.tgz", - "integrity": "sha512-I2rbxctNq9FAPPEcuA1ntZxkTKOPQFy7YBPOaD/MLg1zCvzv21CoNxR0py6J8ZVC35l4qE4nhxB0f7TF5/+Ldw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.1.tgz", + "integrity": "sha512-KPJhRlhsl8CjgGXK/DoDcrFGfAqoqvuwlbxy+uOO4g2Azn1dhH+GVfC3RAp+6PoL5PWPb+vt6Z23FP+Mr6qeCw==", "dependencies": { - "@smithy/types": "^2.8.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2677,13 +3250,13 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.9.tgz", - "integrity": "sha512-tUyW/9xrRy+s7RXkmQhgYkAPMpTIF8izK4orhHjNFEKR3QZiOCbWB546Y8iB/Fpbm3O9+q0Af9rpywLKJOwtaQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.1.tgz", + "integrity": "sha512-epzK3x1xNxA9oJgHQ5nz+2j6DsJKdHfieb+YgJ7ATWxzNcB7Hc+Uya2TUck5MicOPhDV8HZImND7ZOecVr+OWg==", "dependencies": { - "@smithy/property-provider": "^2.0.17", - "@smithy/shared-ini-file-loader": "^2.2.8", - "@smithy/types": "^2.8.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2691,14 +3264,14 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.2.2.tgz", - "integrity": "sha512-XO58TO/Eul/IBQKFKaaBtXJi0ItEQQCT+NI4IiKHCY/4KtqaUT6y/wC1EvDqlA9cP7Dyjdj7FdPs4DyynH3u7g==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.3.1.tgz", + "integrity": "sha512-gLA8qK2nL9J0Rk/WEZSvgin4AppvuCYRYg61dcUo/uKxvMZsMInL5I5ZdJTogOvdfVug3N2dgI5ffcUfS4S9PA==", "dependencies": { - "@smithy/abort-controller": "^2.0.16", - "@smithy/protocol-http": "^3.0.12", - "@smithy/querystring-builder": "^2.0.16", - "@smithy/types": "^2.8.0", + "@smithy/abort-controller": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/querystring-builder": "^2.1.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2706,11 +3279,11 @@ } }, "node_modules/@smithy/property-provider": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.17.tgz", - "integrity": "sha512-+VkeZbVu7qtQ2DjI48Qwaf9fPOr3gZIwxQpuLJgRRSkWsdSvmaTCxI3gzRFKePB63Ts9r4yjn4HkxSCSkdWmcQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.1.tgz", + "integrity": "sha512-FX7JhhD/o5HwSwg6GLK9zxrMUrGnb3PzNBrcthqHKBc3dH0UfgEAU24xnJ8F0uow5mj17UeBEOI6o3CF2k7Mhw==", "dependencies": { - "@smithy/types": "^2.8.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2718,11 +3291,11 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.12.tgz", - "integrity": "sha512-Xz4iaqLiaBfbQpB9Hgi3VcZYbP7xRDXYhd8XWChh4v94uw7qwmvlxdU5yxzfm6ACJM66phHrTbS5TVvj5uQ72w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.1.1.tgz", + "integrity": "sha512-6ZRTSsaXuSL9++qEwH851hJjUA0OgXdQFCs+VDw4tGH256jQ3TjYY/i34N4vd24RV3nrjNsgd1yhb57uMoKbzQ==", "dependencies": { - "@smithy/types": "^2.8.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2730,12 +3303,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.16.tgz", - "integrity": "sha512-Q/GsJT0C0mijXMRs7YhZLLCP5FcuC4797lYjKQkME5CZohnLC4bEhylAd2QcD3gbMKNjCw8+T2I27WKiV/wToA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.1.tgz", + "integrity": "sha512-C/ko/CeEa8jdYE4gt6nHO5XDrlSJ3vdCG0ZAc6nD5ZIE7LBp0jCx4qoqp7eoutBu7VrGMXERSRoPqwi1WjCPbg==", "dependencies": { - "@smithy/types": "^2.8.0", - "@smithy/util-uri-escape": "^2.0.0", + "@smithy/types": "^2.9.1", + "@smithy/util-uri-escape": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2743,11 +3316,11 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.16.tgz", - "integrity": "sha512-c4ueAuL6BDYKWpkubjrQthZKoC3L5kql5O++ovekNxiexRXTlLIVlCR4q3KziOktLIw66EU9SQljPXd/oN6Okg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.1.tgz", + "integrity": "sha512-H4+6jKGVhG1W4CIxfBaSsbm98lOO88tpDWmZLgkJpt8Zkk/+uG0FmmqMuCAc3HNM2ZDV+JbErxr0l5BcuIf/XQ==", "dependencies": { - "@smithy/types": "^2.8.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2755,22 +3328,22 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.9.tgz", - "integrity": "sha512-0K+8GvtwI7VkGmmInPydM2XZyBfIqLIbfR7mDQ+oPiz8mIinuHbV6sxOLdvX1Jv/myk7XTK9orgt3tuEpBu/zg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.1.tgz", + "integrity": "sha512-txEdZxPUgM1PwGvDvHzqhXisrc5LlRWYCf2yyHfvITWioAKat7srQvpjMAvgzf0t6t7j8yHrryXU9xt7RZqFpw==", "dependencies": { - "@smithy/types": "^2.8.0" + "@smithy/types": "^2.9.1" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.8.tgz", - "integrity": "sha512-E62byatbwSWrtq9RJ7xN40tqrRKDGrEL4EluyNpaIDvfvet06a/QC58oHw2FgVaEgkj0tXZPjZaKrhPfpoU0qw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.1.tgz", + "integrity": "sha512-2E2kh24igmIznHLB6H05Na4OgIEilRu0oQpYXo3LCNRrawHAcfDKq9004zJs+sAMt2X5AbY87CUCJ7IpqpSgdw==", "dependencies": { - "@smithy/types": "^2.8.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2778,17 +3351,17 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.19.tgz", - "integrity": "sha512-nwc3JihdM+kcJjtORv/n7qRHN2Kfh7S2RJI2qr8pz9UcY5TD8rSCRGQ0g81HgyS3jZ5X9U/L4p014P3FonBPhg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.1.tgz", + "integrity": "sha512-Hb7xub0NHuvvQD3YwDSdanBmYukoEkhqBjqoxo+bSdC0ryV9cTfgmNjuAQhTPYB6yeU7hTR+sPRiFMlxqv6kmg==", "dependencies": { - "@smithy/eventstream-codec": "^2.0.16", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.8.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.9", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", + "@smithy/eventstream-codec": "^2.1.1", + "@smithy/is-array-buffer": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-uri-escape": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2796,15 +3369,15 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.2.1.tgz", - "integrity": "sha512-SpD7FLK92XV2fon2hMotaNDa2w5VAy5/uVjP9WFmjGSgWM8pTPVkHcDl1yFs5Z8LYbij0FSz+DbCBK6i+uXXUA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.3.1.tgz", + "integrity": "sha512-YsTdU8xVD64r2pLEwmltrNvZV6XIAC50LN6ivDopdt+YiF/jGH6PY9zUOu0CXD/d8GMB8gbhnpPsdrjAXHS9QA==", "dependencies": { - "@smithy/middleware-endpoint": "^2.3.0", - "@smithy/middleware-stack": "^2.0.10", - "@smithy/protocol-http": "^3.0.12", - "@smithy/types": "^2.8.0", - "@smithy/util-stream": "^2.0.24", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-stream": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2812,9 +3385,9 @@ } }, "node_modules/@smithy/types": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.8.0.tgz", - "integrity": "sha512-h9sz24cFgt/W1Re22OlhQKmUZkNh244ApgRsUDYinqF8R+QgcsBIX344u2j61TPshsTz3CvL6HYU1DnQdsSrHA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.9.1.tgz", + "integrity": "sha512-vjXlKNXyprDYDuJ7UW5iobdmyDm6g8dDG+BFUncAg/3XJaN45Gy5RWWWUVgrzIK7S4R1KWgIX5LeJcfvSI24bw==", "dependencies": { "tslib": "^2.5.0" }, @@ -2823,21 +3396,21 @@ } }, "node_modules/@smithy/url-parser": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.16.tgz", - "integrity": "sha512-Wfz5WqAoRT91TjRy1JeLR0fXtkIXHGsMbgzKFTx7E68SrZ55TB8xoG+vm11Ru4gheFTMXjAjwAxv1jQdC+pAQA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.1.tgz", + "integrity": "sha512-qC9Bv8f/vvFIEkHsiNrUKYNl8uKQnn4BdhXl7VzQRP774AwIjiSMMwkbT+L7Fk8W8rzYVifzJNYxv1HwvfBo3Q==", "dependencies": { - "@smithy/querystring-parser": "^2.0.16", - "@smithy/types": "^2.8.0", + "@smithy/querystring-parser": "^2.1.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" } }, "node_modules/@smithy/util-base64": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", - "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.1.1.tgz", + "integrity": "sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g==", "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-buffer-from": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2845,17 +3418,17 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.1.tgz", - "integrity": "sha512-NXYp3ttgUlwkaug4bjBzJ5+yIbUbUx8VsSLuHZROQpoik+gRkIBeEG9MPVYfvPNpuXb/puqodeeUXcKFe7BLOQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz", + "integrity": "sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==", "dependencies": { "tslib": "^2.5.0" } }, "node_modules/@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz", + "integrity": "sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==", "dependencies": { "tslib": "^2.5.0" }, @@ -2864,11 +3437,11 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz", + "integrity": "sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==", "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", + "@smithy/is-array-buffer": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2876,9 +3449,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.1.0.tgz", - "integrity": "sha512-S6V0JvvhQgFSGLcJeT1CBsaTR03MM8qTuxMH9WPCCddlSo2W0V5jIHimHtIQALMLEDPGQ0ROSRr/dU0O+mxiQg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz", + "integrity": "sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==", "dependencies": { "tslib": "^2.5.0" }, @@ -2887,13 +3460,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.24", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.24.tgz", - "integrity": "sha512-TsP5mBuLgO2C21+laNG2nHYZEyUdkbGURv2tHvSuQQxLz952MegX95uwdxOY2jR2H4GoKuVRfdJq7w4eIjGYeg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.1.tgz", + "integrity": "sha512-lqLz/9aWRO6mosnXkArtRuQqqZBhNpgI65YDpww4rVQBuUT7qzKbDLG5AmnQTCiU4rOquaZO/Kt0J7q9Uic7MA==", "dependencies": { - "@smithy/property-provider": "^2.0.17", - "@smithy/smithy-client": "^2.2.1", - "@smithy/types": "^2.8.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", "bowser": "^2.11.0", "tslib": "^2.5.0" }, @@ -2902,16 +3475,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.32", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.32.tgz", - "integrity": "sha512-d0S33dXA2cq1NyorVMroMrEtqKMr3MlyLITcfTBf9pXiigYiPMOtbSI7czHIfDbuVuM89Cg0urAgpt73QV9mPQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.1.1.tgz", + "integrity": "sha512-tYVrc+w+jSBfBd267KDnvSGOh4NMz+wVH7v4CClDbkdPfnjvImBZsOURncT5jsFwR9KCuDyPoSZq4Pa6+eCUrA==", "dependencies": { - "@smithy/config-resolver": "^2.0.23", - "@smithy/credential-provider-imds": "^2.1.5", - "@smithy/node-config-provider": "^2.1.9", - "@smithy/property-provider": "^2.0.17", - "@smithy/smithy-client": "^2.2.1", - "@smithy/types": "^2.8.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2919,12 +3492,12 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.0.8.tgz", - "integrity": "sha512-l8zVuyZZ61IzZBYp5NWvsAhbaAjYkt0xg9R4xUASkg5SEeTT2meHOJwJHctKMFUXe4QZbn9fR2MaBYjP2119+w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.1.tgz", + "integrity": "sha512-sI4d9rjoaekSGEtq3xSb2nMjHMx8QXcz2cexnVyRWsy4yQ9z3kbDpX+7fN0jnbdOp0b3KSTZJZ2Yb92JWSanLw==", "dependencies": { - "@smithy/node-config-provider": "^2.1.9", - "@smithy/types": "^2.8.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2932,9 +3505,9 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz", + "integrity": "sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==", "dependencies": { "tslib": "^2.5.0" }, @@ -2943,11 +3516,11 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.9.tgz", - "integrity": "sha512-PnCnBJ07noMX1lMDTEefmxSlusWJUiLfrme++MfK5TD0xz8NYmakgoXy5zkF/16zKGmiwOeKAztWT/Vjk1KRIQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.1.tgz", + "integrity": "sha512-mKNrk8oz5zqkNcbcgAAepeJbmfUW6ogrT2Z2gDbIUzVzNAHKJQTYmH9jcy0jbWb+m7ubrvXKb6uMjkSgAqqsFA==", "dependencies": { - "@smithy/types": "^2.8.0", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2955,12 +3528,12 @@ } }, "node_modules/@smithy/util-retry": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.9.tgz", - "integrity": "sha512-46BFWe9RqB6g7f4mxm3W3HlqknqQQmWHKlhoqSFZuGNuiDU5KqmpebMbvC3tjTlUkqn4xa2Z7s3Hwb0HNs5scw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.1.tgz", + "integrity": "sha512-Mg+xxWPTeSPrthpC5WAamJ6PW4Kbo01Fm7lWM1jmGRvmrRdsd3192Gz2fBXAMURyXpaNxyZf6Hr/nQ4q70oVEA==", "dependencies": { - "@smithy/service-error-classification": "^2.0.9", - "@smithy/types": "^2.8.0", + "@smithy/service-error-classification": "^2.1.1", + "@smithy/types": "^2.9.1", "tslib": "^2.5.0" }, "engines": { @@ -2968,17 +3541,17 @@ } }, "node_modules/@smithy/util-stream": { - "version": "2.0.24", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.24.tgz", - "integrity": "sha512-hRpbcRrOxDriMVmbya+Mv77VZVupxRAsfxVDKS54XuiURhdiwCUXJP0X1iJhHinuUf6n8pBF0MkG9C8VooMnWw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.1.tgz", + "integrity": "sha512-J7SMIpUYvU4DQN55KmBtvaMc7NM3CZ2iWICdcgaovtLzseVhAqFRYqloT3mh0esrFw+3VEK6nQFteFsTqZSECQ==", "dependencies": { - "@smithy/fetch-http-handler": "^2.3.2", - "@smithy/node-http-handler": "^2.2.2", - "@smithy/types": "^2.8.0", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-buffer-from": "^2.1.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -2986,9 +3559,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", + "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", "dependencies": { "tslib": "^2.5.0" }, @@ -2997,11 +3570,11 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", - "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.1.1.tgz", + "integrity": "sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A==", "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-buffer-from": "^2.1.1", "tslib": "^2.5.0" }, "engines": { @@ -3009,9 +3582,9 @@ } }, "node_modules/@swc/core": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.99.tgz", - "integrity": "sha512-8O996RfuPC4ieb4zbYMfbyCU9k4gSOpyCNnr7qBQ+o7IEmh8JCV6B8wwu+fT/Om/6Lp34KJe1IpJ/24axKS6TQ==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.107.tgz", + "integrity": "sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==", "dev": true, "hasInstallScript": true, "optional": true, @@ -3028,15 +3601,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.99", - "@swc/core-darwin-x64": "1.3.99", - "@swc/core-linux-arm64-gnu": "1.3.99", - "@swc/core-linux-arm64-musl": "1.3.99", - "@swc/core-linux-x64-gnu": "1.3.99", - "@swc/core-linux-x64-musl": "1.3.99", - "@swc/core-win32-arm64-msvc": "1.3.99", - "@swc/core-win32-ia32-msvc": "1.3.99", - "@swc/core-win32-x64-msvc": "1.3.99" + "@swc/core-darwin-arm64": "1.3.107", + "@swc/core-darwin-x64": "1.3.107", + "@swc/core-linux-arm-gnueabihf": "1.3.107", + "@swc/core-linux-arm64-gnu": "1.3.107", + "@swc/core-linux-arm64-musl": "1.3.107", + "@swc/core-linux-x64-gnu": "1.3.107", + "@swc/core-linux-x64-musl": "1.3.107", + "@swc/core-win32-arm64-msvc": "1.3.107", + "@swc/core-win32-ia32-msvc": "1.3.107", + "@swc/core-win32-x64-msvc": "1.3.107" }, "peerDependencies": { "@swc/helpers": "^0.5.0" @@ -3048,9 +3622,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.99.tgz", - "integrity": "sha512-Qj7Jct68q3ZKeuJrjPx7k8SxzWN6PqLh+VFxzA+KwLDpQDPzOlKRZwkIMzuFjLhITO4RHgSnXoDk/Syz0ZeN+Q==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.107.tgz", + "integrity": "sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==", "cpu": [ "arm64" ], @@ -3065,9 +3639,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.99.tgz", - "integrity": "sha512-wR7m9QVJjgiBu1PSOHy7s66uJPa45Kf9bZExXUL+JAa9OQxt5y+XVzr+n+F045VXQOwdGWplgPnWjgbUUHEVyw==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.107.tgz", + "integrity": "sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==", "cpu": [ "x64" ], @@ -3081,10 +3655,27 @@ "node": ">=10" } }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.107.tgz", + "integrity": "sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.99.tgz", - "integrity": "sha512-gcGv1l5t0DScEONmw5OhdVmEI/o49HCe9Ik38zzH0NtDkc+PDYaCcXU5rvfZP2qJFaAAr8cua8iJcOunOSLmnA==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.107.tgz", + "integrity": "sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==", "cpu": [ "arm64" ], @@ -3099,9 +3690,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.99.tgz", - "integrity": "sha512-XL1/eUsTO8BiKsWq9i3iWh7H99iPO61+9HYiWVKhSavknfj4Plbn+XyajDpxsauln5o8t+BRGitymtnAWJM4UQ==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.107.tgz", + "integrity": "sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==", "cpu": [ "arm64" ], @@ -3116,9 +3707,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.99.tgz", - "integrity": "sha512-fGrXYE6DbTfGNIGQmBefYxSk3rp/1lgbD0nVg4rl4mfFRQPi7CgGhrrqSuqZ/ezXInUIgoCyvYGWFSwjLXt/Qg==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.107.tgz", + "integrity": "sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==", "cpu": [ "x64" ], @@ -3133,9 +3724,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.99.tgz", - "integrity": "sha512-kvgZp/mqf3IJ806gUOL6gN6VU15+DfzM1Zv4Udn8GqgXiUAvbQehrtruid4Snn5pZTLj4PEpSCBbxgxK1jbssA==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.107.tgz", + "integrity": "sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==", "cpu": [ "x64" ], @@ -3150,9 +3741,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.99.tgz", - "integrity": "sha512-yt8RtZ4W/QgFF+JUemOUQAkVW58cCST7mbfKFZ1v16w3pl3NcWd9OrtppFIXpbjU1rrUX2zp2R7HZZzZ2Zk/aQ==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.107.tgz", + "integrity": "sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==", "cpu": [ "arm64" ], @@ -3167,9 +3758,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.99.tgz", - "integrity": "sha512-62p5fWnOJR/rlbmbUIpQEVRconICy5KDScWVuJg1v3GPLBrmacjphyHiJC1mp6dYvvoEWCk/77c/jcQwlXrDXw==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.107.tgz", + "integrity": "sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==", "cpu": [ "ia32" ], @@ -3184,9 +3775,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.99", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.99.tgz", - "integrity": "sha512-PdppWhkoS45VGdMBxvClVgF1hVjqamtvYd82Gab1i4IV45OSym2KinoDCKE1b6j3LwBLOn2J9fvChGSgGfDCHQ==", + "version": "1.3.107", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.107.tgz", + "integrity": "sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==", "cpu": [ "x64" ], @@ -3326,12 +3917,14 @@ "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true }, "node_modules/@types/jsonwebtoken": { "version": "9.0.5", @@ -3544,10 +4137,17 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, + "node_modules/@types/resolve": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", + "integrity": "sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==", + "dev": true + }, "node_modules/@types/semver": { "version": "7.5.6", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==" + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true }, "node_modules/@types/send": { "version": "0.17.4", @@ -3600,15 +4200,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", - "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz", + "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==", + "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/type-utils": "6.13.2", - "@typescript-eslint/utils": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/type-utils": "6.20.0", + "@typescript-eslint/utils": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -3637,6 +4238,7 @@ "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" }, @@ -3652,17 +4254,19 @@ "node_modules/@typescript-eslint/eslint-plugin/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==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz", - "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", + "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==", + "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", "debug": "^4.3.4" }, "engines": { @@ -3685,6 +4289,7 @@ "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" }, @@ -3700,15 +4305,17 @@ "node_modules/@typescript-eslint/parser/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==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz", - "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2" + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -3719,12 +4326,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz", - "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz", + "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==", + "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/utils": "6.13.2", + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/utils": "6.20.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -3748,6 +4356,7 @@ "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" }, @@ -3763,12 +4372,14 @@ "node_modules/@typescript-eslint/type-utils/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==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/@typescript-eslint/types": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", - "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -3778,15 +4389,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz", - "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -3803,10 +4416,20 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -3819,22 +4442,39 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/typescript-estree/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==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz", - "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", + "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", "semver": "^7.5.4" }, "engines": { @@ -3849,11 +4489,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", - "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/types": "6.20.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -3898,7 +4539,8 @@ "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true }, "node_modules/@vitest/expect": { "version": "1.0.4", @@ -4040,6 +4682,7 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -4051,6 +4694,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -4143,6 +4787,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, @@ -4154,6 +4799,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -4323,6 +4969,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "is-array-buffer": "^3.0.1" @@ -4340,6 +4987,7 @@ "version": "3.1.7", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -4358,6 +5006,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, "engines": { "node": ">=8" } @@ -4366,6 +5015,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -4384,6 +5034,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -4401,6 +5052,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -4418,6 +5070,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.2", @@ -4639,29 +5292,6 @@ "node": ">=8" } }, - "node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -4732,6 +5362,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -4845,6 +5476,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -4875,17 +5507,6 @@ "node": ">=4" } }, - "node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/check-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", @@ -4950,31 +5571,6 @@ "node": ">=6" } }, - "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", - "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -4995,6 +5591,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -5005,7 +5602,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/color-support": { "version": "1.1.3", @@ -5048,7 +5646,8 @@ "node_modules/confusing-browser-globals": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true }, "node_modules/console-control-strings": { "version": "1.1.0", @@ -5123,6 +5722,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -5144,6 +5744,7 @@ "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, "dependencies": { "ms": "^2.1.1" } @@ -5163,7 +5764,8 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "node_modules/deepmerge": { "version": "4.3.1", @@ -5190,6 +5792,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -5275,6 +5878,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, "dependencies": { "path-type": "^4.0.0" }, @@ -5286,6 +5890,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, "dependencies": { "esutils": "^2.0.2" }, @@ -5304,10 +5909,35 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", @@ -5322,11 +5952,6 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, - "node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" - }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -5368,6 +5993,7 @@ "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "arraybuffer.prototype.slice": "^1.0.2", @@ -5420,6 +6046,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, "dependencies": { "get-intrinsic": "^1.2.2", "has-tostringtag": "^1.0.0", @@ -5433,6 +6060,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, "dependencies": { "hasown": "^2.0.0" } @@ -5441,6 +6069,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -5507,6 +6136,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { "node": ">=10" }, @@ -5518,6 +6148,7 @@ "version": "8.56.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5572,6 +6203,7 @@ "version": "15.0.0", "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, "dependencies": { "confusing-browser-globals": "^1.0.10", "object.assign": "^4.1.2", @@ -5590,6 +6222,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -5598,6 +6231,7 @@ "version": "17.1.0", "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz", "integrity": "sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig==", + "dev": true, "dependencies": { "eslint-config-airbnb-base": "^15.0.0" }, @@ -5624,6 +6258,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -5682,6 +6317,7 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, "dependencies": { "debug": "^3.2.7" }, @@ -5698,6 +6334,7 @@ "version": "2.29.1", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, "dependencies": { "array-includes": "^3.1.7", "array.prototype.findlastindex": "^1.2.3", @@ -5728,6 +6365,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, "dependencies": { "esutils": "^2.0.2" }, @@ -5739,6 +6377,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, "dependencies": { "minimist": "^1.2.0" }, @@ -5750,6 +6389,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -5758,6 +6398,7 @@ "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -5808,6 +6449,7 @@ "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -5823,6 +6465,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -5834,6 +6477,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5849,6 +6493,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -5857,6 +6502,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5872,6 +6518,7 @@ "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" }, @@ -5888,6 +6535,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -5899,6 +6547,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -5906,17 +6555,20 @@ "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/eslint/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==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/eslint/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5928,6 +6580,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -5947,6 +6600,7 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -5963,6 +6617,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -5974,6 +6629,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -5985,6 +6641,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { "node": ">=4.0" } @@ -5993,6 +6650,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6119,6 +6777,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -6133,7 +6792,8 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-json-stringify": { "version": "5.9.1", @@ -6152,7 +6812,8 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fast-querystring": { "version": "1.1.2", @@ -6241,6 +6902,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -6252,6 +6914,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6306,6 +6969,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -6321,6 +6985,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -6334,6 +6999,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6353,6 +7019,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -6366,7 +7033,8 @@ "node_modules/flatted": { "version": "3.2.9", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true }, "node_modules/follow-redirects": { "version": "1.15.4", @@ -6505,6 +7173,7 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -6522,6 +7191,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6645,6 +7315,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -6695,6 +7366,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -6725,6 +7397,7 @@ "version": "13.23.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, "dependencies": { "type-fest": "^0.20.2" }, @@ -6739,6 +7412,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, "dependencies": { "define-properties": "^1.1.3" }, @@ -6753,6 +7427,7 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -6793,7 +7468,8 @@ "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true }, "node_modules/handlebars": { "version": "4.7.8", @@ -6819,6 +7495,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7000,6 +7677,15 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -7034,6 +7720,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, "engines": { "node": ">= 4" } @@ -7048,6 +7735,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -7063,6 +7751,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "engines": { "node": ">=0.8.19" } @@ -7093,6 +7782,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, "dependencies": { "get-intrinsic": "^1.2.2", "hasown": "^2.0.0", @@ -7181,6 +7871,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.0", @@ -7199,6 +7890,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -7222,6 +7914,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -7259,6 +7952,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7273,6 +7967,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -7303,6 +7998,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -7310,21 +8006,11 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -7336,6 +8022,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } @@ -7344,6 +8031,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7358,6 +8046,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -7371,6 +8060,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -7397,6 +8087,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -7420,6 +8111,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7434,6 +8126,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -7458,21 +8151,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -7483,12 +8166,14 @@ "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/jackspeak": { "version": "2.3.6", @@ -7543,7 +8228,8 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true }, "node_modules/json-parse-better-errors": { "version": "1.0.2", @@ -7603,7 +8289,8 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "node_modules/json5": { "version": "2.2.3", @@ -7677,6 +8364,7 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -7777,6 +8465,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -7875,6 +8564,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -7938,7 +8628,8 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, "node_modules/lodash.once": { "version": "4.1.1", @@ -7951,21 +8642,6 @@ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", "dev": true }, - "node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -8077,6 +8753,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "engines": { "node": ">= 8" } @@ -8093,6 +8770,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -8105,6 +8783,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -8146,6 +8825,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, "engines": { "node": ">=6" } @@ -8273,6 +8953,19 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.2" } }, + "node_modules/mylas": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.13.tgz", + "integrity": "sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/raouldeheer" + } + }, "node_modules/mysql2": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.5.tgz", @@ -8349,7 +9042,8 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, "node_modules/negotiator": { "version": "0.6.3", @@ -8416,9 +9110,9 @@ } }, "node_modules/nodemailer": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.7.tgz", - "integrity": "sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==", + "version": "6.9.9", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", + "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==", "engines": { "node": ">=6.0.0" } @@ -8560,6 +9254,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "engines": { "node": ">= 0.4" } @@ -8568,6 +9263,7 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -8585,6 +9281,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -8598,6 +9295,7 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -8614,6 +9312,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -8625,6 +9324,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -8696,6 +9396,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -8715,6 +9416,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", @@ -8727,28 +9429,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", - "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.9.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.3.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "string-width": "^6.1.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8767,6 +9447,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -8794,6 +9475,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -8902,6 +9584,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { "node": ">=8" } @@ -8918,6 +9601,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -8961,6 +9645,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "engines": { "node": ">=8" } @@ -9276,6 +9961,18 @@ "pathe": "^1.1.0" } }, + "node_modules/plimit-lit": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", + "integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==", + "dev": true, + "dependencies": { + "queue-lit": "^1.5.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/postcss": { "version": "8.4.32", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", @@ -9414,6 +10111,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "engines": { "node": ">= 0.8.0" } @@ -9634,10 +10332,20 @@ "node": ">=0.4.x" } }, + "node_modules/queue-lit": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", + "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -9786,6 +10494,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -9826,6 +10535,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } @@ -9839,21 +10549,6 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ret": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", @@ -9982,6 +10677,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -10009,6 +10705,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1", @@ -10045,6 +10742,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", @@ -10193,6 +10891,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, "dependencies": { "define-data-property": "^1.0.1", "functions-have-names": "^1.2.3", @@ -10223,6 +10922,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -10234,6 +10934,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -10278,6 +10979,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { "node": ">=8" } @@ -10369,19 +11071,10 @@ "integrity": "sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==", "dev": true }, - "node_modules/stdin-discarder": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", - "dependencies": { - "bl": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" }, "node_modules/string_decoder": { "version": "1.3.0", @@ -10391,22 +11084,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string-width": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", - "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", @@ -10453,6 +11130,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -10469,6 +11147,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -10482,6 +11161,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -10495,6 +11175,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -10700,7 +11381,8 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/thenify": { "version": "3.3.1", @@ -10767,6 +11449,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -10835,6 +11518,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, "engines": { "node": ">=16.13.0" }, @@ -10891,6 +11575,32 @@ } } }, + "node_modules/tsc-alias": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.8.tgz", + "integrity": "sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "globby": "^11.0.4", + "mylas": "^2.1.9", + "normalize-path": "^3.0.0", + "plimit-lit": "^1.2.6" + }, + "bin": { + "tsc-alias": "dist/bin/index.js" + } + }, + "node_modules/tsc-alias/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/tsconfck": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.2.tgz", @@ -11408,15 +12118,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/tsup/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, "node_modules/tsup/node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -11536,6 +12237,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -11556,6 +12258,7 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, "engines": { "node": ">=10" }, @@ -11579,6 +12282,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1", @@ -11592,6 +12296,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "for-each": "^0.3.3", @@ -11609,6 +12314,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", @@ -11627,6 +12333,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "for-each": "^0.3.3", @@ -11640,6 +12347,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11675,6 +12383,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -12528,6 +13237,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -12542,6 +13252,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", diff --git a/backend/package.json b/backend/package.json index 54e66f6af..4ec33aa45 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,12 +2,12 @@ "name": "backend", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "./dist/main.mjs", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine", "dev:docker": "nodemon", - "build": "rimraf dist && tsup", + "build": "tsup", "start": "node dist/main.mjs", "type:check": "tsc --noEmit", "lint:fix": "eslint --fix --ext js,ts ./src", @@ -44,11 +44,13 @@ "@types/pg": "^8.10.9", "@types/picomatch": "^2.3.3", "@types/prompt-sync": "^4.2.3", + "@types/resolve": "^1.20.6", "@types/uuid": "^9.0.7", - "@typescript-eslint/eslint-plugin": "^6.13.2", - "@typescript-eslint/parser": "^6.13.2", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "eslint": "^8.56.0", "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-airbnb-typescript": "^17.1.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", @@ -59,6 +61,7 @@ "prompt-sync": "^4.2.0", "rimraf": "^5.0.5", "ts-node": "^10.9.1", + "tsc-alias": "^1.8.8", "tsconfig-paths": "^4.2.0", "tsup": "^8.0.1", "tsx": "^4.4.0", @@ -71,6 +74,7 @@ "@casl/ability": "^6.5.0", "@fastify/cookie": "^9.2.0", "@fastify/cors": "^8.4.1", + "@fastify/etag": "^5.1.0", "@fastify/formbody": "^7.4.0", "@fastify/helmet": "^11.1.1", "@fastify/passport": "^2.4.0", @@ -81,6 +85,7 @@ "@node-saml/passport-saml": "^4.0.4", "@octokit/rest": "^20.0.2", "@octokit/webhooks-types": "^7.3.1", + "@serdnam/pino-cloudwatch-transport": "^1.0.4", "@sindresorhus/slugify": "^2.2.1", "@ucast/mongo2js": "^1.3.4", "ajv": "^8.12.0", @@ -91,7 +96,6 @@ "bcrypt": "^5.1.1", "bullmq": "^5.1.1", "dotenv": "^16.3.1", - "eslint-config-airbnb-typescript": "^17.1.0", "fastify": "^4.24.3", "fastify-plugin": "^4.5.1", "handlebars": "^4.7.8", @@ -105,7 +109,7 @@ "mysql2": "^3.6.5", "nanoid": "^5.0.4", "node-cache": "^5.1.2", - "nodemailer": "^6.9.7", + "nodemailer": "^6.9.9", "ora": "^7.0.1", "passport-github": "^1.1.0", "passport-gitlab2": "^5.0.0", @@ -122,4 +126,4 @@ "zod": "^3.22.4", "zod-to-json-schema": "^3.22.0" } -} +} \ No newline at end of file diff --git a/backend/scripts/generate-schema-types.ts b/backend/scripts/generate-schema-types.ts index 5d51a5164..68330613c 100644 --- a/backend/scripts/generate-schema-types.ts +++ b/backend/scripts/generate-schema-types.ts @@ -3,13 +3,9 @@ import dotenv from "dotenv"; import path from "path"; import knex from "knex"; import { writeFileSync } from "fs"; -import promptSync from "prompt-sync"; - -const prompt = promptSync({ sigint: true }); dotenv.config({ - path: path.join(__dirname, "../.env"), - debug: true + path: path.join(__dirname, "../../.env.migration") }); const db = knex({ @@ -94,17 +90,7 @@ const main = async () => { .orderBy("table_name") ).filter((el) => !el.tableName.includes("_migrations")); - console.log("Select a table to generate schema"); - console.table(tables); - console.log("all: all tables"); - const selectedTables = prompt("Type table numbers comma seperated: "); - const tableNumbers = - selectedTables !== "all" ? selectedTables.split(",").map((el) => Number(el)) : []; - for (let i = 0; i < tables.length; i += 1) { - // skip if not desired table - if (selectedTables !== "all" && !tableNumbers.includes(i)) continue; - const { tableName } = tables[i]; const columns = await db(tableName).columnInfo(); const columnNames = Object.keys(columns); @@ -124,16 +110,16 @@ const main = async () => { if (colInfo.nullable) { ztype = ztype.concat(".nullable().optional()"); } - schema = schema.concat(`${!schema ? "\n" : ""} ${columnName}: ${ztype},\n`); + schema = schema.concat( + `${!schema ? "\n" : ""} ${columnName}: ${ztype}${colNum === columnNames.length - 1 ? "" : ","}\n` + ); } const dashcase = tableName.split("_").join("-"); const pascalCase = tableName .split("_") - .reduce( - (prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`, - "" - ); + .reduce((prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`, ""); + writeFileSync( path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`), `// Code generated by automation script, DO NOT EDIT. @@ -152,15 +138,6 @@ export type T${pascalCase}Insert = Omit; export type T${pascalCase}Update = Partial>; ` ); - - // const file = readFileSync(path.join(__dirname, "../src/db/schemas/index.ts"), "utf8"); - // if (!file.includes(`export * from "./${dashcase};"`)) { - // appendFileSync( - // path.join(__dirname, "../src/db/schemas/index.ts"), - // `\nexport * from "./${dashcase}";`, - // "utf8" - // ); - // } } process.exit(0); diff --git a/backend/src/@types/fastify-zod.d.ts b/backend/src/@types/fastify-zod.d.ts index cff119c2f..393579391 100644 --- a/backend/src/@types/fastify-zod.d.ts +++ b/backend/src/@types/fastify-zod.d.ts @@ -1,9 +1,4 @@ -import { - FastifyInstance, - RawReplyDefaultExpression, - RawRequestDefaultExpression, - RawServerDefault -} from "fastify"; +import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify"; import { Logger } from "pino"; import { ZodTypeProvider } from "@app/server/plugins/fastify-zod"; diff --git a/backend/src/@types/fastify.d.ts b/backend/src/@types/fastify.d.ts index af6bf8b93..ef850bb8c 100644 --- a/backend/src/@types/fastify.d.ts +++ b/backend/src/@types/fastify.d.ts @@ -51,6 +51,7 @@ declare module "fastify" { // used for mfa session authentication mfa: { userId: string; + orgId?: string; user: TUsers; }; // identity injection. depending on which kinda of token the information is filled in auth @@ -58,6 +59,7 @@ declare module "fastify" { permission: { type: ActorType; id: string; + orgId?: string; }; // passport data passportUser: { diff --git a/backend/src/@types/knex.d.ts b/backend/src/@types/knex.d.ts index 05ce1e59d..5bf563810 100644 --- a/backend/src/@types/knex.d.ts +++ b/backend/src/@types/knex.d.ts @@ -177,11 +177,7 @@ declare module "knex/types/tables" { TUserEncryptionKeysInsert, TUserEncryptionKeysUpdate >; - [TableName.AuthTokens]: Knex.CompositeTableType< - TAuthTokens, - TAuthTokensInsert, - TAuthTokensUpdate - >; + [TableName.AuthTokens]: Knex.CompositeTableType; [TableName.AuthTokenSession]: Knex.CompositeTableType< TAuthTokenSessions, TAuthTokenSessionsInsert, @@ -192,32 +188,16 @@ declare module "knex/types/tables" { TBackupPrivateKeyInsert, TBackupPrivateKeyUpdate >; - [TableName.Organization]: Knex.CompositeTableType< - TOrganizations, - TOrganizationsInsert, - TOrganizationsUpdate - >; - [TableName.OrgMembership]: Knex.CompositeTableType< - TOrgMemberships, - TOrgMembershipsInsert, - TOrgMembershipsUpdate - >; + [TableName.Organization]: Knex.CompositeTableType; + [TableName.OrgMembership]: Knex.CompositeTableType; [TableName.OrgRoles]: Knex.CompositeTableType; [TableName.IncidentContact]: Knex.CompositeTableType< TIncidentContacts, TIncidentContactsInsert, TIncidentContactsUpdate >; - [TableName.UserAction]: Knex.CompositeTableType< - TUserActions, - TUserActionsInsert, - TUserActionsUpdate - >; - [TableName.SuperAdmin]: Knex.CompositeTableType< - TSuperAdmin, - TSuperAdminInsert, - TSuperAdminUpdate - >; + [TableName.UserAction]: Knex.CompositeTableType; + [TableName.SuperAdmin]: Knex.CompositeTableType; [TableName.ApiKey]: Knex.CompositeTableType; [TableName.Project]: Knex.CompositeTableType; [TableName.ProjectMembership]: Knex.CompositeTableType< @@ -230,73 +210,33 @@ declare module "knex/types/tables" { TProjectEnvironmentsInsert, TProjectEnvironmentsUpdate >; - [TableName.ProjectBot]: Knex.CompositeTableType< - TProjectBots, - TProjectBotsInsert, - TProjectBotsUpdate - >; - [TableName.ProjectRoles]: Knex.CompositeTableType< - TProjectRoles, - TProjectRolesInsert, - TProjectRolesUpdate - >; - [TableName.ProjectKeys]: Knex.CompositeTableType< - TProjectKeys, - TProjectKeysInsert, - TProjectKeysUpdate - >; + [TableName.ProjectBot]: Knex.CompositeTableType; + [TableName.ProjectRoles]: Knex.CompositeTableType; + [TableName.ProjectKeys]: Knex.CompositeTableType; [TableName.Secret]: Knex.CompositeTableType; [TableName.SecretBlindIndex]: Knex.CompositeTableType< TSecretBlindIndexes, TSecretBlindIndexesInsert, TSecretBlindIndexesUpdate >; - [TableName.SecretVersion]: Knex.CompositeTableType< - TSecretVersions, - TSecretVersionsInsert, - TSecretVersionsUpdate - >; - [TableName.SecretFolder]: Knex.CompositeTableType< - TSecretFolders, - TSecretFoldersInsert, - TSecretFoldersUpdate - >; + [TableName.SecretVersion]: Knex.CompositeTableType; + [TableName.SecretFolder]: Knex.CompositeTableType; [TableName.SecretFolderVersion]: Knex.CompositeTableType< TSecretFolderVersions, TSecretFolderVersionsInsert, TSecretFolderVersionsUpdate >; - [TableName.SecretTag]: Knex.CompositeTableType< - TSecretTags, - TSecretTagsInsert, - TSecretTagsUpdate - >; - [TableName.SecretImport]: Knex.CompositeTableType< - TSecretImports, - TSecretImportsInsert, - TSecretImportsUpdate - >; - [TableName.Integration]: Knex.CompositeTableType< - TIntegrations, - TIntegrationsInsert, - TIntegrationsUpdate - >; + [TableName.SecretTag]: Knex.CompositeTableType; + [TableName.SecretImport]: Knex.CompositeTableType; + [TableName.Integration]: Knex.CompositeTableType; [TableName.Webhook]: Knex.CompositeTableType; - [TableName.ServiceToken]: Knex.CompositeTableType< - TServiceTokens, - TServiceTokensInsert, - TServiceTokensUpdate - >; + [TableName.ServiceToken]: Knex.CompositeTableType; [TableName.IntegrationAuth]: Knex.CompositeTableType< TIntegrationAuths, TIntegrationAuthsInsert, TIntegrationAuthsUpdate >; - [TableName.Identity]: Knex.CompositeTableType< - TIdentities, - TIdentitiesInsert, - TIdentitiesUpdate - >; + [TableName.Identity]: Knex.CompositeTableType; [TableName.IdentityUniversalAuth]: Knex.CompositeTableType< TIdentityUniversalAuths, TIdentityUniversalAuthsInsert, @@ -362,11 +302,7 @@ declare module "knex/types/tables" { TSecretRotationOutputsInsert, TSecretRotationOutputsUpdate >; - [TableName.Snapshot]: Knex.CompositeTableType< - TSecretSnapshots, - TSecretSnapshotsInsert, - TSecretSnapshotsUpdate - >; + [TableName.Snapshot]: Knex.CompositeTableType; [TableName.SnapshotSecret]: Knex.CompositeTableType< TSecretSnapshotSecrets, TSecretSnapshotSecretsInsert, @@ -377,11 +313,7 @@ declare module "knex/types/tables" { TSecretSnapshotFoldersInsert, TSecretSnapshotFoldersUpdate >; - [TableName.SamlConfig]: Knex.CompositeTableType< - TSamlConfigs, - TSamlConfigsInsert, - TSamlConfigsUpdate - >; + [TableName.SamlConfig]: Knex.CompositeTableType; [TableName.OrgBot]: Knex.CompositeTableType; [TableName.AuditLog]: Knex.CompositeTableType; [TableName.GitAppInstallSession]: Knex.CompositeTableType< @@ -395,11 +327,7 @@ declare module "knex/types/tables" { TSecretScanningGitRisksInsert, TSecretScanningGitRisksUpdate >; - [TableName.TrustedIps]: Knex.CompositeTableType< - TTrustedIps, - TTrustedIpsInsert, - TTrustedIpsUpdate - >; + [TableName.TrustedIps]: Knex.CompositeTableType; // Junction tables [TableName.JnSecretTag]: Knex.CompositeTableType< TSecretTagJunction, diff --git a/backend/src/db/instance.ts b/backend/src/db/instance.ts index 1eb54c136..2a321a3bc 100644 --- a/backend/src/db/instance.ts +++ b/backend/src/db/instance.ts @@ -1,10 +1,18 @@ import knex from "knex"; export type TDbClient = ReturnType; -export const initDbConnection = (dbConnectionUri: string) => { +export const initDbConnection = ({ dbConnectionUri, dbRootCert }: { dbConnectionUri: string; dbRootCert?: string }) => { const db = knex({ client: "pg", - connection: dbConnectionUri + connection: { + connectionString: dbConnectionUri, + ssl: dbRootCert + ? { + rejectUnauthorized: true, + ca: Buffer.from(dbRootCert, "base64").toString("ascii") + } + : false + } }); return db; diff --git a/backend/src/db/knexfile.ts b/backend/src/db/knexfile.ts index ec7458da6..73eb507f4 100644 --- a/backend/src/db/knexfile.ts +++ b/backend/src/db/knexfile.ts @@ -5,9 +5,9 @@ import dotenv from "dotenv"; import type { Knex } from "knex"; import path from "path"; -// Update with your config settings. +// Update with your config settings. . dotenv.config({ - path: path.join(__dirname, "../../.env"), + path: path.join(__dirname, "../../../.env.migration"), debug: true }); export default { diff --git a/backend/src/db/migrations/20231220052508_secret-version.ts b/backend/src/db/migrations/20231220052508_secret-version.ts index 0bcee295b..46e3264bf 100644 --- a/backend/src/db/migrations/20231220052508_secret-version.ts +++ b/backend/src/db/migrations/20231220052508_secret-version.ts @@ -38,12 +38,7 @@ export async function up(knex: Knex): Promise { } await createOnUpdateTrigger(knex, TableName.SecretVersion); // many to many relation between tags - await createJunctionTable( - knex, - TableName.SecretVersionTag, - TableName.SecretVersion, - TableName.SecretTag - ); + await createJunctionTable(knex, TableName.SecretVersionTag, TableName.SecretVersion, TableName.SecretTag); } export async function down(knex: Knex): Promise { diff --git a/backend/src/db/migrations/20231222172455_integration.ts b/backend/src/db/migrations/20231222172455_integration.ts index 4ef34cab1..43fd9986a 100644 --- a/backend/src/db/migrations/20231222172455_integration.ts +++ b/backend/src/db/migrations/20231222172455_integration.ts @@ -50,10 +50,7 @@ export async function up(knex: Knex): Promise { t.string("integration").notNullable(); t.jsonb("metadata"); t.uuid("integrationAuthId").notNullable(); - t.foreign("integrationAuthId") - .references("id") - .inTable(TableName.IntegrationAuth) - .onDelete("CASCADE"); + t.foreign("integrationAuthId").references("id").inTable(TableName.IntegrationAuth).onDelete("CASCADE"); t.uuid("envId").notNullable(); t.string("secretPath").defaultTo("/").notNullable(); t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE"); diff --git a/backend/src/db/migrations/20231228074908_identity-universal-auth.ts b/backend/src/db/migrations/20231228074908_identity-universal-auth.ts index 76d05db91..48d58ccd4 100644 --- a/backend/src/db/migrations/20231228074908_identity-universal-auth.ts +++ b/backend/src/db/migrations/20231228074908_identity-universal-auth.ts @@ -31,10 +31,7 @@ export async function up(knex: Knex): Promise { t.boolean("isClientSecretRevoked").defaultTo(false).notNullable(); t.timestamps(true, true, true); t.uuid("identityUAId").notNullable(); - t.foreign("identityUAId") - .references("id") - .inTable(TableName.IdentityUniversalAuth) - .onDelete("CASCADE"); + t.foreign("identityUAId").references("id").inTable(TableName.IdentityUniversalAuth).onDelete("CASCADE"); }); } await createOnUpdateTrigger(knex, TableName.IdentityUniversalAuth); diff --git a/backend/src/db/migrations/20231228075011_identity-access-token.ts b/backend/src/db/migrations/20231228075011_identity-access-token.ts index 33c5f96ef..e8e26fc70 100644 --- a/backend/src/db/migrations/20231228075011_identity-access-token.ts +++ b/backend/src/db/migrations/20231228075011_identity-access-token.ts @@ -19,7 +19,7 @@ export async function up(knex: Knex): Promise { .references("id") .inTable(TableName.IdentityUaClientSecret) .onDelete("CASCADE"); - t.uuid("identityId").notNullable(); + t.uuid("identityId").notNullable(); t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE"); t.timestamps(true, true, true); }); diff --git a/backend/src/db/migrations/20240101054849_secret-approval-policy.ts b/backend/src/db/migrations/20240101054849_secret-approval-policy.ts index 7fde0d314..3de1ce280 100644 --- a/backend/src/db/migrations/20240101054849_secret-approval-policy.ts +++ b/backend/src/db/migrations/20240101054849_secret-approval-policy.ts @@ -21,15 +21,9 @@ export async function up(knex: Knex): Promise { await knex.schema.createTable(TableName.SecretApprovalPolicyApprover, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.uuid("approverId").notNullable(); - t.foreign("approverId") - .references("id") - .inTable(TableName.ProjectMembership) - .onDelete("CASCADE"); + t.foreign("approverId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE"); t.uuid("policyId").notNullable(); - t.foreign("policyId") - .references("id") - .inTable(TableName.SecretApprovalPolicy) - .onDelete("CASCADE"); + t.foreign("policyId").references("id").inTable(TableName.SecretApprovalPolicy).onDelete("CASCADE"); t.timestamps(true, true, true); }); } diff --git a/backend/src/db/migrations/20240101104907_secret-approval-request.ts b/backend/src/db/migrations/20240101104907_secret-approval-request.ts index 21a9e944e..f35f87819 100644 --- a/backend/src/db/migrations/20240101104907_secret-approval-request.ts +++ b/backend/src/db/migrations/20240101104907_secret-approval-request.ts @@ -11,23 +11,14 @@ export async function up(knex: Knex): Promise { t.boolean("hasMerged").defaultTo(false).notNullable(); t.string("status").defaultTo("open").notNullable(); t.jsonb("conflicts"); - t.foreign("policyId") - .references("id") - .inTable(TableName.SecretApprovalPolicy) - .onDelete("CASCADE"); + t.foreign("policyId").references("id").inTable(TableName.SecretApprovalPolicy).onDelete("CASCADE"); t.string("slug").notNullable(); t.uuid("folderId").notNullable(); t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE"); t.uuid("statusChangeBy"); - t.foreign("statusChangeBy") - .references("id") - .inTable(TableName.ProjectMembership) - .onDelete("SET NULL"); + t.foreign("statusChangeBy").references("id").inTable(TableName.ProjectMembership).onDelete("SET NULL"); t.uuid("committerId").notNullable(); - t.foreign("committerId") - .references("id") - .inTable(TableName.ProjectMembership) - .onDelete("CASCADE"); + t.foreign("committerId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE"); t.timestamps(true, true, true); }); } @@ -40,10 +31,7 @@ export async function up(knex: Knex): Promise { t.foreign("member").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE"); t.string("status").notNullable(); t.uuid("requestId").notNullable(); - t.foreign("requestId") - .references("id") - .inTable(TableName.SecretApprovalRequest) - .onDelete("CASCADE"); + t.foreign("requestId").references("id").inTable(TableName.SecretApprovalRequest).onDelete("CASCADE"); t.timestamps(true, true, true); }); } @@ -73,18 +61,12 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); // commit details t.uuid("requestId").notNullable(); - t.foreign("requestId") - .references("id") - .inTable(TableName.SecretApprovalRequest) - .onDelete("CASCADE"); + t.foreign("requestId").references("id").inTable(TableName.SecretApprovalRequest).onDelete("CASCADE"); t.string("op").notNullable(); t.uuid("secretId"); t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("SET NULL"); t.uuid("secretVersion"); - t.foreign("secretVersion") - .references("id") - .inTable(TableName.SecretVersion) - .onDelete("SET NULL"); + t.foreign("secretVersion").references("id").inTable(TableName.SecretVersion).onDelete("SET NULL"); }); } await createOnUpdateTrigger(knex, TableName.SecretApprovalRequestSecret); @@ -93,10 +75,7 @@ export async function up(knex: Knex): Promise { await knex.schema.createTable(TableName.SecretApprovalRequestSecretTag, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.uuid("secretId").notNullable(); - t.foreign("secretId") - .references("id") - .inTable(TableName.SecretApprovalRequestSecret) - .onDelete("CASCADE"); + t.foreign("secretId").references("id").inTable(TableName.SecretApprovalRequestSecret).onDelete("CASCADE"); t.uuid("tagId").notNullable(); t.foreign("tagId").references("id").inTable(TableName.SecretTag).onDelete("CASCADE"); t.timestamps(true, true, true); diff --git a/backend/src/db/migrations/20240102152111_secret-rotation.ts b/backend/src/db/migrations/20240102152111_secret-rotation.ts index f7009488a..ea962cc6e 100644 --- a/backend/src/db/migrations/20240102152111_secret-rotation.ts +++ b/backend/src/db/migrations/20240102152111_secret-rotation.ts @@ -32,10 +32,7 @@ export async function up(knex: Knex): Promise { t.uuid("secretId").notNullable(); t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("CASCADE"); t.uuid("rotationId").notNullable(); - t.foreign("rotationId") - .references("id") - .inTable(TableName.SecretRotation) - .onDelete("CASCADE"); + t.foreign("rotationId").references("id").inTable(TableName.SecretRotation).onDelete("CASCADE"); }); } } diff --git a/backend/src/db/migrations/20240104140641_secret-snapshot.ts b/backend/src/db/migrations/20240104140641_secret-snapshot.ts index 01b1e24fb..3dfb06772 100644 --- a/backend/src/db/migrations/20240104140641_secret-snapshot.ts +++ b/backend/src/db/migrations/20240104140641_secret-snapshot.ts @@ -25,10 +25,7 @@ export async function up(knex: Knex): Promise { t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE"); // not a relation kept like that to keep it when rolled back t.uuid("secretVersionId").notNullable(); - t.foreign("secretVersionId") - .references("id") - .inTable(TableName.SecretVersion) - .onDelete("CASCADE"); + t.foreign("secretVersionId").references("id").inTable(TableName.SecretVersion).onDelete("CASCADE"); t.uuid("snapshotId").notNullable(); t.foreign("snapshotId").references("id").inTable(TableName.Snapshot).onDelete("CASCADE"); t.timestamps(true, true, true); @@ -42,10 +39,7 @@ export async function up(knex: Knex): Promise { t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE"); // not a relation kept like that to keep it when rolled back t.uuid("folderVersionId").notNullable(); - t.foreign("folderVersionId") - .references("id") - .inTable(TableName.SecretFolderVersion) - .onDelete("CASCADE"); + t.foreign("folderVersionId").references("id").inTable(TableName.SecretFolderVersion).onDelete("CASCADE"); t.uuid("snapshotId").notNullable(); t.foreign("snapshotId").references("id").inTable(TableName.Snapshot).onDelete("CASCADE"); t.timestamps(true, true, true); diff --git a/backend/src/db/migrations/20240111051011_secret-scanning.ts b/backend/src/db/migrations/20240111051011_secret-scanning.ts index af011d558..28a57bf08 100644 --- a/backend/src/db/migrations/20240111051011_secret-scanning.ts +++ b/backend/src/db/migrations/20240111051011_secret-scanning.ts @@ -15,7 +15,7 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); }); } - createOnUpdateTrigger(knex, TableName.GitAppInstallSession); + await createOnUpdateTrigger(knex, TableName.GitAppInstallSession); if (!(await knex.schema.hasTable(TableName.GitAppOrg))) { await knex.schema.createTable(TableName.GitAppOrg, (t) => { @@ -28,7 +28,7 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); }); } - createOnUpdateTrigger(knex, TableName.GitAppOrg); + await createOnUpdateTrigger(knex, TableName.GitAppOrg); if (!(await knex.schema.hasTable(TableName.SecretScanningGitRisk))) { await knex.schema.createTable(TableName.SecretScanningGitRisk, (t) => { @@ -66,7 +66,7 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); }); } - createOnUpdateTrigger(knex, TableName.SecretScanningGitRisk); + await createOnUpdateTrigger(knex, TableName.SecretScanningGitRisk); } export async function down(knex: Knex): Promise { diff --git a/backend/src/db/migrations/20240204171758_org-based-auth.ts b/backend/src/db/migrations/20240204171758_org-based-auth.ts new file mode 100644 index 000000000..f2b2f913c --- /dev/null +++ b/backend/src/db/migrations/20240204171758_org-based-auth.ts @@ -0,0 +1,25 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable(TableName.Organization, (t) => { + t.boolean("authEnforced").defaultTo(false); + t.index("slug"); + }); + + await knex.schema.alterTable(TableName.SamlConfig, (t) => { + t.datetime("lastUsed"); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable(TableName.Organization, (t) => { + t.dropColumn("authEnforced"); + t.dropIndex("slug"); + }); + + await knex.schema.alterTable(TableName.SamlConfig, (t) => { + t.dropColumn("lastUsed"); + }); +} diff --git a/backend/src/db/schemas/api-keys.ts b/backend/src/db/schemas/api-keys.ts index c3f384a5a..ff29a54e1 100644 --- a/backend/src/db/schemas/api-keys.ts +++ b/backend/src/db/schemas/api-keys.ts @@ -15,7 +15,7 @@ export const ApiKeysSchema = z.object({ secretHash: z.string(), createdAt: z.date(), updatedAt: z.date(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TApiKeys = z.infer; diff --git a/backend/src/db/schemas/audit-logs.ts b/backend/src/db/schemas/audit-logs.ts index 90c389b94..f7143bb57 100644 --- a/backend/src/db/schemas/audit-logs.ts +++ b/backend/src/db/schemas/audit-logs.ts @@ -20,7 +20,7 @@ export const AuditLogsSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), orgId: z.string().uuid().nullable().optional(), - projectId: z.string().nullable().optional(), + projectId: z.string().nullable().optional() }); export type TAuditLogs = z.infer; diff --git a/backend/src/db/schemas/auth-token-sessions.ts b/backend/src/db/schemas/auth-token-sessions.ts index 9dbf8e09f..46ed7c201 100644 --- a/backend/src/db/schemas/auth-token-sessions.ts +++ b/backend/src/db/schemas/auth-token-sessions.ts @@ -16,7 +16,7 @@ export const AuthTokenSessionsSchema = z.object({ lastUsed: z.date(), createdAt: z.date(), updatedAt: z.date(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TAuthTokenSessions = z.infer; diff --git a/backend/src/db/schemas/auth-tokens.ts b/backend/src/db/schemas/auth-tokens.ts index 4a612b11d..9ae8eed44 100644 --- a/backend/src/db/schemas/auth-tokens.ts +++ b/backend/src/db/schemas/auth-tokens.ts @@ -17,7 +17,7 @@ export const AuthTokensSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), userId: z.string().uuid().nullable().optional(), - orgId: z.string().uuid().nullable().optional(), + orgId: z.string().uuid().nullable().optional() }); export type TAuthTokens = z.infer; diff --git a/backend/src/db/schemas/backup-private-key.ts b/backend/src/db/schemas/backup-private-key.ts index 9b6e787b1..5930bbd4a 100644 --- a/backend/src/db/schemas/backup-private-key.ts +++ b/backend/src/db/schemas/backup-private-key.ts @@ -18,7 +18,7 @@ export const BackupPrivateKeySchema = z.object({ verifier: z.string(), createdAt: z.date(), updatedAt: z.date(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TBackupPrivateKey = z.infer; diff --git a/backend/src/db/schemas/git-app-install-sessions.ts b/backend/src/db/schemas/git-app-install-sessions.ts index 16f62eab7..6c6db40ea 100644 --- a/backend/src/db/schemas/git-app-install-sessions.ts +++ b/backend/src/db/schemas/git-app-install-sessions.ts @@ -13,7 +13,7 @@ export const GitAppInstallSessionsSchema = z.object({ userId: z.string().uuid().nullable().optional(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TGitAppInstallSessions = z.infer; diff --git a/backend/src/db/schemas/git-app-org.ts b/backend/src/db/schemas/git-app-org.ts index f5226811d..57e0d474a 100644 --- a/backend/src/db/schemas/git-app-org.ts +++ b/backend/src/db/schemas/git-app-org.ts @@ -13,7 +13,7 @@ export const GitAppOrgSchema = z.object({ userId: z.string().uuid(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TGitAppOrg = z.infer; diff --git a/backend/src/db/schemas/identities.ts b/backend/src/db/schemas/identities.ts index b8ff6c36f..005adf025 100644 --- a/backend/src/db/schemas/identities.ts +++ b/backend/src/db/schemas/identities.ts @@ -12,7 +12,7 @@ export const IdentitiesSchema = z.object({ name: z.string(), authMethod: z.string().nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIdentities = z.infer; diff --git a/backend/src/db/schemas/identity-access-tokens.ts b/backend/src/db/schemas/identity-access-tokens.ts index 62f74f4aa..cbd71e5c5 100644 --- a/backend/src/db/schemas/identity-access-tokens.ts +++ b/backend/src/db/schemas/identity-access-tokens.ts @@ -8,7 +8,7 @@ import { z } from "zod"; import { TImmutableDBKeys } from "./models"; export const IdentityAccessTokensSchema = z.object({ - id: z.string().uuid(), + id: z.string(), accessTokenTTL: z.coerce.number().default(2592000), accessTokenMaxTTL: z.coerce.number().default(2592000), accessTokenNumUses: z.coerce.number().default(0), @@ -19,7 +19,7 @@ export const IdentityAccessTokensSchema = z.object({ identityUAClientSecretId: z.string().nullable().optional(), identityId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIdentityAccessTokens = z.infer; diff --git a/backend/src/db/schemas/identity-org-memberships.ts b/backend/src/db/schemas/identity-org-memberships.ts index c5c9a0f61..647ec7124 100644 --- a/backend/src/db/schemas/identity-org-memberships.ts +++ b/backend/src/db/schemas/identity-org-memberships.ts @@ -14,7 +14,7 @@ export const IdentityOrgMembershipsSchema = z.object({ orgId: z.string().uuid(), createdAt: z.date(), updatedAt: z.date(), - identityId: z.string().uuid(), + identityId: z.string().uuid() }); export type TIdentityOrgMemberships = z.infer; diff --git a/backend/src/db/schemas/identity-project-memberships.ts b/backend/src/db/schemas/identity-project-memberships.ts index 9a57952a4..866324c8b 100644 --- a/backend/src/db/schemas/identity-project-memberships.ts +++ b/backend/src/db/schemas/identity-project-memberships.ts @@ -14,7 +14,7 @@ export const IdentityProjectMembershipsSchema = z.object({ projectId: z.string(), identityId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIdentityProjectMemberships = z.infer; diff --git a/backend/src/db/schemas/identity-ua-client-secrets.ts b/backend/src/db/schemas/identity-ua-client-secrets.ts index f17b1ed7f..60f8d862f 100644 --- a/backend/src/db/schemas/identity-ua-client-secrets.ts +++ b/backend/src/db/schemas/identity-ua-client-secrets.ts @@ -19,7 +19,7 @@ export const IdentityUaClientSecretsSchema = z.object({ isClientSecretRevoked: z.boolean().default(false), createdAt: z.date(), updatedAt: z.date(), - identityUAId: z.string().uuid(), + identityUAId: z.string().uuid() }); export type TIdentityUaClientSecrets = z.infer; diff --git a/backend/src/db/schemas/identity-universal-auths.ts b/backend/src/db/schemas/identity-universal-auths.ts index 1dbc61ea8..5a8f0c7ec 100644 --- a/backend/src/db/schemas/identity-universal-auths.ts +++ b/backend/src/db/schemas/identity-universal-auths.ts @@ -17,7 +17,7 @@ export const IdentityUniversalAuthsSchema = z.object({ accessTokenTrustedIps: z.unknown(), createdAt: z.date(), updatedAt: z.date(), - identityId: z.string().uuid(), + identityId: z.string().uuid() }); export type TIdentityUniversalAuths = z.infer; diff --git a/backend/src/db/schemas/incident-contacts.ts b/backend/src/db/schemas/incident-contacts.ts index c1492e0fa..431bf05ab 100644 --- a/backend/src/db/schemas/incident-contacts.ts +++ b/backend/src/db/schemas/incident-contacts.ts @@ -12,7 +12,7 @@ export const IncidentContactsSchema = z.object({ email: z.string(), createdAt: z.date(), updatedAt: z.date(), - orgId: z.string().uuid(), + orgId: z.string().uuid() }); export type TIncidentContacts = z.infer; diff --git a/backend/src/db/schemas/integration-auths.ts b/backend/src/db/schemas/integration-auths.ts index d2983658c..db602c0af 100644 --- a/backend/src/db/schemas/integration-auths.ts +++ b/backend/src/db/schemas/integration-auths.ts @@ -29,7 +29,7 @@ export const IntegrationAuthsSchema = z.object({ keyEncoding: z.string(), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIntegrationAuths = z.infer; diff --git a/backend/src/db/schemas/integrations.ts b/backend/src/db/schemas/integrations.ts index b2163dc4d..62f73d190 100644 --- a/backend/src/db/schemas/integrations.ts +++ b/backend/src/db/schemas/integrations.ts @@ -25,9 +25,9 @@ export const IntegrationsSchema = z.object({ metadata: z.unknown().nullable().optional(), integrationAuthId: z.string().uuid(), envId: z.string().uuid(), - secretPath: z.string().default('/'), + secretPath: z.string().default("/"), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIntegrations = z.infer; diff --git a/backend/src/db/schemas/org-bots.ts b/backend/src/db/schemas/org-bots.ts index 400ab19c8..b328f1aaf 100644 --- a/backend/src/db/schemas/org-bots.ts +++ b/backend/src/db/schemas/org-bots.ts @@ -23,7 +23,7 @@ export const OrgBotsSchema = z.object({ privateKeyKeyEncoding: z.string(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TOrgBots = z.infer; diff --git a/backend/src/db/schemas/org-memberships.ts b/backend/src/db/schemas/org-memberships.ts index 932c84d00..b2fffa117 100644 --- a/backend/src/db/schemas/org-memberships.ts +++ b/backend/src/db/schemas/org-memberships.ts @@ -10,13 +10,13 @@ import { TImmutableDBKeys } from "./models"; export const OrgMembershipsSchema = z.object({ id: z.string().uuid(), role: z.string(), - status: z.string().default('invited'), + status: z.string().default("invited"), inviteEmail: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), userId: z.string().uuid().nullable().optional(), orgId: z.string().uuid(), - roleId: z.string().uuid().nullable().optional(), + roleId: z.string().uuid().nullable().optional() }); export type TOrgMemberships = z.infer; diff --git a/backend/src/db/schemas/org-roles.ts b/backend/src/db/schemas/org-roles.ts index 9718cdb26..72b582f96 100644 --- a/backend/src/db/schemas/org-roles.ts +++ b/backend/src/db/schemas/org-roles.ts @@ -15,7 +15,7 @@ export const OrgRolesSchema = z.object({ permissions: z.unknown(), createdAt: z.date(), updatedAt: z.date(), - orgId: z.string().uuid(), + orgId: z.string().uuid() }); export type TOrgRoles = z.infer; diff --git a/backend/src/db/schemas/organizations.ts b/backend/src/db/schemas/organizations.ts index e0f70d1c0..087a1b7e0 100644 --- a/backend/src/db/schemas/organizations.ts +++ b/backend/src/db/schemas/organizations.ts @@ -13,7 +13,8 @@ export const OrganizationsSchema = z.object({ customerId: z.string().nullable().optional(), slug: z.string(), createdAt: z.date(), - updatedAt: z.date() + updatedAt: z.date(), + authEnforced: z.boolean().default(false).nullable().optional() }); export type TOrganizations = z.infer; diff --git a/backend/src/db/schemas/project-bots.ts b/backend/src/db/schemas/project-bots.ts index 90ced9b3e..c68576943 100644 --- a/backend/src/db/schemas/project-bots.ts +++ b/backend/src/db/schemas/project-bots.ts @@ -22,7 +22,7 @@ export const ProjectBotsSchema = z.object({ projectId: z.string(), senderId: z.string().uuid().nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TProjectBots = z.infer; diff --git a/backend/src/db/schemas/project-environments.ts b/backend/src/db/schemas/project-environments.ts index aa3e392c7..8b95dbba0 100644 --- a/backend/src/db/schemas/project-environments.ts +++ b/backend/src/db/schemas/project-environments.ts @@ -14,7 +14,7 @@ export const ProjectEnvironmentsSchema = z.object({ position: z.number(), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TProjectEnvironments = z.infer; diff --git a/backend/src/db/schemas/project-keys.ts b/backend/src/db/schemas/project-keys.ts index 64e33d574..720cd79bf 100644 --- a/backend/src/db/schemas/project-keys.ts +++ b/backend/src/db/schemas/project-keys.ts @@ -15,7 +15,7 @@ export const ProjectKeysSchema = z.object({ senderId: z.string().uuid().nullable().optional(), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TProjectKeys = z.infer; diff --git a/backend/src/db/schemas/project-memberships.ts b/backend/src/db/schemas/project-memberships.ts index c98befb38..b9f191a84 100644 --- a/backend/src/db/schemas/project-memberships.ts +++ b/backend/src/db/schemas/project-memberships.ts @@ -14,7 +14,7 @@ export const ProjectMembershipsSchema = z.object({ updatedAt: z.date(), userId: z.string().uuid(), projectId: z.string(), - roleId: z.string().uuid().nullable().optional(), + roleId: z.string().uuid().nullable().optional() }); export type TProjectMemberships = z.infer; diff --git a/backend/src/db/schemas/project-roles.ts b/backend/src/db/schemas/project-roles.ts index 190dd1cec..1946ab5e1 100644 --- a/backend/src/db/schemas/project-roles.ts +++ b/backend/src/db/schemas/project-roles.ts @@ -15,7 +15,7 @@ export const ProjectRolesSchema = z.object({ permissions: z.unknown(), createdAt: z.date(), updatedAt: z.date(), - projectId: z.string(), + projectId: z.string() }); export type TProjectRoles = z.infer; diff --git a/backend/src/db/schemas/projects.ts b/backend/src/db/schemas/projects.ts index 005e4bbde..296fa421e 100644 --- a/backend/src/db/schemas/projects.ts +++ b/backend/src/db/schemas/projects.ts @@ -14,7 +14,7 @@ export const ProjectsSchema = z.object({ autoCapitalization: z.boolean().default(true).nullable().optional(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TProjects = z.infer; diff --git a/backend/src/db/schemas/saml-configs.ts b/backend/src/db/schemas/saml-configs.ts index 392fb2bef..6891d8add 100644 --- a/backend/src/db/schemas/saml-configs.ts +++ b/backend/src/db/schemas/saml-configs.ts @@ -23,6 +23,7 @@ export const SamlConfigsSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), orgId: z.string().uuid(), + lastUsed: z.date().nullable().optional() }); export type TSamlConfigs = z.infer; diff --git a/backend/src/db/schemas/secret-approval-policies-approvers.ts b/backend/src/db/schemas/secret-approval-policies-approvers.ts index c6fb75f06..503299d30 100644 --- a/backend/src/db/schemas/secret-approval-policies-approvers.ts +++ b/backend/src/db/schemas/secret-approval-policies-approvers.ts @@ -12,7 +12,7 @@ export const SecretApprovalPoliciesApproversSchema = z.object({ approverId: z.string().uuid(), policyId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalPoliciesApprovers = z.infer; diff --git a/backend/src/db/schemas/secret-approval-policies.ts b/backend/src/db/schemas/secret-approval-policies.ts index ec859bb4e..6c331f1b1 100644 --- a/backend/src/db/schemas/secret-approval-policies.ts +++ b/backend/src/db/schemas/secret-approval-policies.ts @@ -14,7 +14,7 @@ export const SecretApprovalPoliciesSchema = z.object({ approvals: z.number().default(1), envId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalPolicies = z.infer; diff --git a/backend/src/db/schemas/secret-approval-request-secret-tags.ts b/backend/src/db/schemas/secret-approval-request-secret-tags.ts index 47e11e6a9..f5e7ba632 100644 --- a/backend/src/db/schemas/secret-approval-request-secret-tags.ts +++ b/backend/src/db/schemas/secret-approval-request-secret-tags.ts @@ -12,7 +12,7 @@ export const SecretApprovalRequestSecretTagsSchema = z.object({ secretId: z.string().uuid(), tagId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalRequestSecretTags = z.infer; diff --git a/backend/src/db/schemas/secret-approval-requests-reviewers.ts b/backend/src/db/schemas/secret-approval-requests-reviewers.ts index 4ed9b50a1..a3657f1f9 100644 --- a/backend/src/db/schemas/secret-approval-requests-reviewers.ts +++ b/backend/src/db/schemas/secret-approval-requests-reviewers.ts @@ -13,7 +13,7 @@ export const SecretApprovalRequestsReviewersSchema = z.object({ status: z.string(), requestId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalRequestsReviewers = z.infer; diff --git a/backend/src/db/schemas/secret-approval-requests-secrets.ts b/backend/src/db/schemas/secret-approval-requests-secrets.ts index 2fe6c6692..810a4f2cf 100644 --- a/backend/src/db/schemas/secret-approval-requests-secrets.ts +++ b/backend/src/db/schemas/secret-approval-requests-secrets.ts @@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretApprovalRequestsSecretsSchema = z.object({ id: z.string().uuid(), version: z.number().default(1).nullable().optional(), - secretBlindIndex: z.string(), + secretBlindIndex: z.string().nullable().optional(), secretKeyCiphertext: z.string(), secretKeyIV: z.string(), secretKeyTag: z.string(), @@ -23,15 +23,15 @@ export const SecretApprovalRequestsSecretsSchema = z.object({ secretReminderNote: z.string().nullable().optional(), secretReminderRepeatDays: z.number().nullable().optional(), skipMultilineEncoding: z.boolean().default(false).nullable().optional(), - algorithm: z.string().default('aes-256-gcm'), - keyEncoding: z.string().default('utf8'), + algorithm: z.string().default("aes-256-gcm"), + keyEncoding: z.string().default("utf8"), metadata: z.unknown().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), requestId: z.string().uuid(), op: z.string(), secretId: z.string().uuid().nullable().optional(), - secretVersion: z.string().uuid().nullable().optional(), + secretVersion: z.string().uuid().nullable().optional() }); export type TSecretApprovalRequestsSecrets = z.infer; diff --git a/backend/src/db/schemas/secret-approval-requests.ts b/backend/src/db/schemas/secret-approval-requests.ts index 7d5f2f443..590c283f5 100644 --- a/backend/src/db/schemas/secret-approval-requests.ts +++ b/backend/src/db/schemas/secret-approval-requests.ts @@ -11,14 +11,14 @@ export const SecretApprovalRequestsSchema = z.object({ id: z.string().uuid(), policyId: z.string().uuid(), hasMerged: z.boolean().default(false), - status: z.string().default('open'), + status: z.string().default("open"), conflicts: z.unknown().nullable().optional(), slug: z.string(), folderId: z.string().uuid(), statusChangeBy: z.string().uuid().nullable().optional(), committerId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalRequests = z.infer; diff --git a/backend/src/db/schemas/secret-blind-indexes.ts b/backend/src/db/schemas/secret-blind-indexes.ts index 17eacb473..fa919babd 100644 --- a/backend/src/db/schemas/secret-blind-indexes.ts +++ b/backend/src/db/schemas/secret-blind-indexes.ts @@ -12,11 +12,11 @@ export const SecretBlindIndexesSchema = z.object({ encryptedSaltCipherText: z.string(), saltIV: z.string(), saltTag: z.string(), - algorithm: z.string().default('aes-256-gcm'), - keyEncoding: z.string().default('utf8'), + algorithm: z.string().default("aes-256-gcm"), + keyEncoding: z.string().default("utf8"), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretBlindIndexes = z.infer; diff --git a/backend/src/db/schemas/secret-folder-versions.ts b/backend/src/db/schemas/secret-folder-versions.ts index 895f81ebc..8c550d065 100644 --- a/backend/src/db/schemas/secret-folder-versions.ts +++ b/backend/src/db/schemas/secret-folder-versions.ts @@ -14,7 +14,7 @@ export const SecretFolderVersionsSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), envId: z.string().uuid(), - folderId: z.string().uuid(), + folderId: z.string().uuid() }); export type TSecretFolderVersions = z.infer; diff --git a/backend/src/db/schemas/secret-folders.ts b/backend/src/db/schemas/secret-folders.ts index aa437c753..648238b0a 100644 --- a/backend/src/db/schemas/secret-folders.ts +++ b/backend/src/db/schemas/secret-folders.ts @@ -14,7 +14,7 @@ export const SecretFoldersSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), envId: z.string().uuid(), - parentId: z.string().uuid().nullable().optional(), + parentId: z.string().uuid().nullable().optional() }); export type TSecretFolders = z.infer; diff --git a/backend/src/db/schemas/secret-imports.ts b/backend/src/db/schemas/secret-imports.ts index 109d41ec2..9c1ee905f 100644 --- a/backend/src/db/schemas/secret-imports.ts +++ b/backend/src/db/schemas/secret-imports.ts @@ -15,7 +15,7 @@ export const SecretImportsSchema = z.object({ position: z.number(), createdAt: z.date(), updatedAt: z.date(), - folderId: z.string().uuid(), + folderId: z.string().uuid() }); export type TSecretImports = z.infer; diff --git a/backend/src/db/schemas/secret-rotation-outputs.ts b/backend/src/db/schemas/secret-rotation-outputs.ts index b98338c75..3b594365a 100644 --- a/backend/src/db/schemas/secret-rotation-outputs.ts +++ b/backend/src/db/schemas/secret-rotation-outputs.ts @@ -11,7 +11,7 @@ export const SecretRotationOutputsSchema = z.object({ id: z.string().uuid(), key: z.string(), secretId: z.string().uuid(), - rotationId: z.string().uuid(), + rotationId: z.string().uuid() }); export type TSecretRotationOutputs = z.infer; diff --git a/backend/src/db/schemas/secret-rotations.ts b/backend/src/db/schemas/secret-rotations.ts index 6e2bf6547..4c65712fa 100644 --- a/backend/src/db/schemas/secret-rotations.ts +++ b/backend/src/db/schemas/secret-rotations.ts @@ -22,7 +22,7 @@ export const SecretRotationsSchema = z.object({ keyEncoding: z.string().nullable().optional(), envId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretRotations = z.infer; diff --git a/backend/src/db/schemas/secret-scanning-git-risks.ts b/backend/src/db/schemas/secret-scanning-git-risks.ts index 0951d3ba5..85cfcd376 100644 --- a/backend/src/db/schemas/secret-scanning-git-risks.ts +++ b/backend/src/db/schemas/secret-scanning-git-risks.ts @@ -38,7 +38,7 @@ export const SecretScanningGitRisksSchema = z.object({ status: z.string().nullable().optional(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretScanningGitRisks = z.infer; diff --git a/backend/src/db/schemas/secret-snapshot-folders.ts b/backend/src/db/schemas/secret-snapshot-folders.ts index 5f3b1a88c..acf11ab0a 100644 --- a/backend/src/db/schemas/secret-snapshot-folders.ts +++ b/backend/src/db/schemas/secret-snapshot-folders.ts @@ -13,7 +13,7 @@ export const SecretSnapshotFoldersSchema = z.object({ folderVersionId: z.string().uuid(), snapshotId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretSnapshotFolders = z.infer; diff --git a/backend/src/db/schemas/secret-snapshot-secrets.ts b/backend/src/db/schemas/secret-snapshot-secrets.ts index f8a69a695..6a83d1155 100644 --- a/backend/src/db/schemas/secret-snapshot-secrets.ts +++ b/backend/src/db/schemas/secret-snapshot-secrets.ts @@ -13,7 +13,7 @@ export const SecretSnapshotSecretsSchema = z.object({ secretVersionId: z.string().uuid(), snapshotId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretSnapshotSecrets = z.infer; diff --git a/backend/src/db/schemas/secret-snapshots.ts b/backend/src/db/schemas/secret-snapshots.ts index ef9e0b7d0..ed255cb77 100644 --- a/backend/src/db/schemas/secret-snapshots.ts +++ b/backend/src/db/schemas/secret-snapshots.ts @@ -13,7 +13,7 @@ export const SecretSnapshotsSchema = z.object({ folderId: z.string().uuid(), parentFolderId: z.string().uuid().nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretSnapshots = z.infer; diff --git a/backend/src/db/schemas/secret-tag-junction.ts b/backend/src/db/schemas/secret-tag-junction.ts index 467ef7b15..1d25574c5 100644 --- a/backend/src/db/schemas/secret-tag-junction.ts +++ b/backend/src/db/schemas/secret-tag-junction.ts @@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretTagJunctionSchema = z.object({ id: z.string().uuid(), secretsId: z.string().uuid(), - secret_tagsId: z.string().uuid(), + secret_tagsId: z.string().uuid() }); export type TSecretTagJunction = z.infer; diff --git a/backend/src/db/schemas/secret-tags.ts b/backend/src/db/schemas/secret-tags.ts index 78f03dedd..622c29bd3 100644 --- a/backend/src/db/schemas/secret-tags.ts +++ b/backend/src/db/schemas/secret-tags.ts @@ -15,7 +15,7 @@ export const SecretTagsSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), createdBy: z.string().uuid().nullable().optional(), - projectId: z.string(), + projectId: z.string() }); export type TSecretTags = z.infer; diff --git a/backend/src/db/schemas/secret-version-tag-junction.ts b/backend/src/db/schemas/secret-version-tag-junction.ts index 11b3f0032..2c9a24fee 100644 --- a/backend/src/db/schemas/secret-version-tag-junction.ts +++ b/backend/src/db/schemas/secret-version-tag-junction.ts @@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretVersionTagJunctionSchema = z.object({ id: z.string().uuid(), secret_versionsId: z.string().uuid(), - secret_tagsId: z.string().uuid(), + secret_tagsId: z.string().uuid() }); export type TSecretVersionTagJunction = z.infer; diff --git a/backend/src/db/schemas/secret-versions.ts b/backend/src/db/schemas/secret-versions.ts index 3a04a8cd9..d1675e3d2 100644 --- a/backend/src/db/schemas/secret-versions.ts +++ b/backend/src/db/schemas/secret-versions.ts @@ -10,8 +10,8 @@ import { TImmutableDBKeys } from "./models"; export const SecretVersionsSchema = z.object({ id: z.string().uuid(), version: z.number().default(1), - type: z.string().default('shared'), - secretBlindIndex: z.string(), + type: z.string().default("shared"), + secretBlindIndex: z.string().nullable().optional(), secretKeyCiphertext: z.string(), secretKeyIV: z.string(), secretKeyTag: z.string(), @@ -24,15 +24,15 @@ export const SecretVersionsSchema = z.object({ secretReminderNote: z.string().nullable().optional(), secretReminderRepeatDays: z.number().nullable().optional(), skipMultilineEncoding: z.boolean().default(false).nullable().optional(), - algorithm: z.string().default('aes-256-gcm'), - keyEncoding: z.string().default('utf8'), + algorithm: z.string().default("aes-256-gcm"), + keyEncoding: z.string().default("utf8"), metadata: z.unknown().nullable().optional(), envId: z.string().uuid().nullable().optional(), secretId: z.string().uuid(), folderId: z.string().uuid(), userId: z.string().uuid().nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretVersions = z.infer; diff --git a/backend/src/db/schemas/secrets.ts b/backend/src/db/schemas/secrets.ts index a284ae770..3fe5ad8d8 100644 --- a/backend/src/db/schemas/secrets.ts +++ b/backend/src/db/schemas/secrets.ts @@ -10,8 +10,8 @@ import { TImmutableDBKeys } from "./models"; export const SecretsSchema = z.object({ id: z.string().uuid(), version: z.number().default(1), - type: z.string().default('shared'), - secretBlindIndex: z.string(), + type: z.string().default("shared"), + secretBlindIndex: z.string().nullable().optional(), secretKeyCiphertext: z.string(), secretKeyIV: z.string(), secretKeyTag: z.string(), @@ -24,13 +24,13 @@ export const SecretsSchema = z.object({ secretReminderNote: z.string().nullable().optional(), secretReminderRepeatDays: z.number().nullable().optional(), skipMultilineEncoding: z.boolean().default(false).nullable().optional(), - algorithm: z.string().default('aes-256-gcm'), - keyEncoding: z.string().default('utf8'), + algorithm: z.string().default("aes-256-gcm"), + keyEncoding: z.string().default("utf8"), metadata: z.unknown().nullable().optional(), userId: z.string().uuid().nullable().optional(), folderId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecrets = z.infer; diff --git a/backend/src/db/schemas/service-tokens.ts b/backend/src/db/schemas/service-tokens.ts index c12e28432..24720f3e4 100644 --- a/backend/src/db/schemas/service-tokens.ts +++ b/backend/src/db/schemas/service-tokens.ts @@ -21,7 +21,7 @@ export const ServiceTokensSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), createdBy: z.string(), - projectId: z.string(), + projectId: z.string() }); export type TServiceTokens = z.infer; diff --git a/backend/src/db/schemas/super-admin.ts b/backend/src/db/schemas/super-admin.ts index f998cf325..13bf45e7b 100644 --- a/backend/src/db/schemas/super-admin.ts +++ b/backend/src/db/schemas/super-admin.ts @@ -12,7 +12,7 @@ export const SuperAdminSchema = z.object({ initialized: z.boolean().default(false).nullable().optional(), allowSignUp: z.boolean().default(true).nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSuperAdmin = z.infer; diff --git a/backend/src/db/schemas/trusted-ips.ts b/backend/src/db/schemas/trusted-ips.ts index c3311340d..6d9018db3 100644 --- a/backend/src/db/schemas/trusted-ips.ts +++ b/backend/src/db/schemas/trusted-ips.ts @@ -16,7 +16,7 @@ export const TrustedIpsSchema = z.object({ comment: z.string().nullable().optional(), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TTrustedIps = z.infer; diff --git a/backend/src/db/schemas/user-actions.ts b/backend/src/db/schemas/user-actions.ts index a3a07d699..eaa03ba98 100644 --- a/backend/src/db/schemas/user-actions.ts +++ b/backend/src/db/schemas/user-actions.ts @@ -12,7 +12,7 @@ export const UserActionsSchema = z.object({ action: z.string(), createdAt: z.date(), updatedAt: z.date(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TUserActions = z.infer; diff --git a/backend/src/db/schemas/user-encryption-keys.ts b/backend/src/db/schemas/user-encryption-keys.ts index 41a7ae57d..8f35b09fe 100644 --- a/backend/src/db/schemas/user-encryption-keys.ts +++ b/backend/src/db/schemas/user-encryption-keys.ts @@ -12,16 +12,16 @@ export const UserEncryptionKeysSchema = z.object({ clientPublicKey: z.string().nullable().optional(), serverPrivateKey: z.string().nullable().optional(), encryptionVersion: z.number().default(2).nullable().optional(), - protectedKey: z.string().nullable(), - protectedKeyIV: z.string().nullable(), - protectedKeyTag: z.string().nullable(), + protectedKey: z.string().nullable().optional(), + protectedKeyIV: z.string().nullable().optional(), + protectedKeyTag: z.string().nullable().optional(), publicKey: z.string(), encryptedPrivateKey: z.string(), iv: z.string(), tag: z.string(), salt: z.string(), verifier: z.string(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TUserEncryptionKeys = z.infer; diff --git a/backend/src/db/schemas/users.ts b/backend/src/db/schemas/users.ts index b9689883d..4a29de510 100644 --- a/backend/src/db/schemas/users.ts +++ b/backend/src/db/schemas/users.ts @@ -19,7 +19,7 @@ export const UsersSchema = z.object({ mfaMethods: z.string().array().nullable().optional(), devices: z.unknown().nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TUsers = z.infer; diff --git a/backend/src/db/schemas/webhooks.ts b/backend/src/db/schemas/webhooks.ts index 2b7e36cab..7abfb3772 100644 --- a/backend/src/db/schemas/webhooks.ts +++ b/backend/src/db/schemas/webhooks.ts @@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models"; export const WebhooksSchema = z.object({ id: z.string().uuid(), - secretPath: z.string().default('/'), + secretPath: z.string().default("/"), url: z.string(), lastStatus: z.string().nullable().optional(), lastRunErrorMessage: z.string().nullable().optional(), @@ -21,7 +21,7 @@ export const WebhooksSchema = z.object({ keyEncoding: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), - envId: z.string().uuid(), + envId: z.string().uuid() }); export type TWebhooks = z.infer; diff --git a/backend/src/db/seed-data.ts b/backend/src/db/seed-data.ts index a9dedbb11..bb57d5bb4 100644 --- a/backend/src/db/seed-data.ts +++ b/backend/src/db/seed-data.ts @@ -48,14 +48,12 @@ export const generateUserSrpKeys = async (password: string) => { await new Promise((resolve) => { client.init({ username: seedData1.email, password: seedData1.password }, () => resolve(null)); }); - const { salt, verifier } = await new Promise<{ salt: string; verifier: string }>( - (resolve, reject) => { - client.createVerifier((err, res) => { - if (err) return reject(err); - return resolve(res); - }); - } - ); + const { salt, verifier } = await new Promise<{ salt: string; verifier: string }>((resolve, reject) => { + client.createVerifier((err, res) => { + if (err) return reject(err); + return resolve(res); + }); + }); const derivedKey = await argon2.hash(password, { salt: Buffer.from(salt), memoryCost: 65536, diff --git a/backend/src/db/seeds/1-user.ts b/backend/src/db/seeds/1-user.ts index 7855ebc78..ca0042a98 100644 --- a/backend/src/db/seeds/1-user.ts +++ b/backend/src/db/seeds/1-user.ts @@ -14,7 +14,7 @@ export async function seed(knex: Knex): Promise { const [user] = await knex(TableName.Users) .insert([ { - // @ts-ignore to calculate predefined + // @ts-expect-error exluded type id needs to be inserted here to keep it testable id: seedData1.id, email: seedData1.email, superAdmin: true, @@ -48,7 +48,7 @@ export async function seed(knex: Knex): Promise { ]); await knex(TableName.AuthTokenSession).insert({ - // @ts-ignore + // @ts-expect-error exluded type id needs to be inserted here to keep it testable id: seedData1.token.id, userId: seedData1.id, ip: "151.196.220.213", diff --git a/backend/src/db/seeds/2-org.ts b/backend/src/db/seeds/2-org.ts index ec7e9dd43..a9c3ec3c7 100644 --- a/backend/src/db/seeds/2-org.ts +++ b/backend/src/db/seeds/2-org.ts @@ -14,7 +14,7 @@ export async function seed(knex: Knex): Promise { const [org] = await knex(TableName.Organization) .insert([ { - // @ts-ignore because we need that id for api calls + // @ts-expect-error exluded type id needs to be inserted here to keep it testable id: seedData1.organization.id, name: "infisical", slug: "infisical", diff --git a/backend/src/db/seeds/3-project.ts b/backend/src/db/seeds/3-project.ts index 48d4df5c1..7818d5831 100644 --- a/backend/src/db/seeds/3-project.ts +++ b/backend/src/db/seeds/3-project.ts @@ -20,7 +20,7 @@ export async function seed(knex: Knex): Promise { name: seedData1.project.name, orgId: seedData1.organization.id, slug: "first-project", - // @ts-ignore pre calc id + // @ts-expect-error exluded type id needs to be inserted here to keep it testable id: seedData1.project.id }) .returning("*"); @@ -45,7 +45,5 @@ export async function seed(knex: Knex): Promise { })) ) .returning("*"); - await knex(TableName.SecretFolder).insert( - envs.map(({ id }) => ({ name: "root", envId: id, parentId: null })) - ); + await knex(TableName.SecretFolder).insert(envs.map(({ id }) => ({ name: "root", envId: id, parentId: null }))); } diff --git a/backend/src/db/utils.ts b/backend/src/db/utils.ts index 51a097a4b..68c400596 100644 --- a/backend/src/db/utils.ts +++ b/backend/src/db/utils.ts @@ -2,12 +2,7 @@ import { Knex } from "knex"; import { TableName } from "./schemas"; -export const createJunctionTable = ( - knex: Knex, - tableName: TableName, - table1Name: TableName, - table2Name: TableName -) => +export const createJunctionTable = (knex: Knex, tableName: TableName, table1Name: TableName, table2Name: TableName) => knex.schema.createTable(tableName, (table) => { table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); table.uuid(`${table1Name}Id`).unsigned().notNullable(); // Foreign key for table1 diff --git a/backend/src/ee/routes/v1/license-router.ts b/backend/src/ee/routes/v1/license-router.ts index 675dcdc00..41cd11f7d 100644 --- a/backend/src/ee/routes/v1/license-router.ts +++ b/backend/src/ee/routes/v1/license-router.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +// TODO(akhilmhdh): Fix this when licence service gets it type import { z } from "zod"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; @@ -19,6 +22,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.getOrgPlansTableByBillCycle({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId, billingCycle: req.query.billingCycle }); @@ -40,6 +44,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const plan = await server.services.license.getOrgPlan({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId }); return { plan }; @@ -79,9 +84,10 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const data = await server.services.license.startOrgTrail({ + const data = await server.services.license.startOrgTrial({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId, success_url: req.body.success_url }); @@ -89,6 +95,27 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { } }); + server.route({ + url: "/:organizationId/customer-portal-session", + method: "POST", + schema: { + params: z.object({ organizationId: z.string().trim() }), + response: { + 200: z.any() + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const data = await server.services.license.createOrganizationPortalSession({ + actorId: req.permission.id, + actor: req.permission.type, + actorOrgId: req.permission.orgId, + orgId: req.params.organizationId + }); + return data; + } + }); + server.route({ url: "/:organizationId/plan/billing", method: "GET", @@ -103,6 +130,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.getOrgBillingInfo({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId }); return data; @@ -123,6 +151,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.getOrgPlanTable({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId }); return data; @@ -143,6 +172,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.getOrgBillingDetails({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId }); return data; @@ -167,6 +197,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.updateOrgBillingDetails({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId, name: req.body.name, email: req.body.email @@ -189,6 +220,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.getOrgPmtMethods({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId }); return data; @@ -213,6 +245,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.addOrgPmtMethods({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId, success_url: req.body.success_url, cancel_url: req.body.cancel_url @@ -238,6 +271,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.delOrgPmtMethods({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId, pmtMethodId: req.params.pmtMethodId }); @@ -261,6 +295,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.getOrgTaxIds({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId }); return data; @@ -287,6 +322,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.addOrgTaxId({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId, type: req.body.type, value: req.body.value @@ -312,6 +348,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.delOrgTaxId({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId, taxId: req.params.taxId }); @@ -335,6 +372,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.getOrgTaxInvoices({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId }); return data; @@ -357,6 +395,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { const data = await server.services.license.getOrgLicenses({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId }); return data; diff --git a/backend/src/ee/routes/v1/org-role-router.ts b/backend/src/ee/routes/v1/org-role-router.ts index 79e392880..46b80e7a9 100644 --- a/backend/src/ee/routes/v1/org-role-router.ts +++ b/backend/src/ee/routes/v1/org-role-router.ts @@ -26,11 +26,11 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const role = await server.services.orgRole.createRole( req.permission.id, req.params.organizationId, - req.body + req.body, + req.permission.orgId ); return { role }; } @@ -58,12 +58,12 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const role = await server.services.orgRole.updateRole( req.permission.id, req.params.organizationId, req.params.roleId, - req.body + req.body, + req.permission.orgId ); return { role }; } @@ -85,11 +85,11 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const role = await server.services.orgRole.deleteRole( req.permission.id, req.params.organizationId, - req.params.roleId + req.params.roleId, + req.permission.orgId ); return { role }; } @@ -114,10 +114,10 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const roles = await server.services.orgRole.listRoles( req.permission.id, - req.params.organizationId + req.params.organizationId, + req.permission.orgId ); return { data: { roles } }; } @@ -139,10 +139,10 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const { permissions, membership } = await server.services.orgRole.getUserPermission( req.permission.id, - req.params.organizationId + req.params.organizationId, + req.permission.orgId ); return { permissions, membership }; } diff --git a/backend/src/ee/routes/v1/project-role-router.ts b/backend/src/ee/routes/v1/project-role-router.ts index 622762bca..f6fd53e5e 100644 --- a/backend/src/ee/routes/v1/project-role-router.ts +++ b/backend/src/ee/routes/v1/project-role-router.ts @@ -30,7 +30,8 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { req.permission.type, req.permission.id, req.params.projectId, - req.body + req.body, + req.permission.orgId ); return { role }; } @@ -63,7 +64,8 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { req.permission.id, req.params.projectId, req.params.roleId, - req.body + req.body, + req.permission.orgId ); return { role }; } @@ -89,7 +91,8 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { req.permission.type, req.permission.id, req.params.projectId, - req.params.roleId + req.params.roleId, + req.permission.orgId ); return { role }; } @@ -117,7 +120,8 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { const roles = await server.services.projectRole.listRoles( req.permission.type, req.permission.id, - req.params.projectId + req.params.projectId, + req.permission.orgId ); return { data: { roles } }; } @@ -141,10 +145,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const { permissions, membership } = await server.services.projectRole.getUserPermission( req.permission.id, - req.params.projectId + req.params.projectId, + req.permission.orgId ); return { data: { permissions, membership } }; } diff --git a/backend/src/ee/routes/v1/project-router.ts b/backend/src/ee/routes/v1/project-router.ts index 294150c83..cfcecb8f0 100644 --- a/backend/src/ee/routes/v1/project-router.ts +++ b/backend/src/ee/routes/v1/project-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas"; import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -10,12 +11,19 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { method: "GET", url: "/:workspaceId/secret-snapshots", schema: { + description: "Return project secret snapshots ids", + security: [ + { + apiKeyAuth: [], + bearerAuth: [] + } + ], params: z.object({ workspaceId: z.string().trim() }), querystring: z.object({ environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), offset: z.coerce.number().default(0), limit: z.coerce.number().default(20) }), @@ -30,6 +38,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const secretSnapshots = await server.services.snapshot.listSnapshots({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, ...req.query }); @@ -46,7 +55,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { }), querystring: z.object({ environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -59,6 +68,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const count = await server.services.snapshot.projectSecretSnapshotCount({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, environment: req.query.environment, path: req.query.path @@ -71,6 +81,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { method: "GET", url: "/:workspaceId/audit-logs", schema: { + description: "Return audit logs", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ workspaceId: z.string().trim() }), @@ -111,6 +128,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { handler: async (req) => { const auditLogs = await server.services.auditLog.listProjectAuditLogs({ actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, ...req.query, auditLogActor: req.query.actor, diff --git a/backend/src/ee/routes/v1/saml-router.ts b/backend/src/ee/routes/v1/saml-router.ts index a8e29d08c..00dd09c33 100644 --- a/backend/src/ee/routes/v1/saml-router.ts +++ b/backend/src/ee/routes/v1/saml-router.ts @@ -1,3 +1,11 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +// All the any rules are disabled because passport typesense with fastify is really poor + import { Authenticator } from "@fastify/passport"; import fastifySession from "@fastify/session"; import { MultiSamlStrategy } from "@node-saml/passport-saml"; @@ -5,13 +13,12 @@ import { FastifyRequest } from "fastify"; import { z } from "zod"; import { SamlConfigsSchema } from "@app/db/schemas"; -import { SamlProviders } from "@app/ee/services/saml-config/saml-config-types"; +import { SamlProviders, TGetSamlCfgDTO } from "@app/ee/services/saml-config/saml-config-types"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; -import { getServerCfg } from "@app/services/super-admin/super-admin-service"; type TSAMLConfig = { callbackUrl: string; @@ -20,6 +27,7 @@ type TSAMLConfig = { cert: string; audience: string; wantAuthnResponseSigned?: boolean; + disableRequestedAuthnContext?: boolean; }; export const registerSamlRouter = async (server: FastifyZodProvider) => { @@ -33,19 +41,33 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { new MultiSamlStrategy( { passReqToCallback: true, + // eslint-disable-next-line getSamlOptions: async (req, done) => { try { - const { ssoIdentifier } = req.params; - if (!ssoIdentifier) throw new BadRequestError({ message: "Missing sso identitier" }); + const { samlConfigId, orgSlug } = req.params; - const ssoConfig = await server.services.saml.getSaml({ - type: "ssoId", - id: ssoIdentifier - }); - if (!ssoConfig) throw new BadRequestError({ message: "SSO config not found" }); + let ssoLookupDetails: TGetSamlCfgDTO; + + if (orgSlug) { + ssoLookupDetails = { + type: "orgSlug", + orgSlug + }; + } else if (samlConfigId) { + ssoLookupDetails = { + type: "ssoId", + id: samlConfigId + }; + } else { + throw new BadRequestError({ message: "Missing sso identitier or org slug" }); + } + + const ssoConfig = await server.services.saml.getSaml(ssoLookupDetails); + if (!ssoConfig || !ssoConfig.isActive) + throw new BadRequestError({ message: "Failed to authenticate with SAML SSO" }); const samlConfig: TSAMLConfig = { - callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoIdentifier}`, + callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoConfig.id}`, entryPoint: ssoConfig.entryPoint, issuer: ssoConfig.issuer, cert: ssoConfig.cert, @@ -55,7 +77,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { samlConfig.wantAuthnResponseSigned = false; } if (ssoConfig.authProvider === SamlProviders.AZURE_SAML) { - if (req.body.RelayState && JSON.parse(req.body.RelayState).spIntiaited) { + samlConfig.disableRequestedAuthnContext = true; + if (req.body?.RelayState && JSON.parse(req.body.RelayState).spInitiated) { samlConfig.audience = `spn:${ssoConfig.issuer}`; } } @@ -67,19 +90,21 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { } } }, + // eslint-disable-next-line async (req, profile, cb) => { try { - const serverCfg = getServerCfg(); if (!profile) throw new BadRequestError({ message: "Missing profile" }); - const { email, firstName } = profile; - if (!email || !firstName) + const { firstName } = profile; + const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved + + if (!email || !firstName) { throw new BadRequestError({ message: "Invalid request. Missing email or first name" }); + } const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({ email, firstName: profile.firstName as string, lastName: profile.lastName as string, - isSignupAllowed: Boolean(serverCfg.allowSignUp), relayState: (req.body as { RelayState?: string }).RelayState, authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string, orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string @@ -95,11 +120,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { ); server.route({ - url: "/redirect/saml2/:ssoIdentifier", + url: "/redirect/saml2/organizations/:orgSlug", method: "GET", schema: { params: z.object({ - ssoIdentifier: z.string().trim() + orgSlug: z.string().trim() }), querystring: z.object({ callback_port: z.string().optional() @@ -121,11 +146,37 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { }); server.route({ - url: "/saml2/:ssoIdentifier", + url: "/redirect/saml2/:samlConfigId", + method: "GET", + schema: { + params: z.object({ + samlConfigId: z.string().trim() + }), + querystring: z.object({ + callback_port: z.string().optional() + }) + }, + preValidation: (req, res) => + ( + passport.authenticate("saml", { + failureRedirect: "/", + additionalParams: { + RelayState: JSON.stringify({ + spInitiated: true, + callbackPort: req.query.callback_port ?? "" + }) + } + } as any) as any + )(req, res), + handler: () => {} + }); + + server.route({ + url: "/saml2/:samlConfigId", method: "POST", schema: { params: z.object({ - ssoIdentifier: z.string().trim() + samlConfigId: z.string().trim() }) }, preValidation: passport.authenticate("saml", { @@ -137,15 +188,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { handler: (req, res) => { if (req.passportUser.isUserCompleted) { return res.redirect( - `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } return res.redirect( - `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } }); @@ -168,7 +215,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { isActive: z.boolean(), entryPoint: z.string(), issuer: z.string(), - cert: z.string() + cert: z.string(), + lastUsed: z.date().nullable().optional() }) .optional() } @@ -177,6 +225,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { const saml = await server.services.saml.getSaml({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, orgId: req.query.organizationId, type: "org" }); @@ -205,6 +254,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { const saml = await server.services.saml.createSamlCfg({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, orgId: req.body.organizationId, ...req.body }); @@ -235,6 +285,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { const saml = await server.services.saml.updateSamlCfg({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, orgId: req.body.organizationId, ...req.body }); diff --git a/backend/src/ee/routes/v1/secret-approval-policy-router.ts b/backend/src/ee/routes/v1/secret-approval-policy-router.ts index 49d2b95ad..8fce232a7 100644 --- a/backend/src/ee/routes/v1/secret-approval-policy-router.ts +++ b/backend/src/ee/routes/v1/secret-approval-policy-router.ts @@ -34,6 +34,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi const approval = await server.services.secretApprovalPolicy.createSecretApprovalPolicy({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.body.workspaceId, ...req.body, name: req.body.name ?? `${req.body.environment}-${nanoid(3)}` @@ -71,6 +72,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi const approval = await server.services.secretApprovalPolicy.updateSecretApprovalPolicy({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, ...req.body, secretPolicyId: req.params.sapId }); @@ -96,6 +98,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi const approval = await server.services.secretApprovalPolicy.deleteSecretApprovalPolicy({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, secretPolicyId: req.params.sapId }); return { approval }; @@ -111,7 +114,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi }), response: { 200: z.object({ - approvals: sapPubSchema.merge(z.object({approvers:z.string().array()})).array() + approvals: sapPubSchema.merge(z.object({ approvers: z.string().array() })).array() }) } }, @@ -120,6 +123,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi const approvals = await server.services.secretApprovalPolicy.getSecretApprovalPolicyByProjectId({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.query.workspaceId }); return { approvals }; @@ -137,7 +141,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi }), response: { 200: z.object({ - policy: sapPubSchema.merge(z.object({approvers:z.string().array()})).optional() + policy: sapPubSchema.merge(z.object({ approvers: z.string().array() })).optional() }) } }, @@ -146,6 +150,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.query.workspaceId, ...req.query }); diff --git a/backend/src/ee/routes/v1/secret-approval-request-router.ts b/backend/src/ee/routes/v1/secret-approval-request-router.ts index a745a9b04..97eb89109 100644 --- a/backend/src/ee/routes/v1/secret-approval-request-router.ts +++ b/backend/src/ee/routes/v1/secret-approval-request-router.ts @@ -9,10 +9,7 @@ import { SecretVersionsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; -import { - ApprovalStatus, - RequestState -} from "@app/ee/services/secret-approval-request/secret-approval-request-types"; +import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -41,9 +38,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv approvers: z.string().array(), secretPath: z.string().optional().nullable() }), - commits: z - .object({ op: z.string(), secretId: z.string().nullable().optional() }) - .array(), + commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(), environment: z.string(), reviewers: z.object({ member: z.string(), status: z.string() }).array(), approvers: z.string().array() @@ -57,6 +52,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv const approvals = await server.services.secretApprovalRequest.getSecretApprovals({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, ...req.query, projectId: req.query.workspaceId }); @@ -85,6 +81,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv const approvals = await server.services.secretApprovalRequest.requestCount({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.query.workspaceId }); return { approvals }; @@ -109,6 +106,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv const { approval } = await server.services.secretApprovalRequest.mergeSecretApprovalRequest({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, approvalId: req.params.id }); return { approval }; @@ -136,6 +134,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv const review = await server.services.secretApprovalRequest.reviewApproval({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, approvalId: req.params.id, status: req.body.status }); @@ -164,6 +163,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv const approval = await server.services.secretApprovalRequest.updateApprovalStatus({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, approvalId: req.params.id, status: req.body.status }); @@ -174,11 +174,12 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv ...req.auditLogInfo, event: { type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED, + // eslint-disable-next-line metadata: { - [isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: - approval.statusChangeBy as string, + [isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeBy as string, secretApprovalRequestId: approval.id, secretApprovalRequestSlug: approval.slug + // eslint-disable-next-line } as any // akhilmhdh: had to apply any to avoid ts issue with this } @@ -270,6 +271,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv const approval = await server.services.secretApprovalRequest.getSecretApprovalDetails({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, id: req.params.id }); return { approval }; diff --git a/backend/src/ee/routes/v1/secret-rotation-provider-router.ts b/backend/src/ee/routes/v1/secret-rotation-provider-router.ts index bcaf1ab39..e7201b73f 100644 --- a/backend/src/ee/routes/v1/secret-rotation-provider-router.ts +++ b/backend/src/ee/routes/v1/secret-rotation-provider-router.ts @@ -30,6 +30,7 @@ export const registerSecretRotationProviderRouter = async (server: FastifyZodPro const providers = await server.services.secretRotation.getProviderTemplates({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId }); return providers; diff --git a/backend/src/ee/routes/v1/secret-rotation-router.ts b/backend/src/ee/routes/v1/secret-rotation-router.ts index 95eb0bc88..8d2e90ac0 100644 --- a/backend/src/ee/routes/v1/secret-rotation-router.ts +++ b/backend/src/ee/routes/v1/secret-rotation-router.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { SecretRotationOutputsSchema, SecretRotationsSchema, SecretsSchema } from "@app/db/schemas"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -11,7 +12,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) = schema: { body: z.object({ workspaceId: z.string().trim(), - secretPath: z.string().trim(), + secretPath: z.string().trim().transform(removeTrailingSlash), environment: z.string().trim(), interval: z.number().min(1), provider: z.string().trim(), @@ -39,6 +40,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) = const secretRotation = await server.services.secretRotation.createRotation({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, ...req.body, projectId: req.body.workspaceId }); @@ -72,6 +74,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) = const secretRotation = await server.services.secretRotation.restartById({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, rotationId: req.body.id }); return { secretRotation }; @@ -122,6 +125,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) = const secretRotations = await server.services.secretRotation.getByProjectId({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.query.workspaceId }); return { secretRotations }; @@ -154,6 +158,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) = const secretRotation = await server.services.secretRotation.deleteById({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, rotationId: req.params.id }); return { secretRotation }; diff --git a/backend/src/ee/routes/v1/secret-scanning-router.ts b/backend/src/ee/routes/v1/secret-scanning-router.ts index 48898eddb..7d2c5f1ee 100644 --- a/backend/src/ee/routes/v1/secret-scanning-router.ts +++ b/backend/src/ee/routes/v1/secret-scanning-router.ts @@ -22,6 +22,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) = const session = await server.services.secretScanning.createInstallationSession({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, orgId: req.body.organizationId }); return session; @@ -45,6 +46,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) = const { installatedApp } = await server.services.secretScanning.linkInstallationToOrg({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, ...req.body }); return installatedApp; @@ -62,12 +64,12 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) = }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const appInstallationCompleted = - await server.services.secretScanning.getOrgInstallationStatus({ - actor: req.permission.type, - actorId: req.permission.id, - orgId: req.params.organizationId - }); + const appInstallationCompleted = await server.services.secretScanning.getOrgInstallationStatus({ + actor: req.permission.type, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + orgId: req.params.organizationId + }); return { appInstallationCompleted }; } }); @@ -86,6 +88,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) = const { risks } = await server.services.secretScanning.getRisksByOrg({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId }); return { risks }; @@ -107,6 +110,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) = const { risk } = await server.services.secretScanning.updateRiskStatus({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, orgId: req.params.organizationId, riskId: req.params.riskId, ...req.body diff --git a/backend/src/ee/routes/v1/secret-version-router.ts b/backend/src/ee/routes/v1/secret-version-router.ts index 269ed8636..89ee4e011 100644 --- a/backend/src/ee/routes/v1/secret-version-router.ts +++ b/backend/src/ee/routes/v1/secret-version-router.ts @@ -27,6 +27,7 @@ export const registerSecretVersionRouter = async (server: FastifyZodProvider) => const secretVersions = await server.services.secret.getSecretVersions({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, limit: req.query.limit, offset: req.query.offset, secretId: req.params.secretId diff --git a/backend/src/ee/routes/v1/snapshot-router.ts b/backend/src/ee/routes/v1/snapshot-router.ts index c3b9d2d98..0b858255f 100644 --- a/backend/src/ee/routes/v1/snapshot-router.ts +++ b/backend/src/ee/routes/v1/snapshot-router.ts @@ -46,6 +46,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => { const secretSnapshot = await server.services.snapshot.getSnapshotData({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, id: req.params.secretSnapshotId }); return { secretSnapshot }; @@ -56,6 +57,13 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/:secretSnapshotId/rollback", schema: { + description: "Roll back project secrets to those captured in a secret snapshot version.", + security: [ + { + apiKeyAuth: [], + bearerAuth: [] + } + ], params: z.object({ secretSnapshotId: z.string().trim() }), @@ -70,6 +78,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => { const secretSnapshot = await server.services.snapshot.rollbackSnapshot({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, id: req.params.secretSnapshotId }); return { secretSnapshot }; diff --git a/backend/src/ee/routes/v1/trusted-ip-router.ts b/backend/src/ee/routes/v1/trusted-ip-router.ts index fd56a2cda..53bc5b117 100644 --- a/backend/src/ee/routes/v1/trusted-ip-router.ts +++ b/backend/src/ee/routes/v1/trusted-ip-router.ts @@ -24,7 +24,8 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => { const trustedIps = await server.services.trustedIp.listIpsByProjectId({ projectId: req.params.workspaceId, actor: req.permission.type, - actorId: req.permission.id + actorId: req.permission.id, + actorOrgId: req.permission.orgId }); return { trustedIps }; } @@ -54,6 +55,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => { projectId: req.params.workspaceId, actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, ...req.body }); await server.services.auditLog.createAuditLog({ @@ -97,6 +99,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => { projectId: req.params.workspaceId, actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, trustedIpId: req.params.trustedIpId, ...req.body }); @@ -137,6 +140,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => { projectId: req.params.workspaceId, actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, trustedIpId: req.params.trustedIpId }); await server.services.auditLog.createAuditLog({ diff --git a/backend/src/ee/services/audit-log/audit-log-dal.ts b/backend/src/ee/services/audit-log/audit-log-dal.ts index ec5f6ab3d..b3ad8c2b6 100644 --- a/backend/src/ee/services/audit-log/audit-log-dal.ts +++ b/backend/src/ee/services/audit-log/audit-log-dal.ts @@ -2,6 +2,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; +import { DatabaseError } from "@app/lib/errors"; import { ormify, stripUndefinedInWhere } from "@app/lib/knex"; export type TAuditLogDALFactory = ReturnType; @@ -22,40 +23,46 @@ export const auditLogDALFactory = (db: TDbClient) => { const auditLogOrm = ormify(db, TableName.AuditLog); const find = async ( - { - orgId, - projectId, - userAgentType, - startDate, - endDate, - limit = 20, - offset = 0, - actor, - eventType - }: TFindQuery, + { orgId, projectId, userAgentType, startDate, endDate, limit = 20, offset = 0, actor, eventType }: TFindQuery, tx?: Knex ) => { - const sqlQuery = (tx || db)(TableName.AuditLog) - .where( - stripUndefinedInWhere({ - projectId, - orgId, - eventType, - actor, - userAgentType - }) - ) - .limit(limit) - .offset(offset); - if (startDate) { - sqlQuery.where("createdAt", ">=", startDate); + try { + const sqlQuery = (tx || db)(TableName.AuditLog) + .where( + stripUndefinedInWhere({ + projectId, + orgId, + eventType, + actor, + userAgentType + }) + ) + .limit(limit) + .offset(offset) + .orderBy("createdAt", "desc"); + if (startDate) { + void sqlQuery.where("createdAt", ">=", startDate); + } + if (endDate) { + void sqlQuery.where("createdAt", "<=", endDate); + } + const docs = await sqlQuery; + return docs; + } catch (error) { + throw new DatabaseError({ error }); } - if (endDate) { - sqlQuery.where("createdAt", "<=", endDate); - } - const docs = await sqlQuery; - return docs; }; - return { ...auditLogOrm, find }; + // delete all audit log that have expired + const pruneAuditLog = async (tx?: Knex) => { + try { + const today = new Date(); + const docs = await (tx || db)(TableName.AuditLog).where("expiresAt", "<", today).del(); + return docs; + } catch (error) { + throw new DatabaseError({ error, name: "PruneAuditLog" }); + } + }; + + return { ...auditLogOrm, pruneAuditLog, find }; }; diff --git a/backend/src/ee/services/audit-log/audit-log-queue.ts b/backend/src/ee/services/audit-log/audit-log-queue.ts index bcdf92920..6f2c93221 100644 --- a/backend/src/ee/services/audit-log/audit-log-queue.ts +++ b/backend/src/ee/services/audit-log/audit-log-queue.ts @@ -1,3 +1,4 @@ +import { logger } from "@app/lib/logger"; import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; import { TProjectDALFactory } from "@app/services/project/project-dal"; @@ -43,6 +44,8 @@ export const auditLogQueueServiceFactory = ({ const plan = await licenseService.getPlan(orgId); const ttl = plan.auditLogsRetentionDays * MS_IN_DAY; + // skip inserting if audit log retention is 0 meaning its not supported + if (ttl === 0) return; await auditLogDAL.create({ actor: actor.type, actorMetadata: actor.metadata, @@ -57,7 +60,35 @@ export const auditLogQueueServiceFactory = ({ }); }); + queueService.start(QueueName.AuditLogPrune, async () => { + logger.info(`${QueueName.AuditLogPrune}: queue task started`); + await auditLogDAL.pruneAuditLog(); + logger.info(`${QueueName.AuditLogPrune}: queue task completed`); + }); + + // we do a repeat cron job in utc timezone at 12 Midnight each day + const startAuditLogPruneJob = async () => { + // clear previous job + await queueService.stopRepeatableJob( + QueueName.AuditLogPrune, + QueueJobs.AuditLogPrune, + { pattern: "0 0 * * *", utc: true }, + QueueName.AuditLogPrune // just a job id + ); + + await queueService.queue(QueueName.AuditLogPrune, QueueJobs.AuditLogPrune, undefined, { + delay: 5000, + jobId: QueueName.AuditLogPrune, + repeat: { pattern: "0 0 * * *", utc: true } + }); + }; + + queueService.listen(QueueName.AuditLogPrune, "failed", (err) => { + logger.error(err?.failedReason, `${QueueName.AuditLogPrune}: log pruning failed`); + }); + return { - pushToLog + pushToLog, + startAuditLogPruneJob }; }; diff --git a/backend/src/ee/services/audit-log/audit-log-service.ts b/backend/src/ee/services/audit-log/audit-log-service.ts index 7f5d0007f..1c7868fc2 100644 --- a/backend/src/ee/services/audit-log/audit-log-service.ts +++ b/backend/src/ee/services/audit-log/audit-log-service.ts @@ -30,14 +30,12 @@ export const auditLogServiceFactory = ({ startDate, actor, actorId, + actorOrgId, projectId, auditLogActor }: TListProjectAuditLogDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.AuditLogs - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs); const auditLogs = await auditLogDAL.find({ startDate, endDate, @@ -48,20 +46,17 @@ export const auditLogServiceFactory = ({ actor: auditLogActor, projectId }); - return auditLogs.map( - ({ eventType: logEventType, actor: eActor, actorMetadata, eventMetadata, ...el }) => ({ - ...el, - event: { type: logEventType, metadata: eventMetadata }, - actor: { type: eActor, metadata: actorMetadata } - }) - ); + return auditLogs.map(({ eventType: logEventType, actor: eActor, actorMetadata, eventMetadata, ...el }) => ({ + ...el, + event: { type: logEventType, metadata: eventMetadata }, + actor: { type: eActor, metadata: actorMetadata } + })); }; const createAuditLog = async (data: TCreateAuditLogDTO) => { // add all cases in which project id or org id cannot be added if (data.event.type !== EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH) { - if (!data.projectId && !data.orgId) - throw new BadRequestError({ message: "Must either project id or org id" }); + if (!data.projectId && !data.orgId) throw new BadRequestError({ message: "Must either project id or org id" }); } return auditLogQueue.pushToLog(data); }; diff --git a/backend/src/ee/services/license/licence-fns.ts b/backend/src/ee/services/license/licence-fns.ts index 022b4fb9a..7014eac5f 100644 --- a/backend/src/ee/services/license/licence-fns.ts +++ b/backend/src/ee/services/license/licence-fns.ts @@ -31,15 +31,11 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ secretRotation: true }); -export const setupLicenceRequestWithStore = ( - baseURL: string, - refreshUrl: string, - licenseKey: string -) => { +export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => { let token: string; const licenceReq = axios.create({ baseURL, - // timeout: 60 * 1000, + timeout: 35 * 1000 // signal: AbortSignal.timeout(60 * 1000) }); @@ -47,7 +43,7 @@ export const setupLicenceRequestWithStore = ( const appCfg = getConfig(); const { data: { token: authToken } - } = await request.post( + } = await request.post<{ token: string }>( refreshUrl, {}, { @@ -75,18 +71,18 @@ export const setupLicenceRequestWithStore = ( licenceReq.interceptors.response.use( (response) => response, async (err) => { - const originalRequest = err.config; + const originalRequest = (err as AxiosError).config; // eslint-disable-next-line - if ((err as AxiosError)?.response?.status === 401 && !originalRequest._retry) { + if ((err as AxiosError)?.response?.status === 401 && !(originalRequest as any)._retry) { // eslint-disable-next-line - originalRequest._retry = true; + (originalRequest as any)._retry = true; // injected // refresh await refreshLicence(); licenceReq.defaults.headers.common.Authorization = `Bearer ${token}`; - return licenceReq(originalRequest); + return licenceReq(originalRequest!); } return Promise.reject(err); diff --git a/backend/src/ee/services/license/license-dal.ts b/backend/src/ee/services/license/license-dal.ts index 208cb6f9b..4e70dfb5a 100644 --- a/backend/src/ee/services/license/license-dal.ts +++ b/backend/src/ee/services/license/license-dal.ts @@ -13,7 +13,7 @@ export const licenseDALFactory = (db: TDbClient) => { .where({ status: OrgMembershipStatus.Accepted }) .andWhere((bd) => { if (orgId) { - bd.where({ orgId }); + void bd.where({ orgId }); } }) .count(); diff --git a/backend/src/ee/services/license/license-service.ts b/backend/src/ee/services/license/license-service.ts index d633916e0..6d97b537c 100644 --- a/backend/src/ee/services/license/license-service.ts +++ b/backend/src/ee/services/license/license-service.ts @@ -1,3 +1,9 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +// eslint-disable @typescript-eslint/no-unsafe-assignment + +// TODO(akhilmhdh): With tony find out the api structure and fill it here + import { ForbiddenError } from "@casl/ability"; import NodeCache from "node-cache"; @@ -14,6 +20,7 @@ import { InstanceType, TAddOrgPmtMethodDTO, TAddOrgTaxIdDTO, + TCreateOrgPortalSession, TDelOrgPmtMethodDTO, TDelOrgTaxIdDTO, TFeatureSet, @@ -24,7 +31,7 @@ import { TOrgPlanDTO, TOrgPlansTableDTO, TOrgPmtMethodsDTO, - TStartOrgTrailDTO, + TStartOrgTrialDTO, TUpdateOrgBillingDetailsDTO } from "./license-types"; @@ -37,14 +44,10 @@ type TLicenseServiceFactoryDep = { export type TLicenseServiceFactory = ReturnType; const LICENSE_SERVER_CLOUD_LOGIN = "/api/auth/v1/license-server-login"; -const LICENSE_SERVER_ON_PREM_LOGIN = "/api/auth/v1/licence-login"; +const LICENSE_SERVER_ON_PREM_LOGIN = "/api/auth/v1/license-login"; const FEATURE_CACHE_KEY = (orgId: string, projectId?: string) => `${orgId}-${projectId || ""}`; -export const licenseServiceFactory = ({ - orgDAL, - permissionService, - licenseDAL -}: TLicenseServiceFactoryDep) => { +export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }: TLicenseServiceFactoryDep) => { let isValidLicense = false; let instanceType = InstanceType.OnPrem; let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures(); @@ -77,9 +80,7 @@ export const licenseServiceFactory = ({ if (token) { const { data: { currentPlan } - } = await licenseServerOnPremApi.request.get<{ currentPlan: TFeatureSet }>( - "/api/license/v1/plan" - ); + } = await licenseServerOnPremApi.request.get<{ currentPlan: TFeatureSet }>("/api/license/v1/plan"); onPremFeatures = currentPlan; instanceType = InstanceType.EnterpriseOnPrem; logger.info(`Instance type: ${InstanceType.EnterpriseOnPrem}`); @@ -91,7 +92,7 @@ export const licenseServiceFactory = ({ // else it would reach catch statement isValidLicense = true; } catch (error) { - logger.error(`init-license: encountered an error when init license [error=${error}]`); + logger.error(error, `init-license: encountered an error when init license`); } }; @@ -118,7 +119,10 @@ export const licenseServiceFactory = ({ return currentPlan; } } catch (error) { - logger.error(`getPlan: encountered an error when fetching pan [orgId=${orgId}] [projectId=${projectId}] [error=${error}]`); + logger.error( + `getPlan: encountered an error when fetching pan [orgId=${orgId}] [projectId=${projectId}] [error]`, + error + ); return onPremFeatures; } return onPremFeatures; @@ -135,7 +139,7 @@ export const licenseServiceFactory = ({ if (instanceType === InstanceType.Cloud) { const { data: { customerId } - } = await licenseServerCloudApi.request.post( + } = await licenseServerCloudApi.request.post<{ customerId: string }>( "/api/license-server/v1/customers", { email, @@ -158,12 +162,9 @@ export const licenseServiceFactory = ({ const count = await licenseDAL.countOfOrgMembers(orgId); if (org?.customerId) { - await licenseServerCloudApi.request.patch( - `/api/license-server/v1/customers/${org.customerId}/cloud-plan`, - { - quantity: count - } - ); + await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, { + quantity: count + }); } featureStore.del(orgId); } else if (instanceType === InstanceType.EnterpriseOnPrem) { @@ -178,39 +179,28 @@ export const licenseServiceFactory = ({ orgId, actor, actorId, + actorOrgId, billingCycle }: TOrgPlansTableDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const { data } = await licenseServerCloudApi.request.get( `/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}` ); return data; }; - const getOrgPlan = async ({ orgId, actor, actorId, projectId }: TOrgPlanDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const getOrgPlan = async ({ orgId, actor, actorId, actorOrgId, projectId }: TOrgPlanDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const plan = await getPlan(orgId, projectId); return plan; }; - const startOrgTrail = async ({ orgId, actorId, actor, success_url }: TStartOrgTrailDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Billing - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Billing - ); + const startOrgTrial = async ({ orgId, actorId, actor, actorOrgId, success_url }: TStartOrgTrialDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -222,20 +212,64 @@ export const licenseServiceFactory = ({ const { data: { url } } = await licenseServerCloudApi.request.post( - `/api/license-server/v1/customers/${organization.customerId}/session/trail`, + `/api/license-server/v1/customers/${organization.customerId}/session/trial`, { success_url } ); featureStore.del(FEATURE_CACHE_KEY(orgId)); return { url }; }; - const getOrgBillingInfo = async ({ orgId, actor, actorId }: TGetOrgBillInfoDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing + const createOrganizationPortalSession = async ({ orgId, actorId, actor, actorOrgId }: TCreateOrgPortalSession) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); + + const organization = await orgDAL.findOrgById(orgId); + if (!organization) { + throw new BadRequestError({ + message: "Failed to find organization" + }); + } + + const { + data: { pmtMethods } + } = await licenseServerCloudApi.request.get<{ pmtMethods: string[] }>( + `/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods` ); + if (pmtMethods.length < 1) { + // case: organization has no payment method on file + // -> redirect to add payment method portal + const { + data: { url } + } = await licenseServerCloudApi.request.post( + `/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods`, + { + success_url: `${appCfg.SITE_URL}/dashboard`, + cancel_url: `${appCfg.SITE_URL}/dashboard` + } + ); + + return { url }; + } + // case: organization has payment method on file + // -> redirect to billing portal + const { + data: { url } + } = await licenseServerCloudApi.request.post( + `/api/license-server/v1/customers/${organization.customerId}/billing-details/billing-portal`, + { + return_url: `${appCfg.SITE_URL}/dashboard` + } + ); + + return { url }; + }; + + const getOrgBillingInfo = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + const organization = await orgDAL.findOrgById(orgId); if (!organization) { throw new BadRequestError({ @@ -249,12 +283,9 @@ export const licenseServiceFactory = ({ }; // returns org current plan feature table - const getOrgPlanTable = async ({ orgId, actor, actorId }: TGetOrgBillInfoDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const getOrgPlanTable = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -268,12 +299,9 @@ export const licenseServiceFactory = ({ return data; }; - const getOrgBillingDetails = async ({ orgId, actor, actorId }: TGetOrgBillInfoDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const getOrgBillingDetails = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -291,15 +319,13 @@ export const licenseServiceFactory = ({ const updateOrgBillingDetails = async ({ actorId, actor, + actorOrgId, orgId, name, email }: TUpdateOrgBillingDetailsDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -317,12 +343,9 @@ export const licenseServiceFactory = ({ return data; }; - const getOrgPmtMethods = async ({ orgId, actor, actorId }: TOrgPmtMethodsDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const getOrgPmtMethods = async ({ orgId, actor, actorId, actorOrgId }: TOrgPmtMethodsDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -343,14 +366,12 @@ export const licenseServiceFactory = ({ orgId, actor, actorId, + actorOrgId, success_url, cancel_url }: TAddOrgPmtMethodDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -370,12 +391,9 @@ export const licenseServiceFactory = ({ return { url }; }; - const delOrgPmtMethods = async ({ actorId, actor, orgId, pmtMethodId }: TDelOrgPmtMethodDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const delOrgPmtMethods = async ({ actorId, actor, actorOrgId, orgId, pmtMethodId }: TDelOrgPmtMethodDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -390,12 +408,9 @@ export const licenseServiceFactory = ({ return data; }; - const getOrgTaxIds = async ({ orgId, actor, actorId }: TGetOrgTaxIdDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const getOrgTaxIds = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgTaxIdDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -411,12 +426,9 @@ export const licenseServiceFactory = ({ return taxIds; }; - const addOrgTaxId = async ({ actorId, actor, orgId, type, value }: TAddOrgTaxIdDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const addOrgTaxId = async ({ actorId, actor, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -435,12 +447,9 @@ export const licenseServiceFactory = ({ return data; }; - const delOrgTaxId = async ({ orgId, actor, actorId, taxId }: TDelOrgTaxIdDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const delOrgTaxId = async ({ orgId, actor, actorId, actorOrgId, taxId }: TDelOrgTaxIdDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -455,12 +464,9 @@ export const licenseServiceFactory = ({ return data; }; - const getOrgTaxInvoices = async ({ actorId, actor, orgId }: TOrgInvoiceDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, orgId }: TOrgInvoiceDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -471,18 +477,13 @@ export const licenseServiceFactory = ({ const { data: { invoices } - } = await licenseServerCloudApi.request.get( - `/api/license-server/v1/customers/${organization.customerId}/invoices` - ); + } = await licenseServerCloudApi.request.get(`/api/license-server/v1/customers/${organization.customerId}/invoices`); return invoices; }; - const getOrgLicenses = async ({ orgId, actor, actorId }: TOrgLicensesDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + const getOrgLicenses = async ({ orgId, actor, actorId, actorOrgId }: TOrgLicensesDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -493,9 +494,7 @@ export const licenseServiceFactory = ({ const { data: { licenses } - } = await licenseServerCloudApi.request.get( - `/api/license-server/v1/customers/${organization.customerId}/licenses` - ); + } = await licenseServerCloudApi.request.get(`/api/license-server/v1/customers/${organization.customerId}/licenses`); return licenses; }; @@ -511,7 +510,8 @@ export const licenseServiceFactory = ({ refreshPlan, getOrgPlan, getOrgPlansTableByBillCycle, - startOrgTrail, + startOrgTrial, + createOrganizationPortalSession, getOrgBillingInfo, getOrgPlanTable, getOrgBillingDetails, diff --git a/backend/src/ee/services/license/license-types.ts b/backend/src/ee/services/license/license-types.ts index f4cd2db1c..762aff5b2 100644 --- a/backend/src/ee/services/license/license-types.ts +++ b/backend/src/ee/services/license/license-types.ts @@ -40,10 +40,12 @@ export type TOrgPlanDTO = { projectId?: string; } & TOrgPermission; -export type TStartOrgTrailDTO = { +export type TStartOrgTrialDTO = { success_url: string; } & TOrgPermission; +export type TCreateOrgPortalSession = TOrgPermission; + export type TGetOrgBillInfoDTO = TOrgPermission; export type TOrgPlanTableDTO = TOrgPermission; diff --git a/backend/src/ee/services/permission/permission-dal.ts b/backend/src/ee/services/permission/permission-dal.ts index d2529f18b..ea195bc06 100644 --- a/backend/src/ee/services/permission/permission-dal.ts +++ b/backend/src/ee/services/permission/permission-dal.ts @@ -9,13 +9,11 @@ export const permissionDALFactory = (db: TDbClient) => { const getOrgPermission = async (userId: string, orgId: string) => { try { const membership = await db(TableName.OrgMembership) - .leftJoin( - TableName.OrgRoles, - `${TableName.OrgMembership}.roleId`, - `${TableName.OrgRoles}.id` - ) + .leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`) + .join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`) .where("userId", userId) .where(`${TableName.OrgMembership}.orgId`, orgId) + .select(db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced")) .select("permissions") .select(selectAllTableCols(TableName.OrgMembership)) .first(); @@ -29,14 +27,12 @@ export const permissionDALFactory = (db: TDbClient) => { const getOrgIdentityPermission = async (identityId: string, orgId: string) => { try { const membership = await db(TableName.IdentityOrgMembership) - .leftJoin( - TableName.OrgRoles, - `${TableName.IdentityOrgMembership}.roleId`, - `${TableName.OrgRoles}.id` - ) + .leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`) + .join(TableName.Organization, `${TableName.IdentityOrgMembership}.orgId`, `${TableName.Organization}.id`) .where("identityId", identityId) .where(`${TableName.IdentityOrgMembership}.orgId`, orgId) .select(selectAllTableCols(TableName.IdentityOrgMembership)) + .select(db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced")) .select("permissions") .first(); return membership; @@ -48,14 +44,16 @@ export const permissionDALFactory = (db: TDbClient) => { const getProjectPermission = async (userId: string, projectId: string) => { try { const membership = await db(TableName.ProjectMembership) - .leftJoin( - TableName.ProjectRoles, - `${TableName.ProjectMembership}.roleId`, - `${TableName.ProjectRoles}.id` - ) + .leftJoin(TableName.ProjectRoles, `${TableName.ProjectMembership}.roleId`, `${TableName.ProjectRoles}.id`) + .join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) + .join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`) .where("userId", userId) .where(`${TableName.ProjectMembership}.projectId`, projectId) .select(selectAllTableCols(TableName.ProjectMembership)) + .select( + db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"), + db.ref("orgId").withSchema(TableName.Project) + ) .select("permissions") .first(); diff --git a/backend/src/ee/services/permission/permission-service.ts b/backend/src/ee/services/permission/permission-service.ts index 8549d00ee..4735312e4 100644 --- a/backend/src/ee/services/permission/permission-service.ts +++ b/backend/src/ee/services/permission/permission-service.ts @@ -16,12 +16,7 @@ import { TOrgRoleDALFactory } from "@app/services/org/org-role-dal"; import { TProjectRoleDALFactory } from "@app/services/project-role/project-role-dal"; import { TServiceTokenDALFactory } from "@app/services/service-token/service-token-dal"; -import { - orgAdminPermissions, - orgMemberPermissions, - orgNoAccessPermissions, - OrgPermissionSet -} from "./org-permission"; +import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission"; import { TPermissionDALFactory } from "./permission-dal"; import { buildServiceTokenProjectPermission, @@ -99,12 +94,15 @@ export const permissionServiceFactory = ({ /* * Get user permission in an organization * */ - const getUserOrgPermission = async (userId: string, orgId: string) => { + const getUserOrgPermission = async (userId: string, orgId: string, userOrgId?: string) => { const membership = await permissionDAL.getOrgPermission(userId, orgId); if (!membership) throw new UnauthorizedError({ name: "User not in org" }); if (membership.role === OrgMembershipRole.Custom && !membership.permissions) { throw new BadRequestError({ name: "Custom permission not found" }); } + if (membership.orgAuthEnforced && membership.orgId !== userOrgId) { + throw new BadRequestError({ name: "Cannot access org-scoped resource" }); + } return { permission: buildOrgPermission(membership.role, membership.permissions), membership }; }; @@ -117,10 +115,10 @@ export const permissionServiceFactory = ({ return { permission: buildOrgPermission(membership.role, membership.permissions), membership }; }; - const getOrgPermission = async (type: ActorType, id: string, orgId: string) => { + const getOrgPermission = async (type: ActorType, id: string, orgId: string, actorOrgId?: string) => { switch (type) { case ActorType.USER: - return getUserOrgPermission(id, orgId); + return getUserOrgPermission(id, orgId, actorOrgId); case ActorType.IDENTITY: return getIdentityOrgPermission(id, orgId); default: @@ -147,12 +145,17 @@ export const permissionServiceFactory = ({ }; // user permission for a project in an organization - const getUserProjectPermission = async (userId: string, projectId: string) => { + const getUserProjectPermission = async (userId: string, projectId: string, userOrgId?: string) => { const membership = await permissionDAL.getProjectPermission(userId, projectId); if (!membership) throw new UnauthorizedError({ name: "User not in project" }); if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) { throw new BadRequestError({ name: "Custom permission not found" }); } + + if (membership.orgAuthEnforced && membership.orgId !== userOrgId) { + throw new BadRequestError({ name: "Cannot access org-scoped resource" }); + } + return { permission: buildProjectPermission(membership.role, membership.permissions), membership @@ -165,6 +168,7 @@ export const permissionServiceFactory = ({ if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) { throw new BadRequestError({ name: "Custom permission not found" }); } + return { permission: buildProjectPermission(membership.role, membership.permissions), membership @@ -188,19 +192,22 @@ export const permissionServiceFactory = ({ ? { permission: MongoAbility; membership: undefined } : { permission: MongoAbility; - membership: (T extends ActorType.USER - ? TProjectMemberships - : TIdentityProjectMemberships) & { permissions?: unknown }; + membership: (T extends ActorType.USER ? TProjectMemberships : TIdentityProjectMemberships) & { + orgAuthEnforced: boolean; + orgId: string; + permissions?: unknown; + }; }; const getProjectPermission = async ( type: T, id: string, - projectId: string + projectId: string, + actorOrgId?: string ): Promise> => { switch (type) { case ActorType.USER: - return getUserProjectPermission(id, projectId) as Promise>; + return getUserProjectPermission(id, projectId, actorOrgId) as Promise>; case ActorType.SERVICE: return getServiceTokenProjectPermission(id, projectId) as Promise>; case ActorType.IDENTITY: @@ -214,9 +221,7 @@ export const permissionServiceFactory = ({ }; const getProjectPermissionByRole = async (role: string, projectId: string) => { - const isCustomRole = !Object.values(ProjectMembershipRole).includes( - role as ProjectMembershipRole - ); + const isCustomRole = !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole); if (isCustomRole) { const projectRole = await projectRoleDAL.findOne({ slug: role, projectId }); if (!projectRole) throw new BadRequestError({ message: "Role not found" }); diff --git a/backend/src/ee/services/permission/project-permission.ts b/backend/src/ee/services/permission/project-permission.ts index f3fb67705..5245c26e4 100644 --- a/backend/src/ee/services/permission/project-permission.ts +++ b/backend/src/ee/services/permission/project-permission.ts @@ -239,17 +239,29 @@ export const buildServiceTokenProjectPermission = ( const { can, build } = new AbilityBuilder>(createMongoAbility); scopes.forEach(({ secretPath, environment }) => { if (canWrite) { - // TODO: @Akhi - // @ts-expect-error type - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets, { secretPath: { $glob: secretPath }, environment }); - // @ts-expect-error type - can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets, { secretPath: { $glob: secretPath }, environment }); - // @ts-expect-error type - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets, {secretPath: { $glob: secretPath }, environment }); + // TODO: @Akhi + // @ts-expect-error type + can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets, { + secretPath: { $glob: secretPath }, + environment + }); + // @ts-expect-error type + can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets, { + secretPath: { $glob: secretPath }, + environment + }); + // @ts-expect-error type + can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets, { + secretPath: { $glob: secretPath }, + environment + }); } if (canRead) { - // @ts-expect-error type - can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets, { secretPath: { $glob: secretPath }, environment }); + // @ts-expect-error type + can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets, { + secretPath: { $glob: secretPath }, + environment + }); } }); @@ -258,6 +270,8 @@ export const buildServiceTokenProjectPermission = ( export const projectNoAccessPermissions = buildNoAccessProjectPermission(); +/* eslint-disable */ + /** * Extracts and formats permissions from a CASL Ability object or a raw permission set. * @param ability @@ -287,3 +301,5 @@ export const isAtLeastAsPrivilegedWorkspace = ( return set1.size >= set2.size; }; + +/* eslint-enable */ diff --git a/backend/src/ee/services/saml-config/saml-config-dal.ts b/backend/src/ee/services/saml-config/saml-config-dal.ts index 95f6828bc..1e7b9e47e 100644 --- a/backend/src/ee/services/saml-config/saml-config-dal.ts +++ b/backend/src/ee/services/saml-config/saml-config-dal.ts @@ -1,10 +1,31 @@ import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; +import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; export type TSamlConfigDALFactory = ReturnType; export const samlConfigDALFactory = (db: TDbClient) => { const samlCfgOrm = ormify(db, TableName.SamlConfig); - return samlCfgOrm; + + const findEnforceableSamlCfg = async (orgId: string) => { + try { + const samlCfg = await db(TableName.SamlConfig) + .where({ + orgId, + isActive: true + }) + .whereNotNull("lastUsed") + .first(); + + return samlCfg; + } catch (error) { + throw new DatabaseError({ error, name: "Find org by id" }); + } + }; + + return { + ...samlCfgOrm, + findEnforceableSamlCfg + }; }; diff --git a/backend/src/ee/services/saml-config/saml-config-service.ts b/backend/src/ee/services/saml-config/saml-config-service.ts index da8cb02b1..767729179 100644 --- a/backend/src/ee/services/saml-config/saml-config-service.ts +++ b/backend/src/ee/services/saml-config/saml-config-service.ts @@ -18,7 +18,7 @@ import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption"; import { BadRequestError } from "@app/lib/errors"; -import { AuthTokenType } from "@app/services/auth/auth-type"; +import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type"; import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TUserDALFactory } from "@app/services/user/user-dal"; @@ -27,20 +27,14 @@ import { TLicenseServiceFactory } from "../license/license-service"; import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission"; import { TPermissionServiceFactory } from "../permission/permission-service"; import { TSamlConfigDALFactory } from "./saml-config-dal"; -import { - SamlProviders, - TCreateSamlCfgDTO, - TGetSamlCfgDTO, - TSamlLoginDTO, - TUpdateSamlCfgDTO -} from "./saml-config-types"; +import { TCreateSamlCfgDTO, TGetSamlCfgDTO, TSamlLoginDTO, TUpdateSamlCfgDTO } from "./saml-config-types"; type TSamlConfigServiceFactoryDep = { samlConfigDAL: TSamlConfigDALFactory; userDAL: Pick; orgDAL: Pick< TOrgDALFactory, - "createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" + "createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById" >; orgBotDAL: Pick; permissionService: Pick; @@ -60,6 +54,7 @@ export const samlConfigServiceFactory = ({ const createSamlCfg = async ({ cert, actor, + actorOrgId, orgId, issuer, actorId, @@ -67,11 +62,8 @@ export const samlConfigServiceFactory = ({ entryPoint, authProvider }: TCreateSamlCfgDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Sso - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso); const plan = await licenseService.getPlan(orgId); if (!plan.samlSSO) @@ -128,16 +120,8 @@ export const samlConfigServiceFactory = ({ keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding }); - const { - ciphertext: encryptedEntryPoint, - iv: entryPointIV, - tag: entryPointTag - } = encryptSymmetric(entryPoint, key); - const { - ciphertext: encryptedIssuer, - iv: issuerIV, - tag: issuerTag - } = encryptSymmetric(issuer, key); + const { ciphertext: encryptedEntryPoint, iv: entryPointIV, tag: entryPointTag } = encryptSymmetric(entryPoint, key); + const { ciphertext: encryptedIssuer, iv: issuerIV, tag: issuerTag } = encryptSymmetric(issuer, key); const { ciphertext: encryptedCert, iv: certIV, tag: certTag } = encryptSymmetric(cert, key); const samlConfig = await samlConfigDAL.create({ @@ -154,12 +138,14 @@ export const samlConfigServiceFactory = ({ certIV, certTag }); + return samlConfig; }; const updateSamlCfg = async ({ orgId, actor, + actorOrgId, cert, actorId, issuer, @@ -167,11 +153,8 @@ export const samlConfigServiceFactory = ({ entryPoint, authProvider }: TUpdateSamlCfgDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Sso - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso); const plan = await licenseService.getPlan(orgId); if (!plan.samlSSO) throw new BadRequestError({ @@ -179,10 +162,9 @@ export const samlConfigServiceFactory = ({ "Failed to update SAML SSO configuration due to plan restriction. Upgrade plan to update SSO configuration." }); - const updateQuery: TSamlConfigsUpdate = { authProvider, isActive }; + const updateQuery: TSamlConfigsUpdate = { authProvider, isActive, lastUsed: null }; const orgBot = await orgBotDAL.findOne({ orgId }); - if (!orgBot) - throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" }); + if (!orgBot) throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" }); const key = infisicalSymmetricDecrypt({ ciphertext: orgBot.encryptedSymmetricKey, iv: orgBot.symmetricKeyIV, @@ -201,11 +183,7 @@ export const samlConfigServiceFactory = ({ updateQuery.entryPointTag = entryPointTag; } if (issuer) { - const { - ciphertext: encryptedIssuer, - iv: issuerIV, - tag: issuerTag - } = encryptSymmetric(issuer, key); + const { ciphertext: encryptedIssuer, iv: issuerIV, tag: issuerTag } = encryptSymmetric(issuer, key); updateQuery.encryptedIssuer = encryptedIssuer; updateQuery.issuerIV = issuerIV; updateQuery.issuerTag = issuerTag; @@ -217,6 +195,8 @@ export const samlConfigServiceFactory = ({ updateQuery.certTag = certTag; } const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery); + await orgDAL.updateById(orgId, { authEnforced: false }); + return ssoConfig; }; @@ -225,8 +205,29 @@ export const samlConfigServiceFactory = ({ if (dto.type === "org") { ssoConfig = await samlConfigDAL.findOne({ orgId: dto.orgId }); if (!ssoConfig) return; + } else if (dto.type === "orgSlug") { + const org = await orgDAL.findOne({ slug: dto.orgSlug }); + if (!org) return; + ssoConfig = await samlConfigDAL.findOne({ orgId: org.id }); } else if (dto.type === "ssoId") { - ssoConfig = await samlConfigDAL.findById(dto.id); + // TODO: + // We made this change because saml config ids were not moved over during the migration + // This will patch this issue. + // Remove in the future + const UUIDToMongoId: Record = { + "64c81ff7905fadcfead01e9a": "0978bcbe-8f94-4d95-8600-009787262613", + "652d4777c74d008c85c8bed5": "42044bf5-119e-443e-a51b-0308ac7e45ea", + "6527df39771217236f8721f6": "6311ec4b-d692-4422-b52a-337f719ae6b0", + "650374a561d12cd3d835aeb8": "6453516c-930d-4ff0-ad3b-496ba6eb80ca", + "655d67d10a0f4d307c8b1536": "73b9f1b1-f946-4f18-9a2d-310f157f7df5", + "64f23239a5d4ed17f1e544c4": "9256337f-e3da-43d7-8266-39c9276e8426", + "65348e49db355e6e4782571f": "b8a227c7-843e-410e-8982-b4976a599b69", + "657a219fc8a80c2eff97eb38": "fcab1573-ae7f-4fcf-9645-646207acf035" + }; + + const id = UUIDToMongoId[dto.id] ?? dto.id; + + ssoConfig = await samlConfigDAL.findById(id); } if (!ssoConfig) throw new BadRequestError({ message: "Failed to find organization SSO data" }); @@ -235,12 +236,10 @@ export const samlConfigServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( dto.actor, dto.actorId, - ssoConfig!.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Sso + ssoConfig.orgId, + dto.actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso); } const { entryPointTag, @@ -255,8 +254,7 @@ export const samlConfigServiceFactory = ({ } = ssoConfig; const orgBot = await orgBotDAL.findOne({ orgId: ssoConfig.orgId }); - if (!orgBot) - throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" }); + if (!orgBot) throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" }); const key = infisicalSymmetricDecrypt({ ciphertext: orgBot.encryptedSymmetricKey, iv: orgBot.symmetricKeyIV, @@ -297,36 +295,20 @@ export const samlConfigServiceFactory = ({ isActive: ssoConfig.isActive, entryPoint, issuer, - cert + cert, + lastUsed: ssoConfig.lastUsed }; }; - const samlLogin = async ({ - firstName, - email, - lastName, - authProvider, - orgId, - relayState, - isSignupAllowed - }: TSamlLoginDTO) => { + const samlLogin = async ({ firstName, email, lastName, authProvider, orgId, relayState }: TSamlLoginDTO) => { const appCfg = getConfig(); let user = await userDAL.findUserByEmail(email); - const isSamlSignUpDisabled = !isSignupAllowed && !user; - if (isSamlSignUpDisabled) - throw new BadRequestError({ message: "User signup disabled", name: "Saml SSO login" }); const organization = await orgDAL.findOrgById(orgId); if (!organization) throw new BadRequestError({ message: "Org not found" }); if (user) { - const hasSamlEnabled = (user.authMethods || []).some((method) => - Object.values(SamlProviders).includes(method as SamlProviders) - ); await userDAL.transaction(async (tx) => { - if (!hasSamlEnabled) { - await userDAL.updateById(user.id, { authMethods: [authProvider] }, tx); - } const [orgMembership] = await orgDAL.findMembership({ userId: user.id, orgId }, { tx }); if (!orgMembership) { await orgDAL.createMembership( @@ -356,7 +338,7 @@ export const samlConfigServiceFactory = ({ email, firstName, lastName, - authMethods: [authProvider] + authMethods: [AuthMethod.EMAIL] }, tx ); @@ -383,7 +365,7 @@ export const samlConfigServiceFactory = ({ isUserCompleted, ...(relayState ? { - callbackPort: JSON.parse(relayState).callbackPort as string + callbackPort: (JSON.parse(relayState) as { callbackPort: string }).callbackPort } : {}) }, @@ -392,6 +374,9 @@ export const samlConfigServiceFactory = ({ expiresIn: appCfg.JWT_PROVIDER_AUTH_LIFETIME } ); + + await samlConfigDAL.update({ orgId }, { lastUsed: new Date() }); + return { isUserCompleted, providerAuthToken }; }; diff --git a/backend/src/ee/services/saml-config/saml-config-types.ts b/backend/src/ee/services/saml-config/saml-config-types.ts index 18a511af5..a2c2c63c0 100644 --- a/backend/src/ee/services/saml-config/saml-config-types.ts +++ b/backend/src/ee/services/saml-config/saml-config-types.ts @@ -25,7 +25,11 @@ export type TUpdateSamlCfgDTO = Partial<{ TOrgPermission; export type TGetSamlCfgDTO = - | { type: "org"; orgId: string; actor: ActorType; actorId: string } + | { type: "org"; orgId: string; actor: ActorType; actorId: string; actorOrgId?: string } + | { + type: "orgSlug"; + orgSlug: string; + } | { type: "ssoId"; id: string; @@ -37,7 +41,6 @@ export type TSamlLoginDTO = { lastName?: string; authProvider: string; orgId: string; - isSignupAllowed: boolean; // saml thingy relayState?: string; }; diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-approver-dal.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-approver-dal.ts index cee303436..f32439499 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-approver-dal.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-approver-dal.ts @@ -2,9 +2,7 @@ import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; import { ormify } from "@app/lib/knex"; -export type TSecretApprovalPolicyApproverDALFactory = ReturnType< - typeof secretApprovalPolicyApproverDALFactory ->; +export type TSecretApprovalPolicyApproverDALFactory = ReturnType; export const secretApprovalPolicyApproverDALFactory = (db: TDbClient) => { const sapApproverOrm = ormify(db, TableName.SecretApprovalPolicyApprover); diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts index ea895262d..eec3d9a1d 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts @@ -3,13 +3,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; import { TableName, TSecretApprovalPolicies } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { - buildFindFilter, - mergeOneToManyRelation, - ormify, - selectAllTableCols, - TFindFilter -} from "@app/lib/knex"; +import { buildFindFilter, mergeOneToManyRelation, ormify, selectAllTableCols, TFindFilter } from "@app/lib/knex"; export type TSecretApprovalPolicyDALFactory = ReturnType; @@ -18,12 +12,9 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => { const sapFindQuery = (tx: Knex, filter: TFindFilter) => tx(TableName.SecretApprovalPolicy) + // eslint-disable-next-line .where(buildFindFilter(filter)) - .join( - TableName.Environment, - `${TableName.SecretApprovalPolicy}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`) .join( TableName.SecretApprovalPolicyApprover, `${TableName.SecretApprovalPolicy}.id`, @@ -59,10 +50,7 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => { } }; - const find = async ( - filter: TFindFilter, - tx?: Knex - ) => { + const find = async (filter: TFindFilter, tx?: Knex) => { try { const docs = await sapFindQuery(tx || db, filter); const formatedDoc = mergeOneToManyRelation( diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts index 078d688c5..9d65ec7cc 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import picomatch from "picomatch"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { containsGlobPatterns } from "@app/lib/picomatch"; import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal"; @@ -34,9 +31,7 @@ type TSecretApprovalPolicyServiceFactoryDep = { projectMembershipDAL: Pick; }; -export type TSecretApprovalPolicyServiceFactory = ReturnType< - typeof secretApprovalPolicyServiceFactory ->; +export type TSecretApprovalPolicyServiceFactory = ReturnType; export const secretApprovalPolicyServiceFactory = ({ secretApprovalPolicyDAL, @@ -49,6 +44,7 @@ export const secretApprovalPolicyServiceFactory = ({ name, actor, actorId, + actorOrgId, approvals, approvers, projectId, @@ -58,7 +54,7 @@ export const secretApprovalPolicyServiceFactory = ({ if (approvals > approvers.length) throw new BadRequestError({ message: "Approvals cannot be greater than approvers" }); - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretApproval @@ -101,22 +97,20 @@ export const secretApprovalPolicyServiceFactory = ({ name, actorId, actor, + actorOrgId, approvals, secretPolicyId }: TUpdateSapDTO) => { const secretApprovalPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId); - if (!secretApprovalPolicy) - throw new BadRequestError({ message: "Secret approval policy not found" }); + if (!secretApprovalPolicy) throw new BadRequestError({ message: "Secret approval policy not found" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - secretApprovalPolicy.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.SecretApproval + secretApprovalPolicy.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval); const updatedSap = await secretApprovalPolicyDAL.transaction(async (tx) => { const doc = await secretApprovalPolicyDAL.updateById( @@ -158,14 +152,15 @@ export const secretApprovalPolicyServiceFactory = ({ }; }; - const deleteSecretApprovalPolicy = async ({ secretPolicyId, actor, actorId }: TDeleteSapDTO) => { + const deleteSecretApprovalPolicy = async ({ secretPolicyId, actor, actorId, actorOrgId }: TDeleteSapDTO) => { const sapPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId); if (!sapPolicy) throw new BadRequestError({ message: "Secret approval policy not found" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - sapPolicy.projectId + sapPolicy.projectId, + actorOrgId ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, @@ -176,22 +171,15 @@ export const secretApprovalPolicyServiceFactory = ({ return sapPolicy; }; - const getSecretApprovalPolicyByProjectId = async ({ actorId, actor, projectId }: TListSapDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretApproval - ); + const getSecretApprovalPolicyByProjectId = async ({ actorId, actor, actorOrgId, projectId }: TListSapDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); const sapPolicies = await secretApprovalPolicyDAL.find({ projectId }); return sapPolicies; }; - const getSecretApprovalPolicy = async ( - projectId: string, - environment: string, - secretPath: string - ) => { + const getSecretApprovalPolicy = async (projectId: string, environment: string, secretPath: string) => { const env = await projectEnvDAL.findOne({ slug: environment, projectId }); if (!env) throw new BadRequestError({ message: "Environment not found" }); @@ -199,14 +187,11 @@ export const secretApprovalPolicyServiceFactory = ({ if (!policies.length) return; // this will filter policies either without scoped to secret path or the one that matches with secret path const policiesFilteredByPath = policies.filter( - ({ secretPath: policyPath }) => - !policyPath || picomatch.isMatch(secretPath, policyPath, { strictSlashes: false }) + ({ secretPath: policyPath }) => !policyPath || picomatch.isMatch(secretPath, policyPath, { strictSlashes: false }) ); // now sort by priority. exact secret path gets first match followed by glob followed by just env scoped // if that is tie get by first createdAt - const policiesByPriority = policiesFilteredByPath.sort( - (a, b) => getPolicyScore(b) - getPolicyScore(a) - ); + const policiesByPriority = policiesFilteredByPath.sort((a, b) => getPolicyScore(b) - getPolicyScore(a)); const finalPolicy = policiesByPriority.shift(); return finalPolicy; }; @@ -215,10 +200,11 @@ export const secretApprovalPolicyServiceFactory = ({ projectId, actor, actorId, + actorOrgId, environment, secretPath }: TGetBoardSapDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { secretPath, environment }) diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts index cda07eff9..05fe1b8f8 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts @@ -1,15 +1,14 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { SecretApprovalRequestsSchema, TableName, TSecretApprovalRequests } from "@app/db/schemas"; -import { DatabaseError } from "@app/lib/errors"; import { - ormify, - selectAllTableCols, - sqlNestRelationships, - stripUndefinedInWhere, - TFindFilter -} from "@app/lib/knex"; + SecretApprovalRequestsSchema, + TableName, + TSecretApprovalRequests, + TSecretApprovalRequestsSecrets +} from "@app/db/schemas"; +import { DatabaseError } from "@app/lib/errors"; +import { ormify, selectAllTableCols, sqlNestRelationships, stripUndefinedInWhere, TFindFilter } from "@app/lib/knex"; import { RequestState } from "./secret-approval-request-types"; @@ -31,11 +30,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { const findQuery = (filter: TFindFilter, tx: Knex) => tx(TableName.SecretApprovalRequest) .where(filter) - .join( - TableName.SecretFolder, - `${TableName.SecretApprovalRequest}.folderId`, - `${TableName.SecretFolder}.id` - ) + .join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`) .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .join( TableName.SecretApprovalPolicy, @@ -87,8 +82,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { { key: "reviewerMemberId", label: "reviewers" as const, - mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => - member ? { member, status } : undefined + mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined) }, { key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId } ] @@ -109,26 +103,19 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { .with( "temp", (tx || db)(TableName.SecretApprovalRequest) - .join( - TableName.SecretFolder, - `${TableName.SecretApprovalRequest}.folderId`, - `${TableName.SecretFolder}.id` - ) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .join( TableName.SecretApprovalPolicyApprover, `${TableName.SecretApprovalRequest}.policyId`, `${TableName.SecretApprovalPolicyApprover}.policyId` ) .where({ projectId }) - .andWhere((bd) => - bd - .where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId) - .orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId) + .andWhere( + (bd) => + void bd + .where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId) + .orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId) ) .select("status", `${TableName.SecretApprovalRequest}.id`) .groupBy(`${TableName.SecretApprovalRequest}.id`, "status") @@ -141,11 +128,11 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { return { open: parseInt( - (docs.find(({ status }) => status === RequestState.Open)?.count as string) || "0", + (docs.find(({ status }) => status === RequestState.Open) as { count: string })?.count || "0", 10 ), closed: parseInt( - (docs.find(({ status }) => status === RequestState.Closed)?.count as string) || "0", + (docs.find(({ status }) => status === RequestState.Closed) as { count: string })?.count || "0", 10 ) }; @@ -155,31 +142,15 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { }; const findByProjectId = async ( - { - status, - limit = 20, - offset = 0, - projectId, - committer, - environment, - membershipId - }: TFindQueryFilter, + { status, limit = 20, offset = 0, projectId, committer, environment, membershipId }: TFindQueryFilter, tx?: Knex ) => { try { // akhilmhdh: If ever u wanted a 1 to so many relationship connected with pagination // this is the place u wanna look at. const query = (tx || db)(TableName.SecretApprovalRequest) - .join( - TableName.SecretFolder, - `${TableName.SecretApprovalRequest}.folderId`, - `${TableName.SecretFolder}.id` - ) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .join( TableName.SecretApprovalPolicy, `${TableName.SecretApprovalRequest}.policyId`, @@ -195,7 +166,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { `${TableName.SecretApprovalRequest}.id`, `${TableName.SecretApprovalRequestReviewer}.requestId` ) - .leftJoin( + .leftJoin( TableName.SecretApprovalRequestSecret, `${TableName.SecretApprovalRequestSecret}.requestId`, `${TableName.SecretApprovalRequest}.id` @@ -208,10 +179,11 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { committerId: committer }) ) - .andWhere((bd) => - bd - .where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId) - .orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId) + .andWhere( + (bd) => + void bd + .where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId) + .orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId) ) .select(selectAllTableCols(TableName.SecretApprovalRequest)) .select( @@ -257,8 +229,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { { key: "reviewerMemberId", label: "reviewers" as const, - mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => - member ? { member, status: s } : undefined + mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => (member ? { member, status: s } : undefined) }, { key: "approverId", diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-reviewer-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-reviewer-dal.ts index a2a93f258..13478f38d 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-reviewer-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-reviewer-dal.ts @@ -2,9 +2,7 @@ import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; import { ormify } from "@app/lib/knex"; -export type TSecretApprovalRequestReviewerDALFactory = ReturnType< - typeof secretApprovalRequestReviewerDALFactory ->; +export type TSecretApprovalRequestReviewerDALFactory = ReturnType; export const secretApprovalRequestReviewerDALFactory = (db: TDbClient) => { const secretApprovalRequestReviewerOrm = ormify(db, TableName.SecretApprovalRequestReviewer); diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts index a0d25ae65..9b4742255 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts @@ -1,13 +1,11 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { SecretApprovalRequestsSecretsSchema, TableName } from "@app/db/schemas"; +import { SecretApprovalRequestsSecretsSchema, TableName, TSecretTags } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex"; -export type TSecretApprovalRequestSecretDALFactory = ReturnType< - typeof secretApprovalRequestSecretDALFactory ->; +export type TSecretApprovalRequestSecretDALFactory = ReturnType; export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { const secretApprovalRequestSecretOrm = ormify(db, TableName.SecretApprovalRequestSecret); @@ -25,16 +23,8 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { `${TableName.SecretApprovalRequestSecret}.id`, `${TableName.SecretApprovalRequestSecretTag}.secretId` ) - .leftJoin( - TableName.SecretTag, - `${TableName.SecretApprovalRequestSecretTag}.tagId`, - `${TableName.SecretTag}.id` - ) - .leftJoin( - TableName.Secret, - `${TableName.SecretApprovalRequestSecret}.secretId`, - `${TableName.Secret}.id` - ) + .leftJoin(TableName.SecretTag, `${TableName.SecretApprovalRequestSecretTag}.tagId`, `${TableName.SecretTag}.id`) + .leftJoin(TableName.Secret, `${TableName.SecretApprovalRequestSecret}.secretId`, `${TableName.Secret}.id`) .leftJoin( TableName.SecretVersion, `${TableName.SecretVersion}.id`, @@ -45,7 +35,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { `${TableName.SecretVersionTag}.${TableName.SecretVersion}Id`, `${TableName.SecretVersion}.id` ) - .leftJoin( + .leftJoin( db.ref(TableName.SecretTag).as("secVerTag"), `${TableName.SecretVersionTag}.${TableName.SecretTag}Id`, db.ref("id").withSchema("secVerTag") @@ -75,37 +65,24 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { db.ref("secretValueCiphertext").withSchema(TableName.Secret).as("orgSecValueCiphertext"), db.ref("secretCommentIV").withSchema(TableName.Secret).as("orgSecCommentIV"), db.ref("secretCommentTag").withSchema(TableName.Secret).as("orgSecCommentTag"), - db - .ref("secretCommentCiphertext") - .withSchema(TableName.Secret) - .as("orgSecCommentCiphertext") + db.ref("secretCommentCiphertext").withSchema(TableName.Secret).as("orgSecCommentCiphertext") ) .select( db.ref("version").withSchema(TableName.SecretVersion).as("secVerVersion"), db.ref("secretKeyIV").withSchema(TableName.SecretVersion).as("secVerKeyIV"), db.ref("secretKeyTag").withSchema(TableName.SecretVersion).as("secVerKeyTag"), - db - .ref("secretKeyCiphertext") - .withSchema(TableName.SecretVersion) - .as("secVerKeyCiphertext"), + db.ref("secretKeyCiphertext").withSchema(TableName.SecretVersion).as("secVerKeyCiphertext"), db.ref("secretValueIV").withSchema(TableName.SecretVersion).as("secVerValueIV"), db.ref("secretValueTag").withSchema(TableName.SecretVersion).as("secVerValueTag"), - db - .ref("secretValueCiphertext") - .withSchema(TableName.SecretVersion) - .as("secVerValueCiphertext"), + db.ref("secretValueCiphertext").withSchema(TableName.SecretVersion).as("secVerValueCiphertext"), db.ref("secretCommentIV").withSchema(TableName.SecretVersion).as("secVerCommentIV"), db.ref("secretCommentTag").withSchema(TableName.SecretVersion).as("secVerCommentTag"), - db - .ref("secretCommentCiphertext") - .withSchema(TableName.SecretVersion) - .as("secVerCommentCiphertext") + db.ref("secretCommentCiphertext").withSchema(TableName.SecretVersion).as("secVerCommentCiphertext") ); const formatedDoc = sqlNestRelationships({ data: doc, key: "id", - parentMapper: (data) => - SecretApprovalRequestsSecretsSchema.omit({ secretVersion: true }).parse(data), + parentMapper: (data) => SecretApprovalRequestsSecretsSchema.omit({ secretVersion: true }).parse(data), childrenMapper: [ { key: "tagJnId", @@ -186,15 +163,14 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { { key: "secVerTagId", label: "tags" as const, - mapper: ({ - secVerTagId: id, - secVerTagName: name, - secVerTagSlug: slug, - secVerTagColor: color - }) => ({ + mapper: ({ secVerTagId: id, secVerTagName: name, secVerTagSlug: slug, secVerTagColor: color }) => ({ + // eslint-disable-next-line id, + // eslint-disable-next-line name, + // eslint-disable-next-line slug, + // eslint-disable-next-line color }) } diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index 4809bd846..ef10db804 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -42,10 +42,7 @@ type TSecretApprovalRequestServiceFactoryDep = { secretApprovalRequestDAL: TSecretApprovalRequestDALFactory; secretApprovalRequestSecretDAL: TSecretApprovalRequestSecretDALFactory; secretApprovalRequestReviewerDAL: TSecretApprovalRequestReviewerDALFactory; - folderDAL: Pick< - TSecretFolderDALFactory, - "findBySecretPath" | "findById" | "findSecretPathByFolderIds" - >; + folderDAL: Pick; secretTagDAL: Pick; secretBlindIndexDAL: Pick; snapshotService: Pick; @@ -61,9 +58,7 @@ type TSecretApprovalRequestServiceFactoryDep = { secretQueueService: Pick; }; -export type TSecretApprovalRequestServiceFactory = ReturnType< - typeof secretApprovalRequestServiceFactory ->; +export type TSecretApprovalRequestServiceFactory = ReturnType; export const secretApprovalRequestServiceFactory = ({ secretApprovalRequestDAL, @@ -78,14 +73,14 @@ export const secretApprovalRequestServiceFactory = ({ secretVersionDAL, secretQueueService }: TSecretApprovalRequestServiceFactoryDep) => { - const requestCount = async ({ projectId, actor, actorId }: TApprovalRequestCountDTO) => { - if (actor === ActorType.SERVICE) - throw new BadRequestError({ message: "Cannot use service token" }); + const requestCount = async ({ projectId, actor, actorId, actorOrgId }: TApprovalRequestCountDTO) => { + if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); const { membership } = await permissionService.getProjectPermission( actor as ActorType.USER, actorId, - projectId + projectId, + actorOrgId ); const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, membership.id); @@ -96,16 +91,16 @@ export const secretApprovalRequestServiceFactory = ({ projectId, actorId, actor, + actorOrgId, status, environment, committer, limit, offset }: TListApprovalsDTO) => { - if (actor === ActorType.SERVICE) - throw new BadRequestError({ message: "Cannot use service token" }); + if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); - const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); const approvals = await secretApprovalRequestDAL.findByProjectId({ projectId, committer, @@ -118,19 +113,18 @@ export const secretApprovalRequestServiceFactory = ({ return approvals; }; - const getSecretApprovalDetails = async ({ actor, actorId, id }: TSecretApprovalDetailsDTO) => { - if (actor === ActorType.SERVICE) - throw new BadRequestError({ message: "Cannot use service token" }); + const getSecretApprovalDetails = async ({ actor, actorId, actorOrgId, id }: TSecretApprovalDetailsDTO) => { + if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); const secretApprovalRequest = await secretApprovalRequestDAL.findById(id); - if (!secretApprovalRequest) - throw new BadRequestError({ message: "Secret approval request not found" }); + if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); const { policy } = secretApprovalRequest; const { membership } = await permissionService.getProjectPermission( actor, actorId, - secretApprovalRequest.projectId + secretApprovalRequest.projectId, + actorOrgId ); if ( membership.role !== ProjectMembershipRole.Admin && @@ -147,17 +141,17 @@ export const secretApprovalRequestServiceFactory = ({ return { ...secretApprovalRequest, secretPath: secretPath?.[0]?.path || "/", commits: secrets }; }; - const reviewApproval = async ({ approvalId, actor, status, actorId }: TReviewRequestDTO) => { + const reviewApproval = async ({ approvalId, actor, status, actorId, actorOrgId }: TReviewRequestDTO) => { const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId); - if (!secretApprovalRequest) - throw new BadRequestError({ message: "Secret approval request not found" }); + if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" }); const { policy } = secretApprovalRequest; const { membership } = await permissionService.getProjectPermission( ActorType.USER, actorId, - secretApprovalRequest.projectId + secretApprovalRequest.projectId, + actorOrgId ); if ( membership.role !== ProjectMembershipRole.Admin && @@ -189,17 +183,17 @@ export const secretApprovalRequestServiceFactory = ({ return reviewStatus; }; - const updateApprovalStatus = async ({ actorId, status, approvalId, actor }: TStatusChangeDTO) => { + const updateApprovalStatus = async ({ actorId, status, approvalId, actor, actorOrgId }: TStatusChangeDTO) => { const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId); - if (!secretApprovalRequest) - throw new BadRequestError({ message: "Secret approval request not found" }); + if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" }); const { policy } = secretApprovalRequest; const { membership } = await permissionService.getProjectPermission( ActorType.USER, actorId, - secretApprovalRequest.projectId + secretApprovalRequest.projectId, + actorOrgId ); if ( membership.role !== ProjectMembershipRole.Admin && @@ -209,8 +203,7 @@ export const secretApprovalRequestServiceFactory = ({ throw new UnauthorizedError({ message: "User has no access" }); } - if (secretApprovalRequest.hasMerged) - throw new BadRequestError({ message: "Approval request has been merged" }); + if (secretApprovalRequest.hasMerged) throw new BadRequestError({ message: "Approval request has been merged" }); if (secretApprovalRequest.status === RequestState.Closed && status === RequestState.Closed) throw new BadRequestError({ message: "Approval request is already closed" }); if (secretApprovalRequest.status === RequestState.Open && status === RequestState.Open) @@ -226,19 +219,15 @@ export const secretApprovalRequestServiceFactory = ({ const mergeSecretApprovalRequest = async ({ approvalId, actor, - actorId + actorId, + actorOrgId }: TMergeSecretApprovalRequestDTO) => { const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId); - if (!secretApprovalRequest) - throw new BadRequestError({ message: "Secret approval request not found" }); + if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" }); const { policy, folderId, projectId } = secretApprovalRequest; - const { membership } = await permissionService.getProjectPermission( - ActorType.USER, - actorId, - projectId - ); + const { membership } = await permissionService.getProjectPermission(ActorType.USER, actorId, projectId, actorOrgId); if ( membership.role !== ProjectMembershipRole.Admin && secretApprovalRequest.committerId !== membership.id && @@ -256,21 +245,24 @@ export const secretApprovalRequestServiceFactory = ({ (approverId) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED ).length; - if (!hasMinApproval) - throw new BadRequestError({ message: "Doesn't have minimum approvals needed" }); - const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId( - secretApprovalRequest.id - ); + if (!hasMinApproval) throw new BadRequestError({ message: "Doesn't have minimum approvals needed" }); + const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId(secretApprovalRequest.id); if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" }); const conflicts: Array<{ secretId: string; op: CommitType }> = []; let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Create); if (secretCreationCommits.length) { - const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = - await secretService.fnSecretBlindIndexCheckV2({ - folderId, - inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => ({ secretBlindIndex })) - }); + const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({ + folderId, + inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => { + if (!secretBlindIndex) { + throw new BadRequestError({ + message: "Missing secret blind index" + }); + } + return { secretBlindIndex }; + }) + }); secretCreationCommits .filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""]) .forEach((el) => { @@ -283,16 +275,19 @@ export const secretApprovalRequestServiceFactory = ({ let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Update); if (secretUpdationCommits.length) { - const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = - await secretService.fnSecretBlindIndexCheckV2({ - folderId, - inputSecrets: secretUpdationCommits - .filter( - ({ secretBlindIndex, secret }) => - secret && secret.secretBlindIndex !== secretBlindIndex - ) - .map(({ secretBlindIndex }) => ({ secretBlindIndex })) - }); + const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({ + folderId, + inputSecrets: secretUpdationCommits + .filter(({ secretBlindIndex, secret }) => secret && secret.secretBlindIndex !== secretBlindIndex) + .map(({ secretBlindIndex }) => { + if (!secretBlindIndex) { + throw new BadRequestError({ + message: "Missing secret blind index" + }); + } + return { secretBlindIndex }; + }) + }); secretUpdationCommits .filter( ({ secretBlindIndex, secretId }) => @@ -304,14 +299,11 @@ export const secretApprovalRequestServiceFactory = ({ secretUpdationCommits = secretUpdationCommits.filter( ({ secretBlindIndex, secretId }) => - Boolean(secretId) && - (secretBlindIndex ? !conflictGroupByBlindIndex[secretBlindIndex] : true) + Boolean(secretId) && (secretBlindIndex ? !conflictGroupByBlindIndex[secretBlindIndex] : true) ); } - const secretDeletionCommits = secretApprovalSecrets.filter( - ({ op }) => op === CommitType.Delete - ); + const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Delete); const mergeStatus = await secretApprovalRequestDAL.transaction(async (tx) => { const newSecrets = secretCreationCommits.length @@ -381,10 +373,14 @@ export const secretApprovalRequestServiceFactory = ({ folderId, tx, actorId: "", - inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => ({ - secretBlindIndex, - type: SecretType.Shared - })) + inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => { + if (!secretBlindIndex) { + throw new BadRequestError({ + message: "Missing secret blind index" + }); + } + return { secretBlindIndex, type: SecretType.Shared }; + }) }) : []; const updatedSecretApproval = await secretApprovalRequestDAL.updateById( @@ -419,18 +415,19 @@ export const secretApprovalRequestServiceFactory = ({ data, actorId, actor, + actorOrgId, policy, projectId, secretPath, environment }: TGenerateSecretApprovalRequestDTO) => { - if (actor === ActorType.SERVICE) - throw new BadRequestError({ message: "Cannot use service token" }); + if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); const { permission, membership } = await permissionService.getProjectPermission( actor, actorId, - projectId + projectId, + actorOrgId ); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, @@ -438,13 +435,11 @@ export const secretApprovalRequestServiceFactory = ({ ); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); - if (!folder) - throw new BadRequestError({ message: "Folder not found", name: "GenSecretApproval" }); + if (!folder) throw new BadRequestError({ message: "Folder not found", name: "GenSecretApproval" }); const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); const commits: Omit[] = []; const commitTagIds: Record = {}; @@ -478,35 +473,28 @@ export const secretApprovalRequestServiceFactory = ({ // get all blind index // Find all those secrets // if not throw not found - const { keyName2BlindIndex, secrets: secretsToBeUpdated } = - await secretService.fnSecretBlindIndexCheck({ - inputSecrets: updatedSecrets, - folderId, - isNew: false, - blindIndexCfg - }); + const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await secretService.fnSecretBlindIndexCheck({ + inputSecrets: updatedSecrets, + folderId, + isNew: false, + blindIndexCfg + }); // now find any secret that needs to update its name // same process as above - const nameUpdatedSecrets = updatedSecrets.filter(({ newSecretName }) => - Boolean(newSecretName) - ); - const { keyName2BlindIndex: newKeyName2BlindIndex } = - await secretService.fnSecretBlindIndexCheck({ - inputSecrets: nameUpdatedSecrets, - folderId, - isNew: true, - blindIndexCfg - }); + const nameUpdatedSecrets = updatedSecrets.filter(({ newSecretName }) => Boolean(newSecretName)); + const { keyName2BlindIndex: newKeyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({ + inputSecrets: nameUpdatedSecrets, + folderId, + isNew: true, + blindIndexCfg + }); - const secsGroupedByBlindIndex = groupBy(secretsToBeUpdated, (el) => el.secretBlindIndex); + const secsGroupedByBlindIndex = groupBy(secretsToBeUpdated, (el) => el.secretBlindIndex as string); const updatedSecretIds = updatedSecrets.map( (el) => secsGroupedByBlindIndex[keyName2BlindIndex[el.secretName]][0].id ); - const latestSecretVersions = await secretVersionDAL.findLatestVersionMany( - folderId, - updatedSecretIds - ); + const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(folderId, updatedSecretIds); commits.push( ...updatedSecrets.map(({ newSecretName, secretName, tagIds, ...el }) => { const secretId = secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].id; @@ -540,20 +528,23 @@ export const secretApprovalRequestServiceFactory = ({ isNew: false, blindIndexCfg }); - const secretsGroupedByBlindIndex = groupBy(secrets, (i) => i.secretBlindIndex); + const secretsGroupedByBlindIndex = groupBy(secrets, (i) => { + if (!i.secretBlindIndex) throw new BadRequestError({ message: "Missing secret blind index" }); + return i.secretBlindIndex; + }); const deletedSecretIds = deletedSecrets.map( (el) => secretsGroupedByBlindIndex[keyName2BlindIndex[el.secretName]][0].id ); - const latestSecretVersions = await secretVersionDAL.findLatestVersionMany( - folderId, - deletedSecretIds - ); + const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(folderId, deletedSecretIds); commits.push( ...deletedSecrets.map((el) => { const secretId = secretsGroupedByBlindIndex[keyName2BlindIndex[el.secretName]][0].id; + if (!latestSecretVersions[secretId].secretBlindIndex) + throw new BadRequestError({ message: "Failed to find secret blind index" }); return { op: CommitType.Delete as const, ...latestSecretVersions[secretId], + secretBlindIndex: latestSecretVersions[secretId].secretBlindIndex as string, secret: secretId, secretVersion: latestSecretVersions[secretId].id }; @@ -628,7 +619,13 @@ export const secretApprovalRequestServiceFactory = ({ ), tx ); - const commitsGroupByBlindIndex = groupBy(approvalCommits, (i) => i.secretBlindIndex); + + const commitsGroupByBlindIndex = groupBy(approvalCommits, (i) => { + if (!i.secretBlindIndex) { + throw new BadRequestError({ message: "Missing secret blind index" }); + } + return i.secretBlindIndex; + }); if (tagIds.length) { await secretApprovalRequestSecretDAL.insertApprovalSecretTags( Object.keys(commitTagIds).flatMap((blindIndex) => diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts index c010277d5..008b977e6 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts @@ -1,8 +1,4 @@ -import { - TImmutableDBKeys, - TSecretApprovalPolicies, - TSecretApprovalRequestsSecrets -} from "@app/db/schemas"; +import { TImmutableDBKeys, TSecretApprovalPolicies, TSecretApprovalRequestsSecrets } from "@app/db/schemas"; import { TProjectPermission } from "@app/lib/types"; export enum CommitType { @@ -24,14 +20,7 @@ export enum ApprovalStatus { type TApprovalCreateSecret = Omit< TSecretApprovalRequestsSecrets, - | TImmutableDBKeys - | "version" - | "algorithm" - | "keyEncoding" - | "requestId" - | "op" - | "secretVersion" - | "secretBlindIndex" + TImmutableDBKeys | "version" | "algorithm" | "keyEncoding" | "requestId" | "op" | "secretVersion" | "secretBlindIndex" > & { secretName: string; tagIds?: string[]; diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-dal.ts b/backend/src/ee/services/secret-rotation/secret-rotation-dal.ts index d1504e3d7..7feafdc6b 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-dal.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-dal.ts @@ -14,21 +14,13 @@ export const secretRotationDALFactory = (db: TDbClient) => { const findQuery = (filter: TFindFilter, tx: Knex) => tx(TableName.SecretRotation) .where(filter) - .join( - TableName.Environment, - `${TableName.SecretRotation}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretRotation}.envId`, `${TableName.Environment}.id`) .leftJoin( TableName.SecretRotationOutput, `${TableName.SecretRotation}.id`, `${TableName.SecretRotationOutput}.rotationId` ) - .join( - TableName.Secret, - `${TableName.SecretRotationOutput}.secretId`, - `${TableName.Secret}.id` - ) + .join(TableName.Secret, `${TableName.SecretRotationOutput}.secretId`, `${TableName.Secret}.id`) .select(selectAllTableCols(TableName.SecretRotation)) .select(tx.ref("name").withSchema(TableName.Environment).as("envName")) .select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug")) @@ -102,11 +94,7 @@ export const secretRotationDALFactory = (db: TDbClient) => { const findById = async (id: string, tx?: Knex) => { try { const doc = await (tx || db)(TableName.SecretRotation) - .join( - TableName.Environment, - `${TableName.SecretRotation}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretRotation}.envId`, `${TableName.Environment}.id`) .where({ [`${TableName.SecretRotation}.id` as "id"]: id }) .select(selectAllTableCols(TableName.SecretRotation)) .select( @@ -125,8 +113,7 @@ export const secretRotationDALFactory = (db: TDbClient) => { } }; - const findRotationOutputsByRotationId = async (rotationId: string) => - secretRotationOutputOrm.find({ rotationId }); + const findRotationOutputsByRotationId = async (rotationId: string) => secretRotationOutputOrm.find({ rotationId }); return { ...secretRotationOrm, diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue-fn.ts b/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue-fn.ts index bcd96e090..c67477bfd 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue-fn.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue-fn.ts @@ -1,3 +1,8 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable no-param-reassign */ import axios from "axios"; import jmespath from "jmespath"; @@ -6,12 +11,7 @@ import knex from "knex"; import { getConfig } from "@app/lib/config/env"; import { alphaNumericNanoId } from "@app/lib/nanoid"; -import { - TAssignOp, - TDbProviderClients, - TDirectAssignOp, - THttpProviderFunction -} from "../templates/types"; +import { TAssignOp, TDbProviderClients, TDirectAssignOp, THttpProviderFunction } from "../templates/types"; import { TSecretRotationData, TSecretRotationDbFn } from "./secret-rotation-queue-types"; const REGEX = /\${([^}]+)}/g; @@ -37,7 +37,7 @@ export const interpolate = (data: any, getValue: (key: string) => unknown) => { if ((data as { ref: string })?.ref) return getValue((data as { ref: string }).ref); const temp = data as Record; // for converting ts object to record type Object.keys(temp).forEach((key) => { - temp[key as keyof typeof temp] = interpolate(data[key as keyof typeof temp], getValue); + temp[key] = interpolate(data[key], getValue); }); } return data; @@ -59,10 +59,7 @@ const getInterpolationValue = (variables: TSecretRotationData) => (key: string) return variables[type as keyof TSecretRotationData][keyName]; }; -export const secretRotationHttpFn = async ( - func: THttpProviderFunction, - variables: TSecretRotationData -) => { +export const secretRotationHttpFn = async (func: THttpProviderFunction, variables: TSecretRotationData) => { // string interpolation const headers = interpolate(func.header, getInterpolationValue(variables)); const url = interpolate(func.url, getInterpolationValue(variables)); @@ -112,10 +109,7 @@ export const secretRotationDbFn = async ({ return data; }; -export const secretRotationPreSetFn = ( - op: Record, - variables: TSecretRotationData -) => { +export const secretRotationPreSetFn = (op: Record, variables: TSecretRotationData) => { const getValFn = getInterpolationValue(variables); Object.entries(op || {}).forEach(([key, assignFn]) => { const [type, keyName] = key.split(".") as [keyof TSecretRotationData, string]; @@ -123,10 +117,7 @@ export const secretRotationPreSetFn = ( }); }; -export const secretRotationHttpSetFn = async ( - func: THttpProviderFunction, - variables: TSecretRotationData -) => { +export const secretRotationHttpSetFn = async (func: THttpProviderFunction, variables: TSecretRotationData) => { const getValFn = getInterpolationValue(variables); // http setter const res = await secretRotationHttpFn(func, variables); @@ -140,10 +131,7 @@ export const secretRotationHttpSetFn = async ( }); }; -export const getDbSetQuery = ( - db: TDbProviderClients, - variables: { username: string; password: string } -) => { +export const getDbSetQuery = (db: TDbProviderClients, variables: { username: string; password: string }) => { if (db === TDbProviderClients.Pg) { return { query: `ALTER USER ?? WITH PASSWORD '${variables.password}'`, diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue.ts b/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue.ts index 441e890d9..9e69f0a8f 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue.ts @@ -6,6 +6,7 @@ import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption"; import { daysToMillisecond, secondsToMillis } from "@app/lib/dates"; +import { BadRequestError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; @@ -17,11 +18,7 @@ import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; import { TSecretRotationDALFactory } from "../secret-rotation-dal"; import { rotationTemplates } from "../templates"; -import { - TDbProviderClients, - TProviderFunctionTypes, - TSecretRotationProviderTemplate -} from "../templates/types"; +import { TDbProviderClients, TProviderFunctionTypes, TSecretRotationProviderTemplate } from "../templates/types"; import { getDbSetQuery, secretRotationDbFn, @@ -29,11 +26,7 @@ import { secretRotationHttpSetFn, secretRotationPreSetFn } from "./secret-rotation-queue-fn"; -import { - TSecretRotationData, - TSecretRotationDbFn, - TSecretRotationEncData -} from "./secret-rotation-queue-types"; +import { TSecretRotationData, TSecretRotationDbFn, TSecretRotationEncData } from "./secret-rotation-queue-types"; export type TSecretRotationQueueFactory = ReturnType; @@ -69,7 +62,7 @@ export const secretRotationQueueFactory = ({ }: TSecretRotationQueueFactoryDep) => { const addToQueue = async (rotationId: string, interval: number) => { const appCfg = getConfig(); - queue.queue( + await queue.queue( QueueName.SecretRotation, QueueJobs.SecretRotation, { rotationId }, @@ -77,10 +70,7 @@ export const secretRotationQueueFactory = ({ jobId: rotationId, repeat: { // on prod it this will be in days, in development this will be second - every: - appCfg.NODE_ENV === "development" - ? secondsToMillis(interval) - : daysToMillisecond(interval), + every: appCfg.NODE_ENV === "development" ? secondsToMillis(interval) : daysToMillisecond(interval), immediately: true } } @@ -94,10 +84,7 @@ export const secretRotationQueueFactory = ({ QueueJobs.SecretRotation, { // on prod it this will be in days, in development this will be second - every: - appCfg.NODE_ENV === "development" - ? secondsToMillis(interval) - : daysToMillisecond(interval) + every: appCfg.NODE_ENV === "development" ? secondsToMillis(interval) : daysToMillisecond(interval) }, rotationId ); @@ -107,22 +94,16 @@ export const secretRotationQueueFactory = ({ const { rotationId } = job.data; logger.info(`secretRotationQueue.process: [rotationDocument=${rotationId}]`); const secretRotation = await secretRotationDAL.findById(rotationId); - const rotationProvider = rotationTemplates.find( - ({ name }) => name === secretRotation?.provider - ); + const rotationProvider = rotationTemplates.find(({ name }) => name === secretRotation?.provider); try { - if (!rotationProvider || !secretRotation) - throw new DisableRotationErrors({ message: "Provider not found" }); + if (!rotationProvider || !secretRotation) throw new DisableRotationErrors({ message: "Provider not found" }); const rotationOutputs = await secretRotationDAL.findRotationOutputsByRotationId(rotationId); - if (!rotationOutputs.length) - throw new DisableRotationErrors({ message: "Secrets not found" }); + if (!rotationOutputs.length) throw new DisableRotationErrors({ message: "Secrets not found" }); // deep copy - const provider = JSON.parse( - JSON.stringify(rotationProvider) - ) as TSecretRotationProviderTemplate; + const provider = JSON.parse(JSON.stringify(rotationProvider)) as TSecretRotationProviderTemplate; // now get the encrypted variable values // in includes the inputs, the previous outputs @@ -155,20 +136,11 @@ export const secretRotationQueueFactory = ({ ? variables.inputs.username2 : variables.inputs.username1; } else { - newCredential.internal.username = lastCred - ? lastCred.internal.username - : variables.inputs.username1; + newCredential.internal.username = lastCred ? lastCred.internal.username : variables.inputs.username1; } // set a random value for new password newCredential.internal.rotated_password = alphaNumericNanoId(32); - const { - admin_username: username, - admin_password: password, - host, - database, - port, - ca - } = newCredential.inputs; + const { admin_username: username, admin_password: password, host, database, port, ca } = newCredential.inputs; const dbFunctionArg = { username, password, @@ -176,10 +148,7 @@ export const secretRotationQueueFactory = ({ database, port, ca: ca as string, - client: - provider.template.client === TDbProviderClients.MySql - ? "mysql2" - : provider.template.client + client: provider.template.client === TDbProviderClients.MySql ? "mysql2" : provider.template.client } as TSecretRotationDbFn; // set function await secretRotationDbFn({ @@ -259,10 +228,14 @@ export const secretRotationQueueFactory = ({ tx ); await secretVersionDAL.insertMany( - updatedSecrets.map(({ id, updatedAt, createdAt, ...el }) => ({ - ...el, - secretId: id - })), + updatedSecrets.map(({ id, updatedAt, createdAt, ...el }) => { + if (!el.secretBlindIndex) throw new BadRequestError({ message: "Missing blind index" }); + return { + ...el, + secretId: id, + secretBlindIndex: el.secretBlindIndex + }; + }), tx ); }); @@ -283,7 +256,7 @@ export const secretRotationQueueFactory = ({ logger.error(error); if (error instanceof DisableRotationErrors) { if (job.id) { - queue.stopRepeatableJobByJobId(QueueName.SecretRotation, job.id); + await queue.stopRepeatableJobByJobId(QueueName.SecretRotation, job.id); } } diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts index 88f555d94..75c19c6e9 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts @@ -14,13 +14,7 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/pr import { TSecretRotationDALFactory } from "./secret-rotation-dal"; import { TSecretRotationQueueFactory } from "./secret-rotation-queue"; import { TSecretRotationEncData } from "./secret-rotation-queue/secret-rotation-queue-types"; -import { - TCreateSecretRotationDTO, - TDeleteDTO, - TGetByIdDTO, - TListByProjectIdDTO, - TRestartDTO -} from "./secret-rotation-types"; +import { TCreateSecretRotationDTO, TDeleteDTO, TListByProjectIdDTO, TRestartDTO } from "./secret-rotation-types"; import { rotationTemplates } from "./templates"; type TSecretRotationServiceFactoryDep = { @@ -45,12 +39,9 @@ export const secretRotationServiceFactory = ({ folderDAL, secretDAL }: TSecretRotationServiceFactoryDep) => { - const getProviderTemplates = async ({ actor, actorId, projectId }: TProjectPermission) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRotation - ); + const getProviderTemplates = async ({ actor, actorId, actorOrgId, projectId }: TProjectPermission) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); return { custom: [], @@ -62,6 +53,7 @@ export const secretRotationServiceFactory = ({ projectId, actorId, actor, + actorOrgId, inputs, outputs, interval, @@ -69,7 +61,7 @@ export const secretRotationServiceFactory = ({ secretPath, environment }: TCreateSecretRotationDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretRotation @@ -93,8 +85,7 @@ export const secretRotationServiceFactory = ({ const plan = await licenseService.getPlan(project.orgId); if (!plan.secretRotation) throw new BadRequestError({ - message: - "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." + message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." }); const selectedTemplate = rotationTemplates.find(({ name }) => name === provider); @@ -148,33 +139,14 @@ export const secretRotationServiceFactory = ({ return secretRotation; }; - const getById = async ({ rotationId, actor, actorId }: TGetByIdDTO) => { - const [doc] = await secretRotationDAL.find({ id: rotationId }); - if (!doc) throw new BadRequestError({ message: "Rotation not found" }); - - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - doc.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRotation - ); - return doc; - }; - - const getByProjectId = async ({ actorId, projectId, actor }: TListByProjectIdDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRotation - ); + const getByProjectId = async ({ actorId, projectId, actor, actorOrgId }: TListByProjectIdDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); const doc = await secretRotationDAL.find({ projectId }); return doc; }; - const restartById = async ({ actor, actorId, rotationId }: TRestartDTO) => { + const restartById = async ({ actor, actorId, actorOrgId, rotationId }: TRestartDTO) => { const doc = await secretRotationDAL.findById(rotationId); if (!doc) throw new BadRequestError({ message: "Rotation not found" }); @@ -182,33 +154,21 @@ export const secretRotationServiceFactory = ({ const plan = await licenseService.getPlan(project.orgId); if (!plan.secretRotation) throw new BadRequestError({ - message: - "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." + message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - doc.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.SecretRotation - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation); await secretRotationQueue.removeFromQueue(doc.id, doc.interval); await secretRotationQueue.addToQueue(doc.id, doc.interval); return doc; }; - const deleteById = async ({ actor, actorId, rotationId }: TDeleteDTO) => { + const deleteById = async ({ actor, actorId, actorOrgId, rotationId }: TDeleteDTO) => { const doc = await secretRotationDAL.findById(rotationId); if (!doc) throw new BadRequestError({ message: "Rotation not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - doc.projectId - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.SecretRotation @@ -223,7 +183,6 @@ export const secretRotationServiceFactory = ({ return { getProviderTemplates, - getById, getByProjectId, createRotation, restartById, diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-types.ts b/backend/src/ee/services/secret-rotation/secret-rotation-types.ts index 52d248765..990bf3eca 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-types.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-types.ts @@ -18,7 +18,3 @@ export type TDeleteDTO = { export type TRestartDTO = { rotationId: string; } & Omit; - -export type TGetByIdDTO = { - rotationId: string; -} & Omit; diff --git a/backend/src/ee/services/secret-rotation/templates/mysql.ts b/backend/src/ee/services/secret-rotation/templates/mysql.ts index 86a382d6e..723560a3e 100644 --- a/backend/src/ee/services/secret-rotation/templates/mysql.ts +++ b/backend/src/ee/services/secret-rotation/templates/mysql.ts @@ -23,15 +23,7 @@ export const MYSQL_TEMPLATE = { }, ca: { type: "string", desc: "SSL certificate for db auth(string)" } }, - required: [ - "admin_username", - "admin_password", - "host", - "database", - "username1", - "username2", - "port" - ], + required: ["admin_username", "admin_password", "host", "database", "username1", "username2", "port"], additionalProperties: false }, outputs: { diff --git a/backend/src/ee/services/secret-rotation/templates/postgres.ts b/backend/src/ee/services/secret-rotation/templates/postgres.ts index 318ca650d..c894631cb 100644 --- a/backend/src/ee/services/secret-rotation/templates/postgres.ts +++ b/backend/src/ee/services/secret-rotation/templates/postgres.ts @@ -23,15 +23,7 @@ export const POSTGRES_TEMPLATE = { }, ca: { type: "string", desc: "SSL certificate for db auth(string)" } }, - required: [ - "admin_username", - "admin_password", - "host", - "database", - "username1", - "username2", - "port" - ], + required: ["admin_username", "admin_password", "host", "database", "username1", "username2", "port"], additionalProperties: false }, outputs: { diff --git a/backend/src/ee/services/secret-scanning/git-app-dal.ts b/backend/src/ee/services/secret-scanning/git-app-dal.ts index 8a3ec706e..8044b0d6b 100644 --- a/backend/src/ee/services/secret-scanning/git-app-dal.ts +++ b/backend/src/ee/services/secret-scanning/git-app-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TGitAppOrgInsert } from "@app/db/schemas"; +import { TableName, TGitAppOrgInsert } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; @@ -12,11 +12,7 @@ export const gitAppDALFactory = (db: TDbClient) => { const upsert = async (data: TGitAppOrgInsert, tx?: Knex) => { try { - const [doc] = await (tx || db)(TableName.GitAppOrg) - .insert(data) - .onConflict("orgId") - .merge() - .returning("*"); + const [doc] = await (tx || db)(TableName.GitAppOrg).insert(data).onConflict("orgId").merge().returning("*"); return doc; } catch (error) { throw new DatabaseError({ error, name: "UpsertGitAppOrm" }); diff --git a/backend/src/ee/services/secret-scanning/git-app-install-session-dal.ts b/backend/src/ee/services/secret-scanning/git-app-install-session-dal.ts index 9956d88ec..11f8eb53c 100644 --- a/backend/src/ee/services/secret-scanning/git-app-install-session-dal.ts +++ b/backend/src/ee/services/secret-scanning/git-app-install-session-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TGitAppInstallSessionsInsert } from "@app/db/schemas"; +import { TableName, TGitAppInstallSessionsInsert } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; diff --git a/backend/src/ee/services/secret-scanning/secret-scanning-dal.ts b/backend/src/ee/services/secret-scanning/secret-scanning-dal.ts index 4b5dcb378..828322ad2 100644 --- a/backend/src/ee/services/secret-scanning/secret-scanning-dal.ts +++ b/backend/src/ee/services/secret-scanning/secret-scanning-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TSecretScanningGitRisksInsert } from "@app/db/schemas"; +import { TableName, TSecretScanningGitRisksInsert } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; @@ -12,10 +12,7 @@ export const secretScanningDALFactory = (db: TDbClient) => { const upsert = async (data: TSecretScanningGitRisksInsert[], tx?: Knex) => { try { - const docs = await (tx || db)(TableName.SecretScanningGitRisk) - .insert(data) - .onConflict("fingerprint") - .merge(); + const docs = await (tx || db)(TableName.SecretScanningGitRisk).insert(data).onConflict("fingerprint").merge(); return docs; } catch (error) { throw new DatabaseError({ error, name: "GitRiskUpsert" }); diff --git a/backend/src/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns.ts b/backend/src/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns.ts index b444cf56b..2e74a1caf 100644 --- a/backend/src/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns.ts +++ b/backend/src/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns.ts @@ -1,3 +1,4 @@ +import { Octokit } from "@octokit/rest"; import { exec } from "child_process"; import { mkdir, readFile, rm, writeFile } from "fs"; import { tmpdir } from "os"; @@ -11,7 +12,7 @@ export function createTempFolder(): Promise { const tempFolderName = Math.random().toString(36).substring(2); const tempFolderPath = join(tempDir, tempFolderName); - mkdir(tempFolderPath, (err: any) => { + mkdir(tempFolderPath, (err) => { if (err) { reject(err); } else { @@ -115,7 +116,7 @@ export function convertKeysToLowercase(obj: T): T { } export async function scanFullRepoContentAndGetFindings( - octokit: any, + octokit: Octokit, installationId: string, repositoryFullName: string ): Promise { @@ -125,11 +126,13 @@ export async function scanFullRepoContentAndGetFindings( try { const { data: { token } - } = await octokit.apps.createInstallationAccessToken({ installation_id: installationId }); + } = await octokit.apps.createInstallationAccessToken({ + installation_id: Number(installationId) + }); await cloneRepo(token, repositoryFullName, repoPath); await runInfisicalScanOnRepo(repoPath, findingsPath); const findingsData = await readFindingsFile(findingsPath); - return JSON.parse(findingsData); + return JSON.parse(findingsData) as SecretMatch[]; } finally { await deleteTempFolder(tempFolder); } @@ -144,7 +147,7 @@ export async function scanContentAndGetFindings(textContent: string): Promise { if (!Object.keys(allFindingsByFingerprint).length) return; - secretScanningDAL.upsert( + await secretScanningDAL.upsert( Object.keys(allFindingsByFingerprint).map((key) => ({ installationId, email: allFindingsByFingerprint[key].Email, @@ -186,7 +179,9 @@ export const secretScanningQueueFactory = ({ }); const findings = await scanFullRepoContentAndGetFindings( - octokit, + // this is because of collision of octokit in probot and github + // eslint-disable-next-line + octokit as any, installationId, repository.fullName ); diff --git a/backend/src/ee/services/secret-scanning/secret-scanning-service.ts b/backend/src/ee/services/secret-scanning/secret-scanning-service.ts index 63aae869f..7066fd485 100644 --- a/backend/src/ee/services/secret-scanning/secret-scanning-service.ts +++ b/backend/src/ee/services/secret-scanning/secret-scanning-service.ts @@ -4,10 +4,7 @@ import { ForbiddenError } from "@casl/ability"; import { WebhookEventMap } from "@octokit/webhooks-types"; import { ProbotOctokit } from "probot"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { getConfig } from "@app/lib/config/env"; import { UnauthorizedError } from "@app/lib/errors"; @@ -42,12 +39,9 @@ export const secretScanningServiceFactory = ({ permissionService, secretScanningQueue }: TSecretScanningServiceFactoryDep) => { - const createInstallationSession = async ({ actor, orgId, actorId }: TInstallAppSessionDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.SecretScanning - ); + const createInstallationSession = async ({ actor, orgId, actorId, actorOrgId }: TInstallAppSessionDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning); const sessionId = crypto.randomBytes(16).toString("hex"); await gitAppInstallSessionDAL.upsert({ orgId, sessionId, userId: actorId }); @@ -58,16 +52,14 @@ export const secretScanningServiceFactory = ({ sessionId, actorId, installationId, - actor + actor, + actorOrgId }: TLinkInstallSessionDTO) => { const session = await gitAppInstallSessionDAL.findOne({ sessionId }); if (!session) throw new UnauthorizedError({ message: "Session not found" }); - const { permission } = await permissionService.getOrgPermission(actor, actorId, session.orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.SecretScanning - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, session.orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning); const installatedApp = await gitAppOrgDAL.transaction(async (tx) => { await gitAppInstallSessionDAL.deleteById(session.id, tx); return gitAppOrgDAL.upsert({ orgId: session.orgId, installationId, userId: actorId }, tx); @@ -97,39 +89,24 @@ export const secretScanningServiceFactory = ({ return { installatedApp }; }; - const getOrgInstallationStatus = async ({ actorId, orgId, actor }: TGetOrgInstallStatusDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.SecretScanning - ); + const getOrgInstallationStatus = async ({ actorId, orgId, actor, actorOrgId }: TGetOrgInstallStatusDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning); const appInstallation = await gitAppOrgDAL.findOne({ orgId }); return Boolean(appInstallation); }; - const getRisksByOrg = async ({ actor, orgId, actorId }: TGetOrgRisksDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.SecretScanning - ); + const getRisksByOrg = async ({ actor, orgId, actorId, actorOrgId }: TGetOrgRisksDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning); const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] }); return { risks }; }; - const updateRiskStatus = async ({ - actorId, - orgId, - actor, - riskId, - status - }: TUpdateRiskStatusDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.SecretScanning - ); + const updateRiskStatus = async ({ actorId, orgId, actor, actorOrgId, riskId, status }: TUpdateRiskStatusDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning); const isRiskResolved = Boolean( [ @@ -169,11 +146,7 @@ export const secretScanningServiceFactory = ({ const handleRepoDeleteEvent = async (installationId: string, repositoryIds: string[]) => { await secretScanningDAL.transaction(async (tx) => { if (repositoryIds.length) { - await Promise.all( - repositoryIds.map((repoId) => - secretScanningDAL.delete({ repositoryId: repoId }, tx) - ) - ); + await Promise.all(repositoryIds.map((repoId) => secretScanningDAL.delete({ repositoryId: repoId }, tx))); } await gitAppOrgDAL.delete({ installationId }, tx); }); diff --git a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts index d1e5bad6b..6ec7a23d5 100644 --- a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts +++ b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts @@ -29,17 +29,11 @@ type TSecretSnapshotServiceFactoryDep = { snapshotSecretDAL: TSnapshotSecretDALFactory; snapshotFolderDAL: TSnapshotFolderDALFactory; secretVersionDAL: Pick; - folderVersionDAL: Pick< - TSecretFolderVersionDALFactory, - "findLatestVersionByFolderId" | "insertMany" - >; + folderVersionDAL: Pick; secretDAL: Pick; secretTagDAL: Pick; secretVersionTagDAL: Pick; - folderDAL: Pick< - TSecretFolderDALFactory, - "findById" | "findBySecretPath" | "delete" | "insertMany" - >; + folderDAL: Pick; permissionService: Pick; licenseService: Pick; }; @@ -64,13 +58,11 @@ export const secretSnapshotServiceFactory = ({ projectId, actorId, actor, + actorOrgId, path }: TProjectSnapshotCountDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); const folder = await folderDAL.findBySecretPath(projectId, environment, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); @@ -84,45 +76,32 @@ export const secretSnapshotServiceFactory = ({ projectId, actorId, actor, + actorOrgId, path, limit = 20, offset = 0 }: TProjectSnapshotListDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); const folder = await folderDAL.findBySecretPath(projectId, environment, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const snapshots = await snapshotDAL.find( - { folderId: folder.id }, - { limit, offset, sort: [["createdAt", "desc"]] } - ); + const snapshots = await snapshotDAL.find({ folderId: folder.id }, { limit, offset, sort: [["createdAt", "desc"]] }); return snapshots; }; - const getSnapshotData = async ({ actorId, actor, id }: TGetSnapshotDataDTO) => { + const getSnapshotData = async ({ actorId, actor, actorOrgId, id }: TGetSnapshotDataDTO) => { const snapshot = await snapshotDAL.findSecretSnapshotDataById(id); if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - snapshot.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); return snapshot; }; const performSnapshot = async (folderId: string) => { try { - if (!licenseService.isValidLicense) - throw new InternalServerError({ message: "Invalid license" }); + if (!licenseService.isValidLicense) throw new InternalServerError({ message: "Invalid license" }); const snapshot = await snapshotDAL.transaction(async (tx) => { const folder = await folderDAL.findById(folderId, tx); @@ -166,15 +145,11 @@ export const secretSnapshotServiceFactory = ({ } }; - const rollbackSnapshot = async ({ id: snapshotId, actor, actorId }: TRollbackSnapshotDTO) => { + const rollbackSnapshot = async ({ id: snapshotId, actor, actorId, actorOrgId }: TRollbackSnapshotDTO) => { const snapshot = await snapshotDAL.findById(snapshotId); if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - snapshot.projectId - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback @@ -199,9 +174,7 @@ export const secretSnapshotServiceFactory = ({ id, // this means don't bump up the version if not root folder // because below ones can be same version as nothing changed - version: deletedTopLevelFolders[folderId] - ? latestFolderVersion + 1 - : latestFolderVersion, + version: deletedTopLevelFolders[folderId] ? latestFolderVersion + 1 : latestFolderVersion, name, parentId: folderId })) @@ -211,22 +184,10 @@ export const secretSnapshotServiceFactory = ({ const secrets = await secretDAL.insertMany( rollbackSnaps.flatMap(({ secretVersions, folderId }) => secretVersions.map( - ({ - latestSecretVersion, - version, - updatedAt, - createdAt, - secretId, - envId, - id, - tags, - ...el - }) => ({ + ({ latestSecretVersion, version, updatedAt, createdAt, secretId, envId, id, tags, ...el }) => ({ ...el, id: secretId, - version: deletedTopLevelSecsGroupById[secretId] - ? latestSecretVersion + 1 - : latestSecretVersion, + version: deletedTopLevelSecsGroupById[secretId] ? latestSecretVersion + 1 : latestSecretVersion, folderId }) ) @@ -239,8 +200,7 @@ export const secretSnapshotServiceFactory = ({ secretVersions.forEach((secVer) => { secVer.tags.forEach((tag) => { secretTagsToBeInsert.push({ secretsId: secVer.secretId, secret_tagsId: tag.id }); - if (!secretVerTagToBeInsert?.[secVer.secretId]) - secretVerTagToBeInsert[secVer.secretId] = []; + if (!secretVerTagToBeInsert?.[secVer.secretId]) secretVerTagToBeInsert[secVer.secretId] = []; secretVerTagToBeInsert[secVer.secretId].push(tag.id); }); }); diff --git a/backend/src/ee/services/secret-snapshot/snapshot-dal.ts b/backend/src/ee/services/secret-snapshot/snapshot-dal.ts index 66c72ca53..41524c6eb 100644 --- a/backend/src/ee/services/secret-snapshot/snapshot-dal.ts +++ b/backend/src/ee/services/secret-snapshot/snapshot-dal.ts @@ -57,11 +57,7 @@ export const snapshotDALFactory = (db: TDbClient) => { const data = await (tx || db)(TableName.Snapshot) .where(`${TableName.Snapshot}.id`, snapshotId) .join(TableName.Environment, `${TableName.Snapshot}.envId`, `${TableName.Environment}.id`) - .leftJoin( - TableName.SnapshotSecret, - `${TableName.Snapshot}.id`, - `${TableName.SnapshotSecret}.snapshotId` - ) + .leftJoin(TableName.SnapshotSecret, `${TableName.Snapshot}.id`, `${TableName.SnapshotSecret}.snapshotId`) .leftJoin( TableName.SecretVersion, `${TableName.SnapshotSecret}.secretVersionId`, @@ -77,11 +73,7 @@ export const snapshotDALFactory = (db: TDbClient) => { `${TableName.SecretVersionTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id` ) - .leftJoin( - TableName.SnapshotFolder, - `${TableName.SnapshotFolder}.snapshotId`, - `${TableName.Snapshot}.id` - ) + .leftJoin(TableName.SnapshotFolder, `${TableName.SnapshotFolder}.snapshotId`, `${TableName.Snapshot}.id`) .leftJoin( TableName.SecretFolderVersion, `${TableName.SnapshotFolder}.folderVersionId`, @@ -131,13 +123,13 @@ export const snapshotDALFactory = (db: TDbClient) => { { key: "tagVersionId", label: "tags" as const, - mapper: ({ - tagId: id, - tagName: name, - tagSlug: slug, - tagColor: color, - tagVersionId: vId - }) => ({ id, name, slug, color, vId }) + mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color, tagVersionId: vId }) => ({ + id, + name, + slug, + color, + vId + }) } ] }, @@ -162,7 +154,8 @@ export const snapshotDALFactory = (db: TDbClient) => { try { const data = await (tx || db) .withRecursive("parent", (qb) => { - qb.from(TableName.Snapshot) + void qb + .from(TableName.Snapshot) .leftJoin( TableName.SnapshotFolder, `${TableName.SnapshotFolder}.snapshotId`, @@ -180,44 +173,37 @@ export const snapshotDALFactory = (db: TDbClient) => { db.ref("folderId").withSchema(TableName.SecretFolderVersion).as("folderVerId") ) .where(`${TableName.Snapshot}.id`, snapshotId) - .union((cb) => - cb - .select(selectAllTableCols(TableName.Snapshot)) - .select({ depth: db.raw("parent.depth + 1") }) - .select( - db.ref("name").withSchema(TableName.SecretFolderVersion).as("folderVerName"), - db.ref("folderId").withSchema(TableName.SecretFolderVersion).as("folderVerId") - ) - .from(TableName.Snapshot) - .join( - db(TableName.Snapshot) - .groupBy("folderId") - .max("createdAt") - .select("folderId") - .as("latestVersion"), - `${TableName.Snapshot}.createdAt`, - "latestVersion.max" - ) - .leftJoin( - TableName.SnapshotFolder, - `${TableName.SnapshotFolder}.snapshotId`, - `${TableName.Snapshot}.id` - ) - .leftJoin( - TableName.SecretFolderVersion, - `${TableName.SnapshotFolder}.folderVersionId`, - `${TableName.SecretFolderVersion}.id` - ) - .join("parent", "parent.folderVerId", `${TableName.Snapshot}.folderId`) + .union( + (cb) => + void cb + .select(selectAllTableCols(TableName.Snapshot)) + .select({ depth: db.raw("parent.depth + 1") }) + .select( + db.ref("name").withSchema(TableName.SecretFolderVersion).as("folderVerName"), + db.ref("folderId").withSchema(TableName.SecretFolderVersion).as("folderVerId") + ) + .from(TableName.Snapshot) + .join( + db(TableName.Snapshot).groupBy("folderId").max("createdAt").select("folderId").as("latestVersion"), + `${TableName.Snapshot}.createdAt`, + "latestVersion.max" + ) + .leftJoin( + TableName.SnapshotFolder, + `${TableName.SnapshotFolder}.snapshotId`, + `${TableName.Snapshot}.id` + ) + .leftJoin( + TableName.SecretFolderVersion, + `${TableName.SnapshotFolder}.folderVersionId`, + `${TableName.SecretFolderVersion}.id` + ) + .join("parent", "parent.folderVerId", `${TableName.Snapshot}.folderId`) ); }) .orderBy("depth", "asc") .from("parent") - .leftJoin( - TableName.SnapshotSecret, - `parent.id`, - `${TableName.SnapshotSecret}.snapshotId` - ) + .leftJoin(TableName.SnapshotSecret, `parent.id`, `${TableName.SnapshotSecret}.snapshotId`) .leftJoin( TableName.SecretVersion, `${TableName.SnapshotSecret}.secretVersionId`, @@ -270,11 +256,7 @@ export const snapshotDALFactory = (db: TDbClient) => { const formated = sqlNestRelationships({ data, key: "snapshotId", - parentMapper: ({ - snapshotId: id, - snapshotFolderId: folderId, - snapshotParentFolderId: parentFolderId - }) => ({ + parentMapper: ({ snapshotId: id, snapshotFolderId: folderId, snapshotParentFolderId: parentFolderId }) => ({ id, folderId, parentFolderId @@ -285,19 +267,19 @@ export const snapshotDALFactory = (db: TDbClient) => { label: "secretVersions" as const, mapper: (el) => ({ ...SecretVersionsSchema.parse(el), - latestSecretVersion: el.latestSecretVersion + latestSecretVersion: el.latestSecretVersion as number }), childrenMapper: [ { key: "tagVersionId", label: "tags" as const, - mapper: ({ - tagId: id, - tagName: name, - tagSlug: slug, - tagColor: color, - tagVersionId: vId - }) => ({ id, name, slug, color, vId }) + mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color, tagVersionId: vId }) => ({ + id, + name, + slug, + color, + vId + }) } ] }, @@ -307,7 +289,7 @@ export const snapshotDALFactory = (db: TDbClient) => { mapper: ({ folderVerId: id, folderVerName: name, latestFolderVersion }) => ({ id, name, - latestFolderVersion + latestFolderVersion: latestFolderVersion as number }) } ] @@ -326,11 +308,7 @@ export const snapshotDALFactory = (db: TDbClient) => { const docs = await (tx || db)(TableName.Snapshot) .where(`${TableName.Snapshot}.folderId`, folderId) .join( - (tx || db)(TableName.Snapshot) - .groupBy("folderId") - .max("createdAt") - .select("folderId") - .as("latestVersion"), + (tx || db)(TableName.Snapshot).groupBy("folderId").max("createdAt").select("folderId").as("latestVersion"), (bd) => { bd.on(`${TableName.Snapshot}.folderId`, "latestVersion.folderId").andOn( `${TableName.Snapshot}.createdAt`, diff --git a/backend/src/ee/services/trusted-ip/trusted-ip-service.ts b/backend/src/ee/services/trusted-ip/trusted-ip-service.ts index 72b0eef9f..14c73db1f 100644 --- a/backend/src/ee/services/trusted-ip/trusted-ip-service.ts +++ b/backend/src/ee/services/trusted-ip/trusted-ip-service.ts @@ -26,12 +26,9 @@ export const trustedIpServiceFactory = ({ licenseService, projectDAL }: TTrustedIpServiceFactoryDep) => { - const listIpsByProjectId = async ({ projectId, actor, actorId }: TProjectPermission) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.IpAllowList - ); + const listIpsByProjectId = async ({ projectId, actor, actorId, actorOrgId }: TProjectPermission) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList); const trustedIps = await trustedIpDAL.find({ projectId }); @@ -42,22 +39,19 @@ export const trustedIpServiceFactory = ({ projectId, actorId, actor, + actorOrgId, ipAddress: ip, comment, isActive }: TCreateIpDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.IpAllowList - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); const project = await projectDAL.findById(projectId); const plan = await licenseService.getPlan(project.orgId); if (!plan.ipAllowlisting) throw new BadRequestError({ - message: - "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." + message: "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." }); const isValidIp = isValidIpOrCidr(ip); @@ -83,22 +77,19 @@ export const trustedIpServiceFactory = ({ projectId, actorId, actor, + actorOrgId, ipAddress: ip, comment, trustedIpId }: TUpdateIpDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.IpAllowList - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); const project = await projectDAL.findById(projectId); const plan = await licenseService.getPlan(project.orgId); if (!plan.ipAllowlisting) throw new BadRequestError({ - message: - "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." + message: "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." }); const isValidIp = isValidIpOrCidr(ip); @@ -122,19 +113,15 @@ export const trustedIpServiceFactory = ({ return { trustedIp, project }; // for audit log }; - const deleteProjectIp = async ({ projectId, actorId, actor, trustedIpId }: TDeleteIpDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.IpAllowList - ); + const deleteProjectIp = async ({ projectId, actorId, actor, actorOrgId, trustedIpId }: TDeleteIpDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); const project = await projectDAL.findById(projectId); const plan = await licenseService.getPlan(project.orgId); if (!plan.ipAllowlisting) throw new BadRequestError({ - message: - "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." + message: "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." }); const [trustedIp] = await trustedIpDAL.delete({ projectId, id: trustedIpId }); diff --git a/backend/src/lib/casl/index.ts b/backend/src/lib/casl/index.ts index 1e853cc4d..9e5cb29d3 100644 --- a/backend/src/lib/casl/index.ts +++ b/backend/src/lib/casl/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { buildMongoQueryMatcher, MongoAbility } from "@casl/ability"; import { FieldCondition, FieldInstruction, JsInterpreter } from "@ucast/mongo2js"; import picomatch from "picomatch"; @@ -12,7 +13,7 @@ const $glob: FieldInstruction = { }; const glob: JsInterpreter> = (node, object, context) => { - const secretPath = context.get(object, node.field); + const secretPath = context.get(object, node.field) as string; const permissionSecretGlobPath = node.value; return picomatch.isMatch(secretPath, permissionSecretGlobPath, { strictSlashes: false }); }; @@ -23,7 +24,7 @@ export const conditionsMatcher = buildMongoQueryMatcher({ $glob }, { glob }); * Extracts and formats permissions from a CASL Ability object or a raw permission set. */ const extractPermissions = (ability: MongoAbility) => - ability.rules.map((permission) => `${permission.action}_${permission.subject}`); + ability.rules.map((permission) => `${permission.action as string}_${permission.subject as string}`); /** * Compares two sets of permissions to determine if the first set is at least as privileged as the second set. diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts index 25d985f41..4542c7fc3 100644 --- a/backend/src/lib/config/env.ts +++ b/backend/src/lib/config/env.ts @@ -15,9 +15,11 @@ const envSchema = z PORT: z.coerce.number().default(4000), REDIS_URL: zpStr(z.string()), HOST: zpStr(z.string().default("localhost")), - DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database conntection string")), + DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database connection string")), + DB_ROOT_CERT: zpStr(z.string().describe("Postgres database base64-encoded CA cert").optional()), NODE_ENV: z.enum(["development", "test", "production"]).default("production"), SALT_ROUNDS: z.coerce.number().default(10), + INITIAL_ORGANIZATION_NAME: zpStr(z.string().optional()), // TODO(akhilmhdh): will be changed to one ENCRYPTION_KEY: zpStr(z.string().optional()), ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()), @@ -38,9 +40,7 @@ const envSchema = z // Telemetry TELEMETRY_ENABLED: zodStrBool.default("true"), POSTHOG_HOST: zpStr(z.string().optional().default("https://app.posthog.com")), - POSTHOG_PROJECT_API_KEY: zpStr( - z.string().optional().default("phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE") - ), + POSTHOG_PROJECT_API_KEY: zpStr(z.string().optional().default("phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE")), LOOPS_API_KEY: zpStr(z.string().optional()), // jwt options AUTH_SECRET: zpStr(z.string()).default(process.env.JWT_AUTH_SECRET), // for those still using old JWT_AUTH_SECRET @@ -56,7 +56,12 @@ const envSchema = z CLIENT_SECRET_GITHUB_LOGIN: zpStr(z.string().optional()), CLIENT_ID_GITLAB_LOGIN: zpStr(z.string().optional()), CLIENT_SECRET_GITLAB_LOGIN: zpStr(z.string().optional()), - CLIENT_GITLAB_LOGIN_URL: zpStr(z.string().optional().default(process.env.URL_GITLAB_LOGIN ?? GITLAB_URL)), // fallback since URL_GITLAB_LOGIN has been renamed + CLIENT_GITLAB_LOGIN_URL: zpStr( + z + .string() + .optional() + .default(process.env.URL_GITLAB_LOGIN ?? GITLAB_URL) + ), // fallback since URL_GITLAB_LOGIN has been renamed // integration client secrets // heroku CLIENT_ID_HEROKU: zpStr(z.string().optional()), @@ -90,7 +95,7 @@ const envSchema = z SECRET_SCANNING_GIT_APP_ID: zpStr(z.string().optional()), SECRET_SCANNING_PRIVATE_KEY: zpStr(z.string().optional()), // LICENCE - LICENSE_SERVER_URL: zpStr(z.string().optional()), + LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")), LICENSE_SERVER_KEY: zpStr(z.string().optional()), LICENSE_KEY: zpStr(z.string().optional()), STANDALONE_MODE: z @@ -121,7 +126,7 @@ export const initEnvConfig = (logger: Logger) => { logger.error(parsedEnv.error.issues); process.exit(-1); } - + envCfg = Object.freeze(parsedEnv.data); return envCfg; }; diff --git a/backend/src/lib/config/request.ts b/backend/src/lib/config/request.ts index 091713727..8636b7476 100644 --- a/backend/src/lib/config/request.ts +++ b/backend/src/lib/config/request.ts @@ -5,6 +5,7 @@ export const request = axios.create(); axiosRetry(request, { retries: 3, + // eslint-disable-next-line retryDelay: axiosRetry.exponentialDelay, retryCondition: (err) => axiosRetry.isNetworkError(err) || axiosRetry.isRetryableError(err) }); diff --git a/backend/src/lib/crypto/encryption.ts b/backend/src/lib/crypto/encryption.ts index 938db632c..74febccec 100644 --- a/backend/src/lib/crypto/encryption.ts +++ b/backend/src/lib/crypto/encryption.ts @@ -1,6 +1,6 @@ import crypto from "node:crypto"; -import * as argon2 from "argon2"; +import argon2 from "argon2"; import nacl from "tweetnacl"; import naclUtils from "tweetnacl-util"; @@ -20,11 +20,7 @@ export const BLOCK_SIZE_BYTES_16 = 16; export const decryptSymmetric = ({ ciphertext, iv, tag, key }: TDecryptSymmetricInput): string => { const secretKey = crypto.createSecretKey(key, "base64"); - const decipher = crypto.createDecipheriv( - SecretEncryptionAlgo.AES_256_GCM, - secretKey, - Buffer.from(iv, "base64") - ); + const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, secretKey, Buffer.from(iv, "base64")); decipher.setAuthTag(Buffer.from(tag, "base64")); let cleartext = decipher.update(ciphertext, "base64", "utf8"); cleartext += decipher.final("utf8"); @@ -62,17 +58,8 @@ export const encryptSymmetric128BitHexKeyUTF8 = (plaintext: string, key: string) }; }; -export const decryptSymmetric128BitHexKeyUTF8 = ({ - ciphertext, - iv, - tag, - key -}: TDecryptSymmetricInput): string => { - const decipher = crypto.createDecipheriv( - SecretEncryptionAlgo.AES_256_GCM, - key, - Buffer.from(iv, "base64") - ); +export const decryptSymmetric128BitHexKeyUTF8 = ({ ciphertext, iv, tag, key }: TDecryptSymmetricInput): string => { + const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, key, Buffer.from(iv, "base64")); decipher.setAuthTag(Buffer.from(tag, "base64")); @@ -104,12 +91,7 @@ export type TDecryptAsymmetricInput = { privateKey: string; }; -export const decryptAsymmetric = ({ - ciphertext, - nonce, - publicKey, - privateKey -}: TDecryptAsymmetricInput) => { +export const decryptAsymmetric = ({ ciphertext, nonce, publicKey, privateKey }: TDecryptAsymmetricInput) => { const plaintext: Uint8Array | null = nacl.box.open( naclUtils.decodeBase64(ciphertext), naclUtils.decodeBase64(nonce), @@ -223,7 +205,7 @@ export const infisicalSymmetricEncypt = (data: string) => { throw new Error("Missing both encryption keys"); }; -export const infisicalSymmetricDecrypt = ({ +export const infisicalSymmetricDecrypt = ({ keyEncoding, ciphertext, tag, diff --git a/backend/src/lib/fn/array.ts b/backend/src/lib/fn/array.ts index abe8e3466..1e075101b 100644 --- a/backend/src/lib/fn/array.ts +++ b/backend/src/lib/fn/array.ts @@ -23,13 +23,10 @@ export const groupBy = ( * to convert each item in the list to a comparable identity * value */ -export const unique = ( - array: readonly T[], - toKey?: (item: T) => K -): T[] => { +export const unique = (array: readonly T[], toKey?: (item: T) => K): T[] => { const valueMap = array.reduce( (acc, item) => { - const key = toKey ? toKey(item) : (item as any as string | number | symbol); + const key = toKey ? toKey(item) : (item as unknown as string | number | symbol); if (acc[key]) return acc; acc[key] = item; return acc; diff --git a/backend-mongo/src/ee/secretRotation/db.ts b/backend/src/lib/fn/dates.ts similarity index 100% rename from backend-mongo/src/ee/secretRotation/db.ts rename to backend/src/lib/fn/dates.ts diff --git a/backend/src/lib/fn/object.ts b/backend/src/lib/fn/object.ts index 2fadc685c..87db80343 100644 --- a/backend/src/lib/fn/object.ts +++ b/backend/src/lib/fn/object.ts @@ -2,10 +2,7 @@ * Pick a list of properties from an object * into a new object */ -export const pick = ( - obj: T, - keys: TKeys[] -): Pick => { +export const pick = (obj: T, keys: TKeys[]): Pick => { if (!obj) return {} as Pick; return keys.reduce( (acc, key) => { @@ -21,9 +18,9 @@ export const pick = ( * object. Optional second argument shakes out values * by custom evaluation. */ -export const shake = ( +export const shake = ( obj: T, - filter: (value: any) => boolean = (x) => x === undefined || x === null + filter: (value: unknown) => boolean = (x) => x === undefined || x === null ): Omit => { if (!obj) return {} as T; const keys = Object.keys(obj) as (keyof T)[]; diff --git a/backend/src/lib/fn/string.ts b/backend/src/lib/fn/string.ts index 27d84357d..c3651fd4e 100644 --- a/backend/src/lib/fn/string.ts +++ b/backend/src/lib/fn/string.ts @@ -2,4 +2,10 @@ import path from "path"; // given two paths irrespective of ending with / or not // this will return true if its equal -export const isSamePath = async (from: string, to: string) => !path.relative(from, to); +export const isSamePath = (from: string, to: string) => !path.relative(from, to); + +export const removeTrailingSlash = (str: string) => { + if (str === "/") return str; + + return str.endsWith("/") ? str.slice(0, -1) : str; +}; diff --git a/backend/src/lib/ip/index.ts b/backend/src/lib/ip/index.ts index f14ed4f41..30f710d19 100644 --- a/backend/src/lib/ip/index.ts +++ b/backend/src/lib/ip/index.ts @@ -111,13 +111,7 @@ export type TIp = { /** * Validates the IP address [ipAddress] against the trusted IPs [trustedIps]. */ -export const checkIPAgainstBlocklist = ({ - ipAddress, - trustedIps -}: { - ipAddress: string; - trustedIps: TIp[]; -}) => { +export const checkIPAgainstBlocklist = ({ ipAddress, trustedIps }: { ipAddress: string; trustedIps: TIp[] }) => { const blockList = new net.BlockList(); for (const trustedIp of trustedIps) { diff --git a/backend/src/lib/knex/index.ts b/backend/src/lib/knex/index.ts index 515089e65..37fae624e 100644 --- a/backend/src/lib/knex/index.ts +++ b/backend/src/lib/knex/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ import { Knex } from "knex"; import { Tables } from "knex/types/tables"; @@ -15,22 +16,22 @@ export const withTransaction = (db: Knex, dal: K) => ({ ...dal }); -export type TFindFilter = Partial & { +export type TFindFilter = Partial & { $in?: Partial<{ [k in keyof R]: R[k][] }>; }; export const buildFindFilter = - ({ $in, ...filter }: TFindFilter) => + ({ $in, ...filter }: TFindFilter) => (bd: Knex.QueryBuilder) => { - bd.where(filter); + void bd.where(filter); if ($in) { Object.entries($in).forEach(([key, val]) => { - bd.whereIn(key as any, val as any); + void bd.whereIn(key as never, val as never); }); } return bd; }; -export type TFindOpt = { +export type TFindOpt = { limit?: number; offset?: number; sort?: Array<[keyof R, "asc" | "desc"] | [keyof R, "asc" | "desc", "first" | "last"]>; @@ -40,11 +41,7 @@ export type TFindOpt = { // What is ormify // It is to inject typical operations like find, findOne, update, delete, create // This will avoid writing most common ones each time -export const ormify = ( - db: Knex, - tableName: Tname, - dal?: DbOps -) => ({ +export const ormify = (db: Knex, tableName: Tname, dal?: DbOps) => ({ transaction: async (cb: (tx: Knex) => Promise) => db.transaction(async (trx) => { const res = await cb(trx); @@ -53,7 +50,7 @@ export const ormify = ( findById: async (id: string, tx?: Knex) => { try { const result = await (tx || db)(tableName) - .where({ id } as any) + .where({ id } as never) .first("*"); return result; } catch (error) { @@ -74,12 +71,10 @@ export const ormify = ( ) => { try { const query = (tx || db)(tableName).where(buildFindFilter(filter)); - if (limit) query.limit(limit); - if (offset) query.offset(offset); + if (limit) void query.limit(limit); + if (offset) void query.offset(offset); if (sort) { - query.orderBy( - sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })) - ); + void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls }))); } const res = await query; return res; @@ -90,7 +85,7 @@ export const ormify = ( create: async (data: Tables[Tname]["insert"], tx?: Knex) => { try { const [res] = await (tx || db)(tableName) - .insert(data as any) + .insert(data as never) .returning("*"); return res; } catch (error) { @@ -101,7 +96,7 @@ export const ormify = ( try { if (!data.length) return []; const res = await (tx || db)(tableName) - .insert(data as any) + .insert(data as never) .returning("*"); return res; } catch (error) { @@ -111,23 +106,19 @@ export const ormify = ( updateById: async (id: string, data: Tables[Tname]["update"], tx?: Knex) => { try { const [res] = await (tx || db)(tableName) - .where({ id } as any) - .update(data as any) + .where({ id } as never) + .update(data as never) .returning("*"); return res; } catch (error) { throw new DatabaseError({ error, name: "Update by id" }); } }, - update: async ( - filter: TFindFilter, - data: Tables[Tname]["update"], - tx?: Knex - ) => { + update: async (filter: TFindFilter, data: Tables[Tname]["update"], tx?: Knex) => { try { const res = await (tx || db)(tableName) .where(buildFindFilter(filter)) - .update(data as any) + .update(data as never) .returning("*"); return res; } catch (error) { @@ -137,7 +128,7 @@ export const ormify = ( deleteById: async (id: string, tx?: Knex) => { try { const [res] = await (tx || db)(tableName) - .where({ id } as any) + .where({ id } as never) .delete() .returning("*"); return res; @@ -147,10 +138,7 @@ export const ormify = ( }, delete: async (filter: TFindFilter, tx?: Knex) => { try { - const res = await (tx || db)(tableName) - .where(buildFindFilter(filter)) - .delete() - .returning("*"); + const res = await (tx || db)(tableName).where(buildFindFilter(filter)).delete().returning("*"); return res; } catch (error) { throw new DatabaseError({ error, name: "Delete" }); diff --git a/backend/src/lib/knex/join.ts b/backend/src/lib/knex/join.ts index 59144fbcc..6580bfa3d 100644 --- a/backend/src/lib/knex/join.ts +++ b/backend/src/lib/knex/join.ts @@ -1,7 +1,16 @@ +/* eslint-disable @typescript-eslint/ban-types */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +// TODO(akhilmhdh): make this better later + export const mergeOneToManyRelation = < - T extends Record, + T extends Record, Pk extends keyof T, - P extends Record, + P extends Record, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint C extends any, Ck extends string = "child" >( @@ -22,7 +31,7 @@ export const mergeOneToManyRelation = < const parent = parentMapper(row) as any; parent[childKey] = []; groupedRecord.push(parent); - prevPkId = pk; + prevPkId = pk as string; prevPkIndex += 1; } groupedRecord[prevPkIndex][childKey].push(childMapper(row)); @@ -41,6 +50,7 @@ export type TSqlPackRelationships< childrenMapper: C; }; +// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint export type TChildMapper = { key: keyof T; label: U; @@ -82,7 +92,7 @@ const sqlChildMapper = < const ck = `${prefix}-${label}-${doc[childPk]}`; const val = mapper(doc); if (!lookupTable.has(ck)) { - if (typeof val !== "undefined" && val !== null) docsByPk[pk as keyof P][label].push(val); + if (typeof val !== "undefined" && val !== null) docsByPk[pk][label].push(val); lookupTable.add(ck); } if (nestedMappers && val) { diff --git a/backend/src/lib/logger/index.ts b/backend/src/lib/logger/index.ts index df012e434..21cb66920 100644 --- a/backend/src/lib/logger/index.ts +++ b/backend/src/lib/logger/index.ts @@ -1 +1 @@ -export { initLogger,logger } from "./logger"; +export { initLogger, logger } from "./logger"; diff --git a/backend/src/lib/logger/logger.ts b/backend/src/lib/logger/logger.ts index 82db1725c..c124fcf4c 100644 --- a/backend/src/lib/logger/logger.ts +++ b/backend/src/lib/logger/logger.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ // logger follows a singleton pattern // easier to use it that's all. import pino, { Logger } from "pino"; @@ -14,10 +15,10 @@ const logLevelToSeverityLookup: Record = { // eslint-disable-next-line import/no-mutable-exports export let logger: Readonly; -// akhilmhdh: -// The logger is not placed in the main app config to avoid a circular dependency. -// The config requires the logger to display errors when an invalid environment is supplied. -// On the other hand, the logger needs the config to obtain credentials for AWS or other transports. +// akhilmhdh: +// The logger is not placed in the main app config to avoid a circular dependency. +// The config requires the logger to display errors when an invalid environment is supplied. +// On the other hand, the logger needs the config to obtain credentials for AWS or other transports. // By keeping the logger separate, it becomes an independent package. const loggerConfig = z.object({ @@ -25,14 +26,23 @@ const loggerConfig = z.object({ AWS_CLOUDWATCH_LOG_REGION: z.string().default("us-east-1"), AWS_CLOUDWATCH_LOG_ACCESS_KEY_ID: z.string().min(1).optional(), AWS_CLOUDWATCH_LOG_ACCESS_KEY_SECRET: z.string().min(1).optional(), - AWS_CLOUDWATCH_LOG_INTERVAL: z.coerce.number().default(1000) + AWS_CLOUDWATCH_LOG_INTERVAL: z.coerce.number().default(1000), + NODE_ENV: z.enum(["development", "test", "production"]).default("production") }); export const initLogger = async () => { - const targets: pino.TransportMultiOptions["targets"][number][] = [ - { level: "info", target: "pino/file", options: {} } - ]; const cfg = loggerConfig.parse(process.env); + const targets: pino.TransportMultiOptions["targets"][number][] = [ + { + level: "info", + target: "pino/file", + options: { + destination: 1, + mkdir: true + } + } + ]; + if (cfg.AWS_CLOUDWATCH_LOG_ACCESS_KEY_ID && cfg.AWS_CLOUDWATCH_LOG_ACCESS_KEY_SECRET) { targets.push({ target: "@serdnam/pino-cloudwatch-transport", @@ -66,6 +76,7 @@ export const initLogger = async () => { }) } }, + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument transport ); return logger; diff --git a/backend/src/lib/types/index.ts b/backend/src/lib/types/index.ts index 5c194d789..918322d62 100644 --- a/backend/src/lib/types/index.ts +++ b/backend/src/lib/types/index.ts @@ -4,12 +4,14 @@ export type TOrgPermission = { actor: ActorType; actorId: string; orgId: string; + actorOrgId?: string; }; export type TProjectPermission = { actor: ActorType; actorId: string; projectId: string; + actorOrgId?: string; }; export type RequiredKeys = { diff --git a/backend/src/lib/zod/index.ts b/backend/src/lib/zod/index.ts index 331a5fbb6..a3cded66b 100644 --- a/backend/src/lib/zod/index.ts +++ b/backend/src/lib/zod/index.ts @@ -1,10 +1,7 @@ -import { z,ZodTypeAny } from "zod"; +import { z, ZodTypeAny } from "zod"; // this is a patched zod string to remove empty string to undefined -export const zpStr = ( - schema: T, - opt: { stripNull: boolean } = { stripNull: true } -) => +export const zpStr = (schema: T, opt: { stripNull: boolean } = { stripNull: true }) => z.preprocess((val) => { if (opt.stripNull && val === null) return undefined; if (typeof val !== "string") return val; diff --git a/backend/src/main.ts b/backend/src/main.ts index 4891157ad..fab576d3b 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -12,32 +12,38 @@ dotenv.config(); const run = async () => { const logger = await initLogger(); const appCfg = initEnvConfig(logger); - const db = initDbConnection(appCfg.DB_CONNECTION_URI); + const db = initDbConnection({ + dbConnectionUri: appCfg.DB_CONNECTION_URI, + dbRootCert: appCfg.DB_ROOT_CERT + }); + const smtp = smtpServiceFactory(formatSmtpConfig()); const queue = queueServiceFactory(appCfg.REDIS_URL); const server = await main({ db, smtp, logger, queue }); const bootstrap = await bootstrapCheck({ db }); + // eslint-disable-next-line process.on("SIGINT", async () => { await server.close(); await db.destroy(); process.exit(0); }); + // eslint-disable-next-line process.on("SIGTERM", async () => { await server.close(); await db.destroy(); process.exit(0); }); - server.listen({ + await server.listen({ port: appCfg.PORT, host: appCfg.HOST, listenTextResolver: (address) => { - bootstrap(); + void bootstrap(); return address; } }); }; -run(); +void run(); diff --git a/backend/src/queue/queue-service.ts b/backend/src/queue/queue-service.ts index 8a170487f..58c829549 100644 --- a/backend/src/queue/queue-service.ts +++ b/backend/src/queue/queue-service.ts @@ -1,4 +1,4 @@ -import { Job, JobsOptions, Queue, RepeatOptions, Worker, WorkerListener } from "bullmq"; +import { Job, JobsOptions, Queue, QueueOptions, RepeatOptions, Worker, WorkerListener } from "bullmq"; import Redis from "ioredis"; import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types"; @@ -11,6 +11,7 @@ export enum QueueName { SecretRotation = "secret-rotation", SecretReminder = "secret-reminder", AuditLog = "audit-log", + AuditLogPrune = "audit-log-prune", IntegrationSync = "sync-integrations", SecretWebhook = "secret-webhook", SecretFullRepoScan = "secret-full-repo-scan", @@ -21,6 +22,7 @@ export enum QueueJobs { SecretReminder = "secret-reminder-job", SecretRotation = "secret-rotation-job", AuditLog = "audit-log-job", + AuditLogPrune = "audit-log-prune-job", SecWebhook = "secret-webhook-trigger", IntegrationSync = "secret-integration-pull", SecretScan = "secret-scan" @@ -45,6 +47,10 @@ export type TQueueJobTypes = { name: QueueJobs.AuditLog; payload: TCreateAuditLogDTO; }; + [QueueName.AuditLogPrune]: { + name: QueueJobs.AuditLogPrune; + payload: undefined; + }; [QueueName.SecretWebhook]: { name: QueueJobs.SecWebhook; payload: { projectId: string; environment: string; secretPath: string }; @@ -63,37 +69,36 @@ export type TQueueJobTypes = { export type TQueueServiceFactory = ReturnType; export const queueServiceFactory = (redisUrl: string) => { const connection = new Redis(redisUrl, { maxRetriesPerRequest: null }); - const queueContainer: Record< + const queueContainer = {} as Record< QueueName, Queue - > = {} as any; - const workerContainer: Record< + >; + const workerContainer = {} as Record< QueueName, Worker - > = {} as any; + >; const start = ( name: T, - jobFn: ( - job: Job - ) => Promise + jobFn: (job: Job) => Promise, + queueSettings: Omit = {} ) => { if (queueContainer[name]) { throw new Error(`${name} queue is already initialized`); } - queueContainer[name] = new Queue( - name as string, - { connection } - ); - workerContainer[name] = new Worker< - TQueueJobTypes[T]["payload"], - void, - TQueueJobTypes[T]["name"] - >(name, jobFn, { connection }); + queueContainer[name] = new Queue(name as string, { + ...queueSettings, + connection + }); + + workerContainer[name] = new Worker(name, jobFn, { + ...queueSettings, + connection + }); }; - const listen = async < + const listen = < T extends QueueName, U extends keyof WorkerListener >( @@ -134,9 +139,20 @@ export const queueServiceFactory = (redisUrl: string) => { return q.removeRepeatableByKey(job.repeatJobKey); }; + const stopJobById = async (name: T, jobId: string) => { + const q = queueContainer[name]; + const job = await q.getJob(jobId); + return job?.remove().catch(() => undefined); + }; + + const clearQueue = async (name: QueueName) => { + const q = queueContainer[name]; + await q.drain(); + }; + const shutdown = async () => { await Promise.all(Object.values(workerContainer).map((worker) => worker.close())); }; - return { start, listen, queue, shutdown, stopRepeatableJob, stopRepeatableJobByJobId }; + return { start, listen, queue, shutdown, stopRepeatableJob, stopRepeatableJobByJobId, clearQueue, stopJobById }; }; diff --git a/backend/src/server/app.ts b/backend/src/server/app.ts index c8eb0d28f..ca6b9003a 100644 --- a/backend/src/server/app.ts +++ b/backend/src/server/app.ts @@ -5,6 +5,7 @@ import type { FastifyCookieOptions } from "@fastify/cookie"; import cookie from "@fastify/cookie"; import type { FastifyCorsOptions } from "@fastify/cors"; import cors from "@fastify/cors"; +import fastifyEtag from "@fastify/etag"; import fastifyFormBody from "@fastify/formbody"; import helmet from "@fastify/helmet"; import type { FastifyRateLimitOptions } from "@fastify/rate-limit"; @@ -13,11 +14,10 @@ import fasitfy from "fastify"; import { Knex } from "knex"; import { Logger } from "pino"; +import { getConfig } from "@app/lib/config/env"; import { TQueueServiceFactory } from "@app/queue"; import { TSmtpService } from "@app/services/smtp/smtp-service"; -import { getConfig } from "@lib/config/env"; - import { globalRateLimiterCfg } from "./config/rateLimiter"; import { fastifyErrHandler } from "./plugins/error-handler"; import { registerExternalNextjs } from "./plugins/external-nextjs"; @@ -39,6 +39,7 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => { const server = fasitfy({ logger, trustProxy: true, + connectionTimeout: 30 * 1000, ignoreTrailingSlash: true }).withTypeProvider(); @@ -50,6 +51,8 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => { secret: appCfg.COOKIE_SECRET_SIGN_KEY }); + await server.register(fastifyEtag); + await server.register(cors, { credentials: true, origin: appCfg.SITE_URL || true @@ -72,7 +75,7 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => { if (appCfg.isProductionMode) { await server.register(registerExternalNextjs, { standaloneMode: appCfg.STANDALONE_MODE, - dir: path.join(__dirname, "../"), + dir: path.join(__dirname, "../../"), port: appCfg.PORT }); } diff --git a/backend/src/server/boot-strap-check.ts b/backend/src/server/boot-strap-check.ts index d036db8e3..381e575ef 100644 --- a/backend/src/server/boot-strap-check.ts +++ b/backend/src/server/boot-strap-check.ts @@ -12,9 +12,9 @@ type BootstrapOpt = { db: Knex; }; -const bootstrapCb = () => { +const bootstrapCb = async () => { const appCfg = getConfig(); - const serverCfg = getServerCfg(); + const serverCfg = await getServerCfg(); if (!serverCfg.initialized) { console.info(`Welcome to Infisical diff --git a/backend/src/server/config/rateLimiter.ts b/backend/src/server/config/rateLimiter.ts index 031ef1fb5..444158cbf 100644 --- a/backend/src/server/config/rateLimiter.ts +++ b/backend/src/server/config/rateLimiter.ts @@ -6,12 +6,12 @@ import { getConfig } from "@app/lib/config/env"; export const globalRateLimiterCfg = (): RateLimitPluginOptions => { const appCfg = getConfig(); const redis = appCfg.isRedisConfigured - ? new Redis(appCfg.REDIS_URL as string, { connectTimeout: 500, maxRetriesPerRequest: 1 }) + ? new Redis(appCfg.REDIS_URL, { connectTimeout: 500, maxRetriesPerRequest: 1 }) : null; return { timeWindow: 60 * 1000, - max: 400, + max: 600, redis, allowList: (req) => req.url === "/healthcheck" || req.url === "/api/status", keyGenerator: (req) => req.realIp @@ -20,12 +20,12 @@ export const globalRateLimiterCfg = (): RateLimitPluginOptions => { export const authRateLimit: RateLimitOptions = { timeWindow: 60 * 1000, - max: 300, + max: 600, keyGenerator: (req) => req.realIp }; export const passwordRateLimit: RateLimitOptions = { timeWindow: 60 * 1000, - max: 300, + max: 600, keyGenerator: (req) => req.realIp }; diff --git a/backend/src/server/plugins/auth/inject-identity.ts b/backend/src/server/plugins/auth/inject-identity.ts index f60b9f4c5..7abcd073c 100644 --- a/backend/src/server/plugins/auth/inject-identity.ts +++ b/backend/src/server/plugins/auth/inject-identity.ts @@ -5,16 +5,12 @@ import jwt, { JwtPayload } from "jsonwebtoken"; import { TServiceTokens, TUsers } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { UnauthorizedError } from "@app/lib/errors"; -import { - ActorType, - AuthMode, - AuthModeJwtTokenPayload, - AuthTokenType -} from "@app/services/auth/auth-type"; +import { ActorType, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type"; import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types"; export type TAuthMode = | { + orgId?: string; authMode: AuthMode.JWT; actor: ActorType.USER; userId: string; @@ -26,6 +22,7 @@ export type TAuthMode = actor: ActorType.USER; userId: string; user: TUsers; + orgId?: string; } | { authMode: AuthMode.SERVICE_TOKEN; @@ -87,16 +84,12 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => { switch (authMode) { case AuthMode.JWT: { - const { user, tokenVersionId } = - await server.services.authToken.fnValidateJwtIdentity(token); - req.auth = { authMode: AuthMode.JWT, user, userId: user.id, tokenVersionId, actor }; + const { user, tokenVersionId, orgId } = await server.services.authToken.fnValidateJwtIdentity(token); + req.auth = { authMode: AuthMode.JWT, user, userId: user.id, tokenVersionId, actor, orgId }; break; } case AuthMode.IDENTITY_ACCESS_TOKEN: { - const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken( - token, - req.realIp - ); + const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp); req.auth = { authMode: AuthMode.IDENTITY_ACCESS_TOKEN, actor, @@ -106,9 +99,7 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => { break; } case AuthMode.SERVICE_TOKEN: { - const serviceToken = await server.services.serviceToken.fnValidateServiceToken( - token as string - ); + const serviceToken = await server.services.serviceToken.fnValidateServiceToken(token); req.auth = { authMode: AuthMode.SERVICE_TOKEN as const, serviceToken, diff --git a/backend/src/server/plugins/auth/inject-permission.ts b/backend/src/server/plugins/auth/inject-permission.ts index 410621611..572814d64 100644 --- a/backend/src/server/plugins/auth/inject-permission.ts +++ b/backend/src/server/plugins/auth/inject-permission.ts @@ -9,7 +9,7 @@ export const injectPermission = fp(async (server) => { if (!req.auth) return; if (req.auth.actor === ActorType.USER) { - req.permission = { type: ActorType.USER, id: req.auth.userId }; + req.permission = { type: ActorType.USER, id: req.auth.userId, orgId: req.auth?.orgId }; } else if (req.auth.actor === ActorType.IDENTITY) { req.permission = { type: ActorType.IDENTITY, id: req.auth.identityId }; } else if (req.auth.actor === ActorType.SERVICE) { diff --git a/backend/src/server/plugins/auth/superAdmin.ts b/backend/src/server/plugins/auth/superAdmin.ts index 2cd7181bd..d5dee581b 100644 --- a/backend/src/server/plugins/auth/superAdmin.ts +++ b/backend/src/server/plugins/auth/superAdmin.ts @@ -1,12 +1,17 @@ -import { FastifyRequest } from "fastify"; +import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify"; import { UnauthorizedError } from "@app/lib/errors"; import { ActorType } from "@app/services/auth/auth-type"; -export const verifySuperAdmin = async (req: T) => { +export const verifySuperAdmin = ( + req: T, + _res: FastifyReply, + done: HookHandlerDoneFunction +) => { if (req.auth.actor !== ActorType.USER || !req.auth.user.superAdmin) throw new UnauthorizedError({ name: "Unauthorized access", message: "Requires superadmin access" }); + done(); }; diff --git a/backend/src/server/plugins/auth/verify-auth.ts b/backend/src/server/plugins/auth/verify-auth.ts index cfd856dbe..a1274f356 100644 --- a/backend/src/server/plugins/auth/verify-auth.ts +++ b/backend/src/server/plugins/auth/verify-auth.ts @@ -1,17 +1,17 @@ -import { FastifyRequest } from "fastify"; +import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify"; import { UnauthorizedError } from "@app/lib/errors"; import { AuthMode } from "@app/services/auth/auth-type"; export const verifyAuth = (authStrats: AuthMode[]) => - async (req: T) => { + (req: T, _res: FastifyReply, done: HookHandlerDoneFunction) => { if (!Array.isArray(authStrats)) throw new Error("Auth strategy must be array"); - if (!req.auth) - throw new UnauthorizedError({ name: "Unauthorized access", message: "Token missing" }); + if (!req.auth) throw new UnauthorizedError({ name: "Unauthorized access", message: "Token missing" }); const isAccessAllowed = authStrats.some((strat) => strat === req.auth.authMode); if (!isAccessAllowed) { throw new UnauthorizedError({ name: `${req.url} Unauthorized Access` }); } + done(); }; diff --git a/backend/src/server/plugins/error-handler.ts b/backend/src/server/plugins/error-handler.ts index da3162ef8..8587c93bd 100644 --- a/backend/src/server/plugins/error-handler.ts +++ b/backend/src/server/plugins/error-handler.ts @@ -2,33 +2,27 @@ import { ForbiddenError } from "@casl/ability"; import fastifyPlugin from "fastify-plugin"; import { ZodError } from "zod"; -import { - BadRequestError, - DatabaseError, - ForbiddenRequestError, - InternalServerError, - UnauthorizedError -} from "@app/lib/errors"; +import { BadRequestError, DatabaseError, InternalServerError, UnauthorizedError } from "@app/lib/errors"; export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider) => { server.setErrorHandler((error, req, res) => { req.log.error(error); if (error instanceof BadRequestError) { - res.status(400).send({ statusCode: 400, message: error.message, error: error.name }); - } else if (error instanceof UnauthorizedError || error instanceof ForbiddenRequestError) { - res.status(403).send({ statusCode: 403, message: error.message, error: error.name }); + void res.status(400).send({ statusCode: 400, message: error.message, error: error.name }); + } else if (error instanceof UnauthorizedError) { + void res.status(403).send({ statusCode: 403, message: error.message, error: error.name }); } else if (error instanceof DatabaseError || error instanceof InternalServerError) { - res.status(500).send({ statusCode: 500, message: "Something went wrong", error: error.name }); + void res.status(500).send({ statusCode: 500, message: "Something went wrong", error: error.name }); } else if (error instanceof ZodError) { - res.status(403).send({ statusCode: 403, error: "ValidationFailure", message: error.issues }); + void res.status(403).send({ statusCode: 403, error: "ValidationFailure", message: error.issues }); } else if (error instanceof ForbiddenError) { - res.status(403).send({ - statusCode: 403, + void res.status(401).send({ + statusCode: 401, error: "PermissionDenied", message: `You are not allowed to ${error.action} on ${error.subjectType}` }); } else { - res.send(error); + void res.send(error); } }); }); diff --git a/backend/src/server/plugins/external-nextjs.ts b/backend/src/server/plugins/external-nextjs.ts index 010e92f96..cc2fe371d 100644 --- a/backend/src/server/plugins/external-nextjs.ts +++ b/backend/src/server/plugins/external-nextjs.ts @@ -20,17 +20,19 @@ export const registerExternalNextjs = async ( if (standaloneMode) { const nextJsBuildPath = path.join(dir, "frontend-build"); - const { default: conf } = await import( + const { default: conf } = (await import( path.join(dir, "frontend-build/.next/required-server-files.json"), // @ts-expect-error type { assert: { type: "json" } } - ); + )) as { default: { config: string } }; + /* eslint-disable */ const { default: NextServer } = ( await import(path.join(dir, "frontend-build/node_modules/next/dist/server/next-server.js")) ).default; + const nextApp = new NextServer({ dev: false, dir: nextJsBuildPath, @@ -43,6 +45,9 @@ export const registerExternalNextjs = async ( server.route({ method: ["GET", "PUT", "PATCH", "POST", "DELETE"], url: "/*", + schema: { + hide: true + }, handler: (req, res) => nextApp .getRequestHandler()(req.raw, res.raw) @@ -52,5 +57,6 @@ export const registerExternalNextjs = async ( }); server.addHook("onClose", () => nextApp.close()); await nextApp.prepare(); + /* eslint-enable */ } }; diff --git a/backend/src/server/plugins/ip.ts b/backend/src/server/plugins/ip.ts index d43bf8d70..b3c8171af 100644 --- a/backend/src/server/plugins/ip.ts +++ b/backend/src/server/plugins/ip.ts @@ -3,10 +3,10 @@ import fp from "fastify-plugin"; /*! https://github.com/pbojinov/request-ip/blob/9501cdf6e73059cc70fc6890adb086348d7cca46/src/index.js. MIT License. 2022 Petar Bojinov - petarbojinov+github@gmail.com */ const headersOrder = [ - "x-client-ip", // Most common - "x-forwarded-for", // Mostly used by proxies "cf-connecting-ip", // Cloudflare "Cf-Pseudo-IPv4", // Cloudflare + "x-client-ip", // Most common + "x-forwarded-for", // Mostly used by proxies "fastly-client-ip", "true-client-ip", // Akamai and Cloudflare "x-real-ip", // Nginx diff --git a/backend/src/server/plugins/secret-scanner.ts b/backend/src/server/plugins/secret-scanner.ts index f90b39114..8790d54d4 100644 --- a/backend/src/server/plugins/secret-scanner.ts +++ b/backend/src/server/plugins/secret-scanner.ts @@ -1,3 +1,4 @@ +import { PushEvent } from "@octokit/webhooks-types"; import { Probot } from "probot"; import SmeeClient from "smee-client"; @@ -22,7 +23,7 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) => app.on("push", async (context) => { const { payload } = context; - await server.services.secretScanning.handleRepoPushEvent(payload as any); + await server.services.secretScanning.handleRepoPushEvent(payload as PushEvent); }); }; @@ -49,16 +50,17 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) => method: "POST", url: "/", handler: async (req, res) => { - const eventName = req.headers["x-github-event"] as any; + const eventName = req.headers["x-github-event"]; const signatureSHA256 = req.headers["x-hub-signature-256"] as string; const id = req.headers["x-github-delivery"] as string; await probot.webhooks.verifyAndReceive({ id, + // @ts-expect-error type name: eventName, payload: req.body as string, signature: signatureSHA256 }); - res.send("ok"); + void res.send("ok"); } }); } diff --git a/backend/src/server/plugins/swagger.ts b/backend/src/server/plugins/swagger.ts index 49ed8bdc8..1eb1a0f4e 100644 --- a/backend/src/server/plugins/swagger.ts +++ b/backend/src/server/plugins/swagger.ts @@ -25,13 +25,13 @@ export const fastifySwagger = fp(async (fastify) => { ], components: { securitySchemes: { - bearer: { + bearerAuth: { type: "http", scheme: "bearer", bearerFormat: "JWT", - description: "A service token in Infisical" + description: "An access token in Infisical" }, - apiKey: { + apiKeyAuth: { type: "apiKey", in: "header", name: "X-API-Key", @@ -43,6 +43,7 @@ export const fastifySwagger = fp(async (fastify) => { }); await fastify.register(swaggerUI, { - routePrefix: "/docs" + routePrefix: "/api/docs", + prefix: "/api/docs" }); }); diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 19bf09727..28f65ea99 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -109,13 +109,9 @@ import { registerV3Routes } from "./v3"; export const registerRoutes = async ( server: FastifyZodProvider, - { - db, - smtp: smtpService, - queue: queueService - }: { db: Knex; smtp: TSmtpService; queue: TQueueServiceFactory } + { db, smtp: smtpService, queue: queueService }: { db: Knex; smtp: TSmtpService; queue: TQueueServiceFactory } ) => { - server.register(registerSecretScannerGhApp, { prefix: "/ss-webhook" }); + await server.register(registerSecretScannerGhApp, { prefix: "/ss-webhook" }); // db layers const userDAL = userDALFactory(db); @@ -233,6 +229,7 @@ export const registerRoutes = async ( orgDAL, incidentContactDAL, tokenService, + projectDAL, smtpService, userDAL, orgBotDAL @@ -420,6 +417,7 @@ export const registerRoutes = async ( const serviceTokenService = serviceTokenServiceFactory({ projectEnvDAL, serviceTokenDAL, + userDAL, permissionService }); @@ -446,6 +444,7 @@ export const registerRoutes = async ( }); await superAdminService.initServerCfg(); + await auditLogQueue.startAuditLogPruneJob(); // setup the communication with license key server await licenseService.init(); // inject all services @@ -514,16 +513,16 @@ export const registerRoutes = async ( }) } }, - handler: () => { + handler: async () => { const cfg = getConfig(); - const serverCfg = getServerCfg() + const serverCfg = await getServerCfg(); return { date: new Date(), message: "Ok" as const, emailConfigured: cfg.isSmtpConfigured, inviteOnlySignup: Boolean(serverCfg.allowSignUp), redisConfigured: cfg.isRedisConfigured, - secretScanningConfigured: cfg.isSecretScanningConfigured, + secretScanningConfigured: cfg.isSecretScanningConfigured }; } }); diff --git a/backend/src/server/routes/sanitizedSchemas.ts b/backend/src/server/routes/sanitizedSchemas.ts index cc9dedd45..03e48c247 100644 --- a/backend/src/server/routes/sanitizedSchemas.ts +++ b/backend/src/server/routes/sanitizedSchemas.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { IntegrationAuthsSchema, SecretApprovalPoliciesSchema } from "@app/db/schemas"; +import { IntegrationAuthsSchema, SecretApprovalPoliciesSchema, UsersSchema } from "@app/db/schemas"; // sometimes the return data must be santizied to avoid leaking important values // always prefer pick over omit in zod @@ -28,6 +28,23 @@ export const sapPubSchema = SecretApprovalPoliciesSchema.merge( }) ); +export const sanitizedServiceTokenUserSchema = UsersSchema.pick({ + authMethods: true, + id: true, + createdAt: true, + updatedAt: true, + devices: true, + email: true, + firstName: true, + lastName: true, + mfaMethods: true +}).merge( + z.object({ + __v: z.number().default(0), + _id: z.string() + }) +); + export const secretRawSchema = z.object({ id: z.string(), _id: z.string(), diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index e5ea127c5..da13d5e00 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -20,8 +20,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { }) } }, - handler: () => { - const config = getServerCfg(); + handler: async () => { + const config = await getServerCfg(); return { config }; } }); @@ -39,10 +39,10 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: (req, _, done) => { - verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req); - verifySuperAdmin(req); - done(); + onRequest: (req, res, done) => { + verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req, res, () => { + verifySuperAdmin(req, res, done); + }); }, handler: async (req) => { const config = await server.services.superAdmin.updateServerCfg(req.body); @@ -72,13 +72,14 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { 200: z.object({ message: z.string(), user: UsersSchema, - token: z.string() + token: z.string(), + new: z.string() }) } }, handler: async (req, res) => { const appCfg = getConfig(); - const serverCfg = getServerCfg(); + const serverCfg = await getServerCfg(); if (serverCfg.initialized) throw new UnauthorizedError({ name: "Admin sign up", message: "Admin has been created" }); const { user, token } = await server.services.superAdmin.adminSignUp({ @@ -97,7 +98,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { } }); - res.setCookie("jid", token.refresh, { + void res.setCookie("jid", token.refresh, { httpOnly: true, path: "/", sameSite: "strict", @@ -107,7 +108,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { return { message: "Successfully set up admin account", user: user.user, - token: token.access + token: token.access, + new: "123" }; } }); diff --git a/backend/src/server/routes/v1/auth-router.ts b/backend/src/server/routes/v1/auth-router.ts index 871b56c1f..bd45f59d9 100644 --- a/backend/src/server/routes/v1/auth-router.ts +++ b/backend/src/server/routes/v1/auth-router.ts @@ -5,18 +5,14 @@ import { getConfig } from "@app/lib/config/env"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { authRateLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; -import { - AuthMode, - AuthModeRefreshJwtTokenPayload, - AuthTokenType -} from "@app/services/auth/auth-type"; +import { AuthMode, AuthModeRefreshJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type"; export const registerAuthRoutes = async (server: FastifyZodProvider) => { server.route({ url: "/logout", method: "POST", - config:{ - rateLimit:authRateLimit + config: { + rateLimit: authRateLimit }, schema: { response: { @@ -31,7 +27,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => { if (req.auth.authMode === AuthMode.JWT) { await server.services.login.logout(req.permission.id, req.auth.tokenVersionId); } - res.cookie("jid", "", { + void res.cookie("jid", "", { httpOnly: true, path: "/", sameSite: "strict", @@ -74,10 +70,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => { message: "Failed to find refresh token" }); - const decodedToken = jwt.verify( - refreshToken, - appCfg.AUTH_SECRET - ) as AuthModeRefreshJwtTokenPayload; + const decodedToken = jwt.verify(refreshToken, appCfg.AUTH_SECRET) as AuthModeRefreshJwtTokenPayload; if (decodedToken.authTokenType !== AuthTokenType.REFRESH_TOKEN) throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" }); @@ -85,8 +78,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => { decodedToken.tokenVersionId, decodedToken.userId ); - if (!tokenVersion) - throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" }); + if (!tokenVersion) throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" }); if (decodedToken.refreshVersion !== tokenVersion.refreshVersion) throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" }); @@ -96,7 +88,8 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => { authTokenType: AuthTokenType.ACCESS_TOKEN, userId: decodedToken.userId, tokenVersionId: tokenVersion.id, - accessVersion: tokenVersion.accessVersion + accessVersion: tokenVersion.accessVersion, + organizationId: decodedToken.organizationId }, appCfg.AUTH_SECRET, { expiresIn: appCfg.JWT_AUTH_LIFETIME } diff --git a/backend/src/server/routes/v1/bot-router.ts b/backend/src/server/routes/v1/bot-router.ts index 4c6e07ffe..507423b0d 100644 --- a/backend/src/server/routes/v1/bot-router.ts +++ b/backend/src/server/routes/v1/bot-router.ts @@ -29,6 +29,7 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => { const bot = await server.services.projectBot.findBotByProjectId({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.params.projectId }); return { bot }; @@ -68,6 +69,7 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => { const bot = await server.services.projectBot.setBotActiveState({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, botId: req.params.botId, botKey: req.body.botKey, isActive: req.body.isActive diff --git a/backend/src/server/routes/v1/identity-access-token-router.ts b/backend/src/server/routes/v1/identity-access-token-router.ts index b39fe08a2..78112f896 100644 --- a/backend/src/server/routes/v1/identity-access-token-router.ts +++ b/backend/src/server/routes/v1/identity-access-token-router.ts @@ -5,6 +5,7 @@ export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvid url: "/token/renew", method: "POST", schema: { + description: "Renew access token", body: z.object({ accessToken: z.string().trim() }), @@ -18,10 +19,9 @@ export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvid } }, handler: async (req) => { - const { accessToken, identityAccessToken } = - await server.services.identityAccessToken.renewAccessToken({ - accessToken: req.body.accessToken - }); + const { accessToken, identityAccessToken } = await server.services.identityAccessToken.renewAccessToken({ + accessToken: req.body.accessToken + }); return { accessToken, tokenType: "Bearer" as const, diff --git a/backend/src/server/routes/v1/identity-router.ts b/backend/src/server/routes/v1/identity-router.ts index f86cc9528..4ca4ef324 100644 --- a/backend/src/server/routes/v1/identity-router.ts +++ b/backend/src/server/routes/v1/identity-router.ts @@ -11,6 +11,12 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => { url: "/", onRequest: verifyAuth([AuthMode.JWT]), schema: { + description: "Create identity", + security: [ + { + bearerAuth: [] + } + ], body: z.object({ name: z.string().trim(), organizationId: z.string().trim(), @@ -26,6 +32,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => { const identity = await server.services.identity.createIdentity({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, ...req.body, orgId: req.body.organizationId }); @@ -51,6 +58,12 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => { url: "/:identityId", onRequest: verifyAuth([AuthMode.JWT]), schema: { + description: "Update identity", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ identityId: z.string() }), @@ -68,6 +81,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => { const identity = await server.services.identity.updateIdentity({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, id: req.params.identityId, ...req.body }); @@ -93,6 +107,12 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => { url: "/:identityId", onRequest: verifyAuth([AuthMode.JWT]), schema: { + description: "Delete identity", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ identityId: z.string() }), @@ -106,6 +126,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => { const identity = await server.services.identity.deleteIdentity({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, id: req.params.identityId }); diff --git a/backend/src/server/routes/v1/identity-ua.ts b/backend/src/server/routes/v1/identity-ua.ts index 1c7c6e5f7..11b8e0e8d 100644 --- a/backend/src/server/routes/v1/identity-ua.ts +++ b/backend/src/server/routes/v1/identity-ua.ts @@ -24,6 +24,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { url: "/universal-auth/login", method: "POST", schema: { + description: "Login with Universal Auth", body: z.object({ clientId: z.string().trim(), clientSecret: z.string().trim() @@ -39,11 +40,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { }, handler: async (req) => { const { identityUa, accessToken, identityAccessToken, validClientSecretInfo } = - await server.services.identityUa.login( - req.body.clientId, - req.body.clientSecret, - req.realIp - ); + await server.services.identityUa.login(req.body.clientId, req.body.clientSecret, req.realIp); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, @@ -71,6 +68,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { method: "POST", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Attach Universal Auth configuration onto identity", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ identityId: z.string().trim() }), @@ -116,6 +119,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { const identityUniversalAuth = await server.services.identityUa.attachUa({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, ...req.body, identityId: req.params.identityId }); @@ -128,10 +132,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { identityId: identityUniversalAuth.identityId, accessTokenTTL: identityUniversalAuth.accessTokenTTL, accessTokenMaxTTL: identityUniversalAuth.accessTokenMaxTTL, - accessTokenTrustedIps: - identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[], - clientSecretTrustedIps: - identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[], + accessTokenTrustedIps: identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[], + clientSecretTrustedIps: identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[], accessTokenNumUsesLimit: identityUniversalAuth.accessTokenNumUsesLimit } } @@ -146,6 +148,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { method: "PATCH", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Update Universal Auth configuration on identity", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ identityId: z.string() }), @@ -184,6 +192,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { const identityUniversalAuth = await server.services.identityUa.updateUa({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, ...req.body, identityId: req.params.identityId }); @@ -197,10 +206,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { identityId: identityUniversalAuth.identityId, accessTokenTTL: identityUniversalAuth.accessTokenTTL, accessTokenMaxTTL: identityUniversalAuth.accessTokenMaxTTL, - accessTokenTrustedIps: - identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[], - clientSecretTrustedIps: - identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[], + accessTokenTrustedIps: identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[], + clientSecretTrustedIps: identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[], accessTokenNumUsesLimit: identityUniversalAuth.accessTokenNumUsesLimit } } @@ -215,6 +222,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { method: "GET", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Retrieve Universal Auth configuration on identity", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ identityId: z.string() }), @@ -228,6 +241,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { const identityUniversalAuth = await server.services.identityUa.getIdentityUa({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, identityId: req.params.identityId }); @@ -251,6 +265,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { method: "POST", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Create Universal Auth Client Secret for identity", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ identityId: z.string() }), @@ -267,13 +287,13 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const { clientSecret, clientSecretData, orgId } = - await server.services.identityUa.createUaClientSecret({ - actor: req.permission.type, - actorId: req.permission.id, - identityId: req.params.identityId, - ...req.body - }); + const { clientSecret, clientSecretData, orgId } = await server.services.identityUa.createUaClientSecret({ + actor: req.permission.type, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + identityId: req.params.identityId, + ...req.body + }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, @@ -296,6 +316,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { method: "GET", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "List Universal Auth Client Secrets for identity", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ identityId: z.string() }), @@ -306,12 +332,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const { clientSecrets: clientSecretData, orgId } = - await server.services.identityUa.getUaClientSecrets({ - actor: req.permission.type, - actorId: req.permission.id, - identityId: req.params.identityId - }); + const { clientSecrets: clientSecretData, orgId } = await server.services.identityUa.getUaClientSecrets({ + actor: req.permission.type, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + identityId: req.params.identityId + }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, @@ -332,6 +358,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { method: "POST", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Revoke Universal Auth Client Secrets for identity", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ identityId: z.string(), clientSecretId: z.string() @@ -346,6 +378,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { const clientSecretData = await server.services.identityUa.revokeUaClientSecret({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, identityId: req.params.identityId, clientSecretId: req.params.clientSecretId }); diff --git a/backend/src/server/routes/v1/integration-auth-router.ts b/backend/src/server/routes/v1/integration-auth-router.ts index 1d92813f2..4d7aa1b1e 100644 --- a/backend/src/server/routes/v1/integration-auth-router.ts +++ b/backend/src/server/routes/v1/integration-auth-router.ts @@ -53,6 +53,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const integrationAuth = await server.services.integrationAuth.getIntegrationAuth({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId }); return { integrationAuth }; @@ -78,6 +79,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const integrationAuth = await server.services.integrationAuth.deleteIntegrationAuths({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, integration: req.query.integration, projectId: req.query.projectId }); @@ -115,6 +117,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const integrationAuth = await server.services.integrationAuth.deleteIntegrationAuthById({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId }); @@ -154,6 +157,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const integrationAuth = await server.services.integrationAuth.oauthExchange({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.body.workspaceId, ...req.body }); @@ -196,6 +200,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const integrationAuth = await server.services.integrationAuth.saveIntegrationToken({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.body.workspaceId, ...req.body }); @@ -242,6 +247,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const apps = await server.services.integrationAuth.getIntegrationApps({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, ...req.query }); @@ -272,6 +278,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const teams = await server.services.integrationAuth.getIntegrationAuthTeams({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId }); return { teams }; @@ -299,6 +306,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const branches = await server.services.integrationAuth.getVercelBranches({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, appId: req.query.appId }); @@ -327,6 +335,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const groups = await server.services.integrationAuth.getChecklyGroups({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, accountId: req.query.accountId }); @@ -352,6 +361,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const orgs = await server.services.integrationAuth.getQoveryOrgs({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId }); return { orgs }; @@ -379,6 +389,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const projects = await server.services.integrationAuth.getQoveryProjects({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, orgId: req.query.orgId }); @@ -407,6 +418,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const environments = await server.services.integrationAuth.getQoveryEnvs({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, projectId: req.query.projectId }); @@ -435,6 +447,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const apps = await server.services.integrationAuth.getQoveryApps({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, environmentId: req.query.environmentId }); @@ -463,6 +476,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const containers = await server.services.integrationAuth.getQoveryContainers({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, environmentId: req.query.environmentId }); @@ -491,6 +505,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const jobs = await server.services.integrationAuth.getQoveryJobs({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, environmentId: req.query.environmentId }); @@ -519,6 +534,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const environments = await server.services.integrationAuth.getRailwayEnvironments({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, appId: req.query.appId }); @@ -547,6 +563,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const services = await server.services.integrationAuth.getRailwayServices({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, appId: req.query.appId }); @@ -582,6 +599,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const workspaces = await server.services.integrationAuth.getBitbucketWorkspaces({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId }); return { workspaces }; @@ -614,6 +632,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const secretGroups = await server.services.integrationAuth.getNorthFlankSecretGroups({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, appId: req.query.appId }); @@ -647,6 +666,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) const buildConfigs = await server.services.integrationAuth.getTeamcityBuildConfigs({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationAuthId, appId: req.query.appId }); diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 23d8ef8be..ab0ba36eb 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { IntegrationsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; -import { shake } from "@app/lib/fn"; +import { removeTrailingSlash, shake } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -16,7 +16,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { app: z.string().trim().optional(), isActive: z.boolean(), appId: z.string().trim().optional(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), sourceEnvironment: z.string().trim(), targetEnvironment: z.string().trim().optional(), targetEnvironmentId: z.string().trim().optional(), @@ -50,6 +50,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { const { integration, integrationAuth } = await server.services.integration.createIntegration({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, ...req.body }); await server.services.auditLog.createAuditLog({ @@ -57,6 +58,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { projectId: integrationAuth.projectId, event: { type: EventType.CREATE_INTEGRATION, + // eslint-disable-next-line metadata: shake({ integrationId: integration.id.toString(), integration: integration.integration, @@ -71,6 +73,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { targetServiceId: integration.targetServiceId, path: integration.path, region: integration.region + // eslint-disable-next-line }) as any } }); @@ -89,7 +92,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { app: z.string().trim(), appId: z.string().trim(), isActive: z.boolean(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), targetEnvironment: z.string().trim(), owner: z.string().trim(), environment: z.string().trim() @@ -105,6 +108,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { const integration = await server.services.integration.updateIntegration({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationId, ...req.body }); @@ -130,6 +134,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { const integration = await server.services.integration.deleteIntegration({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.integrationId }); @@ -138,6 +143,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { projectId: integration.projectId, event: { type: EventType.DELETE_INTEGRATION, + // eslint-disable-next-line metadata: shake({ integrationId: integration.id, integration: integration.integration, @@ -152,6 +158,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { targetServiceId: integration.targetServiceId, path: integration.path, region: integration.region + // eslint-disable-next-line }) as any } }); diff --git a/backend/src/server/routes/v1/invite-org-router.ts b/backend/src/server/routes/v1/invite-org-router.ts index 67503ac02..0d1fe5070 100644 --- a/backend/src/server/routes/v1/invite-org-router.ts +++ b/backend/src/server/routes/v1/invite-org-router.ts @@ -26,7 +26,8 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => { const completeInviteLink = await server.services.org.inviteUserToOrganization({ orgId: req.body.organizationId, userId: req.permission.id, - inviteeEmail: req.body.inviteeEmail + inviteeEmail: req.body.inviteeEmail, + actorOrgId: req.permission.orgId }); return { diff --git a/backend/src/server/routes/v1/organization-router.ts b/backend/src/server/routes/v1/organization-router.ts index f5fd06c87..bfda652f0 100644 --- a/backend/src/server/routes/v1/organization-router.ts +++ b/backend/src/server/routes/v1/organization-router.ts @@ -1,11 +1,6 @@ import { z } from "zod"; -import { - IncidentContactsSchema, - OrganizationsSchema, - OrgMembershipsSchema, - UsersSchema -} from "@app/db/schemas"; +import { IncidentContactsSchema, OrganizationsSchema, OrgMembershipsSchema, UsersSchema } from "@app/db/schemas"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -44,7 +39,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { handler: async (req) => { const organization = await server.services.org.findOrganizationById( req.permission.id, - req.params.organizationId + req.params.organizationId, + req.permission.orgId ); return { organization }; } @@ -78,7 +74,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { handler: async (req) => { const users = await server.services.org.findAllOrgMembers( req.permission.id, - req.params.organizationId + req.params.organizationId, + req.permission.orgId ); return { users }; } @@ -86,10 +83,18 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { server.route({ method: "PATCH", - url: "/:organizationId/name", + url: "/:organizationId", schema: { params: z.object({ organizationId: z.string().trim() }), - body: z.object({ name: z.string().trim() }), + body: z.object({ + name: z.string().trim().optional(), + slug: z + .string() + .trim() + .regex(/^[a-zA-Z0-9-]+$/, "Name must only contain alphanumeric characters or hyphens") + .optional(), + authEnforced: z.boolean().optional() + }), response: { 200: z.object({ message: z.string(), @@ -99,11 +104,14 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const organization = await server.services.org.updateOrgName( - req.permission.id, - req.params.organizationId, - req.body.name - ); + const organization = await server.services.org.updateOrg({ + actor: req.permission.type, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + orgId: req.params.organizationId, + data: req.body + }); + return { message: "Successfully changed organization name", organization @@ -126,7 +134,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { handler: async (req) => { const incidentContactsOrg = await req.server.services.org.findIncidentContacts( req.permission.id, - req.params.organizationId + req.params.organizationId, + req.permission.orgId ); return { incidentContactsOrg }; } @@ -149,7 +158,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { const incidentContactsOrg = await req.server.services.org.createIncidentContact( req.permission.id, req.params.organizationId, - req.body.email + req.body.email, + req.permission.orgId ); return { incidentContactsOrg }; } @@ -171,7 +181,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { const incidentContactsOrg = await req.server.services.org.deleteIncidentContact( req.permission.id, req.params.organizationId, - req.params.incidentContactId + req.params.incidentContactId, + req.permission.orgId ); return { incidentContactsOrg }; } diff --git a/backend/src/server/routes/v1/password-router.ts b/backend/src/server/routes/v1/password-router.ts index 8ec7697f7..d5c5054df 100644 --- a/backend/src/server/routes/v1/password-router.ts +++ b/backend/src/server/routes/v1/password-router.ts @@ -12,7 +12,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/srp1", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { body: z.object({ @@ -39,7 +39,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/change-password", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { body: z.object({ @@ -64,7 +64,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { const appCfg = getConfig(); await server.services.password.changePassword({ ...req.body, userId: req.permission.id }); - res.cookie("jid", appCfg.COOKIE_SECRET_SIGN_KEY, { + void res.cookie("jid", appCfg.COOKIE_SECRET_SIGN_KEY, { httpOnly: true, path: "/", sameSite: "strict", @@ -78,7 +78,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/email/password-reset", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { body: z.object({ @@ -103,7 +103,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/email/password-reset-verify", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { body: z.object({ @@ -119,10 +119,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const { token, user } = await server.services.password.verifyPasswordResetEmail( - req.body.email, - req.body.code - ); + const { token, user } = await server.services.password.verifyPasswordResetEmail(req.body.email, req.body.code); return { message: "Successfully verified email", @@ -136,7 +133,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/backup-private-key", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, onRequest: verifyAuth([AuthMode.JWT]), schema: { @@ -156,10 +153,10 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const token = validateSignUpAuthorization(req.headers.authorization as string, "",false)! + const token = validateSignUpAuthorization(req.headers.authorization as string, "", false)!; const backupPrivateKey = await server.services.password.createBackupPrivateKey({ ...req.body, - userId: token.userId, + userId: token.userId }); if (!backupPrivateKey) throw new Error("Failed to create backup key"); @@ -171,7 +168,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "GET", url: "/backup-private-key", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { response: { @@ -182,10 +179,8 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const token = validateSignUpAuthorization(req.headers.authorization as string, "",false)! - const backupPrivateKey = await server.services.password.getBackupPrivateKeyOfUser( - token.userId - ); + const token = validateSignUpAuthorization(req.headers.authorization as string, "", false)!; + const backupPrivateKey = await server.services.password.getBackupPrivateKeyOfUser(token.userId); if (!backupPrivateKey) throw new Error("Failed to find backup key"); return { message: "Successfully fetched backup private key", backupPrivateKey }; @@ -213,10 +208,10 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const token = validateSignUpAuthorization(req.headers.authorization as string, "",false)! + const token = validateSignUpAuthorization(req.headers.authorization as string, "", false)!; await server.services.password.resetPasswordByBackupKey({ ...req.body, - userId: token.userId, + userId: token.userId }); return { message: "Successfully updated backup private key" }; diff --git a/backend/src/server/routes/v1/project-env-router.ts b/backend/src/server/routes/v1/project-env-router.ts index 44be1a3d6..b93ffe928 100644 --- a/backend/src/server/routes/v1/project-env-router.ts +++ b/backend/src/server/routes/v1/project-env-router.ts @@ -10,6 +10,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { url: "/:workspaceId/environments", method: "POST", schema: { + description: "Create environment", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ workspaceId: z.string().trim() }), @@ -30,6 +37,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { const environment = await server.services.projectEnv.createEnvironment({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, ...req.body }); @@ -57,6 +65,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { url: "/:workspaceId/environments/:id", method: "PATCH", schema: { + description: "Update environment", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ workspaceId: z.string().trim(), id: z.string().trim() @@ -79,6 +94,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { const { environment, old } = await server.services.projectEnv.updateEnvironment({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, id: req.params.id, ...req.body @@ -112,6 +128,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { url: "/:workspaceId/environments/:id", method: "DELETE", schema: { + description: "Delete environment", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ workspaceId: z.string().trim(), id: z.string().trim() @@ -129,6 +152,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { const environment = await server.services.projectEnv.deleteEnvironment({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, id: req.params.id }); diff --git a/backend/src/server/routes/v1/project-key-router.ts b/backend/src/server/routes/v1/project-key-router.ts index 482392947..b34260117 100644 --- a/backend/src/server/routes/v1/project-key-router.ts +++ b/backend/src/server/routes/v1/project-key-router.ts @@ -30,6 +30,7 @@ export const registerProjectKeyRouter = async (server: FastifyZodProvider) => { projectId: req.params.workspaceId, actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, nonce: req.body.key.nonce, receiverId: req.body.key.userId, encryptedKey: req.body.key.encryptedKey diff --git a/backend/src/server/routes/v1/project-membership-router.ts b/backend/src/server/routes/v1/project-membership-router.ts index bacfcb1bb..09de72233 100644 --- a/backend/src/server/routes/v1/project-membership-router.ts +++ b/backend/src/server/routes/v1/project-membership-router.ts @@ -1,11 +1,6 @@ import { z } from "zod"; -import { - OrgMembershipsSchema, - ProjectMembershipsSchema, - UserEncryptionKeysSchema, - UsersSchema -} from "@app/db/schemas"; +import { OrgMembershipsSchema, ProjectMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -15,6 +10,13 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider url: "/:workspaceId/memberships", method: "GET", schema: { + description: "Return project user memberships", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ workspaceId: z.string().trim() }), @@ -40,6 +42,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider const memberships = await server.services.projectMembership.getProjectMemberships({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId }); return { memberships }; @@ -75,6 +78,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider const data = await server.services.projectMembership.addUsersToProject({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, members: req.body.members }); @@ -99,6 +103,13 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider url: "/:workspaceId/memberships/:membershipId", method: "PATCH", schema: { + description: "Update project user membership", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ workspaceId: z.string().trim(), membershipId: z.string().trim() @@ -117,6 +128,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider const membership = await server.services.projectMembership.updateProjectMembership({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, membershipId: req.params.membershipId, role: req.body.role @@ -143,6 +155,13 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider url: "/:workspaceId/memberships/:membershipId", method: "DELETE", schema: { + description: "Delete project user membership", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ workspaceId: z.string().trim(), membershipId: z.string().trim() @@ -158,6 +177,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider const membership = await server.services.projectMembership.deleteProjectMembership({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, membershipId: req.params.membershipId }); diff --git a/backend/src/server/routes/v1/project-router.ts b/backend/src/server/routes/v1/project-router.ts index 5e8ee8b46..f0faaa0f4 100644 --- a/backend/src/server/routes/v1/project-router.ts +++ b/backend/src/server/routes/v1/project-router.ts @@ -46,6 +46,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const publicKeys = await server.services.projectKey.getProjectPublicKeys({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId }); return { publicKeys }; @@ -81,7 +82,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const users = await server.services.projectMembership.getProjectMemberships({ actorId: req.permission.id, actor: req.permission.type, - projectId: req.params.workspaceId + projectId: req.params.workspaceId, + actorOrgId: req.permission.orgId }); return { users }; } @@ -122,6 +124,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const workspace = await server.services.project.getAProject({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId }); return { workspace }; @@ -148,6 +151,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actor: req.permission.type, orgId: req.body.organizationId, + actorOrgId: req.permission.orgId, workspaceName: req.body.workspaceName }); return { workspace }; @@ -172,6 +176,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const workspace = await server.services.project.deleteProject({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId }); return { workspace }; @@ -200,6 +205,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const workspace = await server.services.project.updateName({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, name: req.body.name }); @@ -232,6 +238,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const workspace = await server.services.project.toggleAutoCapitalization({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, autoCapitalization: req.body.autoCapitalization }); @@ -255,7 +262,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { response: { 200: z.object({ invitee: UsersSchema, - latestKey: ProjectKeysSchema + latestKey: ProjectKeysSchema.optional() }) } }, @@ -264,6 +271,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const { invitee, latestKey } = await server.services.projectMembership.inviteUserToProject({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId, email: req.body.email }); @@ -309,6 +317,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const integrations = await server.services.integration.listIntegrationByProject({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId }); return { integrations }; @@ -333,6 +342,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const authorizations = await server.services.integrationAuth.listIntegrationAuthByProjectId({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId }); return { authorizations }; @@ -357,6 +367,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const serviceTokenData = await server.services.serviceToken.getProjectServiceTokens({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, projectId: req.params.workspaceId }); return { serviceTokenData }; diff --git a/backend/src/server/routes/v1/secret-folder-router.ts b/backend/src/server/routes/v1/secret-folder-router.ts index fd9798dac..8a80fb728 100644 --- a/backend/src/server/routes/v1/secret-folder-router.ts +++ b/backend/src/server/routes/v1/secret-folder-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { SecretFoldersSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -10,13 +11,20 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => url: "/", method: "POST", schema: { + description: "Create folders", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), name: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), // backward compatiability with cli - directory: z.string().trim().default("/") + directory: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -24,17 +32,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const path = req.body.path || req.body.directory; const folder = await server.services.folder.createFolder({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, ...req.body, projectId: req.body.workspaceId, path @@ -60,6 +64,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => url: "/:folderId", method: "PATCH", schema: { + description: "Update folder", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ // old way this was name folderId: z.string() @@ -68,9 +79,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => workspaceId: z.string().trim(), environment: z.string().trim(), name: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), // backward compatiability with cli - directory: z.string().trim().default("/") + directory: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -78,17 +89,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const path = req.body.path || req.body.directory; const { folder, old } = await server.services.folder.updateFolder({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, ...req.body, projectId: req.body.workspaceId, id: req.params.folderId, @@ -116,15 +123,22 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => url: "/:folderId", method: "DELETE", schema: { + description: "Delete a folder", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ folderId: z.string() }), body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), // keep this here as cli need directory - directory: z.string().trim().default("/") + directory: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -132,17 +146,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const path = req.body.path || req.body.directory; const folder = await server.services.folder.deleteFolder({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, ...req.body, projectId: req.body.workspaceId, id: req.params.folderId, @@ -169,12 +179,19 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => url: "/", method: "GET", schema: { + description: "Get folders", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), // backward compatiability with cli - directory: z.string().trim().default("/") + directory: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -182,17 +199,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const path = req.query.path || req.query.directory; const folders = await server.services.folder.getFolders({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, ...req.query, projectId: req.query.workspaceId, path diff --git a/backend/src/server/routes/v1/secret-import-router.ts b/backend/src/server/routes/v1/secret-import-router.ts index 5f37c5f5e..2ec2d5ce2 100644 --- a/backend/src/server/routes/v1/secret-import-router.ts +++ b/backend/src/server/routes/v1/secret-import-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -10,13 +11,20 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => url: "/", method: "POST", schema: { + description: "Create secret imports", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), import: z.object({ environment: z.string().trim(), - path: z.string().trim() + path: z.string().trim().transform(removeTrailingSlash) }) }), response: { @@ -30,16 +38,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secretImport = await server.services.secretImport.createImport({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, ...req.body, projectId: req.body.workspaceId, data: req.body.import @@ -68,16 +72,27 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => url: "/:secretImportId", method: "PATCH", schema: { + description: "Update secret imports", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ secretImportId: z.string().trim() }), body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), import: z.object({ environment: z.string().trim().optional(), - path: z.string().trim().optional(), + path: z + .string() + .trim() + .optional() + .transform((val) => (val ? removeTrailingSlash(val) : val)), position: z.number().optional() }) }), @@ -92,16 +107,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secretImport = await server.services.secretImport.updateImport({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.secretImportId, ...req.body, projectId: req.body.workspaceId, @@ -131,13 +142,20 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => url: "/:secretImportId", method: "DELETE", schema: { + description: "Delete secret imports", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ secretImportId: z.string().trim() }), body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -150,16 +168,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secretImport = await server.services.secretImport.deleteImport({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.secretImportId, ...req.body, projectId: req.body.workspaceId @@ -188,10 +202,17 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => url: "/", method: "GET", schema: { + description: "Get secret imports", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -206,16 +227,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secretImports = await server.services.secretImport.getImports({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, ...req.query, projectId: req.query.workspaceId }); @@ -243,7 +260,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -263,16 +280,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const importedSecrets = await server.services.secretImport.getSecretsFromImports({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, ...req.query, projectId: req.query.workspaceId }); diff --git a/backend/src/server/routes/v1/secret-tag-router.ts b/backend/src/server/routes/v1/secret-tag-router.ts index 3cafb11d2..7ca3e4893 100644 --- a/backend/src/server/routes/v1/secret-tag-router.ts +++ b/backend/src/server/routes/v1/secret-tag-router.ts @@ -23,6 +23,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => { const workspaceTags = await server.services.secretTag.getProjectTags({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.params.projectId }); return { workspaceTags }; @@ -52,6 +53,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => { const workspaceTag = await server.services.secretTag.createTag({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.params.projectId, ...req.body }); @@ -78,6 +80,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => { const workspaceTag = await server.services.secretTag.deleteTag({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, id: req.params.tagId }); return { workspaceTag }; diff --git a/backend/src/server/routes/v1/sso-router.ts b/backend/src/server/routes/v1/sso-router.ts index dfd1e9150..bfcf2f6ae 100644 --- a/backend/src/server/routes/v1/sso-router.ts +++ b/backend/src/server/routes/v1/sso-router.ts @@ -1,3 +1,11 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +// All the any rules are disabled because passport typesense with fastify is really poor + import { Authenticator } from "@fastify/passport"; import fastifySession from "@fastify/session"; import { Strategy as GitHubStrategy } from "passport-github"; @@ -19,9 +27,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { await server.register(passport.initialize()); await server.register(passport.secureSession()); // passport oauth strategy for Google - const isGoogleOauthActive = Boolean( - appCfg.CLIENT_ID_GOOGLE_LOGIN && appCfg.CLIENT_SECRET_GOOGLE_LOGIN - ); + const isGoogleOauthActive = Boolean(appCfg.CLIENT_ID_GOOGLE_LOGIN && appCfg.CLIENT_SECRET_GOOGLE_LOGIN); if (isGoogleOauthActive) { passport.use( new GoogleStrategy( @@ -29,13 +35,14 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { passReqToCallback: true, clientID: appCfg.CLIENT_ID_GOOGLE_LOGIN as string, clientSecret: appCfg.CLIENT_SECRET_GOOGLE_LOGIN as string, - callbackURL: "/api/v1/sso/google", + callbackURL: `${appCfg.SITE_URL}/api/v1/sso/google`, scope: ["profile", " email"] }, + // eslint-disable-next-line async (req, _accessToken, _refreshToken, profile, cb) => { try { const email = profile?.emails?.[0]?.value; - const serverCfg = getServerCfg(); + const serverCfg = await getServerCfg(); if (!email) throw new BadRequestError({ message: "Email not found", @@ -61,9 +68,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { } // Passport strategy for Github - const isGithubOauthActive = Boolean( - appCfg.CLIENT_SECRET_GITHUB_LOGIN && appCfg.CLIENT_ID_GITHUB_LOGIN - ); + const isGithubOauthActive = Boolean(appCfg.CLIENT_SECRET_GITHUB_LOGIN && appCfg.CLIENT_ID_GITHUB_LOGIN); if (isGithubOauthActive) { passport.use( new GitHubStrategy( @@ -71,14 +76,15 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { passReqToCallback: true, clientID: appCfg.CLIENT_ID_GITHUB_LOGIN as string, clientSecret: appCfg.CLIENT_SECRET_GITHUB_LOGIN as string, - callbackURL: "/api/v1/sso/github", + callbackURL: `${appCfg.SITE_URL}/api/v1/sso/github`, scope: ["user:email"] }, + // eslint-disable-next-line async (req, accessToken, _refreshToken, profile, cb) => { try { const ghEmails = await fetchGithubEmails(accessToken); const { email } = ghEmails.filter((gitHubEmail) => gitHubEmail.primary)[0]; - const serverCfg = getServerCfg(); + const serverCfg = await getServerCfg(); const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({ email, firstName: profile.displayName, @@ -99,9 +105,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { // passport strategy for gitlab const isGitlabOauthActive = Boolean( - appCfg.CLIENT_ID_GITLAB_LOGIN && - appCfg.CLIENT_SECRET_GITLAB_LOGIN && - appCfg.CLIENT_GITLAB_LOGIN_URL + appCfg.CLIENT_ID_GITLAB_LOGIN && appCfg.CLIENT_SECRET_GITLAB_LOGIN && appCfg.CLIENT_GITLAB_LOGIN_URL ); if (isGitlabOauthActive) { passport.use( @@ -110,13 +114,13 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { passReqToCallback: true, clientID: appCfg.CLIENT_ID_GITLAB_LOGIN, clientSecret: appCfg.CLIENT_SECRET_GITLAB_LOGIN, - callbackURL: "/api/v1/sso/gitlab", + callbackURL: `${appCfg.SITE_URL}/api/v1/sso/gitlab`, baseURL: appCfg.CLIENT_GITLAB_LOGIN_URL }, async (req: any, _accessToken: string, _refreshToken: string, profile: any, cb: any) => { try { const email = profile.emails[0].value; - const serverCfg = getServerCfg(); + const serverCfg = await getServerCfg(); const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({ email, firstName: profile.displayName, @@ -152,6 +156,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { state: req.query.callback_port, authInfo: false // this is due to zod type difference + // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any )(req, res), handler: () => {} @@ -165,19 +170,15 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { failureRedirect: "/login/provider/error", authInfo: false // this is due to zod type difference - }) as any, + }) as never, handler: (req, res) => { if (req.passportUser.isUserCompleted) { return res.redirect( - `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } return res.redirect( - `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } }); @@ -214,15 +215,11 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { handler: (req, res) => { if (req.passportUser.isUserCompleted) { return res.redirect( - `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } return res.redirect( - `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } }); @@ -242,6 +239,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { state: req.query.callback_port, authInfo: false // this is due to zod type difference + // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any )(req, res), handler: () => {} @@ -255,19 +253,16 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { failureRedirect: "/login/provider/error", authInfo: false // this is due to zod type difference + // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any, handler: (req, res) => { if (req.passportUser.isUserCompleted) { return res.redirect( - `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } return res.redirect( - `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } }); diff --git a/backend/src/server/routes/v1/user-action-router.ts b/backend/src/server/routes/v1/user-action-router.ts index 5ce66eb79..c730cdb91 100644 --- a/backend/src/server/routes/v1/user-action-router.ts +++ b/backend/src/server/routes/v1/user-action-router.ts @@ -21,10 +21,7 @@ export const registerUserActionRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const userAction = await server.services.user.createUserAction( - req.permission.id, - req.body.action - ); + const userAction = await server.services.user.createUserAction(req.permission.id, req.body.action); return { userAction, message: "Successfully recorded user action" }; } }); @@ -44,10 +41,7 @@ export const registerUserActionRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const userAction = await server.services.user.getUserAction( - req.permission.id, - req.query.action - ); + const userAction = await server.services.user.getUserAction(req.permission.id, req.query.action); return { userAction }; } }); diff --git a/backend/src/server/routes/v1/webhook-router.ts b/backend/src/server/routes/v1/webhook-router.ts index 89564d9a3..9a20a5d22 100644 --- a/backend/src/server/routes/v1/webhook-router.ts +++ b/backend/src/server/routes/v1/webhook-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { WebhooksSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -33,7 +34,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { environment: z.string().trim(), webhookUrl: z.string().url().trim(), webhookSecretKey: z.string().trim().optional(), - secretPath: z.string().trim().default("/") + secretPath: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -46,6 +47,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { const webhook = await server.services.webhook.createWebhook({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.body.workspaceId, ...req.body }); @@ -91,6 +93,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { const webhook = await server.services.webhook.updateWebhook({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, id: req.params.webhookId, isDisabled: req.body.isDisabled }); @@ -127,6 +130,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { const webhook = await server.services.webhook.deleteWebhook({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, id: req.params.webhookId }); @@ -168,6 +172,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { const webhook = await server.services.webhook.testWebhook({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, id: req.params.webhookId }); return { message: "Successfully tested webhook", webhook }; @@ -182,7 +187,11 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim().optional(), - secretPath: z.string().trim().optional() + secretPath: z + .string() + .trim() + .optional() + .transform((val) => (val ? removeTrailingSlash(val) : val)) }), response: { 200: z.object({ @@ -195,6 +204,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { const webhooks = await server.services.webhook.listWebhooks({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, ...req.query, projectId: req.query.workspaceId }); diff --git a/backend/src/server/routes/v2/identity-org-router.ts b/backend/src/server/routes/v2/identity-org-router.ts index f1beb4e86..1832e8962 100644 --- a/backend/src/server/routes/v2/identity-org-router.ts +++ b/backend/src/server/routes/v2/identity-org-router.ts @@ -10,6 +10,13 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => { url: "/:orgId/identity-memberships", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Return organization identity memberships", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ orgId: z.string().trim() }), @@ -34,6 +41,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => { const identityMemberships = await server.services.identity.listOrgIdentities({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, orgId: req.params.orgId }); return { identityMemberships }; diff --git a/backend/src/server/routes/v2/identity-project-router.ts b/backend/src/server/routes/v2/identity-project-router.ts index ea797e0cb..fdd810b4b 100644 --- a/backend/src/server/routes/v2/identity-project-router.ts +++ b/backend/src/server/routes/v2/identity-project-router.ts @@ -32,6 +32,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider) const identityMembership = await server.services.identityProject.createProjectIdentity({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, identityId: req.params.identityId, projectId: req.params.projectId, role: req.body.role @@ -45,6 +46,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider) url: "/:projectId/identity-memberships/:identityId", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Update project identity memberships", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ projectId: z.string().trim(), identityId: z.string().trim() @@ -62,6 +69,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider) const identityMembership = await server.services.identityProject.updateProjectIdentity({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, identityId: req.params.identityId, projectId: req.params.projectId, role: req.body.role @@ -75,6 +83,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider) url: "/:projectId/identity-memberships/:identityId", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Delete project identity memberships", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ projectId: z.string().trim(), identityId: z.string().trim() @@ -89,6 +103,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider) const identityMembership = await server.services.identityProject.deleteProjectIdentity({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, identityId: req.params.identityId, projectId: req.params.projectId }); @@ -101,6 +116,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider) url: "/:projectId/identity-memberships", onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Return project identity memberships", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ projectId: z.string().trim() }), @@ -125,6 +146,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider) const identityMemberships = await server.services.identityProject.listProjectIdentities({ actor: req.permission.type, actorId: req.permission.id, + actorOrgId: req.permission.orgId, projectId: req.params.projectId }); return { identityMemberships }; diff --git a/backend/src/server/routes/v2/index.ts b/backend/src/server/routes/v2/index.ts index a73f9de52..1f423c084 100644 --- a/backend/src/server/routes/v2/index.ts +++ b/backend/src/server/routes/v2/index.ts @@ -19,8 +19,8 @@ export const registerV2Routes = async (server: FastifyZodProvider) => { ); await server.register( async (projectServer) => { - projectServer.register(registerProjectRouter); - projectServer.register(registerIdentityProjectRouter); + await projectServer.register(registerProjectRouter); + await projectServer.register(registerIdentityProjectRouter); }, { prefix: "/workspace" } ); diff --git a/backend/src/server/routes/v2/mfa-router.ts b/backend/src/server/routes/v2/mfa-router.ts index 6efda7221..2c9465aa8 100644 --- a/backend/src/server/routes/v2/mfa-router.ts +++ b/backend/src/server/routes/v2/mfa-router.ts @@ -1,8 +1,8 @@ -import jwt, { JwtPayload } from "jsonwebtoken"; +import jwt from "jsonwebtoken"; import { z } from "zod"; import { getConfig } from "@app/lib/config/env"; -import { AuthTokenType } from "@app/services/auth/auth-type"; +import { AuthModeMfaJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type"; export const registerMfaRouter = async (server: FastifyZodProvider) => { const cfg = getConfig(); @@ -12,22 +12,21 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => { const authorizationHeader = req.headers.authorization; if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) { - res.status(401).send({ error: "Missing bearer token" }); + void res.status(401).send({ error: "Missing bearer token" }); return res; } const token = authorizationHeader.split(" ")[1]; if (!token) { - res.status(401).send({ error: "Missing bearer token" }); + void res.status(401).send({ error: "Missing bearer token" }); return res; } - const decodedToken = jwt.verify(token, cfg.AUTH_SECRET) as JwtPayload; - if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN) - throw new Error("Unauthorized access"); + const decodedToken = jwt.verify(token, cfg.AUTH_SECRET) as AuthModeMfaJwtTokenPayload; + if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN) throw new Error("Unauthorized access"); const user = await server.store.user.findById(decodedToken.userId); if (!user) throw new Error("User not found"); - req.mfa = { userId: user.id, user }; + req.mfa = { userId: user.id, user, orgId: decodedToken.organizationId }; }); server.route({ @@ -76,17 +75,24 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => { userAgent, ip: req.realIp, userId: req.mfa.userId, + orgId: req.mfa.orgId, mfaToken: req.body.mfaToken }); - res.setCookie("jid", token.refresh, { + void res.setCookie("jid", token.refresh, { httpOnly: true, path: "/", sameSite: "strict", secure: appCfg.HTTPS_ENABLED }); - return { token: token.access, ...user }; + return { + ...user, + token: token.access, + protectedKey: user.protectedKey || null, + protectedKeyIV: user.protectedKeyIV || null, + protectedKeyTag: user.protectedKeyTag || null + }; } }); }; diff --git a/backend/src/server/routes/v2/organization-router.ts b/backend/src/server/routes/v2/organization-router.ts index ed4a894c5..01ef7973a 100644 --- a/backend/src/server/routes/v2/organization-router.ts +++ b/backend/src/server/routes/v2/organization-router.ts @@ -1,11 +1,6 @@ import { z } from "zod"; -import { - OrganizationsSchema, - OrgMembershipsSchema, - UserEncryptionKeysSchema, - UsersSchema -} from "@app/db/schemas"; +import { OrganizationsSchema, OrgMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { ActorType, AuthMode } from "@app/services/auth/auth-type"; @@ -14,6 +9,13 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { method: "GET", url: "/:organizationId/memberships", schema: { + description: "Return organization user memberships", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ organizationId: z.string().trim() }), @@ -40,16 +42,69 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { const users = await server.services.org.findAllOrgMembers( req.permission.id, - req.params.organizationId + req.params.organizationId, + req.permission.orgId ); return { users }; } }); + server.route({ + method: "GET", + url: "/:organizationId/workspaces", + schema: { + description: "Return projects in organization that user is part of", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], + params: z.object({ + organizationId: z.string().trim() + }), + response: { + 200: z.object({ + workspaces: z + .object({ + id: z.string(), + name: z.string(), + organization: z.string(), + environments: z + .object({ + name: z.string(), + slug: z.string() + }) + .array() + }) + .array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const workspaces = await server.services.org.findAllWorkspaces({ + actor: req.permission.type, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + orgId: req.params.organizationId + }); + + return { workspaces }; + } + }); + server.route({ method: "PATCH", url: "/:organizationId/memberships/:membershipId", schema: { + description: "Update organization user memberships", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }), body: z.object({ role: z.string().trim() @@ -68,7 +123,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { userId: req.permission.id, role: req.body.role, orgId: req.params.organizationId, - membershipId: req.params.membershipId + membershipId: req.params.membershipId, + actorOrgId: req.permission.orgId }); return { membership }; } @@ -78,6 +134,13 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { method: "DELETE", url: "/:organizationId/memberships/:membershipId", schema: { + description: "Delete organization user memberships", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }), response: { 200: z.object({ @@ -92,7 +155,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { const membership = await server.services.org.deleteOrgMembership({ userId: req.permission.id, orgId: req.params.organizationId, - membershipId: req.params.membershipId + membershipId: req.params.membershipId, + actorOrgId: req.permission.orgId }); return { membership }; } @@ -143,7 +207,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { const organization = await server.services.org.deleteOrganizationById( req.permission.id, - req.params.organizationId + req.params.organizationId, + req.permission.orgId ); return { organization }; } diff --git a/backend/src/server/routes/v2/project-router.ts b/backend/src/server/routes/v2/project-router.ts index 86f25d89d..d38efdbb9 100644 --- a/backend/src/server/routes/v2/project-router.ts +++ b/backend/src/server/routes/v2/project-router.ts @@ -10,6 +10,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { url: "/:workspaceId/encrypted-key", method: "GET", schema: { + description: "Return encrypted project key", + security: [ + { + apiKeyAuth: [] + } + ], params: z.object({ workspaceId: z.string().trim() }), @@ -28,7 +34,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { const key = await server.services.projectKey.getLatestProjectKey({ actor: req.permission.type, actorId: req.permission.id, - projectId: req.params.workspaceId + projectId: req.params.workspaceId, + actorOrgId: req.permission.orgId }); await server.services.auditLog.createAuditLog({ @@ -37,7 +44,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { event: { type: EventType.GET_WORKSPACE_KEY, metadata: { - keyId: key.id + keyId: key?.id as string } } }); diff --git a/backend/src/server/routes/v2/service-token-router.ts b/backend/src/server/routes/v2/service-token-router.ts index 950720c4d..2b6445dea 100644 --- a/backend/src/server/routes/v2/service-token-router.ts +++ b/backend/src/server/routes/v2/service-token-router.ts @@ -2,9 +2,12 @@ import { z } from "zod"; import { ServiceTokensSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; +import { sanitizedServiceTokenUserSchema } from "../sanitizedSchemas"; + export const sanitizedServiceTokenSchema = ServiceTokensSchema.omit({ secretHash: true, encryptedKey: true, @@ -18,16 +21,48 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) => method: "GET", onRequest: verifyAuth([AuthMode.SERVICE_TOKEN]), schema: { + description: "Return Infisical Token data", + security: [ + { + bearerAuth: [] + } + ], response: { - 200: ServiceTokensSchema.merge(z.object({ workspace: z.string() })) + 200: ServiceTokensSchema.merge( + z.object({ + workspace: z.string(), + user: sanitizedServiceTokenUserSchema.merge( + z.object({ + _id: z.string(), + __v: z.number().default(0) + }) + ), + _id: z.string(), + __v: z.number().default(0) + }) + ) } }, handler: async (req) => { - const serviceTokenData = await server.services.serviceToken.getServiceToken({ + const { serviceToken, user } = await server.services.serviceToken.getServiceToken({ actorId: req.permission.id, actor: req.permission.type }); - return { ...serviceTokenData, workspace: serviceTokenData.projectId }; + + const formattedUser = { + ...user, + _id: user.id, + __v: 0 + } as const; + + const formattedServiceToken = { + ...serviceToken, + _id: serviceToken.id, + __v: 0 + } as const; + + // We return the user here because older versions of the deprecated Python SDK depend on it to properly parse the API response. + return { ...formattedServiceToken, workspace: serviceToken.projectId, user: formattedUser }; } }); @@ -42,7 +77,7 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) => scopes: z .object({ environment: z.string().trim(), - secretPath: z.string().trim() + secretPath: z.string().trim().transform(removeTrailingSlash) }) .array() .min(1), @@ -63,6 +98,7 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) => const { serviceToken, token } = await server.services.serviceToken.createServiceToken({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, ...req.body, projectId: req.body.workspaceId }); @@ -100,6 +136,7 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) => const serviceTokenData = await server.services.serviceToken.deleteServiceToken({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, id: req.params.serviceTokenId }); diff --git a/backend/src/server/routes/v2/user-router.ts b/backend/src/server/routes/v2/user-router.ts index 071a70539..8fc114e76 100644 --- a/backend/src/server/routes/v2/user-router.ts +++ b/backend/src/server/routes/v2/user-router.ts @@ -1,11 +1,6 @@ import { z } from "zod"; -import { - AuthTokenSessionsSchema, - OrganizationsSchema, - UserEncryptionKeysSchema, - UsersSchema -} from "@app/db/schemas"; +import { AuthTokenSessionsSchema, OrganizationsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; import { ApiKeysSchema } from "@app/db/schemas/api-keys"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMethod, AuthMode } from "@app/services/auth/auth-type"; @@ -47,11 +42,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { }, preHandler: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), handler: async (req) => { - const user = await server.services.user.updateUserName( - req.permission.id, - req.body.firstName, - req.body.lastName - ); + const user = await server.services.user.updateUserName(req.permission.id, req.body.firstName, req.body.lastName); return { user }; } }); @@ -71,10 +62,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { }, preHandler: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), handler: async (req) => { - const user = await server.services.user.updateAuthMethods( - req.permission.id, - req.body.authMethods - ); + const user = await server.services.user.updateAuthMethods(req.permission.id, req.body.authMethods); return { user }; } }); @@ -83,6 +71,12 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { method: "GET", url: "/me/organizations", schema: { + description: "Return organizations that current user is part of", + security: [ + { + apiKeyAuth: [] + } + ], response: { 200: z.object({ organizations: OrganizationsSchema.array() @@ -128,11 +122,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const apiKeys = await server.services.apiKey.createApiKey( - req.permission.id, - req.body.name, - req.body.expiresIn - ); + const apiKeys = await server.services.apiKey.createApiKey(req.permission.id, req.body.name, req.body.expiresIn); return apiKeys; } }); @@ -152,10 +142,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const apiKeyData = await server.services.apiKey.deleteApiKey( - req.permission.id, - req.params.apiKeyDataId - ); + const apiKeyData = await server.services.apiKey.deleteApiKey(req.permission.id, req.params.apiKeyDataId); return { apiKeyData }; } }); @@ -198,6 +185,12 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { method: "GET", url: "/me", schema: { + description: "Retrieve the current user on the request", + security: [ + { + apiKeyAuth: [] + } + ], response: { 200: z.object({ user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true })) diff --git a/backend/src/server/routes/v3/login-router.ts b/backend/src/server/routes/v3/login-router.ts index 22d7b8be7..0cda8e5ff 100644 --- a/backend/src/server/routes/v3/login-router.ts +++ b/backend/src/server/routes/v3/login-router.ts @@ -81,7 +81,7 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => { return { mfaEnabled: true, token: data.token } as const; // for discriminated union } - res.setCookie("jid", data.token.refresh, { + void res.setCookie("jid", data.token.refresh, { httpOnly: true, path: "/", sameSite: "strict", @@ -96,9 +96,9 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => { encryptedPrivateKey: data.user.encryptedPrivateKey, iv: data.user.iv, tag: data.user.tag, - protectedKey: data.user.protectedKey, - protectedKeyIV: data.user.protectedKeyIV, - protectedKeyTag: data.user.protectedKeyTag + protectedKey: data.user.protectedKey || null, + protectedKeyIV: data.user.protectedKeyIV || null, + protectedKeyTag: data.user.protectedKeyTag || null } as const; } }); diff --git a/backend/src/server/routes/v3/secret-blind-index-router.ts b/backend/src/server/routes/v3/secret-blind-index-router.ts index 643f63c81..94e6cab83 100644 --- a/backend/src/server/routes/v3/secret-blind-index-router.ts +++ b/backend/src/server/routes/v3/secret-blind-index-router.ts @@ -21,7 +21,8 @@ export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider) const count = await server.services.secretBlindIndex.getSecretBlindIndexStatus({ projectId: req.params.projectId, actorId: req.permission.id, - actor: req.permission.type + actor: req.permission.type, + actorOrgId: req.permission.orgId }); return count === 0; } @@ -52,14 +53,15 @@ export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider) const secrets = await server.services.secretBlindIndex.getProjectSecrets({ projectId: req.params.projectId, actorId: req.permission.id, - actor: req.permission.type + actor: req.permission.type, + actorOrgId: req.permission.orgId }); return { secrets }; } }); server.route({ - url: "/:projectId/secrets/name", + url: "/:projectId/secrets/names", method: "POST", schema: { params: z.object({ @@ -85,7 +87,8 @@ export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider) projectId: req.params.projectId, secretsToUpdate: req.body.secretsToUpdate, actorId: req.permission.id, - actor: req.permission.type + actor: req.permission.type, + actorOrgId: req.permission.orgId }); return { message: "Successfully named workspace secrets" }; } diff --git a/backend/src/server/routes/v3/secret-router.ts b/backend/src/server/routes/v3/secret-router.ts index 04d187aef..5f47ae3c5 100644 --- a/backend/src/server/routes/v3/secret-router.ts +++ b/backend/src/server/routes/v3/secret-router.ts @@ -12,6 +12,8 @@ import { import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types"; import { BadRequestError } from "@app/lib/errors"; +import { removeTrailingSlash } from "@app/lib/fn"; +import { getUserAgentType } from "@app/server/plugins/audit-log"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { ActorType, AuthMode } from "@app/services/auth/auth-type"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; @@ -36,10 +38,17 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { url: "/raw", method: "GET", schema: { + description: "List secrets", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], querystring: z.object({ workspaceId: z.string().trim().optional(), environment: z.string().trim().optional(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), include_imports: z .enum(["true", "false"]) .default("false") @@ -60,12 +69,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { // just for delivery hero usecase let { secretPath, environment, workspaceId } = req.query; @@ -79,12 +83,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { } } - if (!workspaceId || !environment) - throw new BadRequestError({ message: "Missing workspace id or environment" }); + if (!workspaceId || !environment) throw new BadRequestError({ message: "Missing workspace id or environment" }); const { secrets, imports } = await server.services.secret.getSecretsRaw({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, environment, projectId: workspaceId, path: secretPath, @@ -112,6 +116,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId, environment, secretPath: req.query.secretPath, + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -123,13 +128,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { url: "/raw/:secretName", method: "GET", schema: { + description: "Get a secret by name", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ secretName: z.string().trim() }), querystring: z.object({ workspaceId: z.string().trim().optional(), environment: z.string().trim().optional(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), version: z.coerce.number().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared), include_imports: z @@ -143,12 +155,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { let { secretPath, environment, workspaceId } = req.query; if (req.auth.actor === ActorType.SERVICE) { @@ -161,12 +168,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { } } - if (!workspaceId || !environment) - throw new BadRequestError({ message: "Missing workspace id or environment" }); + if (!workspaceId || !environment) throw new BadRequestError({ message: "Missing workspace id or environment" }); const secret = await server.services.secret.getSecretByNameRaw({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, environment, projectId: workspaceId, path: secretPath, @@ -199,6 +206,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId, environment, secretPath: req.query.secretPath, + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -210,16 +218,21 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { url: "/raw/:secretName", method: "POST", schema: { + description: "Create secret", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ secretName: z.string().trim() }), body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), - secretValue: z - .string() - .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), + secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), secretComment: z.string().trim().optional().default(""), skipMultilineEncoding: z.boolean().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared) @@ -230,16 +243,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secret = await server.services.secret.createSecretRaw({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, environment: req.body.environment, projectId: req.body.workspaceId, secretPath: req.body.secretPath, @@ -256,7 +265,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { event: { type: EventType.CREATE_SECRET, metadata: { - environment: req.body.environment, + environment: req.body.environment, secretPath: req.body.secretPath, secretId: secret.id, secretKey: req.params.secretName, @@ -273,7 +282,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.body.workspaceId, environment: req.body.environment, secretPath: req.body.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -286,16 +295,21 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { url: "/raw/:secretName", method: "PATCH", schema: { + description: "Update secret", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ secretName: z.string().trim() }), body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretValue: z - .string() - .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), - secretPath: z.string().trim().default("/"), + secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), skipMultilineEncoding: z.boolean().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared) }), @@ -305,16 +319,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secret = await server.services.secret.updateSecretRaw({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, environment: req.body.environment, projectId: req.body.workspaceId, secretPath: req.body.secretPath, @@ -347,7 +357,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.body.workspaceId, environment: req.body.environment, secretPath: req.body.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -359,13 +369,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { url: "/raw/:secretName", method: "DELETE", schema: { + description: "Delete secret", + security: [ + { + bearerAuth: [], + apiKeyAuth: [] + } + ], params: z.object({ secretName: z.string().trim() }), body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), type: z.nativeEnum(SecretType).default(SecretType.Shared) }), response: { @@ -374,16 +391,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secret = await server.services.secret.deleteSecretRaw({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, environment: req.body.environment, projectId: req.body.workspaceId, secretPath: req.body.secretPath, @@ -414,7 +427,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.body.workspaceId, environment: req.body.environment, secretPath: req.body.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -430,7 +443,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), include_imports: z .enum(["true", "false"]) .default("false") @@ -441,6 +454,9 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secrets: SecretsSchema.omit({ secretBlindIndex: true }) .merge( z.object({ + _id: z.string(), + workspace: z.string(), + environment: z.string(), tags: SecretTagsSchema.pick({ id: true, slug: true, @@ -455,23 +471,27 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secretPath: z.string(), environment: z.string(), folderId: z.string().optional(), - secrets: SecretsSchema.omit({ secretBlindIndex: true }).array() + secrets: SecretsSchema.omit({ secretBlindIndex: true }) + .merge( + z.object({ + _id: z.string(), + workspace: z.string(), + environment: z.string() + }) + ) + .array() }) .array() .optional() }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { secrets, imports } = await server.services.secret.getSecrets({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, environment: req.query.environment, projectId: req.query.workspaceId, path: req.query.secretPath, @@ -491,18 +511,33 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { } }); - server.services.telemetry.sendPostHogEvents({ - event: PostHogEventTypes.SecretPulled, - distinctId: getDistinctId(req), - properties: { - numberOfSecrets: secrets.length, - workspaceId: req.query.workspaceId, - environment: req.query.environment, - secretPath: req.query.secretPath, - - ...req.auditLogInfo + // TODO: Move to telemetry plugin + let shouldRecordK8Event = false; + if (req.headers["user-agent"] === "k8-operatoer") { + const randomNumber = Math.random(); + if (randomNumber > 0.95) { + shouldRecordK8Event = true; } - }); + } + + const shouldCapture = + req.query.workspaceId !== "650e71fbae3e6c8572f436d4" && + (req.headers["user-agent"] !== "k8-operator" || shouldRecordK8Event); + const approximateNumberTotalSecrets = secrets.length * 20; + if (shouldCapture) { + server.services.telemetry.sendPostHogEvents({ + event: PostHogEventTypes.SecretPulled, + distinctId: getDistinctId(req), + properties: { + numberOfSecrets: shouldRecordK8Event ? approximateNumberTotalSecrets : secrets.length, + workspaceId: req.query.workspaceId, + environment: req.query.environment, + secretPath: req.query.secretPath, + channel: getUserAgentType(req.headers["user-agent"]), + ...req.auditLogInfo + } + }); + } return { secrets, imports }; } @@ -518,7 +553,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), type: z.nativeEnum(SecretType).default(SecretType.Shared), version: z.coerce.number().optional(), include_imports: z @@ -528,20 +563,21 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + workspace: z.string(), + environment: z.string() + }) + ) }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secret = await server.services.secret.getSecretByName({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, environment: req.query.environment, projectId: req.query.workspaceId, path: req.query.secretPath, @@ -574,7 +610,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.query.workspaceId, environment: req.query.environment, secretPath: req.query.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -590,7 +626,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: z.string().trim(), environment: z.string().trim(), type: z.nativeEnum(SecretType).default(SecretType.Shared), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secretKeyCiphertext: z.string().trim(), secretKeyIV: z.string().trim(), secretKeyTag: z.string().trim(), @@ -609,20 +645,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + _id: z.string(), + workspace: z.string(), + environment: z.string() + }) + ) }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { workspaceId: projectId, @@ -650,32 +685,32 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Create]: [ - { - secretName: req.params.secretName, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - skipMultilineEncoding, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV - } - ] - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + actorOrgId: req.permission.orgId, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Create]: [ + { + secretName: req.params.secretName, + secretValueCiphertext, + secretValueIV, + secretValueTag, + secretCommentIV, + secretCommentTag, + secretCommentCiphertext, + skipMultilineEncoding, + secretKeyTag, + secretKeyCiphertext, + secretKeyIV + } + ] + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -696,6 +731,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { const secret = await server.services.secret.createSecret({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, path: secretPath, type, environment: req.body.environment, @@ -737,7 +773,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.body.workspaceId, environment: req.body.environment, secretPath: req.body.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -758,7 +794,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { environment: z.string().trim(), secretId: z.string().trim().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secretValueCiphertext: z.string().trim(), secretValueIV: z.string().trim(), secretValueTag: z.string().trim(), @@ -779,20 +815,19 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + _id: z.string(), + workspace: z.string(), + environment: z.string() + }) + ) }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { secretValueCiphertext, @@ -820,39 +855,40 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, secretPath, environment, projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Update]: [ - { - secretName: req.params.secretName, - newSecretName, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - skipMultilineEncoding, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV, - tagIds: tags - } - ] - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + actorOrgId: req.permission.orgId, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Update]: [ + { + secretName: req.params.secretName, + newSecretName, + secretValueCiphertext, + secretValueIV, + secretValueTag, + secretCommentIV, + secretCommentTag, + secretCommentCiphertext, + skipMultilineEncoding, + secretKeyTag, + secretKeyCiphertext, + secretKeyIV, + tagIds: tags + } + ] + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -873,6 +909,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { const secret = await server.services.secret.updateSecret({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, path: secretPath, type, environment, @@ -918,7 +955,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.body.workspaceId, environment: req.body.environment, secretPath: req.body.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -935,7 +972,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }), body: z.object({ type: z.nativeEnum(SecretType).default(SecretType.Shared), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secretId: z.string().trim().optional(), workspaceId: z.string().trim(), environment: z.string().trim() @@ -943,47 +980,47 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + _id: z.string(), + workspace: z.string(), + environment: z.string() + }) + ) }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { secretPath, type, workspaceId: projectId, secretId, environment } = req.body; if (req.body.type !== SecretType.Personal && req.permission.type === ActorType.USER) { const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, secretPath, environment, projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Delete]: [ - { - secretName: req.params.secretName - } - ] - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + actorOrgId: req.permission.orgId, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Delete]: [ + { + secretName: req.params.secretName + } + ] + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -1004,6 +1041,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { const secret = await server.services.secret.deleteSecret({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, path: secretPath, type, environment, @@ -1035,7 +1073,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.body.workspaceId, environment: req.body.environment, secretPath: req.body.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -1050,7 +1088,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secrets: z .object({ secretName: z.string().trim(), @@ -1075,41 +1113,35 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { z.object({ secrets: SecretsSchema.omit({ secretBlindIndex: true }).array() }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { environment, workspaceId: projectId, secretPath, secrets: inputSecrets } = req.body; if (req.permission.type === ActorType.USER) { const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, secretPath, environment, projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Create]: inputSecrets.filter(({ type }) => type === "shared") - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + actorOrgId: req.permission.orgId, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Create]: inputSecrets.filter(({ type }) => type === "shared") + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -1130,6 +1162,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { const secrets = await server.services.secret.createManySecret({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, path: secretPath, environment, projectId, @@ -1161,7 +1194,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.body.workspaceId, environment: req.body.environment, secretPath: req.body.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -1176,7 +1209,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secrets: z .object({ secretName: z.string().trim(), @@ -1201,41 +1234,35 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { z.object({ secrets: SecretsSchema.omit({ secretBlindIndex: true }).array() }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { environment, workspaceId: projectId, secretPath, secrets: inputSecrets } = req.body; if (req.permission.type === ActorType.USER) { const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, secretPath, environment, projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Update]: inputSecrets.filter(({ type }) => type === "shared") - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + actorOrgId: req.permission.orgId, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Update]: inputSecrets.filter(({ type }) => type === "shared") + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -1255,6 +1282,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { const secrets = await server.services.secret.updateManySecret({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, path: secretPath, environment, projectId, @@ -1286,7 +1314,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.body.workspaceId, environment: req.body.environment, secretPath: req.body.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); @@ -1301,7 +1329,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secrets: z .object({ secretName: z.string().trim(), @@ -1315,41 +1343,35 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { z.object({ secrets: SecretsSchema.omit({ secretBlindIndex: true }).array() }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { environment, workspaceId: projectId, secretPath, secrets: inputSecrets } = req.body; if (req.permission.type === ActorType.USER) { const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, secretPath, environment, projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Delete]: inputSecrets.filter(({ type }) => type === "shared") - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + actorOrgId: req.permission.orgId, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Delete]: inputSecrets.filter(({ type }) => type === "shared") + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, ...req.auditLogInfo, @@ -1368,6 +1390,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { const secrets = await server.services.secret.deleteManySecret({ actorId: req.permission.id, actor: req.permission.type, + actorOrgId: req.permission.orgId, path: req.body.secretPath, environment, projectId, @@ -1399,7 +1422,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: req.body.workspaceId, environment: req.body.environment, secretPath: req.body.secretPath, - + channel: getUserAgentType(req.headers["user-agent"]), ...req.auditLogInfo } }); diff --git a/backend/src/server/routes/v3/signup-router.ts b/backend/src/server/routes/v3/signup-router.ts index fe64a4ec2..209e86ac7 100644 --- a/backend/src/server/routes/v3/signup-router.ts +++ b/backend/src/server/routes/v3/signup-router.ts @@ -48,10 +48,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const { token, user } = await server.services.signup.verifyEmailSignup( - req.body.email, - req.body.code - ); + const { token, user } = await server.services.signup.verifyEmailSignup(req.body.email, req.body.code); return { message: "Successfuly verified email", token, user }; } }); @@ -93,21 +90,16 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { if (!userAgent) throw new Error("user agent header is required"); const appCfg = getConfig(); - const { user, accessToken, refreshToken } = - await server.services.signup.completeEmailAccountSignup({ - ...req.body, - ip: req.realIp, - userAgent, - authorization: req.headers.authorization as string - }); + const { user, accessToken, refreshToken } = await server.services.signup.completeEmailAccountSignup({ + ...req.body, + ip: req.realIp, + userAgent, + authorization: req.headers.authorization as string + }); - server.services.telemetry.sendLoopsEvent( - user.email, - user.firstName || "", - user.lastName || "" - ); + void server.services.telemetry.sendLoopsEvent(user.email, user.firstName || "", user.lastName || ""); - server.services.telemetry.sendPostHogEvents({ + void server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.UserSignedUp, distinctId: user.email, properties: { @@ -116,7 +108,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { } }); - res.setCookie("jid", refreshToken, { + void res.setCookie("jid", refreshToken, { httpOnly: true, path: "/", sameSite: "strict", @@ -161,14 +153,25 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { if (!userAgent) throw new Error("user agent header is required"); const appCfg = getConfig(); - const { user, accessToken, refreshToken } = - await server.services.signup.completeAccountInvite({ - ...req.body, - ip: req.realIp, - userAgent - }); + const { user, accessToken, refreshToken } = await server.services.signup.completeAccountInvite({ + ...req.body, + ip: req.realIp, + userAgent, + authorization: req.headers.authorization as string + }); - res.setCookie("jid", refreshToken, { + void server.services.telemetry.sendLoopsEvent(user.email, user.firstName || "", user.lastName || ""); + + void server.services.telemetry.sendPostHogEvents({ + event: PostHogEventTypes.UserSignedUp, + distinctId: user.email, + properties: { + email: user.email, + attributionSource: "Team Invite" + } + }); + + void res.setCookie("jid", refreshToken, { httpOnly: true, path: "/", sameSite: "strict", diff --git a/backend/src/services/api-key/api-key-service.ts b/backend/src/services/api-key/api-key-service.ts index 23057e1a4..39ccbecce 100644 --- a/backend/src/services/api-key/api-key-service.ts +++ b/backend/src/services/api-key/api-key-service.ts @@ -45,8 +45,7 @@ export const apiKeyServiceFactory = ({ apiKeyDAL, userDAL }: TApiKeyServiceFacto const deleteApiKey = async (userId: string, apiKeyId: string) => { const [apiKeyData] = await apiKeyDAL.delete({ id: apiKeyId, userId }); - if (!apiKeyData) - throw new BadRequestError({ message: "Failed to find api key", name: "delete api key" }); + if (!apiKeyData) throw new BadRequestError({ message: "Failed to find api key", name: "delete api key" }); return formatApiKey(apiKeyData); }; diff --git a/backend/src/services/auth-token/auth-token-dal.ts b/backend/src/services/auth-token/auth-token-dal.ts index edd8ef74c..075ae7384 100644 --- a/backend/src/services/auth-token/auth-token-dal.ts +++ b/backend/src/services/auth-token/auth-token-dal.ts @@ -7,16 +7,12 @@ import { ormify } from "@app/lib/knex"; import { TDeleteTokenForUserDALDTO } from "./auth-token-types"; -export type TTokenDALConfig = {}; - export type TTokenDALFactory = ReturnType; export const tokenDALFactory = (db: TDbClient) => { const authOrm = ormify(db, TableName.AuthTokens); - const findOneTokenSession = async ( - filter: Partial - ): Promise => { + const findOneTokenSession = async (filter: Partial): Promise => { try { const doc = await db(TableName.AuthTokenSession).where(filter).first(); return doc; @@ -31,20 +27,14 @@ export const tokenDALFactory = (db: TDbClient) => { orgId }: TDeleteTokenForUserDALDTO): Promise => { try { - const doc = await db(TableName.AuthTokens) - .where({ userId, type, orgId }) - .delete() - .returning("*"); + const doc = await db(TableName.AuthTokens).where({ userId, type, orgId }).delete().returning("*"); return doc; } catch (error) { throw new DatabaseError({ error, name: "DeleteTokenForUser" }); } }; - const decrementTriesField = async ({ - userId, - type - }: TDeleteTokenForUserDALDTO): Promise => { + const decrementTriesField = async ({ userId, type }: TDeleteTokenForUserDALDTO): Promise => { try { await db(TableName.AuthTokens).where({ userId, type }).decrement("triesLeft", 1); } catch (error) { @@ -101,10 +91,7 @@ export const tokenDALFactory = (db: TDbClient) => { const deleteTokenSession = async (filter: Partial, tx?: Knex) => { try { - const sessions = await (tx || db)(TableName.AuthTokenSession) - .where(filter) - .del() - .returning("*"); + const sessions = await (tx || db)(TableName.AuthTokenSession).where(filter).del().returning("*"); return sessions; } catch (error) { throw new DatabaseError({ name: "Delete token session", error }); diff --git a/backend/src/services/auth-token/auth-token-service.ts b/backend/src/services/auth-token/auth-token-service.ts index d19691209..59f336e5a 100644 --- a/backend/src/services/auth-token/auth-token-service.ts +++ b/backend/src/services/auth-token/auth-token-service.ts @@ -9,12 +9,7 @@ import { UnauthorizedError } from "@app/lib/errors"; import { AuthModeJwtTokenPayload } from "../auth/auth-type"; import { TUserDALFactory } from "../user/user-dal"; import { TTokenDALFactory } from "./auth-token-dal"; -import { - TCreateTokenForUserDTO, - TIssueAuthTokenDTO, - TokenType, - TValidateTokenForUserDTO -} from "./auth-token-types"; +import { TCreateTokenForUserDTO, TIssueAuthTokenDTO, TokenType, TValidateTokenForUserDTO } from "./auth-token-types"; type TAuthTokenServiceFactoryDep = { tokenDAL: TTokenDALFactory; @@ -125,14 +120,10 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL }: TAuthTokenServiceFact return session; }; - const clearTokenSessionById = async ( - userId: string, - sessionId: string - ): Promise => + const clearTokenSessionById = async (userId: string, sessionId: string): Promise => tokenDAL.incrementTokenSessionVersion(userId, sessionId); - const getUserTokenSessionById = async (id: string, userId: string) => - tokenDAL.findOneTokenSession({ id, userId }); + const getUserTokenSessionById = async (id: string, userId: string) => tokenDAL.findOneTokenSession({ id, userId }); const getTokenSessionByUser = async (userId: string) => tokenDAL.findTokenSessions({ userId }); @@ -145,13 +136,12 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL }: TAuthTokenServiceFact userId: token.userId }); if (!session) throw new UnauthorizedError({ name: "Session not found" }); - if (token.accessVersion !== session.accessVersion) - throw new UnauthorizedError({ name: "Stale session" }); + if (token.accessVersion !== session.accessVersion) throw new UnauthorizedError({ name: "Stale session" }); const user = await userDAL.findById(session.userId); if (!user || !user.isAccepted) throw new UnauthorizedError({ name: "Token user not found" }); - return { user, tokenVersionId: token.tokenVersionId }; + return { user, tokenVersionId: token.tokenVersionId, orgId: token.organizationId }; }; return { diff --git a/backend/src/services/auth/auth-fns.ts b/backend/src/services/auth/auth-fns.ts index 91f34345c..b46803b06 100644 --- a/backend/src/services/auth/auth-fns.ts +++ b/backend/src/services/auth/auth-fns.ts @@ -3,30 +3,26 @@ import jwt from "jsonwebtoken"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; -import { - AuthModeProviderJwtTokenPayload, - AuthModeProviderSignUpTokenPayload, - AuthTokenType -} from "./auth-type"; +import { AuthModeProviderJwtTokenPayload, AuthModeProviderSignUpTokenPayload, AuthTokenType } from "./auth-type"; export const validateProviderAuthToken = (providerToken: string, email: string) => { if (!providerToken) throw new UnauthorizedError(); const appCfg = getConfig(); - const decodedToken = jwt.verify( - providerToken, - appCfg.AUTH_SECRET - ) as AuthModeProviderJwtTokenPayload; + const decodedToken = jwt.verify(providerToken, appCfg.AUTH_SECRET) as AuthModeProviderJwtTokenPayload; if (decodedToken.authTokenType !== AuthTokenType.PROVIDER_TOKEN) throw new UnauthorizedError(); if (decodedToken.email !== email) throw new Error("Invalid auth credentials"); + + if (decodedToken.organizationId) { + return { orgId: decodedToken.organizationId }; + } + + return {}; }; export const validateSignUpAuthorization = (token: string, userId: string, validate = true) => { const appCfg = getConfig(); - const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>token?.split(" ", 2) ?? [ - null, - null - ]; + const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>token?.split(" ", 2) ?? [null, null]; if (AUTH_TOKEN_TYPE === null) { throw new BadRequestError({ message: "Missing Authorization Header in the request header." }); } @@ -41,10 +37,7 @@ export const validateSignUpAuthorization = (token: string, userId: string, valid }); } - const decodedToken = jwt.verify( - AUTH_TOKEN_VALUE, - appCfg.AUTH_SECRET - ) as AuthModeProviderSignUpTokenPayload; + const decodedToken = jwt.verify(AUTH_TOKEN_VALUE, appCfg.AUTH_SECRET) as AuthModeProviderSignUpTokenPayload; if (!validate) return decodedToken; if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw new UnauthorizedError(); diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index e10fe6830..6e4d60bba 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -25,11 +25,7 @@ type TAuthLoginServiceFactoryDep = { }; export type TAuthLoginFactory = ReturnType; -export const authLoginServiceFactory = ({ - userDAL, - tokenService, - smtpService -}: TAuthLoginServiceFactoryDep) => { +export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: TAuthLoginServiceFactoryDep) => { /* * Private * Not exported. This is to update user device list @@ -37,9 +33,7 @@ export const authLoginServiceFactory = ({ */ const updateUserDeviceSession = async (user: TUsers, ip: string, userAgent: string) => { const devices = await UserDeviceSchema.parseAsync(user.devices || []); - const isDeviceSeen = devices.some( - (device) => device.ip === ip && device.userAgent === userAgent - ); + const isDeviceSeen = devices.some((device) => device.ip === ip && device.userAgent === userAgent); if (!isDeviceSeen) { const newDeviceList = devices.concat([{ ip, userAgent }]); @@ -62,7 +56,7 @@ export const authLoginServiceFactory = ({ * Private * Send mfa code via email * */ - const sendUserMfaCode = async (userId: string, email: string) => { + const sendUserMfaCode = async ({ userId, email }: { userId: string; email: string }) => { const code = await tokenService.createTokenForUser({ type: TokenType.TOKEN_EMAIL_MFA, userId @@ -82,7 +76,17 @@ export const authLoginServiceFactory = ({ * Check user device and send mail if new device * generate the auth and refresh token. fn shared by mfa verification and login verification with mfa disabled */ - const generateUserTokens = async (user: TUsers, ip: string, userAgent: string) => { + const generateUserTokens = async ({ + user, + ip, + userAgent, + organizationId + }: { + user: TUsers; + ip: string; + userAgent: string; + organizationId?: string; + }) => { const cfg = getConfig(); await updateUserDeviceSession(user, ip, userAgent); const tokenSession = await tokenService.getUserTokenSession({ @@ -96,7 +100,8 @@ export const authLoginServiceFactory = ({ authTokenType: AuthTokenType.ACCESS_TOKEN, userId: user.id, tokenVersionId: tokenSession.id, - accessVersion: tokenSession.accessVersion + accessVersion: tokenSession.accessVersion, + organizationId }, cfg.AUTH_SECRET, { expiresIn: cfg.JWT_AUTH_LIFETIME } @@ -107,7 +112,8 @@ export const authLoginServiceFactory = ({ authTokenType: AuthTokenType.REFRESH_TOKEN, userId: user.id, tokenVersionId: tokenSession.id, - refreshVersion: tokenSession.refreshVersion + refreshVersion: tokenSession.refreshVersion, + organizationId }, cfg.AUTH_SECRET, { expiresIn: cfg.JWT_REFRESH_LIFETIME } @@ -155,12 +161,17 @@ export const authLoginServiceFactory = ({ if (!userEnc) throw new Error("Failed to find user"); const cfg = getConfig(); + let organizationId; if (!userEnc.authMethods?.includes(AuthMethod.EMAIL)) { - validateProviderAuthToken(providerAuthToken as string, email); + const { orgId } = validateProviderAuthToken(providerAuthToken as string, email); + organizationId = orgId; + } else if (providerAuthToken) { + // SAML SSO + const { orgId } = validateProviderAuthToken(providerAuthToken, email); + organizationId = orgId; } - if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) - throw new Error("Failed to authenticate. Try again?"); + if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?"); const isValidClientProof = await srpCheckClientProof( userEnc.salt, userEnc.verifier, @@ -177,16 +188,35 @@ export const authLoginServiceFactory = ({ // send multi factor auth token if they it enabled if (userEnc.isMfaEnabled) { const mfaToken = jwt.sign( - { authTokenType: AuthTokenType.MFA_TOKEN, userId: userEnc.userId }, + { + authTokenType: AuthTokenType.MFA_TOKEN, + userId: userEnc.userId, + organizationId + }, cfg.AUTH_SECRET, - { expiresIn: cfg.JWT_MFA_LIFETIME } + { + expiresIn: cfg.JWT_MFA_LIFETIME + } ); - await sendUserMfaCode(userEnc.userId, userEnc.email); + + await sendUserMfaCode({ + userId: userEnc.userId, + email: userEnc.email + }); return { isMfaEnabled: true, token: mfaToken } as const; } - const token = await generateUserTokens({ ...userEnc, id: userEnc.userId }, ip, userAgent); + const token = await generateUserTokens({ + user: { + ...userEnc, + id: userEnc.userId + }, + ip, + userAgent, + organizationId + }); + return { token, isMfaEnabled: false, user: userEnc } as const; }; @@ -197,14 +227,17 @@ export const authLoginServiceFactory = ({ const resendMfaToken = async (userId: string) => { const user = await userDAL.findById(userId); if (!user) return; - await sendUserMfaCode(user.id, user.email); + await sendUserMfaCode({ + userId: user.id, + email: user.email + }); }; /* * Multi factor authentication verification of code * Third step of login in which user completes with mfa * */ - const verifyMfaToken = async ({ userId, mfaToken, ip, userAgent }: TVerifyMfaTokenDTO) => { + const verifyMfaToken = async ({ userId, mfaToken, ip, userAgent, orgId }: TVerifyMfaTokenDTO) => { await tokenService.validateTokenForUser({ type: TokenType.TOKEN_EMAIL_MFA, userId, @@ -213,7 +246,16 @@ export const authLoginServiceFactory = ({ const userEnc = await userDAL.findUserEncKeyByUserId(userId); if (!userEnc) throw new Error("Failed to authenticate user"); - const token = await generateUserTokens({ ...userEnc, id: userEnc.userId }, ip, userAgent); + const token = await generateUserTokens({ + user: { + ...userEnc, + id: userEnc.userId + }, + ip, + userAgent, + organizationId: orgId + }); + return { token, user: userEnc }; }; /* @@ -230,8 +272,7 @@ export const authLoginServiceFactory = ({ let user = await userDAL.findUserByEmail(email); const appCfg = getConfig(); const isOauthSignUpDisabled = !isSignupAllowed && !user; - if (isOauthSignUpDisabled) - throw new BadRequestError({ message: "User signup disabled", name: "Oauth 2 login" }); + if (isOauthSignUpDisabled) throw new BadRequestError({ message: "User signup disabled", name: "Oauth 2 login" }); if (!user) { user = await userDAL.create({ email, firstName, lastName, authMethods: [authMethod] }); diff --git a/backend/src/services/auth/auth-login-type.ts b/backend/src/services/auth/auth-login-type.ts index 3d67fef87..67f640bc9 100644 --- a/backend/src/services/auth/auth-login-type.ts +++ b/backend/src/services/auth/auth-login-type.ts @@ -19,6 +19,7 @@ export type TVerifyMfaTokenDTO = { mfaToken: string; ip: string; userAgent: string; + orgId?: string; }; export type TOauthLoginDTO = { diff --git a/backend/src/services/auth/auth-password-service.ts b/backend/src/services/auth/auth-password-service.ts index 3ea656184..ff07d422f 100644 --- a/backend/src/services/auth/auth-password-service.ts +++ b/backend/src/services/auth/auth-password-service.ts @@ -9,11 +9,7 @@ import { TokenType } from "../auth-token/auth-token-types"; import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service"; import { TUserDALFactory } from "../user/user-dal"; import { TAuthDALFactory } from "./auth-dal"; -import { - TChangePasswordDTO, - TCreateBackupPrivateKeyDTO, - TResetPasswordViaBackupKeyDTO -} from "./auth-password-type"; +import { TChangePasswordDTO, TCreateBackupPrivateKeyDTO, TResetPasswordViaBackupKeyDTO } from "./auth-password-type"; import { AuthTokenType } from "./auth-type"; type TAuthPasswordServiceFactoryDep = { @@ -70,8 +66,7 @@ export const authPaswordServiceFactory = ({ serverPrivateKey: null, clientPublicKey: null }); - if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) - throw new Error("Failed to authenticate. Try again?"); + if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?"); const isValidClientProof = await srpCheckClientProof( userEnc.salt, userEnc.verifier, @@ -200,8 +195,7 @@ export const authPaswordServiceFactory = ({ throw new Error("Failed to find user"); } - if (!userEnc.clientPublicKey || !userEnc.serverPrivateKey) - throw new Error("failed to create backup key"); + if (!userEnc.clientPublicKey || !userEnc.serverPrivateKey) throw new Error("failed to create backup key"); const isValidClientProff = await srpCheckClientProof( userEnc.salt, userEnc.verifier, diff --git a/backend/src/services/auth/auth-signup-service.ts b/backend/src/services/auth/auth-signup-service.ts index 17e45845f..021ca7070 100644 --- a/backend/src/services/auth/auth-signup-service.ts +++ b/backend/src/services/auth/auth-signup-service.ts @@ -120,8 +120,10 @@ export const authSignupServiceFactory = ({ throw new Error("Failed to complete account for complete user"); } + let organizationId; if (providerAuthToken) { - validateProviderAuthToken(providerAuthToken, user.email); + const { orgId } = validateProviderAuthToken(providerAuthToken, user.email); + organizationId = orgId; } else { validateSignUpAuthorization(authorization, user.id); } @@ -147,13 +149,7 @@ export const authSignupServiceFactory = ({ return { info: us, key: userEncKey }; }); - const hasSamlEnabled = user?.authMethods?.some((authMethod) => - [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes( - authMethod as AuthMethod - ) - ); - - if (!hasSamlEnabled) { + if (!organizationId) { await orgService.createOrganization(user.id, user.email, organizationName); } @@ -162,9 +158,7 @@ export const authSignupServiceFactory = ({ { userId: user.id, status: OrgMembershipStatus.Accepted } ); const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))]; - await Promise.allSettled( - uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId)) - ); + await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId))); const tokenSession = await tokenService.getUserTokenSession({ userAgent, @@ -179,7 +173,8 @@ export const authSignupServiceFactory = ({ authTokenType: AuthTokenType.ACCESS_TOKEN, userId: updateduser.info.id, tokenVersionId: tokenSession.id, - accessVersion: tokenSession.accessVersion + accessVersion: tokenSession.accessVersion, + organizationId }, appCfg.AUTH_SECRET, { expiresIn: appCfg.JWT_AUTH_LIFETIME } @@ -190,7 +185,8 @@ export const authSignupServiceFactory = ({ authTokenType: AuthTokenType.REFRESH_TOKEN, userId: updateduser.info.id, tokenVersionId: tokenSession.id, - refreshVersion: tokenSession.refreshVersion + refreshVersion: tokenSession.refreshVersion, + organizationId }, appCfg.AUTH_SECRET, { expiresIn: appCfg.JWT_REFRESH_LIFETIME } @@ -216,13 +212,16 @@ export const authSignupServiceFactory = ({ protectedKeyTag, encryptedPrivateKey, encryptedPrivateKeyIV, - encryptedPrivateKeyTag + encryptedPrivateKeyTag, + authorization }: TCompleteAccountInviteDTO) => { const user = await userDAL.findUserByEmail(email); if (!user || (user && user.isAccepted)) { throw new Error("Failed to complete account for complete user"); } + validateSignUpAuthorization(authorization, user.id); + const [orgMembership] = await orgDAL.findMembership({ inviteEmail: email, status: OrgMembershipStatus.Invited @@ -259,9 +258,7 @@ export const authSignupServiceFactory = ({ tx ); const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))]; - await Promise.allSettled( - uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId)) - ); + await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId))); return { info: us, key: userEncKey }; }); diff --git a/backend/src/services/auth/auth-signup-type.ts b/backend/src/services/auth/auth-signup-type.ts index 69b779e8b..a37a1cd96 100644 --- a/backend/src/services/auth/auth-signup-type.ts +++ b/backend/src/services/auth/auth-signup-type.ts @@ -34,4 +34,5 @@ export type TCompleteAccountInviteDTO = { verifier: string; ip: string; userAgent: string; + authorization: string; }; diff --git a/backend/src/services/auth/auth-type.ts b/backend/src/services/auth/auth-type.ts index 183c6170e..bea0dbe10 100644 --- a/backend/src/services/auth/auth-type.ts +++ b/backend/src/services/auth/auth-type.ts @@ -39,6 +39,13 @@ export type AuthModeJwtTokenPayload = { userId: string; tokenVersionId: string; accessVersion: number; + organizationId?: string; +}; + +export type AuthModeMfaJwtTokenPayload = { + authTokenType: AuthTokenType.MFA_TOKEN; + userId: string; + organizationId?: string; }; export type AuthModeRefreshJwtTokenPayload = { @@ -46,11 +53,13 @@ export type AuthModeRefreshJwtTokenPayload = { userId: string; tokenVersionId: string; refreshVersion: number; + organizationId?: string; }; export type AuthModeProviderJwtTokenPayload = { authTokenType: AuthTokenType.PROVIDER_TOKEN; email: string; + organizationId?: string; }; export type AuthModeProviderSignUpTokenPayload = { diff --git a/backend/src/services/identity-access-token/identity-access-token-dal.ts b/backend/src/services/identity-access-token/identity-access-token-dal.ts index 5919136a0..42fb5bba5 100644 --- a/backend/src/services/identity-access-token/identity-access-token-dal.ts +++ b/backend/src/services/identity-access-token/identity-access-token-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TIdentityAccessTokens } from "@app/db/schemas"; +import { TableName, TIdentityAccessTokens } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols } from "@app/lib/knex"; @@ -14,11 +14,7 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => { try { const doc = await (tx || db)(TableName.IdentityAccessToken) .where(filter) - .join( - TableName.Identity, - `${TableName.Identity}.id`, - `${TableName.IdentityAccessToken}.identityId` - ) + .join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`) .leftJoin( TableName.IdentityUaClientSecret, `${TableName.IdentityAccessToken}.identityUAClientSecretId`, diff --git a/backend/src/services/identity-access-token/identity-access-token-service.ts b/backend/src/services/identity-access-token/identity-access-token-service.ts index 7509b9dff..32774ccbb 100644 --- a/backend/src/services/identity-access-token/identity-access-token-service.ts +++ b/backend/src/services/identity-access-token/identity-access-token-service.ts @@ -7,18 +7,13 @@ import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip"; import { AuthTokenType } from "../auth/auth-type"; import { TIdentityAccessTokenDALFactory } from "./identity-access-token-dal"; -import { - TIdentityAccessTokenJwtPayload, - TRenewAccessTokenDTO -} from "./identity-access-token-types"; +import { TIdentityAccessTokenJwtPayload, TRenewAccessTokenDTO } from "./identity-access-token-types"; type TIdentityAccessTokenServiceFactoryDep = { identityAccessTokenDAL: TIdentityAccessTokenDALFactory; }; -export type TIdentityAccessTokenServiceFactory = ReturnType< - typeof identityAccessTokenServiceFactory ->; +export type TIdentityAccessTokenServiceFactory = ReturnType; export const identityAccessTokenServiceFactory = ({ identityAccessTokenDAL @@ -40,12 +35,12 @@ export const identityAccessTokenServiceFactory = ({ } // ttl check - if (accessTokenTTL > 0) { + if (Number(accessTokenTTL) > 0) { const currentDate = new Date(); if (accessTokenLastRenewedAt) { // access token has been renewed const accessTokenRenewed = new Date(accessTokenLastRenewedAt); - const ttlInMilliseconds = accessTokenTTL * 1000; + const ttlInMilliseconds = Number(accessTokenTTL) * 1000; const expirationDate = new Date(accessTokenRenewed.getTime() + ttlInMilliseconds); if (currentDate > expirationDate) @@ -55,7 +50,7 @@ export const identityAccessTokenServiceFactory = ({ } else { // access token has never been renewed const accessTokenCreated = new Date(accessTokenCreatedAt); - const ttlInMilliseconds = accessTokenTTL * 1000; + const ttlInMilliseconds = Number(accessTokenTTL) * 1000; const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds); if (currentDate > expirationDate) @@ -66,9 +61,9 @@ export const identityAccessTokenServiceFactory = ({ } // max ttl checks - if (accessTokenMaxTTL > 0) { + if (Number(accessTokenMaxTTL) > 0) { const accessTokenCreated = new Date(accessTokenCreatedAt); - const ttlInMilliseconds = accessTokenMaxTTL * 1000; + const ttlInMilliseconds = Number(accessTokenMaxTTL) * 1000; const currentDate = new Date(); const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds); @@ -77,7 +72,7 @@ export const identityAccessTokenServiceFactory = ({ message: "Failed to renew MI access token due to Max TTL expiration" }); - const extendToDate = new Date(currentDate.getTime() + accessTokenTTL); + const extendToDate = new Date(currentDate.getTime() + Number(accessTokenTTL)); if (extendToDate > expirationDate) throw new UnauthorizedError({ message: "Failed to renew MI access token past its Max TTL expiration" @@ -88,9 +83,10 @@ export const identityAccessTokenServiceFactory = ({ const renewAccessToken = async ({ accessToken }: TRenewAccessTokenDTO) => { const appCfg = getConfig(); - const decodedToken = jwt.verify(accessToken, appCfg.AUTH_SECRET) as JwtPayload; - if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) - throw new UnauthorizedError(); + const decodedToken = jwt.verify(accessToken, appCfg.AUTH_SECRET) as JwtPayload & { + identityAccessTokenId: string; + }; + if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) throw new UnauthorizedError(); const identityAccessToken = await identityAccessTokenDAL.findOne({ [`${TableName.IdentityAccessToken}.id` as "id"]: decodedToken.identityAccessTokenId, @@ -100,20 +96,14 @@ export const identityAccessTokenServiceFactory = ({ validateAccessTokenExp(identityAccessToken); - const updatedIdentityAccessToken = await identityAccessTokenDAL.updateById( - identityAccessToken.id, - { - accessTokenLastRenewedAt: new Date() - } - ); + const updatedIdentityAccessToken = await identityAccessTokenDAL.updateById(identityAccessToken.id, { + accessTokenLastRenewedAt: new Date() + }); return { accessToken, identityAccessToken: updatedIdentityAccessToken }; }; - const fnValidateIdentityAccessToken = async ( - token: TIdentityAccessTokenJwtPayload, - ipAddress?: string - ) => { + const fnValidateIdentityAccessToken = async (token: TIdentityAccessTokenJwtPayload, ipAddress?: string) => { const identityAccessToken = await identityAccessTokenDAL.findOne({ [`${TableName.IdentityAccessToken}.id` as "id"]: token.identityAccessTokenId, isAccessTokenRevoked: false diff --git a/backend/src/services/identity-project/identity-project-dal.ts b/backend/src/services/identity-project/identity-project-dal.ts index fd906cf10..dbb864387 100644 --- a/backend/src/services/identity-project/identity-project-dal.ts +++ b/backend/src/services/identity-project/identity-project-dal.ts @@ -14,11 +14,7 @@ export const identityProjectDALFactory = (db: TDbClient) => { try { const docs = await (tx || db)(TableName.IdentityProjectMembership) .where(`${TableName.IdentityProjectMembership}.projectId`, projectId) - .join( - TableName.Identity, - `${TableName.IdentityProjectMembership}.identityId`, - `${TableName.Identity}.id` - ) + .join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`) .leftJoin( TableName.ProjectRoles, `${TableName.IdentityProjectMembership}.roleId`, diff --git a/backend/src/services/identity-project/identity-project-service.ts b/backend/src/services/identity-project/identity-project-service.ts index d8c42dde6..f9d21034f 100644 --- a/backend/src/services/identity-project/identity-project-service.ts +++ b/backend/src/services/identity-project/identity-project-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { ProjectMembershipRole, TProjectRoles } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors"; @@ -24,10 +21,7 @@ type TIdentityProjectServiceFactoryDep = { identityProjectDAL: TIdentityProjectDALFactory; projectDAL: Pick; identityOrgMembershipDAL: Pick; - permissionService: Pick< - TPermissionServiceFactory, - "getProjectPermission" | "getProjectPermissionByRole" - >; + permissionService: Pick; }; export type TIdentityProjectServiceFactory = ReturnType; @@ -42,14 +36,12 @@ export const identityProjectServiceFactory = ({ identityId, actor, actorId, + actorOrgId, projectId, role }: TCreateProjectIdentityDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Identity - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Identity); const existingIdentity = await identityProjectDAL.findOne({ identityId, projectId }); if (existingIdentity) @@ -67,8 +59,10 @@ export const identityProjectServiceFactory = ({ message: `Failed to find identity with id ${identityId}` }); - const { permission: rolePermission, role: customRole } = - await permissionService.getProjectPermissionByRole(role, project.id); + const { permission: rolePermission, role: customRole } = await permissionService.getProjectPermissionByRole( + role, + project.id + ); const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission); if (!hasPriviledge) throw new ForbiddenRequestError({ @@ -90,13 +84,11 @@ export const identityProjectServiceFactory = ({ identityId, role, actor, - actorId + actorId, + actorOrgId }: TUpdateProjectIdentityDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Identity - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); const projectIdentity = await identityProjectDAL.findOne({ identityId, projectId }); if (!projectIdentity) @@ -107,7 +99,8 @@ export const identityProjectServiceFactory = ({ const { permission: identityRolePermission } = await permissionService.getProjectPermission( ActorType.IDENTITY, projectIdentity.identityId, - projectIdentity.projectId + projectIdentity.projectId, + actorOrgId ); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission); if (!hasRequiredPriviledges) @@ -115,8 +108,10 @@ export const identityProjectServiceFactory = ({ let customRole: TProjectRoles | undefined; if (role) { - const { permission: rolePermission, role: customOrgRole } = - await permissionService.getProjectPermissionByRole(role, projectIdentity.projectId); + const { permission: rolePermission, role: customOrgRole } = await permissionService.getProjectPermissionByRole( + role, + projectIdentity.projectId + ); const isCustomRole = Boolean(customOrgRole); const hasRequiredNewRolePermission = isAtLeastAsPrivileged(permission, rolePermission); @@ -139,6 +134,7 @@ export const identityProjectServiceFactory = ({ identityId, actorId, actor, + actorOrgId, projectId }: TDeleteProjectIdentityDTO) => { const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId }); @@ -148,16 +144,15 @@ export const identityProjectServiceFactory = ({ const { permission } = await permissionService.getProjectPermission( actor, actorId, - identityProjectMembership.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Identity + identityProjectMembership.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Identity); const { permission: identityRolePermission } = await permissionService.getProjectPermission( ActorType.IDENTITY, identityId, - identityProjectMembership.projectId + identityProjectMembership.projectId, + actorOrgId ); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission); if (!hasRequiredPriviledges) @@ -167,12 +162,9 @@ export const identityProjectServiceFactory = ({ return deletedIdentity; }; - const listProjectIdentities = async ({ projectId, actor, actorId }: TListProjectIdentityDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Identity - ); + const listProjectIdentities = async ({ projectId, actor, actorId, actorOrgId }: TListProjectIdentityDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Identity); const identityMemberhips = await identityProjectDAL.findByProjectId(projectId); return identityMemberhips; diff --git a/backend/src/services/identity-ua/identity-ua-service.ts b/backend/src/services/identity-ua/identity-ua-service.ts index 1eb528977..3b73da6d4 100644 --- a/backend/src/services/identity-ua/identity-ua-service.ts +++ b/backend/src/services/identity-ua/identity-ua-service.ts @@ -6,10 +6,7 @@ import jwt from "jsonwebtoken"; import { IdentityAuthMethod } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { getConfig } from "@app/lib/config/env"; @@ -71,11 +68,10 @@ export const identityUaServiceFactory = ({ ); if (!validClientSecretInfo) throw new UnauthorizedError(); - const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = - validClientSecretInfo; - if (clientSecretTTL > 0) { + const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo; + if (Number(clientSecretTTL) > 0) { const clientSecretCreated = new Date(validClientSecretInfo.createdAt); - const ttlInMilliseconds = clientSecretTTL * 1000; + const ttlInMilliseconds = Number(clientSecretTTL) * 1000; const currentDate = new Date(); const expirationTime = new Date(clientSecretCreated.getTime() + ttlInMilliseconds); @@ -97,16 +93,12 @@ export const identityUaServiceFactory = ({ isClientSecretRevoked: true }); throw new UnauthorizedError({ - message: - "Failed to authenticate identity credentials due to client secret number of uses limit reached" + message: "Failed to authenticate identity credentials due to client secret number of uses limit reached" }); } const identityAccessToken = await identityUaDAL.transaction(async (tx) => { - const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage( - validClientSecretInfo.id, - tx - ); + const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo.id, tx); const newToken = await identityAccessTokenDAL.create( { identityId: identityUa.identityId, @@ -133,11 +125,12 @@ export const identityUaServiceFactory = ({ appCfg.AUTH_SECRET, { expiresIn: - identityAccessToken.accessTokenMaxTTL === 0 + Number(identityAccessToken.accessTokenMaxTTL) === 0 ? undefined - : identityAccessToken.accessTokenMaxTTL + : Number(identityAccessToken.accessTokenMaxTTL) } ); + return { accessToken, identityUa, validClientSecretInfo, identityAccessToken }; }; @@ -149,7 +142,8 @@ export const identityUaServiceFactory = ({ accessTokenTrustedIps, clientSecretTrustedIps, actorId, - actor + actor, + actorOrgId }: TAttachUaDTO) => { const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" }); @@ -165,32 +159,28 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Identity + identityMembershipOrg.orgId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity); const plan = await licenseService.getPlan(identityMembershipOrg.orgId); - const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map( - (clientSecretTrustedIp) => { - if ( - !plan.ipAllowlisting && - clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && - clientSecretTrustedIp.ipAddress !== "::/0" - ) - throw new BadRequestError({ - message: - "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." - }); - if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress)) - throw new BadRequestError({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - return extractIPDetails(clientSecretTrustedIp.ipAddress); - } - ); + const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map((clientSecretTrustedIp) => { + if ( + !plan.ipAllowlisting && + clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && + clientSecretTrustedIp.ipAddress !== "::/0" + ) + throw new BadRequestError({ + message: + "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." + }); + if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress)) + throw new BadRequestError({ + message: "The IP is not a valid IPv4, IPv6, or CIDR block" + }); + return extractIPDetails(clientSecretTrustedIp.ipAddress); + }); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -241,7 +231,8 @@ export const identityUaServiceFactory = ({ accessTokenTrustedIps, clientSecretTrustedIps, actorId, - actor + actor, + actorOrgId }: TUpdateUaDTO) => { const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" }); @@ -254,8 +245,7 @@ export const identityUaServiceFactory = ({ if ( (accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) > 0 && - (accessTokenTTL || uaIdentityAuth.accessTokenMaxTTL) > - (accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) + (accessTokenTTL || uaIdentityAuth.accessTokenMaxTTL) > (accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) ) { throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); } @@ -263,32 +253,28 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Identity + identityMembershipOrg.orgId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity); const plan = await licenseService.getPlan(identityMembershipOrg.orgId); - const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map( - (clientSecretTrustedIp) => { - if ( - !plan.ipAllowlisting && - clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && - clientSecretTrustedIp.ipAddress !== "::/0" - ) - throw new BadRequestError({ - message: - "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." - }); - if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress)) - throw new BadRequestError({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - return extractIPDetails(clientSecretTrustedIp.ipAddress); - } - ); + const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map((clientSecretTrustedIp) => { + if ( + !plan.ipAllowlisting && + clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && + clientSecretTrustedIp.ipAddress !== "::/0" + ) + throw new BadRequestError({ + message: + "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." + }); + if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress)) + throw new BadRequestError({ + message: "The IP is not a valid IPv4, IPv6, or CIDR block" + }); + return extractIPDetails(clientSecretTrustedIp.ipAddress); + }); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -320,7 +306,7 @@ export const identityUaServiceFactory = ({ return { ...updatedUaAuth, orgId: identityMembershipOrg.orgId }; }; - const getIdentityUa = async ({ identityId, actorId, actor }: TGetUaDTO) => { + const getIdentityUa = async ({ identityId, actorId, actor, actorOrgId }: TGetUaDTO) => { const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" }); if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral) @@ -333,18 +319,17 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Identity + identityMembershipOrg.orgId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); return { ...uaIdentityAuth, orgId: identityMembershipOrg.orgId }; }; const createUaClientSecret = async ({ actor, actorId, + actorOrgId, identityId, ttl, description, @@ -359,17 +344,16 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Identity + identityMembershipOrg.orgId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity); const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, identityMembershipOrg.identityId, - identityMembershipOrg.orgId + identityMembershipOrg.orgId, + actorOrgId ); const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission); if (!hasPriviledge) @@ -402,7 +386,7 @@ export const identityUaServiceFactory = ({ }; }; - const getUaClientSecrets = async ({ actor, actorId, identityId }: TGetUaClientSecretsDTO) => { + const getUaClientSecrets = async ({ actor, actorId, actorOrgId, identityId }: TGetUaClientSecretsDTO) => { const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" }); if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral) @@ -412,17 +396,16 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Identity + identityMembershipOrg.orgId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, identityMembershipOrg.identityId, - identityMembershipOrg.orgId + identityMembershipOrg.orgId, + actorOrgId ); const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission); if (!hasPriviledge) @@ -445,6 +428,7 @@ export const identityUaServiceFactory = ({ identityId, actorId, actor, + actorOrgId, clientSecretId }: TRevokeUaClientSecretDTO) => { const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); @@ -456,17 +440,16 @@ export const identityUaServiceFactory = ({ const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Identity + identityMembershipOrg.orgId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity); const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, identityMembershipOrg.identityId, - identityMembershipOrg.orgId + identityMembershipOrg.orgId, + actorOrgId ); const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission); if (!hasPriviledge) diff --git a/backend/src/services/identity/identity-org-dal.ts b/backend/src/services/identity/identity-org-dal.ts index c5413d058..95d742f33 100644 --- a/backend/src/services/identity/identity-org-dal.ts +++ b/backend/src/services/identity/identity-org-dal.ts @@ -14,11 +14,7 @@ export const identityOrgDALFactory = (db: TDbClient) => { try { const [data] = await (tx || db)(TableName.IdentityOrgMembership) .where(filter) - .join( - TableName.Identity, - `${TableName.IdentityOrgMembership}.identityId`, - `${TableName.Identity}.id` - ) + .join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`) .select(selectAllTableCols(TableName.IdentityOrgMembership)) .select(db.ref("name").withSchema(TableName.Identity)) .select(db.ref("authMethod").withSchema(TableName.Identity)); @@ -35,16 +31,8 @@ export const identityOrgDALFactory = (db: TDbClient) => { try { const docs = await (tx || db)(TableName.IdentityOrgMembership) .where(`${TableName.IdentityOrgMembership}.orgId`, orgId) - .join( - TableName.Identity, - `${TableName.IdentityOrgMembership}.identityId`, - `${TableName.Identity}.id` - ) - .leftJoin( - TableName.OrgRoles, - `${TableName.IdentityOrgMembership}.roleId`, - `${TableName.OrgRoles}.id` - ) + .join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`) + .leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .select(selectAllTableCols(TableName.IdentityOrgMembership)) // cr stands for custom role .select(db.ref("id").as("crId").withSchema(TableName.OrgRoles)) diff --git a/backend/src/services/identity/identity-service.ts b/backend/src/services/identity/identity-service.ts index 09fb5fb36..e37a3a6dd 100644 --- a/backend/src/services/identity/identity-service.ts +++ b/backend/src/services/identity/identity-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { OrgMembershipRole, TOrgRoles } from "@app/db/schemas"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors"; @@ -28,19 +25,17 @@ export const identityServiceFactory = ({ identityOrgMembershipDAL, permissionService }: TIdentityServiceFactoryDep) => { - const createIdentity = async ({ name, role, actor, orgId, actorId }: TCreateIdentityDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Identity - ); + const createIdentity = async ({ name, role, actor, orgId, actorId, actorOrgId }: TCreateIdentityDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity); - const { permission: rolePermission, role: customRole } = - await permissionService.getOrgPermissionByRole(role, orgId); + const { permission: rolePermission, role: customRole } = await permissionService.getOrgPermissionByRole( + role, + orgId + ); const isCustomRole = Boolean(customRole); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, rolePermission); - if (!hasRequiredPriviledges) - throw new BadRequestError({ message: "Failed to create a more privileged identity" }); + if (!hasRequiredPriviledges) throw new BadRequestError({ message: "Failed to create a more privileged identity" }); const identity = await identityDAL.transaction(async (tx) => { const newIdentity = await identityDAL.create({ name }, tx); @@ -56,29 +51,26 @@ export const identityServiceFactory = ({ return newIdentity; }); - return identity; }; - const updateIdentity = async ({ id, role, name, actor, actorId }: TUpdateIdentityDTO) => { + const updateIdentity = async ({ id, role, name, actor, actorId, actorOrgId }: TUpdateIdentityDTO) => { const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id }); - if (!identityOrgMembership) - throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); + if (!identityOrgMembership) throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityOrgMembership.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Identity + identityOrgMembership.orgId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity); const { permission: identityRolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, id, - identityOrgMembership.orgId + identityOrgMembership.orgId, + actorOrgId ); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission); if (!hasRequiredPriviledges) @@ -86,8 +78,10 @@ export const identityServiceFactory = ({ let customRole: TOrgRoles | undefined; if (role) { - const { permission: rolePermission, role: customOrgRole } = - await permissionService.getOrgPermissionByRole(role, identityOrgMembership.orgId); + const { permission: rolePermission, role: customOrgRole } = await permissionService.getOrgPermissionByRole( + role, + identityOrgMembership.orgId + ); const isCustomRole = Boolean(customOrgRole); const hasRequiredNewRolePermission = isAtLeastAsPrivileged(permission, rolePermission); @@ -97,9 +91,7 @@ export const identityServiceFactory = ({ } const identity = await identityDAL.transaction(async (tx) => { - const newIdentity = name - ? await identityDAL.updateById(id, { name }, tx) - : await identityDAL.findById(id, tx); + const newIdentity = name ? await identityDAL.updateById(id, { name }, tx) : await identityDAL.findById(id, tx); if (role) { await identityOrgMembershipDAL.update( { identityId: id }, @@ -116,20 +108,17 @@ export const identityServiceFactory = ({ return { ...identity, orgId: identityOrgMembership.orgId }; }; - const deleteIdentity = async ({ actorId, actor, id }: TDeleteIdentityDTO) => { + const deleteIdentity = async ({ actorId, actor, actorOrgId, id }: TDeleteIdentityDTO) => { const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id }); - if (!identityOrgMembership) - throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); + if (!identityOrgMembership) throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); const { permission } = await permissionService.getOrgPermission( actor, actorId, - identityOrgMembership.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Identity + identityOrgMembership.orgId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity); const { permission: identityRolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, id, @@ -143,12 +132,9 @@ export const identityServiceFactory = ({ return { ...deletedIdentity, orgId: identityOrgMembership.orgId }; }; - const listOrgIdentities = async ({ orgId, actor, actorId }: TOrgPermission) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Identity - ); + const listOrgIdentities = async ({ orgId, actor, actorId, actorOrgId }: TOrgPermission) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); const identityMemberhips = await identityOrgMembershipDAL.findByOrgId(orgId); return identityMemberhips; diff --git a/backend/src/services/integration-auth/integration-app-list.ts b/backend/src/services/integration-auth/integration-app-list.ts index 39e629606..17b1b63ad 100644 --- a/backend/src/services/integration-auth/integration-app-list.ts +++ b/backend/src/services/integration-auth/integration-app-list.ts @@ -22,11 +22,7 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string }) interface GCPApp { projectNumber: string; projectId: string; - lifecycleState: - | "ACTIVE" - | "LIFECYCLE_STATE_UNSPECIFIED" - | "DELETE_REQUESTED" - | "DELETE_IN_PROGRESS"; + lifecycleState: "ACTIVE" | "LIFECYCLE_STATE_UNSPECIFIED" | "DELETE_REQUESTED" | "DELETE_IN_PROGRESS"; name: string; createTime: string; parent: { @@ -59,8 +55,8 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string }) ...(pageToken ? { pageToken } : {}) }); - const res: GCPGetProjectsRes = ( - await request.get(`${IntegrationUrls.GCP_API_URL}/v1/projects`, { + const res = ( + await request.get(`${IntegrationUrls.GCP_API_URL}/v1/projects`, { params, headers: { Authorization: `Bearer ${accessToken}`, @@ -81,8 +77,8 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string }) // eslint-disable-next-line for await (const gcpApp of gcpApps) { try { - const res: GCPGetServiceRes = ( - await request.get( + const res = ( + await request.get( `${IntegrationUrls.GCP_SERVICE_USAGE_URL}/v1/projects/${gcpApp.projectId}/services/${IntegrationUrls.GCP_SECRET_MANAGER_SERVICE_NAME}`, { headers: { @@ -113,7 +109,7 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string }) */ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.HEROKU_API_URL}/apps`, { + await request.get<{ name: string }[]>(`${IntegrationUrls.HEROKU_API_URL}/apps`, { headers: { Accept: "application/vnd.heroku+json; version=3", Authorization: `Bearer ${accessToken}` @@ -121,7 +117,7 @@ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { }) ).data; - const apps = res.map((a: any) => ({ + const apps = res.map((a) => ({ name: a.name })); @@ -131,15 +127,9 @@ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { /** * Return list of names of apps for Vercel integration */ -const getAppsVercel = async ({ - accessToken, - teamId -}: { - teamId?: string | null; - accessToken: string; -}) => { +const getAppsVercel = async ({ accessToken, teamId }: { teamId?: string | null; accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.VERCEL_API_URL}/v9/projects`, { + await request.get<{ projects: { name: string; id: string }[] }>(`${IntegrationUrls.VERCEL_API_URL}/v9/projects`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" @@ -154,7 +144,7 @@ const getAppsVercel = async ({ }) ).data; - const apps = res.projects.map((a: any) => ({ + const apps = res.projects.map((a) => ({ name: a.name, appId: a.id })); @@ -166,7 +156,7 @@ const getAppsVercel = async ({ * Return list of sites for Netlify integration */ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => { - const apps: any = []; + const apps: Array<{ name: string; appId: string }> = []; let page = 1; const perPage = 10; let hasMorePages = true; @@ -179,15 +169,18 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => { filter: "all" }); - const { data } = await request.get(`${IntegrationUrls.NETLIFY_API_URL}/api/v1/sites`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const { data } = await request.get<{ name: string; site_id: string }[]>( + `${IntegrationUrls.NETLIFY_API_URL}/api/v1/sites`, + { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); - data.forEach((a: any) => { + data.forEach((a) => { apps.push({ name: a.name, appId: a.site_id @@ -238,8 +231,8 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => { } ); - if (response.data.length > 0) { - repos = repos.concat(response.data); + if ((response.data as GitHubApp[]).length > 0) { + repos = repos.concat(response.data as GitHubApp[]); page += 1; } else { hasMore = false; @@ -267,7 +260,7 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => { */ const getAppsRender = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.RENDER_API_URL}/v1/services`, { + await request.get<{ service: { name: string; id: string } }[]>(`${IntegrationUrls.RENDER_API_URL}/v1/services`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json", @@ -276,7 +269,7 @@ const getAppsRender = async ({ accessToken }: { accessToken: string }) => { }) ).data; - const apps = res.map((a: any) => ({ + const apps = res.map((a) => ({ name: a.service.name, appId: a.service.id })); @@ -309,7 +302,9 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { projects: { edges } } } - } = await request.post( + } = await request.post<{ + data: { projects: { edges: { node: { name: string; id: string } }[] } }; + }>( IntegrationUrls.RAILWAY_API_URL, { query, @@ -324,7 +319,7 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { } ); - const apps = edges.map((e: any) => ({ + const apps = edges.map((e) => ({ name: e.node.name, appId: e.node.id })); @@ -335,24 +330,21 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { /** * Return list of sites for Laravel Forge integration */ -const getAppsLaravelForge = async ({ - accessToken, - serverId -}: { - accessToken: string; - serverId?: string; -}) => { +const getAppsLaravelForge = async ({ accessToken, serverId }: { accessToken: string; serverId?: string }) => { const res = ( - await request.get(`${IntegrationUrls.LARAVELFORGE_API_URL}/api/v1/servers/${serverId}/sites`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "Content-Type": "application/json" + await request.get<{ sites: { name: string; id: string }[] }>( + `${IntegrationUrls.LARAVELFORGE_API_URL}/api/v1/servers/${serverId}/sites`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json", + "Content-Type": "application/json" + } } - }) + ) ).data.sites; - const apps = res.map((a: any) => ({ + const apps = res.map((a) => ({ name: a.name, appId: a.id })); @@ -382,8 +374,8 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => { } `; - const res: FlyioApp[] = ( - await request.post( + const res = ( + await request.post<{ data: { apps: { nodes: FlyioApp[] } } }>( IntegrationUrls.FLYIO_API_URL, { query, @@ -401,7 +393,7 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => { ) ).data.data.apps.nodes; - const apps = res.map((a: FlyioApp) => ({ + const apps = res.map((a) => ({ name: a.name, appId: a.id })); @@ -414,7 +406,7 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => { */ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.CIRCLECI_API_URL}/v1.1/projects`, { + await request.get<{ reponame: string }[]>(`${IntegrationUrls.CIRCLECI_API_URL}/v1.1/projects`, { headers: { "Circle-Token": accessToken, "Accept-Encoding": "application/json" @@ -422,7 +414,7 @@ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { }) ).data; - const apps = res?.map((a: any) => ({ + const apps = res?.map((a) => ({ name: a?.reponame })); @@ -431,7 +423,7 @@ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.TRAVISCI_API_URL}/repos`, { + await request.get<{ id: string; slug: string }[]>(`${IntegrationUrls.TRAVISCI_API_URL}/repos`, { headers: { Authorization: `token ${accessToken}`, "Accept-Encoding": "application/json" @@ -439,7 +431,7 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => { }) ).data; - const apps = res?.map((a: any) => ({ + const apps = res?.map((a) => ({ name: a?.slug?.split("/")[1], appId: a?.id })); @@ -450,15 +442,9 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => { /** * Return list of projects for Terraform Cloud integration */ -const getAppsTerraformCloud = async ({ - accessToken, - workspacesId -}: { - accessToken: string; - workspacesId?: string; -}) => { +const getAppsTerraformCloud = async ({ accessToken, workspacesId }: { accessToken: string; workspacesId?: string }) => { const res = ( - await request.get( + await request.get<{ data: { attributes: { name: string }; id: string } }>( `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${workspacesId}`, { headers: { @@ -510,15 +496,18 @@ const getAppsGitlab = async ({ per_page: String(perPage) }); - const { data } = await request.get(`${gitLabApiUrl}/v4/groups/${teamId}/projects`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const { data } = await request.get<{ name: string; id: string }[]>( + `${gitLabApiUrl}/v4/groups/${teamId}/projects`, + { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); - data.forEach((a: any) => { + data.forEach((a) => { apps.push({ name: a.name, appId: a.id @@ -535,7 +524,7 @@ const getAppsGitlab = async ({ // case: fetch projects for individual in GitLab const { id } = ( - await request.get(`${gitLabApiUrl}/v4/user`, { + await request.get<{ id: string }>(`${gitLabApiUrl}/v4/user`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" @@ -549,7 +538,7 @@ const getAppsGitlab = async ({ per_page: String(perPage) }); - const { data } = await request.get(`${gitLabApiUrl}/v4/users/${id}/projects`, { + const { data } = await request.get<{ name: string; id: string }[]>(`${gitLabApiUrl}/v4/users/${id}/projects`, { params, headers: { Authorization: `Bearer ${accessToken}`, @@ -557,7 +546,7 @@ const getAppsGitlab = async ({ } }); - data.forEach((a: any) => { + data.forEach((a) => { apps.push({ name: a.name, appId: a.id @@ -580,7 +569,7 @@ const getAppsGitlab = async ({ */ const getAppsTeamCity = async ({ accessToken, url }: { url: string; accessToken: string }) => { const res = ( - await request.get(`${url}/app/rest/projects`, { + await request.get<{ project: { name: string; id: string }[] }>(`${url}/app/rest/projects`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json" @@ -588,7 +577,7 @@ const getAppsTeamCity = async ({ accessToken, url }: { url: string; accessToken: }) ).data.project.slice(1); - const apps = res.map((a: any) => ({ + const apps = res.map((a) => ({ name: a.name, appId: a.id })); @@ -600,14 +589,17 @@ const getAppsTeamCity = async ({ accessToken, url }: { url: string; accessToken: * Return list of projects for Supabase integration */ const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => { - const { data } = await request.get(`${IntegrationUrls.SUPABASE_API_URL}/v1/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const { data } = await request.get<{ name: string; id: string }[]>( + `${IntegrationUrls.SUPABASE_API_URL}/v1/projects`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); - const apps = data.map((a: any) => ({ + const apps = data.map((a) => ({ name: a.name, appId: a.id })); @@ -619,14 +611,14 @@ const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => { * Return list of accounts for the Checkly integration */ const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => { - const { data } = await request.get(`${IntegrationUrls.CHECKLY_API_URL}/v1/accounts`, { + const { data } = await request.get<{ name: string; id: string }[]>(`${IntegrationUrls.CHECKLY_API_URL}/v1/accounts`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json" } }); - const apps = data.map((a: any) => ({ + const apps = data.map((a) => ({ name: a.name, appId: a.id })); @@ -637,14 +629,8 @@ const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => { /** * Return list of projects for the Cloudflare Pages integration */ -const getAppsCloudflarePages = async ({ - accessToken, - accountId -}: { - accessToken: string; - accountId?: string; -}) => { - const { data } = await request.get( +const getAppsCloudflarePages = async ({ accessToken, accountId }: { accessToken: string; accountId?: string }) => { + const { data } = await request.get<{ result: { name: string; id: string }[] }>( `${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accountId}/pages/projects`, { headers: { @@ -654,7 +640,7 @@ const getAppsCloudflarePages = async ({ } ); - const apps = data.result.map((a: any) => ({ + const apps = data.result.map((a) => ({ name: a.name, appId: a.id })); @@ -664,14 +650,8 @@ const getAppsCloudflarePages = async ({ /** * Return list of projects for the Cloudflare Workers integration */ -const getAppsCloudflareWorkers = async ({ - accessToken, - accountId -}: { - accessToken: string; - accountId?: string; -}) => { - const { data } = await request.get( +const getAppsCloudflareWorkers = async ({ accessToken, accountId }: { accessToken: string; accountId?: string }) => { + const { data } = await request.get<{ result: { id: string }[] }>( `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accountId}/workers/services`, { headers: { @@ -681,7 +661,7 @@ const getAppsCloudflareWorkers = async ({ } ); - const apps = data.result.map((a: any) => ({ + const apps = data.result.map((a) => ({ name: a.id, appId: a.id })); @@ -691,13 +671,7 @@ const getAppsCloudflareWorkers = async ({ /** * Return list of repositories for the BitBucket integration based on provided BitBucket workspace */ -const getAppsBitBucket = async ({ - accessToken, - workspaceSlug -}: { - accessToken: string; - workspaceSlug?: string; -}) => { +const getAppsBitBucket = async ({ accessToken, workspaceSlug }: { accessToken: string; workspaceSlug?: string }) => { interface RepositoriesResponse { size: number; page: number; @@ -759,14 +733,17 @@ const getAppsNorthflank = async ({ accessToken }: { accessToken: string }) => { data: { data: { projects } } - } = await request.get(`${IntegrationUrls.NORTHFLANK_API_URL}/v1/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + } = await request.get<{ data: { projects: { name: string; id: string }[] } }>( + `${IntegrationUrls.NORTHFLANK_API_URL}/v1/projects`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); - const apps = projects.map((a: any) => ({ + const apps = projects.map((a) => ({ name: a.name, appId: a.id })); @@ -779,15 +756,18 @@ const getAppsNorthflank = async ({ accessToken }: { accessToken: string }) => { */ const getAppsCodefresh = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.CODEFRESH_API_URL}/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + await request.get<{ projects: { projectName: string; id: string }[] }>( + `${IntegrationUrls.CODEFRESH_API_URL}/projects`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }) + ) ).data; - const apps = res.projects.map((a: any) => ({ + const apps = res.projects.map((a) => ({ name: a.projectName, appId: a.id })); @@ -799,20 +779,23 @@ const getAppsCodefresh = async ({ accessToken }: { accessToken: string }) => { * Return list of projects for Windmill integration */ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => { - const { data } = await request.get(`${IntegrationUrls.WINDMILL_API_URL}/workspaces/list`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const { data } = await request.get<{ id: string; name: string }[]>( + `${IntegrationUrls.WINDMILL_API_URL}/workspaces/list`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); // check for write access of secrets in windmill workspaces - const writeAccessCheck = data.map(async (app: any) => { + const writeAccessCheck = data.map(async (app) => { try { const userPath = "u/user/variable"; const folderPath = "f/folder/variable"; - const { data: writeUser } = await request.post( + const { data: writeUser } = await request.post( `${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`, { path: userPath, @@ -828,7 +811,7 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => { } ); - const { data: writeFolder } = await request.post( + const { data: writeFolder } = await request.post( `${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`, { path: folderPath, @@ -846,38 +829,32 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => { // is write access is allowed then delete the created secrets from workspace if (writeUser && writeFolder) { - await request.delete( - `${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${userPath}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${userPath}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); - await request.delete( - `${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${folderPath}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${folderPath}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); return app; } return { error: "cannot write secret" }; - } catch (err: any) { - return { error: err.message }; + } catch (err) { + return { error: (err as Error).message }; } }); const appsWriteResponses = await Promise.all(writeAccessCheck); - const appsWithWriteAccess = appsWriteResponses.filter((appRes: any) => !appRes.error); + const appsWithWriteAccess = appsWriteResponses.filter((appRes) => !(appRes as { error: string })?.error); - const apps = appsWithWriteAccess.map((a: any) => ({ + const apps = (appsWithWriteAccess as { id: string; name: string }[]).map((a) => ({ name: a.name, appId: a.id })); @@ -908,7 +885,7 @@ const getAppsDigitalOceanAppPlatform = async ({ accessToken }: { accessToken: st } const res = ( - await request.get(`${IntegrationUrls.DIGITAL_OCEAN_API_URL}/v2/apps`, { + await request.get<{ apps: DigitalOceanApp[] }>(`${IntegrationUrls.DIGITAL_OCEAN_API_URL}/v2/apps`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" @@ -916,14 +893,16 @@ const getAppsDigitalOceanAppPlatform = async ({ accessToken }: { accessToken: st }) ).data; - return (res.apps ?? []).map((a: DigitalOceanApp) => ({ + return (res.apps ?? []).map((a) => ({ name: a.spec.name, appId: a.id })); }; const getAppsHasuraCloud = async ({ accessToken }: { accessToken: string }) => { - const res = await request.post( + const res = await request.post<{ + data: { projects: { name: string; tenant: { id: string } }[] }; + }>( IntegrationUrls.HASURA_CLOUD_API_URL, { query: "query MyQuery { projects { name tenant { id } } }" @@ -936,19 +915,15 @@ const getAppsHasuraCloud = async ({ accessToken }: { accessToken: string }) => { } ); - const data = (res?.data?.data?.projects ?? []).map( - ({ name, tenant: { id: appId } }: { name: string; tenant: { id: string } }) => ({ name, appId }) - ); + const data = (res?.data?.data?.projects ?? []).map(({ name, tenant: { id: appId } }) => ({ + name, + appId + })); return data; }; /** * Return list of applications for Cloud66 integration - * @param {Object} obj - * @param {String} obj.accessToken - personal access token for Cloud66 API - * @returns {Object[]} apps - Cloud66 apps - * @returns {String} apps.name - name of Cloud66 app - * @returns {String} apps.appId - uid of Cloud66 app */ const getAppsCloud66 = async ({ accessToken }: { accessToken: string }) => { interface Cloud66Apps { @@ -979,19 +954,19 @@ const getAppsCloud66 = async ({ accessToken }: { accessToken: string }) => { account_name: string; is_cluster: boolean; is_inside_cluster: boolean; - cluster_name: any; + cluster_name: string; application_address: string; configstore_namespace: string; } const stacks = ( - await request.get(`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks`, { + await request.get<{ response: Cloud66Apps[] }>(`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" } }) - ).data.response as Cloud66Apps[]; + ).data.response; const apps = stacks.map((app) => ({ name: app.name, @@ -1016,7 +991,7 @@ export const getApps = async ({ workspaceSlug?: string; url?: string | null; }): Promise => { - switch (integration) { + switch (integration as Integrations) { case Integrations.GCP_SECRET_MANAGER: return getAppsGCPSecretManager({ accessToken diff --git a/backend/src/services/integration-auth/integration-auth-service.ts b/backend/src/services/integration-auth/integration-auth-service.ts index 81b180ea4..0b3f9b4c3 100644 --- a/backend/src/services/integration-auth/integration-auth-service.ts +++ b/backend/src/services/integration-auth/integration-auth-service.ts @@ -1,21 +1,10 @@ import { ForbiddenError } from "@casl/ability"; -import { - SecretEncryptionAlgo, - SecretKeyEncoding, - TIntegrationAuths, - TIntegrationAuthsInsert -} from "@app/db/schemas"; +import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { request } from "@app/lib/config/request"; -import { - decryptSymmetric128BitHexKeyUTF8, - encryptSymmetric128BitHexKeyUTF8 -} from "@app/lib/crypto"; +import { decryptSymmetric128BitHexKeyUTF8, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { BadRequestError } from "@app/lib/errors"; import { TProjectPermission } from "@app/lib/types"; @@ -70,33 +59,24 @@ export const integrationAuthServiceFactory = ({ projectBotDAL, projectBotService }: TIntegrationAuthServiceFactoryDep) => { - const listIntegrationAuthByProjectId = async ({ - actorId, - actor, - projectId - }: TProjectPermission) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const listIntegrationAuthByProjectId = async ({ actorId, actor, actorOrgId, projectId }: TProjectPermission) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const authorizations = await integrationAuthDAL.find({ projectId }); return authorizations; }; - const getIntegrationAuth = async ({ actor, id, actorId }: TGetIntegrationAuthDTO) => { + const getIntegrationAuth = async ({ actor, id, actorId, actorOrgId }: TGetIntegrationAuthDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); return integrationAuth; }; @@ -104,6 +84,7 @@ export const integrationAuthServiceFactory = ({ projectId, actorId, actor, + actorOrgId, integration, url, code @@ -111,15 +92,11 @@ export const integrationAuthServiceFactory = ({ if (!Object.values(Integrations).includes(integration as Integrations)) throw new BadRequestError({ message: "Invalid integration" }); - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); const bot = await projectBotDAL.findOne({ isActive: true, projectId }); - if (!bot) - throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" }); + if (!bot) throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" }); const tokenExchange = await exchangeCode({ integration, code, url }); const updateDoc: TIntegrationAuthsInsert = { @@ -170,6 +147,7 @@ export const integrationAuthServiceFactory = ({ integration, url, actor, + actorOrgId, accessId, namespace, accessToken @@ -177,15 +155,11 @@ export const integrationAuthServiceFactory = ({ if (!Object.values(Integrations).includes(integration as Integrations)) throw new BadRequestError({ message: "Invalid integration" }); - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); const bot = await projectBotDAL.findOne({ isActive: true, projectId }); - if (!bot) - throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" }); + if (!bot) throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" }); const updateDoc: TIntegrationAuthsInsert = { projectId, @@ -251,11 +225,7 @@ export const integrationAuthServiceFactory = ({ }); } - if ( - integrationAuth.refreshCiphertext && - integrationAuth.refreshIV && - integrationAuth.refreshTag - ) { + if (integrationAuth.refreshCiphertext && integrationAuth.refreshIV && integrationAuth.refreshTag) { const refreshToken = decryptSymmetric128BitHexKeyUTF8({ key: botKey, ciphertext: integrationAuth.refreshCiphertext, @@ -287,11 +257,7 @@ export const integrationAuthServiceFactory = ({ } if (!accessToken) throw new BadRequestError({ message: "Missing access token" }); - if ( - integrationAuth.accessIdTag && - integrationAuth.accessIdIV && - integrationAuth.accessIdCiphertext - ) { + if (integrationAuth.accessIdTag && integrationAuth.accessIdIV && integrationAuth.accessIdCiphertext) { accessId = decryptSymmetric128BitHexKeyUTF8({ key: botKey, ciphertext: integrationAuth.accessIdCiphertext, @@ -305,6 +271,7 @@ export const integrationAuthServiceFactory = ({ const getIntegrationApps = async ({ actor, actorId, + actorOrgId, teamId, id, workspaceSlug @@ -315,12 +282,10 @@ export const integrationAuthServiceFactory = ({ const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken, accessId } = await getIntegrationAccessToken(integrationAuth, botKey); @@ -335,19 +300,17 @@ export const integrationAuthServiceFactory = ({ return apps; }; - const getIntegrationAuthTeams = async ({ actor, actorId, id }: TIntegrationAuthTeamsDTO) => { + const getIntegrationAuthTeams = async ({ actor, actorId, actorOrgId, id }: TIntegrationAuthTeamsDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); @@ -359,24 +322,17 @@ export const integrationAuthServiceFactory = ({ return teams; }; - const getVercelBranches = async ({ - appId, - id, - actor, - actorId - }: TIntegrationAuthVercelBranchesDTO) => { + const getVercelBranches = async ({ appId, id, actor, actorId, actorOrgId }: TIntegrationAuthVercelBranchesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); @@ -399,55 +355,43 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getChecklyGroups = async ({ - actorId, - actor, - id, - accountId - }: TIntegrationAuthChecklyGroupsDTO) => { + const getChecklyGroups = async ({ actorId, actor, actorOrgId, id, accountId }: TIntegrationAuthChecklyGroupsDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (accountId) { - const { data } = await request.get( - `${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "X-Checkly-Account": accountId - } + const { data } = await request.get(`${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups`, { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json", + "X-Checkly-Account": accountId } - ); + }); return data.map(({ name, id: groupId }) => ({ name, groupId })); } return []; }; - const getQoveryOrgs = async ({ actorId, actor, id }: TIntegrationAuthQoveryOrgsDTO) => { + const getQoveryOrgs = async ({ actorId, actor, actorOrgId, id }: TIntegrationAuthQoveryOrgsDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { data } = await request.get<{ results: Array<{ id: string; name: string }> }>( @@ -463,24 +407,17 @@ export const integrationAuthServiceFactory = ({ return data.results.map(({ name, id: orgId }) => ({ name, orgId })); }; - const getQoveryProjects = async ({ - actorId, - actor, - id, - orgId - }: TIntegrationAuthQoveryProjectDTO) => { + const getQoveryProjects = async ({ actorId, actor, actorOrgId, id, orgId }: TIntegrationAuthQoveryProjectDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (orgId) { @@ -502,7 +439,8 @@ export const integrationAuthServiceFactory = ({ projectId, id, actor, - actorId + actorId, + actorOrgId }: TIntegrationAuthQoveryEnvironmentsDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); @@ -510,12 +448,10 @@ export const integrationAuthServiceFactory = ({ const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (projectId && projectId !== "none") { @@ -538,24 +474,17 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getQoveryApps = async ({ - id, - actor, - actorId, - environmentId - }: TIntegrationAuthQoveryScopesDTO) => { + const getQoveryApps = async ({ id, actor, actorId, actorOrgId, environmentId }: TIntegrationAuthQoveryScopesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (environmentId) { @@ -581,6 +510,7 @@ export const integrationAuthServiceFactory = ({ id, actor, actorId, + actorOrgId, environmentId }: TIntegrationAuthQoveryScopesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); @@ -589,12 +519,10 @@ export const integrationAuthServiceFactory = ({ const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (environmentId) { @@ -616,24 +544,17 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getQoveryJobs = async ({ - id, - actor, - actorId, - environmentId - }: TIntegrationAuthQoveryScopesDTO) => { + const getQoveryJobs = async ({ id, actor, actorId, actorOrgId, environmentId }: TIntegrationAuthQoveryScopesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (environmentId) { @@ -655,24 +576,17 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getRailwayEnvironments = async ({ - id, - actor, - actorId, - appId - }: TIntegrationAuthRailwayEnvDTO) => { + const getRailwayEnvironments = async ({ id, actor, actorId, actorOrgId, appId }: TIntegrationAuthRailwayEnvDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (appId) { @@ -721,24 +635,18 @@ export const integrationAuthServiceFactory = ({ } return []; }; - const getRailwayServices = async ({ - id, - actor, - actorId, - appId - }: TIntegrationAuthRailwayServicesDTO) => { + + const getRailwayServices = async ({ id, actor, actorId, actorOrgId, appId }: TIntegrationAuthRailwayServicesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (appId) { @@ -806,23 +714,17 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getBitbucketWorkspaces = async ({ - actorId, - actor, - id - }: TIntegrationAuthBitbucketWorkspaceDTO) => { + const getBitbucketWorkspaces = async ({ actorId, actor, actorOrgId, id }: TIntegrationAuthBitbucketWorkspaceDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const workspaces: TBitbucketWorkspace[] = []; @@ -834,12 +736,11 @@ export const integrationAuthServiceFactory = ({ const { data }: { data: { values: TBitbucketWorkspace[]; next: string } } = await request.get( workspaceUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); if (data?.values.length > 0) { data.values.forEach((workspace) => { @@ -860,6 +761,7 @@ export const integrationAuthServiceFactory = ({ id, actor, actorId, + actorOrgId, appId }: TIntegrationAuthNorthflankSecretGroupDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); @@ -868,12 +770,10 @@ export const integrationAuthServiceFactory = ({ const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const secretGroups: { name: string; groupId: string }[] = []; @@ -906,7 +806,7 @@ export const integrationAuthServiceFactory = ({ } ); - secrets.forEach((a: any) => { + secrets.forEach((a) => { secretGroups.push({ name: a.name, groupId: a.id @@ -927,6 +827,7 @@ export const integrationAuthServiceFactory = ({ appId, id, actorId, + actorOrgId, actor }: TGetIntegrationAuthTeamCityBuildConfigDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); @@ -935,29 +836,24 @@ export const integrationAuthServiceFactory = ({ const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (appId) { const { data: { buildType } - } = await request.get<{ buildType: TTeamCityBuildConfig[] }>( - `${integrationAuth.url}/app/rest/buildTypes`, - { - params: { - locator: `project:${appId}` - }, - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } + } = await request.get<{ buildType: TTeamCityBuildConfig[] }>(`${integrationAuth.url}/app/rest/buildTypes`, { + params: { + locator: `project:${appId}` + }, + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" } - ); + }); return buildType.map(({ name, id: buildConfigId }) => ({ name, @@ -971,35 +867,27 @@ export const integrationAuthServiceFactory = ({ projectId, integration, actor, - actorId + actorId, + actorOrgId }: TDeleteIntegrationAuthsDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); const integrations = await integrationAuthDAL.delete({ integration, projectId }); return integrations; }; - const deleteIntegrationAuthById = async ({ - id, - actorId, - actor - }: TDeleteIntegrationAuthByIdDTO) => { + const deleteIntegrationAuthById = async ({ id, actorId, actor, actorOrgId }: TDeleteIntegrationAuthByIdDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Integrations + integrationAuth.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); const delIntegrationAuth = await integrationAuthDAL.transaction(async (tx) => { const doc = await integrationAuthDAL.deleteById(integrationAuth.id, tx); diff --git a/backend/src/services/integration-auth/integration-list.ts b/backend/src/services/integration-auth/integration-list.ts index 334a60e1d..d3cabbcb5 100644 --- a/backend/src/services/integration-auth/integration-list.ts +++ b/backend/src/services/integration-auth/integration-list.ts @@ -67,6 +67,7 @@ export enum IntegrationUrls { QOVERY_API_URL = "https://api.qovery.com", TERRAFORM_CLOUD_API_URL = "https://app.terraform.io", CLOUDFLARE_PAGES_API_URL = "https://api.cloudflare.com", + // eslint-disable-next-line CLOUDFLARE_WORKERS_API_URL = "https://api.cloudflare.com", BITBUCKET_API_URL = "https://api.bitbucket.org", CODEFRESH_API_URL = "https://g.codefresh.io/api", diff --git a/backend/src/services/integration-auth/integration-sync-secret.ts b/backend/src/services/integration-auth/integration-sync-secret.ts index 11ff5c5c2..abb6a7b1e 100644 --- a/backend/src/services/integration-auth/integration-sync-secret.ts +++ b/backend/src/services/integration-auth/integration-sync-secret.ts @@ -1,4 +1,11 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable no-param-reassign,no-await-in-loop */ +// Taken from old code and too much work at present thus disabling the above any rules +// resolve it later: akhilmhdh - TODO + import { CreateSecretCommand, GetSecretValueCommand, @@ -8,6 +15,7 @@ import { } from "@aws-sdk/client-secrets-manager"; import { Octokit } from "@octokit/rest"; import AWS from "aws-sdk"; +import { AxiosError } from "axios"; import sodium from "libsodium-wrappers"; import isEqual from "lodash.isequal"; import { z } from "zod"; @@ -18,9 +26,7 @@ import { BadRequestError } from "@app/lib/errors"; import { Integrations, IntegrationUrls } from "./integration-list"; -const getSecretKeyValuePair = ( - secrets: Record -) => +const getSecretKeyValuePair = (secrets: Record) => Object.keys(secrets).reduce>((prev, key) => { // eslint-disable-next-line prev[key] = secrets?.[key] === null ? null : secrets?.[key]?.value; @@ -87,8 +93,8 @@ const syncSecretsGCPSecretManager = async ({ ...(pageToken ? { pageToken } : {}) }); - const res: GCPSMListSecretsRes = ( - await request.get( + const res = ( + await request.get( `${IntegrationUrls.GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets${filterParam}`, { params, @@ -281,9 +287,7 @@ const syncSecretsAzureKeyVault = async ({ return result; }; - const getAzureKeyVaultSecrets = await paginateAzureKeyVaultSecrets( - `${integration.app}/secrets?api-version=7.3` - ); + const getAzureKeyVaultSecrets = await paginateAzureKeyVaultSecrets(`${integration.app}/secrets?api-version=7.3`); let lastSlashIndex: number; const res = ( @@ -293,14 +297,11 @@ const syncSecretsAzureKeyVault = async ({ lastSlashIndex = getAzureKeyVaultSecret.id.lastIndexOf("/"); } - const azureKeyVaultSecret = await request.get( - `${getAzureKeyVaultSecret.id}?api-version=7.3`, - { - headers: { - Authorization: `Bearer ${accessToken}` - } + const azureKeyVaultSecret = await request.get(`${getAzureKeyVaultSecret.id}?api-version=7.3`, { + headers: { + Authorization: `Bearer ${accessToken}` } - ); + }); return { ...azureKeyVaultSecret.data, @@ -309,7 +310,7 @@ const syncSecretsAzureKeyVault = async ({ }) ) ).reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret }), @@ -378,8 +379,9 @@ const syncSecretsAzureKeyVault = async ({ isSecretSet = true; } catch (err) { - const error: any = err; - if (error?.response?.data?.error?.innererror?.code === "ObjectIsDeletedButRecoverable") { + const error = err as AxiosError; + // eslint-disable-next-line + if ((error?.response?.data as any)?.error?.innererror?.code === "ObjectIsDeletedButRecoverable") { await request.post( `${azIntegration.app}/deletedsecrets/${key}/recover?api-version=7.3`, {}, @@ -405,7 +407,7 @@ const syncSecretsAzureKeyVault = async ({ // Sync/push set secrets for await (const setSecret of setSecrets) { const { key, value } = setSecret; - setSecretAzureKeyVault({ + await setSecretAzureKeyVault({ key, value, integration, @@ -425,11 +427,6 @@ const syncSecretsAzureKeyVault = async ({ /** * Sync/push [secrets] to AWS parameter store - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessId - access id for AWS parameter store integration - * @param {String} obj.accessToken - access token for AWS parameter store integration */ const syncSecretsAWSParameterStore = async ({ integration, @@ -463,60 +460,60 @@ const syncSecretsAWSParameterStore = async ({ const parameterList = (await ssm.getParametersByPath(params).promise()).Parameters; - let awsParameterStoreSecretsObj: { - [key: string]: any; - } = {}; - - if (parameterList) { - awsParameterStoreSecretsObj = parameterList.reduce( - (obj: any, secret: any) => ({ + const awsParameterStoreSecretsObj = (parameterList || []) + .filter(({ Name }) => Boolean(Name)) + .reduce( + (obj, secret) => ({ ...obj, - [secret.Name.substring((integration.path as string).length)]: secret + [(secret.Name as string).substring((integration.path as string).length)]: secret }), - {} + {} as Record ); - } // Identify secrets to create - Object.keys(secrets).map(async (key) => { - if (!(key in awsParameterStoreSecretsObj)) { - // case: secret does not exist in AWS parameter store - // -> create secret - await ssm - .putParameter({ - Name: `${integration.path}${key}`, - Type: "SecureString", - Value: secrets[key].value, - Overwrite: true - }) - .promise(); - // case: secret exists in AWS parameter store - } else if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) { - // case: secret value doesn't match one in AWS parameter store - // -> update secret - await ssm - .putParameter({ - Name: `${integration.path}${key}`, - Type: "SecureString", - Value: secrets[key].value, - Overwrite: true - }) - .promise(); - } - }); + await Promise.all( + Object.keys(secrets).map(async (key) => { + if (!(key in awsParameterStoreSecretsObj)) { + // case: secret does not exist in AWS parameter store + // -> create secret + await ssm + .putParameter({ + Name: `${integration.path}${key}`, + Type: "SecureString", + Value: secrets[key].value, + Overwrite: true + }) + .promise(); + // case: secret exists in AWS parameter store + } else if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) { + // case: secret value doesn't match one in AWS parameter store + // -> update secret + await ssm + .putParameter({ + Name: `${integration.path}${key}`, + Type: "SecureString", + Value: secrets[key].value, + Overwrite: true + }) + .promise(); + } + }) + ); // Identify secrets to delete - Object.keys(awsParameterStoreSecretsObj).map(async (key) => { - if (!(key in secrets)) { - // case: - // -> delete secret - await ssm - .deleteParameter({ - Name: awsParameterStoreSecretsObj[key].Name - }) - .promise(); - } - }); + await Promise.all( + Object.keys(awsParameterStoreSecretsObj).map(async (key) => { + if (!(key in secrets)) { + // case: + // -> delete secret + await ssm + .deleteParameter({ + Name: awsParameterStoreSecretsObj[key].Name as string + }) + .promise(); + } + }) + ); AWS.config.update({ region: undefined, @@ -564,7 +561,7 @@ const syncSecretsAWSSecretManager = async ({ }) ); - let awsSecretManagerSecretObj: { [key: string]: any } = {}; + let awsSecretManagerSecretObj: { [key: string]: AWS.SecretsManager } = {}; if (awsSecretManagerSecret?.SecretString) { awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString); @@ -680,25 +677,24 @@ const syncSecretsVercel = async ({ : {}) }; - const vercelSecrets: VercelSecret[] = ( - await request.get(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const vercelSecrets = ( + await request.get<{ envs: VercelSecret[] }>( + `${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env`, + { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }) - ).data.envs.filter((secret: VercelSecret) => { + ) + ).data.envs.filter((secret) => { if (!secret.target.includes(integration.targetEnvironment as string)) { // case: secret does not have the same target environment return false; } - if ( - integration.targetEnvironment === "preview" && - secret.gitBranch && - integration.path !== secret.gitBranch - ) { + if (integration.targetEnvironment === "preview" && secret.gitBranch && integration.path !== secret.gitBranch) { // case: secret on preview environment does not have same target git branch return false; } @@ -712,16 +708,13 @@ const syncSecretsVercel = async ({ if (vercelSecret.type === "encrypted") { // case: secret is encrypted -> need to decrypt const decryptedSecret = ( - await request.get( - `${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.get(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`, { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ) + }) ).data; res[vercelSecret.key] = decryptedSecret; @@ -791,47 +784,36 @@ const syncSecretsVercel = async ({ // Sync/push new secrets if (newSecrets.length > 0) { - await request.post( - `${IntegrationUrls.VERCEL_API_URL}/v10/projects/${integration.app}/env`, - newSecrets, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.post(`${IntegrationUrls.VERCEL_API_URL}/v10/projects/${integration.app}/env`, newSecrets, { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); } for await (const secret of updateSecrets) { if (secret.type !== "sensitive") { const { id, ...updatedSecret } = secret; - await request.patch( - `${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${id}`, - updatedSecret, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } - } - - for await (const secret of deleteSecrets) { - await request.delete( - `${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, - { + await request.patch(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${id}`, updatedSecret, { params, headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" } + }); + } + } + + for await (const secret of deleteSecrets) { + await request.delete(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); } }; @@ -866,7 +848,7 @@ const syncSecretsNetlify = async ({ }); const res = ( - await request.get( + await request.get( `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, { params: getParams, @@ -877,11 +859,11 @@ const syncSecretsNetlify = async ({ } ) ).data.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret }), - {} + {} as Record ); const newSecrets: NetlifySecret[] = []; // createEnvVars @@ -905,16 +887,16 @@ const syncSecretsNetlify = async ({ } else { // case: Infisical secret exists in Netlify const contexts = res[key].values.reduce( - (obj: any, value: NetlifyValue) => ({ + (obj, value) => ({ ...obj, [value.context]: value }), - {} + {} as Record ); if ((integration.targetEnvironment as string) in contexts) { // case: Netlify secret value exists in integration context - if (secrets[key] !== contexts[integration.targetEnvironment as string].value) { + if (secrets[key].value !== contexts[integration.targetEnvironment as string].value) { // case: Infisical and Netlify secret values are different // -> update Netlify secret context and value updateSecrets.push({ @@ -994,62 +976,63 @@ const syncSecretsNetlify = async ({ } if (updateSecrets.length > 0) { - updateSecrets.forEach(async (secret: NetlifySecret) => { - await request.patch( - `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`, - { - context: secret.values[0].context, - value: secret.values[0].value - }, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + await Promise.all( + updateSecrets.map(async (secret: NetlifySecret) => { + await request.patch( + `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`, + { + context: secret.values[0].context, + value: secret.values[0].value + }, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - } - ); - }); + ); + }) + ); } if (deleteSecrets.length > 0) { - deleteSecrets.forEach(async (key: string) => { - await request.delete( - `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + await Promise.all( + deleteSecrets.map(async (key: string) => { + await request.delete( + `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - } - ); - }); + ); + }) + ); } if (deleteSecretValues.length > 0) { - deleteSecretValues.forEach(async (secret: NetlifySecret) => { - await request.delete( - `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + await Promise.all( + deleteSecretValues.map(async (secret: NetlifySecret) => { + await request.delete( + `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - } - ); - }); + ); + }) + ); } }; /** * Sync/push [secrets] to GitHub repo with name [integration.app] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {TIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for GitHub integration */ const syncSecretsGitHub = async ({ integration, @@ -1096,7 +1079,7 @@ const syncSecretsGitHub = async ({ repo: integration.app as string }) ).data.secrets.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.name]: secret }), @@ -1121,46 +1104,46 @@ const syncSecretsGitHub = async ({ {} ); - Object.keys(encryptedSecrets).map(async (key) => { - if (!(key in secrets)) { - await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", { - owner: integration.owner as string, - repo: integration.app as string, - secret_name: key + await Promise.all( + Object.keys(encryptedSecrets).map(async (key) => { + if (!(key in secrets)) { + return octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", { + owner: integration.owner as string, + repo: integration.app as string, + secret_name: key + }); + } + }) + ); + + await Promise.all( + Object.keys(secrets).map((key) => { + // let encryptedSecret; + return sodium.ready.then(async () => { + // convert secret & base64 key to Uint8Array. + const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL); + const binsec = sodium.from_string(secrets[key].value); + + // encrypt secret using libsodium + const encBytes = sodium.crypto_box_seal(binsec, binkey); + + // convert encrypted Uint8Array to base64 + const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL); + + await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", { + owner: integration.owner as string, + repo: integration.app as string, + secret_name: key, + encrypted_value: encryptedSecret, + key_id: repoPublicKey.key_id + }); }); - } - }); - - Object.keys(secrets).forEach((key) => { - // let encryptedSecret; - sodium.ready.then(async () => { - // convert secret & base64 key to Uint8Array. - const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL); - const binsec = sodium.from_string(secrets[key].value); - - // encrypt secret using libsodium - const encBytes = sodium.crypto_box_seal(binsec, binkey); - - // convert encrypted Uint8Array to base64 - const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL); - - await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", { - owner: integration.owner as string, - repo: integration.app as string, - secret_name: key, - encrypted_value: encryptedSecret, - key_id: repoPublicKey.key_id - }); - }); - }); + }) + ); }; /** * Sync/push [secrets] to Render service with id [integration.appId] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Render integration */ const syncSecretsRender = async ({ integration, @@ -1188,10 +1171,6 @@ const syncSecretsRender = async ({ /** * Sync/push [secrets] to Laravel Forge sites with id [integration.appId] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Laravel Forge integration */ const syncSecretsLaravelForge = async ({ integration, @@ -1204,13 +1183,11 @@ const syncSecretsLaravelForge = async ({ accessId: string | null; accessToken: string; }) => { - function transformObjectToString(obj: any) { + function transformObjectToString(obj: Record) { let result = ""; - for (const key in obj) { - if (obj.hasOwnPropery(key)) { - result += `${key}=${obj[key].value}\n`; - } - } + Object.keys(obj).forEach((key) => { + result += `${key}=${obj[key].value}\n`; + }); return result; } @@ -1231,10 +1208,6 @@ const syncSecretsLaravelForge = async ({ /** * Sync/push [secrets] to Railway project with id [integration.appId] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Railway integration */ const syncSecretsRailway = async ({ integration, @@ -1279,10 +1252,6 @@ const syncSecretsRailway = async ({ /** * Sync/push [secrets] to Fly.io app - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Render integration */ const syncSecretsFlyio = async ({ integration, @@ -1354,7 +1323,7 @@ const syncSecretsFlyio = async ({ }`; const getSecretsRes = ( - await request.post( + await request.post<{ data: { app: { secrets: FlyioSecret[] } } }>( IntegrationUrls.FLYIO_API_URL, { query: GetSecrets, @@ -1372,9 +1341,7 @@ const syncSecretsFlyio = async ({ ) ).data.data.app.secrets; - const deleteSecretsKeys = getSecretsRes - .filter((secret: FlyioSecret) => !(secret.name in secrets)) - .map((secret: FlyioSecret) => secret.name); + const deleteSecretsKeys = getSecretsRes.filter((secret) => !(secret.name in secrets)).map((secret) => secret.name); // unset (delete) secrets const DeleteSecrets = `mutation($input: UnsetSecretsInput!) { @@ -1460,7 +1427,7 @@ const syncSecretsCircleCI = async ({ // get secrets from CircleCI const getSecretsRes = ( - await request.get( + await request.get<{ items: { name: string }[] }>( `${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar`, { headers: { @@ -1472,19 +1439,21 @@ const syncSecretsCircleCI = async ({ ).data?.items; // delete secrets from CircleCI - getSecretsRes.forEach(async (sec: any) => { - if (!(sec.name in secrets)) { - await request.delete( - `${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar/${sec.name}`, - { - headers: { - "Circle-Token": accessToken, - "Content-Type": "application/json" + await Promise.all( + getSecretsRes.map(async (sec) => { + if (!(sec.name in secrets)) { + return request.delete( + `${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar/${sec.name}`, + { + headers: { + "Circle-Token": accessToken, + "Content-Type": "application/json" + } } - } - ); - } - }); + ); + } + }) + ); }; /** @@ -1501,21 +1470,20 @@ const syncSecretsTravisCI = async ({ }) => { // get secrets from travis-ci const getSecretsRes = ( - await request.get( - `${IntegrationUrls.TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`, - { - headers: { - Authorization: `token ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.get<{ + env_vars: { name: string; value: string; repository_id: string; id: string }[]; + }>(`${IntegrationUrls.TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`, { + headers: { + Authorization: `token ${accessToken}`, + "Accept-Encoding": "application/json" } - ) + }) ).data?.env_vars.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.name]: secret }), - {} + {} as Record ); // add secrets @@ -1598,9 +1566,7 @@ const syncSecretsGitLab = async ({ environment_scope: string; } - const gitLabApiUrl = integrationAuth.url - ? `${integrationAuth.url}/api` - : IntegrationUrls.GITLAB_API_URL; + const gitLabApiUrl = integrationAuth.url ? `${integrationAuth.url}/api` : IntegrationUrls.GITLAB_API_URL; const getAllEnvVariables = async (integrationAppId: string, accToken: string) => { const headers = { @@ -1610,14 +1576,13 @@ const syncSecretsGitLab = async ({ }; let allEnvVariables: GitLabSecret[] = []; - let url: string | null = - `${gitLabApiUrl}/v4/projects/${integrationAppId}/variables?per_page=100`; + let url: string | null = `${gitLabApiUrl}/v4/projects/${integrationAppId}/variables?per_page=100`; while (url) { - const response: any = await request.get(url, { headers }); + const response = await request.get(url, { headers }); allEnvVariables = [...allEnvVariables, ...response.data]; - const linkHeader = response.headers.link; + const linkHeader = response.headers.link as string; const nextLink = linkHeader?.split(",").find((part: string) => part.includes('rel="next"')); if (nextLink) { @@ -1649,7 +1614,7 @@ const syncSecretsGitLab = async ({ }); for await (const key of Object.keys(secrets)) { - const existingSecret = getSecretsRes.find((s: any) => s.key === key); + const existingSecret = getSecretsRes.find((s) => s.key === key); if (!existingSecret) { await request.post( `${gitLabApiUrl}/v4/projects/${integration?.appId}/variables`, @@ -1714,7 +1679,7 @@ const syncSecretsSupabase = async ({ secrets: Record; accessToken: string; }) => { - const { data: getSecretsRes } = await request.get( + const { data: getSecretsRes } = await request.get<{ name: string; value: string }[]>( `${IntegrationUrls.SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, { headers: { @@ -1741,33 +1706,25 @@ const syncSecretsSupabase = async ({ } ); - const secretsToDelete: any = []; - getSecretsRes?.forEach((secretObj: any) => { + const secretsToDelete = getSecretsRes?.flatMap((secretObj) => { if ( !(secretObj.name in secrets) && // supbase reserved secret ref: https://supabase.com/docs/guides/functions/secrets#default-secrets - ![ - "SUPABASE_ANON_KEY", - "SUPABASE_SERVICE_ROLE_KEY", - "SUPABASE_DB_URL", - "SUPABASE_URL" - ].includes(secretObj.name) + !["SUPABASE_ANON_KEY", "SUPABASE_SERVICE_ROLE_KEY", "SUPABASE_DB_URL", "SUPABASE_URL"].includes(secretObj.name) ) { - secretsToDelete.push(secretObj.name); + return secretObj.name; } + return []; }); - await request.delete( - `${IntegrationUrls.SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - }, - data: secretsToDelete - } - ); + await request.delete(`${IntegrationUrls.SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, { + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + "Accept-Encoding": "application/json" + }, + data: secretsToDelete + }); }; /** @@ -1788,7 +1745,7 @@ const syncSecretsCheckly = async ({ // sync secrets to checkly group envars let getGroupSecretsRes = ( - await request.get( + await request.get<{ environmentVariables: { key: string; value: string }[] }>( `${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups/${integration.targetServiceId}`, { headers: { @@ -1799,11 +1756,11 @@ const syncSecretsCheckly = async ({ } ) ).data.environmentVariables.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret.value }), - {} + {} as Record ); getGroupSecretsRes = Object.keys(getGroupSecretsRes).reduce( @@ -1846,7 +1803,7 @@ const syncSecretsCheckly = async ({ // sync secrets to checkly global envars let getSecretsRes = ( - await request.get(`${IntegrationUrls.CHECKLY_API_URL}/v1/variables`, { + await request.get<{ key: string; value: string }[]>(`${IntegrationUrls.CHECKLY_API_URL}/v1/variables`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json", @@ -1854,11 +1811,11 @@ const syncSecretsCheckly = async ({ } }) ).data.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret.value }), - {} + {} as Record ); getSecretsRes = Object.keys(getSecretsRes).reduce( @@ -1901,7 +1858,7 @@ const syncSecretsCheckly = async ({ ); // case: secret exists in checkly // -> update/set secret - } else if (secrets[key] !== getSecretsRes[key]) { + } else if (secrets[key].value !== getSecretsRes[key]) { await request.put( `${IntegrationUrls.CHECKLY_API_URL}/v1/variables/${key}`, { @@ -1951,7 +1908,7 @@ const syncSecretsQovery = async ({ accessToken: string; }) => { const getSecretsRes = ( - await request.get( + await request.get<{ results: { id: string; value: string; key: string }[] }>( `${IntegrationUrls.QOVERY_API_URL}/${integration.scope}/${integration.appId}/environmentVariable`, { headers: { @@ -1961,11 +1918,11 @@ const syncSecretsQovery = async ({ } ) ).data.results.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: { id: secret.id, value: secret.value } }), - {} + {} as Record ); // add secrets @@ -2042,7 +1999,7 @@ const syncSecretsTerraformCloud = async ({ }) => { // get secrets from Terraform Cloud const getSecretsRes = ( - await request.get( + await request.get<{ data: { attributes: { key: string; value: string }; id: string }[] }>( `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars`, { headers: { @@ -2052,11 +2009,11 @@ const syncSecretsTerraformCloud = async ({ } ) ).data.data.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.attributes.key]: secret }), - {} + {} as Record ); // create or update secrets on Terraform Cloud @@ -2138,7 +2095,7 @@ const syncSecretsTeamCity = async ({ }: { integrationAuth: TIntegrationAuths; integration: TIntegrations; - secrets: any; + secrets: Record; accessToken: string; }) => { interface TeamCitySecret { @@ -2171,13 +2128,16 @@ const syncSecretsTeamCity = async ({ ) ).data.property .filter((parameter) => !parameter.inherited) - .reduce((obj: any, secret: TeamCitySecret) => { - const secretName = secret.name.replace(/^env\./, ""); - return { - ...obj, - [secretName]: secret.value - }; - }, {}); + .reduce( + (obj, secret) => { + const secretName = secret.name.replace(/^env\./, ""); + return { + ...obj, + [secretName]: secret.value + }; + }, + {} as Record + ); for await (const key of Object.keys(secrets)) { if (!(key in res) || (key in res && secrets[key].value !== res[key])) { @@ -2216,7 +2176,7 @@ const syncSecretsTeamCity = async ({ } else { // case: sync to TeamCity project const res = ( - await request.get( + await request.get<{ property: TeamCitySecret[] }>( `${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`, { headers: { @@ -2225,16 +2185,19 @@ const syncSecretsTeamCity = async ({ } } ) - ).data.property.reduce((obj: any, secret: TeamCitySecret) => { - const secretName = secret.name.replace(/^env\./, ""); - return { - ...obj, - [secretName]: secret.value - }; - }, {}); + ).data.property.reduce( + (obj, secret) => { + const secretName = secret.name.replace(/^env\./, ""); + return { + ...obj, + [secretName]: secret.value + }; + }, + {} as Record + ); for await (const key of Object.keys(secrets)) { - if (!(key in res) || (key in res && secrets[key] !== res[key])) { + if (!(key in res) || (key in res && secrets[key].value !== res[key])) { // case: secret does not exist in TeamCity or secret value has changed // -> create/update secret await request.post( @@ -2256,15 +2219,12 @@ const syncSecretsTeamCity = async ({ for await (const key of Object.keys(res)) { if (!(key in secrets)) { // delete secret - await request.delete( - `${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters/env.${key}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } + await request.delete(`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters/env.${key}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" } - ); + }); } } } @@ -2347,33 +2307,23 @@ const syncSecretsCloudflarePages = async ({ }) => { // get secrets from cloudflare pages const getSecretsRes = ( - await request.get( - `${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } + await request.get<{ + result: { deployment_configs: Record }> }; + }>(`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" } - ) + }) ).data.result.deployment_configs[integration.targetEnvironment as string].env_vars; // copy the secrets object, so we can set deleted keys to null - const secretsObj: any = getSecretKeyValuePair(secrets); - - for (const [key, val] of Object.entries(secretsObj)) { - secretsObj[key] = { type: "secret_text", value: val }; - } - - if (getSecretsRes) { - for await (const key of Object.keys(getSecretsRes)) { - if (!(key in secrets)) { - // case: secret does not exist in infisical - // -> delete secret from cloudflare pages - secretsObj[key] = null; - } - } - } + const secretsObj = Object.fromEntries( + Object.entries(getSecretKeyValuePair(secrets)).map(([key, val]) => [ + key, + key in Object.keys(getSecretsRes) ? { type: "secret_text", value: val } : null + ]) + ); const data = { deployment_configs: { @@ -2411,7 +2361,7 @@ const syncSecretsCloudflareWorkers = async ({ }) => { // get secrets from cloudflare workers const getSecretsRes = ( - await request.get( + await request.get<{ result: { name: string }[] }>( `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`, { headers: { @@ -2422,33 +2372,33 @@ const syncSecretsCloudflareWorkers = async ({ ) ).data.result; - const secretsObj: any = getSecretKeyValuePair(secrets); - - for (const [key, val] of Object.entries(secretsObj)) { - secretsObj[key] = { type: "secret_text", value: val }; - } + const secretsObj = Object.fromEntries( + Object.entries(getSecretKeyValuePair(secrets)).map(([key, val]) => [key, { type: "secret_text", value: val }]) + ); // get deleted secrets list const deletedSecretKeys: string[] = []; if (getSecretsRes) { - getSecretsRes.forEach((secretRes: any) => { + getSecretsRes.forEach((secretRes) => { if (!Object.keys(secrets).includes(secretRes.name)) { deletedSecretKeys.push(secretRes.name); } }); } - deletedSecretKeys.forEach(async (secretKey) => { - await request.delete( - `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets/${secretKey}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" + await Promise.all( + deletedSecretKeys.map(async (secretKey) => { + return request.delete( + `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets/${secretKey}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" + } } - } - ); - }); + ); + }) + ); interface ConvertedSecret { name: string; @@ -2463,35 +2413,30 @@ const syncSecretsCloudflareWorkers = async ({ }; } - const data: ConvertedSecret[] = Object.entries(secretsObj as SecretsObj).map( - ([name, secret]) => ({ - name, - text: secret.value, - type: "secret_text" + const data: ConvertedSecret[] = Object.entries(secretsObj as SecretsObj).map(([name, secret]) => ({ + name, + text: secret.value, + type: "secret_text" + })); + + await Promise.all( + data.map(async (secret) => { + return request.put( + `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`, + secret, + { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" + } + } + ); }) ); - - data.forEach(async (secret) => { - await request.put( - `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`, - secret, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - }); }; /** * Sync/push [secrets] to BitBucket repo with name [integration.app] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {TIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for BitBucket integration */ const syncSecretsBitBucket = async ({ integration, @@ -2700,18 +2645,18 @@ const syncSecretsWindmill = async ({ // get secrets stored in windmill workspace const res = ( - await request.get(`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`, { + await request.get(`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" } }) ).data.reduce( - (obj: any, secret: WindmillSecret) => ({ + (obj, secret) => ({ ...obj, [secret.path]: secret }), - {} + {} as Record ); // eslint-disable-next-line @@ -2778,11 +2723,6 @@ const syncSecretsWindmill = async ({ /** * Sync/push [secrets] to Cloud66 application with name [integration.app] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {TIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Cloud66 integration */ const syncSecretsCloud66 = async ({ integration, @@ -2802,12 +2742,12 @@ const syncSecretsCloud66 = async ({ updated_at: string; is_password: boolean; is_generated: boolean; - history: any[]; + history: unknown[]; } // get all current secrets const res = ( - await request.get( + await request.get<{ response: Cloud66Secret[] }>( `${IntegrationUrls.CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments`, { headers: { @@ -2817,9 +2757,9 @@ const syncSecretsCloud66 = async ({ } ) ).data.response - .filter((secret: Cloud66Secret) => !secret.readonly || !secret.is_generated) + .filter((secret) => !secret.readonly || !secret.is_generated) .reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret }), @@ -2863,15 +2803,12 @@ const syncSecretsCloud66 = async ({ for await (const key of Object.keys(res)) { if (!(key in secrets)) { // delete secret - await request.delete( - `${IntegrationUrls.CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments/${key}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } + await request.delete(`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments/${key}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" } - ); + }); } } }; @@ -2925,8 +2862,7 @@ const syncSecretsHasuraCloud = async ({ const res = await request.post( IntegrationUrls.HASURA_CLOUD_API_URL, { - query: - "query MyQuery($tenantId: uuid!) { getTenantEnv(tenantId: $tenantId) { hash envVars } }", + query: "query MyQuery($tenantId: uuid!) { getTenantEnv(tenantId: $tenantId) { hash envVars } }", variables: { tenantId: integration.appId } diff --git a/backend/src/services/integration-auth/integration-team.ts b/backend/src/services/integration-auth/integration-team.ts index 287e7cc7a..81ef9b70c 100644 --- a/backend/src/services/integration-auth/integration-team.ts +++ b/backend/src/services/integration-auth/integration-team.ts @@ -1,7 +1,7 @@ import { request } from "@app/lib/config/request"; import { BadRequestError } from "@app/lib/errors"; -import { Integrations,IntegrationUrls } from "./integration-list"; +import { Integrations, IntegrationUrls } from "./integration-list"; type Team = { name: string; @@ -12,7 +12,7 @@ const getTeamsGitLab = async ({ url, accessToken }: { url: string; accessToken: let teams: Team[] = []; const res = ( - await request.get(`${gitLabApiUrl}/v4/groups`, { + await request.get<{ name: string; id: string }[]>(`${gitLabApiUrl}/v4/groups`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" @@ -20,7 +20,7 @@ const getTeamsGitLab = async ({ url, accessToken }: { url: string; accessToken: }) ).data; - teams = res.map((t: any) => ({ + teams = res.map((t) => ({ name: t.name, teamId: t.id })); diff --git a/backend/src/services/integration-auth/integration-token.ts b/backend/src/services/integration-auth/integration-token.ts index f67b13a92..0907bd074 100644 --- a/backend/src/services/integration-auth/integration-token.ts +++ b/backend/src/services/integration-auth/integration-token.ts @@ -80,8 +80,8 @@ const exchangeCodeGCP = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeGCPResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.GCP_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -108,8 +108,8 @@ const exchangeCodeAzure = async ({ code }: { code: string }) => { if (!appCfg.CLIENT_ID_AZURE || !appCfg.CLIENT_SECRET_AZURE) { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeAzureResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.AZURE_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -118,7 +118,7 @@ const exchangeCodeAzure = async ({ code }: { code: string }) => { client_id: appCfg.CLIENT_ID_AZURE, client_secret: appCfg.CLIENT_SECRET_AZURE, redirect_uri: `${appCfg.SITE_URL}/integrations/azure-key-vault/oauth2/callback` - } as any) + }) ) ).data; @@ -138,8 +138,8 @@ const exchangeCodeHeroku = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeHerokuResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.HEROKU_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -160,12 +160,6 @@ const exchangeCodeHeroku = async ({ code }: { code: string }) => { /** * Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel * code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for Heroku API - * @returns {String} obj2.refreshToken - refresh token for Heroku API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token */ const exchangeCodeVercel = async ({ code }: { code: string }) => { const appCfg = getConfig(); @@ -173,15 +167,15 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeVercelResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.VERCEL_TOKEN_URL, new URLSearchParams({ code, client_id: appCfg.CLIENT_ID_VERCEL, client_secret: appCfg.CLIENT_SECRET_VERCEL, redirect_uri: `${appCfg.SITE_URL}/integrations/vercel/oauth2/callback` - } as any) + }) ) ).data; @@ -196,12 +190,6 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => { /** * Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel * code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for Heroku API - * @returns {String} obj2.refreshToken - refresh token for Heroku API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token */ const exchangeCodeNetlify = async ({ code }: { code: string }) => { const appCfg = getConfig(); @@ -209,8 +197,8 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeNetlifyResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.NETLIFY_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -218,7 +206,7 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { client_id: appCfg.CLIENT_ID_NETLIFY, client_secret: appCfg.CLIENT_SECRET_NETLIFY, redirect_uri: `${appCfg.SITE_URL}/integrations/netlify/oauth2/callback` - } as any) + }) ) ).data; @@ -230,7 +218,7 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { // }); const res3 = ( - await request.get("https://api.netlify.com/api/v1/accounts", { + await request.get>("https://api.netlify.com/api/v1/accounts", { headers: { Authorization: `Bearer ${res.access_token}` } @@ -252,8 +240,8 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeGithubResponse = ( - await request.get(IntegrationUrls.GITHUB_TOKEN_URL, { + const res = ( + await request.get(IntegrationUrls.GITHUB_TOKEN_URL, { params: { client_id: appCfg.CLIENT_ID_GITHUB, client_secret: appCfg.CLIENT_SECRET_GITHUB, @@ -285,8 +273,8 @@ const exchangeCodeGitlab = async ({ code, url }: { code: string; url?: string }) throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeGitlabResponse = ( - await request.post( + const res = ( + await request.post( url ? `${url}/oauth/token` : IntegrationUrls.GITLAB_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -294,7 +282,7 @@ const exchangeCodeGitlab = async ({ code, url }: { code: string; url?: string }) client_id: appCfg.CLIENT_ID_GITLAB, client_secret: appCfg.CLIENT_SECRET_GITLAB, redirect_uri: `${appCfg.SITE_URL}/integrations/gitlab/oauth2/callback` - } as any), + }), { headers: { "Accept-Encoding": "application/json" @@ -324,8 +312,8 @@ const exchangeCodeBitBucket = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeBitBucketResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.BITBUCKET_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -333,7 +321,7 @@ const exchangeCodeBitBucket = async ({ code }: { code: string }) => { client_id: appCfg.CLIENT_ID_BITBUCKET, client_secret: appCfg.CLIENT_SECRET_BITBUCKET, redirect_uri: `${appCfg.SITE_URL}/integrations/bitbucket/oauth2/callback` - } as any), + }), { headers: { "Accept-Encoding": "application/json" @@ -530,13 +518,7 @@ const exchangeRefreshHeroku = async ({ refreshToken }: { refreshToken: string }) * @param {String} obj.refreshToken - refresh token to use to get new access token for GitLab * @returns */ -const exchangeRefreshGitLab = async ({ - refreshToken, - url -}: { - url?: string | null; - refreshToken: string; -}) => { +const exchangeRefreshGitLab = async ({ refreshToken, url }: { url?: string | null; refreshToken: string }) => { const accessExpiresAt = new Date(); const appCfg = getConfig(); if (!appCfg.CLIENT_ID_GITLAB || !appCfg.CLIENT_SECRET_GITLAB) { @@ -593,7 +575,7 @@ const exchangeRefreshBitBucket = async ({ refreshToken }: { refreshToken: string client_id: appCfg.CLIENT_ID_BITBUCKET, client_secret: appCfg.CLIENT_SECRET_BITBUCKET, redirect_uri: `${appCfg.SITE_URL}/integrations/bitbucket/oauth2/callback` - } as any), + }), { headers: { "Accept-Encoding": "application/json" @@ -624,7 +606,11 @@ const exchangeRefreshGCPSecretManager = async ({ const accessExpiresAt = new Date(); if (metadata?.authMethod === "serviceAccount") { - const serviceAccount = JSON.parse(refreshToken); + const serviceAccount = JSON.parse(refreshToken) as { + client_email: string; + token_uri: string; + private_key: string; + }; const payload = { iss: serviceAccount.client_email, @@ -636,19 +622,18 @@ const exchangeRefreshGCPSecretManager = async ({ const token = jwt.sign(payload, serviceAccount.private_key, { algorithm: "RS256" }); - const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = - await request.post( - IntegrationUrls.GCP_TOKEN_URL, - new URLSearchParams({ - grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", - assertion: token - }).toString(), - { - headers: { - "Content-Type": "application/x-www-form-urlencoded" - } + const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = await request.post( + IntegrationUrls.GCP_TOKEN_URL, + new URLSearchParams({ + grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", + assertion: token + }).toString(), + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" } - ); + } + ); accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); @@ -663,14 +648,14 @@ const exchangeRefreshGCPSecretManager = async ({ if (!appCfg.CLIENT_SECRET_GCP_SECRET_MANAGER || !appCfg.CLIENT_ID_GCP_SECRET_MANAGER) { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const { data }: { data: RefreshTokenGCPSecretManagerResponse } = await request.post( + const { data } = await request.post( IntegrationUrls.GCP_TOKEN_URL, new URLSearchParams({ client_id: appCfg.CLIENT_ID_GCP_SECRET_MANAGER, client_secret: appCfg.CLIENT_SECRET_GCP_SECRET_MANAGER, refresh_token: refreshToken, grant_type: "refresh_token" - } as any) + }) ); accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); diff --git a/backend/src/services/integration/integration-dal.ts b/backend/src/services/integration/integration-dal.ts index 446af0bea..bada253c5 100644 --- a/backend/src/services/integration/integration-dal.ts +++ b/backend/src/services/integration/integration-dal.ts @@ -66,11 +66,7 @@ export const integrationDALFactory = (db: TDbClient) => { try { const integrations = await (tx || db)(TableName.Integration) .where(`${TableName.Environment}.projectId`, projectId) - .join( - TableName.Environment, - `${TableName.Integration}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.Integration}.envId`, `${TableName.Environment}.id`) .select(db.ref("name").withSchema(TableName.Environment).as("envName")) .select(db.ref("slug").withSchema(TableName.Environment).as("envSlug")) .select(db.ref("id").withSchema(TableName.Environment).as("envId")) @@ -99,11 +95,7 @@ export const integrationDALFactory = (db: TDbClient) => { .where("isActive", true) .where(`${TableName.Environment}.slug`, environment) .join(TableName.Environment, `${TableName.Integration}.envId`, `${TableName.Environment}.id`) - .join( - TableName.IntegrationAuth, - `${TableName.IntegrationAuth}.id`, - `${TableName.Integration}.integrationAuthId` - ) + .join(TableName.IntegrationAuth, `${TableName.IntegrationAuth}.id`, `${TableName.Integration}.integrationAuthId`) .select(db.ref("name").withSchema(TableName.Environment).as("envName")) .select(db.ref("slug").withSchema(TableName.Environment).as("envSlug")) .select(db.ref("id").withSchema(TableName.Environment).as("envId")) @@ -119,10 +111,7 @@ export const integrationDALFactory = (db: TDbClient) => { db.ref("refreshCiphertext").withSchema(TableName.IntegrationAuth).as("refreshCiphertextAu"), db.ref("refreshIV").withSchema(TableName.IntegrationAuth).as("refreshIVAu"), db.ref("refreshTag").withSchema(TableName.IntegrationAuth).as("refreshTagAu"), - db - .ref("accessIdCiphertext") - .withSchema(TableName.IntegrationAuth) - .as("accessIdCiphertextAu"), + db.ref("accessIdCiphertext").withSchema(TableName.IntegrationAuth).as("accessIdCiphertextAu"), db.ref("accessIdIV").withSchema(TableName.IntegrationAuth).as("accessIdIVAu"), db.ref("accessIdTag").withSchema(TableName.IntegrationAuth).as("accessIdTagAu"), db.ref("accessIV").withSchema(TableName.IntegrationAuth).as("accessIVAu"), diff --git a/backend/src/services/integration/integration-service.ts b/backend/src/services/integration/integration-service.ts index ba93b37fa..4a6bed75f 100644 --- a/backend/src/services/integration/integration-service.ts +++ b/backend/src/services/integration/integration-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TProjectPermission } from "@app/lib/types"; @@ -12,11 +9,7 @@ import { TIntegrationAuthDALFactory } from "../integration-auth/integration-auth import { TSecretQueueFactory } from "../secret/secret-queue"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TIntegrationDALFactory } from "./integration-dal"; -import { - TCreateIntegrationDTO, - TDeleteIntegrationDTO, - TUpdateIntegrationDTO -} from "./integration-types"; +import { TCreateIntegrationDTO, TDeleteIntegrationDTO, TUpdateIntegrationDTO } from "./integration-types"; type TIntegrationServiceFactoryDep = { integrationDAL: TIntegrationDALFactory; @@ -38,6 +31,7 @@ export const integrationServiceFactory = ({ const createIntegration = async ({ app, actor, + actorOrgId, path, appId, owner, @@ -60,18 +54,12 @@ export const integrationServiceFactory = ({ const { permission } = await permissionService.getProjectPermission( actor, actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ); - - const folder = await folderDAL.findBySecretPath( integrationAuth.projectId, - sourceEnvironment, - secretPath + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); + + const folder = await folderDAL.findBySecretPath(integrationAuth.projectId, sourceEnvironment, secretPath); if (!folder) throw new BadRequestError({ message: "Folder path not found" }); const integration = await integrationDAL.create({ @@ -104,6 +92,7 @@ export const integrationServiceFactory = ({ const updateIntegration = async ({ actorId, actor, + actorOrgId, targetEnvironment, app, id, @@ -119,12 +108,10 @@ export const integrationServiceFactory = ({ const { permission } = await permissionService.getProjectPermission( actor, actorId, - integration.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Integrations + integration.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); const folder = await folderDAL.findBySecretPath(integration.projectId, environment, secretPath); if (!folder) throw new BadRequestError({ message: "Folder path not found" }); @@ -142,30 +129,25 @@ export const integrationServiceFactory = ({ return updatedIntegration; }; - const deleteIntegration = async ({ actorId, id, actor }: TDeleteIntegrationDTO) => { + const deleteIntegration = async ({ actorId, id, actor, actorOrgId }: TDeleteIntegrationDTO) => { const integration = await integrationDAL.findById(id); if (!integration) throw new BadRequestError({ message: "Integration auth not found" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - integration.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Integrations + integration.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); const deletedIntegration = await integrationDAL.deleteById(id); return { ...integration, ...deletedIntegration }; }; - const listIntegrationByProject = async ({ actor, actorId, projectId }: TProjectPermission) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const listIntegrationByProject = async ({ actor, actorId, actorOrgId, projectId }: TProjectPermission) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const integrations = await integrationDAL.findByProjectId(projectId); return integrations; diff --git a/backend/src/services/org/incident-contacts-dal.ts b/backend/src/services/org/incident-contacts-dal.ts index c17516241..1979a9c3e 100644 --- a/backend/src/services/org/incident-contacts-dal.ts +++ b/backend/src/services/org/incident-contacts-dal.ts @@ -7,9 +7,7 @@ export type TIncidentContactsDALFactory = ReturnType { const create = async (orgId: string, email: string) => { try { - const [incidentContact] = await db(TableName.IncidentContact) - .insert({ orgId, email }) - .returning("*"); + const [incidentContact] = await db(TableName.IncidentContact).insert({ orgId, email }).returning("*"); return incidentContact; } catch (error) { throw new DatabaseError({ name: "Incident contact create", error }); @@ -38,10 +36,7 @@ export const incidentContactDALFactory = (db: TDbClient) => { const deleteById = async (id: string, orgId: string) => { try { - const [incidentContact] = await db(TableName.IncidentContact) - .where({ orgId, id }) - .delete() - .returning("*"); + const [incidentContact] = await db(TableName.IncidentContact).where({ orgId, id }).delete().returning("*"); return incidentContact; } catch (error) { throw new DatabaseError({ name: "Incident contact delete", error }); diff --git a/backend/src/services/org/org-dal.ts b/backend/src/services/org/org-dal.ts index 51ab59f68..e914aac42 100644 --- a/backend/src/services/org/org-dal.ts +++ b/backend/src/services/org/org-dal.ts @@ -7,20 +7,17 @@ import { TOrganizationsInsert, TOrgMemberships, TOrgMembershipsInsert, - TOrgMembershipsUpdate + TOrgMembershipsUpdate, + TUserEncryptionKeys } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { - buildFindFilter, - selectAllTableCols, - TFindFilter, - TFindOpt, - withTransaction -} from "@app/lib/knex"; +import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt, withTransaction } from "@app/lib/knex"; export type TOrgDALFactory = ReturnType; export const orgDALFactory = (db: TDbClient) => { + const orgOrm = ormify(db, TableName.Organization); + const findOrgById = async (orgId: string) => { try { const org = await db(TableName.Organization).where({ id: orgId }).first(); @@ -35,12 +32,8 @@ export const orgDALFactory = (db: TDbClient) => { try { const org = await db(TableName.OrgMembership) .where({ userId }) - .join( - TableName.Organization, - `${TableName.OrgMembership}.orgId`, - `${TableName.Organization}.id` - ) - .select(`${TableName.Organization}.*`); + .join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`) + .select(selectAllTableCols(TableName.Organization)); return org; } catch (error) { throw new DatabaseError({ error, name: "Find all org by user id" }); @@ -50,9 +43,9 @@ export const orgDALFactory = (db: TDbClient) => { const findOrgByProjectId = async (projectId: string): Promise => { try { const [org] = await db(TableName.Project) - .where({ [`${[TableName.Project]}.id`]: projectId }) + .where({ [`${TableName.Project}.id` as "id"]: projectId }) .join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`) - .select(`${TableName.Organization}.*`); + .select(selectAllTableCols(TableName.Organization)); return org; } catch (error) { @@ -66,7 +59,7 @@ export const orgDALFactory = (db: TDbClient) => { const members = await db(TableName.OrgMembership) .where({ orgId }) .join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) - .leftJoin( + .leftJoin( TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id` @@ -104,10 +97,7 @@ export const orgDALFactory = (db: TDbClient) => { const deleteById = async (orgId: string, tx?: Knex) => { try { - const [org] = await (tx || db)(TableName.Organization) - .where({ id: orgId }) - .delete() - .returning("*"); + const [org] = await (tx || db)(TableName.Organization).where({ id: orgId }).delete().returning("*"); return org; } catch (error) { throw new DatabaseError({ error, name: "Update organization" }); @@ -141,26 +131,16 @@ export const orgDALFactory = (db: TDbClient) => { const updateMembershipById = async (id: string, data: TOrgMembershipsUpdate, tx?: Knex) => { try { - const [membership] = await (tx || db)(TableName.OrgMembership) - .where({ id }) - .update(data) - .returning("*"); + const [membership] = await (tx || db)(TableName.OrgMembership).where({ id }).update(data).returning("*"); return membership; } catch (error) { throw new DatabaseError({ error, name: "Update org membership" }); } }; - const updateMembership = async ( - filter: Partial, - data: TOrgMembershipsUpdate, - tx?: Knex - ) => { + const updateMembership = async (filter: Partial, data: TOrgMembershipsUpdate, tx?: Knex) => { try { - const membership = await (tx || db)(TableName.OrgMembership) - .where(filter) - .update(data) - .returning("*"); + const membership = await (tx || db)(TableName.OrgMembership).where(filter).update(data).returning("*"); return membership; } catch (error) { throw new DatabaseError({ error, name: "Update org memberships" }); @@ -169,10 +149,7 @@ export const orgDALFactory = (db: TDbClient) => { const deleteMembershipById = async (id: string, orgId: string, tx?: Knex) => { try { - const [membership] = await (tx || db)(TableName.OrgMembership) - .where({ id, orgId }) - .delete() - .returning("*"); + const [membership] = await (tx || db)(TableName.OrgMembership).where({ id, orgId }).delete().returning("*"); return membership; } catch (error) { throw new DatabaseError({ error, name: "Delete org membership" }); @@ -185,18 +162,14 @@ export const orgDALFactory = (db: TDbClient) => { ) => { try { const query = (tx || db)(TableName.OrgMembership) + // eslint-disable-next-line .where(buildFindFilter(filter)) .join(TableName.Users, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`) - .select( - selectAllTableCols(TableName.OrgMembership), - db.ref("email").withSchema(TableName.Users) - ); - if (limit) query.limit(limit); - if (offset) query.offset(offset); + .select(selectAllTableCols(TableName.OrgMembership), db.ref("email").withSchema(TableName.Users)); + if (limit) void query.limit(limit); + if (offset) void query.offset(offset); if (sort) { - query.orderBy( - sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })) - ); + void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls }))); } const res = await query; return res; @@ -206,6 +179,7 @@ export const orgDALFactory = (db: TDbClient) => { }; return withTransaction(db, { + ...orgOrm, findOrgByProjectId, findAllOrgMembers, findOrgById, diff --git a/backend/src/services/org/org-role-service.ts b/backend/src/services/org/org-role-service.ts index 8dfd0e0af..b3d8121c3 100644 --- a/backend/src/services/org/org-role-service.ts +++ b/backend/src/services/org/org-role-service.ts @@ -21,20 +21,15 @@ type TOrgRoleServiceFactoryDep = { export type TOrgRoleServiceFactory = ReturnType; -export const orgRoleServiceFactory = ({ - orgRoleDAL, - permissionService -}: TOrgRoleServiceFactoryDep) => { +export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRoleServiceFactoryDep) => { const createRole = async ( userId: string, orgId: string, - data: Omit + data: Omit, + actorOrgId?: string ) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Role - ); + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Role); const existingRole = await orgRoleDAL.findOne({ slug: data.slug, orgId }); if (existingRole) throw new BadRequestError({ name: "Create Role", message: "Duplicate role" }); const role = await orgRoleDAL.create({ @@ -49,13 +44,11 @@ export const orgRoleServiceFactory = ({ userId: string, orgId: string, roleId: string, - data: Omit + data: Omit, + actorOrgId?: string ) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Role - ); + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Role); if (data?.slug) { const existingRole = await orgRoleDAL.findOne({ slug: data.slug, orgId }); if (existingRole && existingRole.id !== roleId) @@ -69,24 +62,18 @@ export const orgRoleServiceFactory = ({ return updatedRole; }; - const deleteRole = async (userId: string, orgId: string, roleId: string) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Role - ); + const deleteRole = async (userId: string, orgId: string, roleId: string, actorOrgId?: string) => { + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Role); const [deletedRole] = await orgRoleDAL.delete({ id: roleId, orgId }); if (!deleteRole) throw new BadRequestError({ message: "Role not found", name: "Update role" }); return deletedRole; }; - const listRoles = async (userId: string, orgId: string) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Role - ); + const listRoles = async (userId: string, orgId: string, actorOrgId?: string) => { + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Role); const customRoles = await orgRoleDAL.find({ orgId }); const roles = [ { @@ -128,8 +115,8 @@ export const orgRoleServiceFactory = ({ return roles; }; - const getUserPermission = async (userId: string, orgId: string) => { - const { permission, membership } = await permissionService.getUserOrgPermission(userId, orgId); + const getUserPermission = async (userId: string, orgId: string, actorOrgId?: string) => { + const { permission, membership } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); return { permissions: packRules(permission.rules), membership }; }; diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index 4587c3814..7fafca438 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -3,11 +3,9 @@ import slugify from "@sindresorhus/slugify"; import jwt from "jsonwebtoken"; import { OrgMembershipRole, OrgMembershipStatus } from "@app/db/schemas"; +import { TProjects } from "@app/db/schemas/projects"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TSamlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal"; import { getConfig } from "@app/lib/config/env"; @@ -17,9 +15,10 @@ import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { isDisposableEmail } from "@app/lib/validator"; -import { AuthMethod, AuthTokenType } from "../auth/auth-type"; +import { ActorType, AuthMethod, AuthTokenType } from "../auth/auth-type"; import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service"; import { TokenType } from "../auth-token/auth-token-types"; +import { TProjectDALFactory } from "../project/project-dal"; import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service"; import { TUserDALFactory } from "../user/user-dal"; import { TIncidentContactsDALFactory } from "./incident-contacts-dal"; @@ -28,7 +27,9 @@ import { TOrgDALFactory } from "./org-dal"; import { TOrgRoleDALFactory } from "./org-role-dal"; import { TDeleteOrgMembershipDTO, + TFindAllWorkspacesDTO, TInviteUserToOrgDTO, + TUpdateOrgDTO, TUpdateOrgMembershipDTO, TVerifyUserToOrgDTO } from "./org-types"; @@ -38,8 +39,9 @@ type TOrgServiceFactoryDep = { orgBotDAL: TOrgBotDALFactory; orgRoleDAL: TOrgRoleDALFactory; userDAL: TUserDALFactory; + projectDAL: TProjectDALFactory; incidentContactDAL: TIncidentContactsDALFactory; - samlConfigDAL: Pick; + samlConfigDAL: Pick; smtpService: TSmtpService; tokenService: TAuthTokenServiceFactory; permissionService: TPermissionServiceFactory; @@ -58,6 +60,7 @@ export const orgServiceFactory = ({ incidentContactDAL, permissionService, smtpService, + projectDAL, tokenService, orgBotDAL, licenseService, @@ -66,11 +69,10 @@ export const orgServiceFactory = ({ /* * Get organization details by the organization id * */ - const findOrganizationById = async (userId: string, orgId: string) => { - await permissionService.getUserOrgPermission(userId, orgId); + const findOrganizationById = async (userId: string, orgId: string, actorOrgId?: string) => { + await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); const org = await orgDAL.findOrgById(orgId); - if (!org) - throw new BadRequestError({ name: "Org not found", message: "Organization not found" }); + if (!org) throw new BadRequestError({ name: "Org not found", message: "Organization not found" }); return org; }; /* @@ -83,28 +85,71 @@ export const orgServiceFactory = ({ /* * Get all workspace members * */ - const findAllOrgMembers = async (userId: string, orgId: string) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Member - ); + const findAllOrgMembers = async (userId: string, orgId: string, actorOrgId?: string) => { + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member); const members = await orgDAL.findAllOrgMembers(orgId); return members; }; + + const findAllWorkspaces = async ({ actor, actorId, actorOrgId, orgId }: TFindAllWorkspacesDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace); + + const organizationWorkspaceIds = new Set((await projectDAL.find({ orgId })).map((workspace) => workspace.id)); + + let workspaces: (TProjects & { organization: string } & { + environments: { + id: string; + slug: string; + name: string; + }[]; + })[]; + + if (actor === ActorType.USER) { + workspaces = await projectDAL.findAllProjects(actorId); + } else if (actor === ActorType.IDENTITY) { + workspaces = await projectDAL.findAllProjectsByIdentity(actorId); + } else { + throw new BadRequestError({ message: "Invalid actor type" }); + } + + return workspaces.filter((workspace) => organizationWorkspaceIds.has(workspace.id)); + }; + /* - * Update organization settings + * Update organization details * */ - const updateOrgName = async (userId: string, orgId: string, name: string) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Settings - ); - const org = await orgDAL.updateById(orgId, { name }); - if (!org) - throw new BadRequestError({ name: "Org not found", message: "Organization not found" }); + const updateOrg = async ({ + actor, + actorId, + actorOrgId, + orgId, + data: { name, slug, authEnforced } + }: TUpdateOrgDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings); + + if (authEnforced !== undefined) { + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso); + } + + if (authEnforced) { + const samlCfg = await samlConfigDAL.findEnforceableSamlCfg(orgId); + if (!samlCfg) + throw new BadRequestError({ + name: "No enforceable SAML config found", + message: "No enforceable SAML config found" + }); + } + + const org = await orgDAL.updateById(orgId, { + name, + slug: slug ? slugify(slug) : undefined, + authEnforced + }); + if (!org) throw new BadRequestError({ name: "Org not found", message: "Organization not found" }); return org; }; /* @@ -171,9 +216,9 @@ export const orgServiceFactory = ({ /* * Delete organization by id * */ - const deleteOrganizationById = async (userId: string, orgId: string) => { - const { membership } = await permissionService.getUserOrgPermission(userId, orgId); - if (membership.role !== OrgMembershipRole.Admin) + const deleteOrganizationById = async (userId: string, orgId: string, actorOrgId?: string) => { + const { membership } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + if ((membership.role as OrgMembershipRole) !== OrgMembershipRole.Admin) throw new UnauthorizedError({ name: "Delete org by id", message: "Not an admin" }); const organization = await orgDAL.deleteById(orgId); @@ -186,29 +231,19 @@ export const orgServiceFactory = ({ * Org membership management * Not another service because it has close ties with how an org works doesn't make sense to seperate them * */ - const updateOrgMembership = async ({ - role, - orgId, - userId, - membershipId - }: TUpdateOrgMembershipDTO) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Member - ); + const updateOrgMembership = async ({ role, orgId, userId, membershipId, actorOrgId }: TUpdateOrgMembershipDTO) => { + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Member); const isCustomRole = !Object.values(OrgMembershipRole).includes(role as OrgMembershipRole); if (isCustomRole) { const customRole = await orgRoleDAL.findOne({ slug: role, orgId }); - if (!customRole) - throw new BadRequestError({ name: "Update membership", message: "Role not found" }); + if (!customRole) throw new BadRequestError({ name: "Update membership", message: "Role not found" }); const plan = await licenseService.getPlan(orgId); if (!plan?.rbac) throw new BadRequestError({ - message: - "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." + message: "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." }); const [membership] = await orgDAL.updateMembership( @@ -221,35 +256,30 @@ export const orgServiceFactory = ({ return membership; } - const [membership] = await orgDAL.updateMembership( - { id: membershipId, orgId }, - { role, roleId: null } - ); + const [membership] = await orgDAL.updateMembership({ id: membershipId, orgId }, { role, roleId: null }); return membership; }; /* * Invite user to organization */ - const inviteUserToOrganization = async ({ orgId, userId, inviteeEmail }: TInviteUserToOrgDTO) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Member - ); + const inviteUserToOrganization = async ({ orgId, userId, inviteeEmail, actorOrgId }: TInviteUserToOrgDTO) => { + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member); - const samlCfg = await samlConfigDAL.findOne({ orgId }); - if (samlCfg && samlCfg.isActive) { + const org = await orgDAL.findOrgById(orgId); + + if (org?.authEnforced) { throw new BadRequestError({ - message: "Failed to invite member due to SAML SSO configured for organization" + message: "Failed to invite user due to org-level auth enforced for organization" }); } + const plan = await licenseService.getPlan(orgId); if (plan.memberLimit !== null && plan.membersUsed >= plan.memberLimit) { // case: limit imposed on number of members allowed // case: number of members used exceeds the number of members allowed throw new BadRequestError({ - message: - "Failed to invite member due to member limit reached. Upgrade plan to invite more members." + message: "Failed to invite member due to member limit reached. Upgrade plan to invite more members." }); } const invitee = await orgDAL.transaction(async (tx) => { @@ -257,10 +287,7 @@ export const orgServiceFactory = ({ if (inviteeUser) { // if user already exist means its already part of infisical // Thus the signup flow is not needed anymore - const [inviteeMembership] = await orgDAL.findMembership( - { orgId, userId: inviteeUser.id }, - { tx } - ); + const [inviteeMembership] = await orgDAL.findMembership({ orgId, userId: inviteeUser.id }, { tx }); if (inviteeMembership && inviteeMembership.status === OrgMembershipStatus.Accepted) { throw new BadRequestError({ message: "Failed to invite an existing member of org", @@ -317,7 +344,6 @@ export const orgServiceFactory = ({ orgId }); - const org = await orgDAL.findOrgById(orgId); const user = await userDAL.findById(userId); const appCfg = getConfig(); await smtpService.sendMail({ @@ -394,12 +420,9 @@ export const orgServiceFactory = ({ return { token, user }; }; - const deleteOrgMembership = async ({ orgId, userId, membershipId }: TDeleteOrgMembershipDTO) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Member - ); + const deleteOrgMembership = async ({ orgId, userId, membershipId, actorOrgId }: TDeleteOrgMembershipDTO) => { + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Member); const membership = await orgDAL.deleteMembershipById(membershipId, orgId); @@ -410,22 +433,16 @@ export const orgServiceFactory = ({ /* * CRUD operations of incident contacts * */ - const findIncidentContacts = async (userId: string, orgId: string) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.IncidentAccount - ); + const findIncidentContacts = async (userId: string, orgId: string, actorOrgId?: string) => { + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.IncidentAccount); const incidentContacts = await incidentContactDAL.findByOrgId(orgId); return incidentContacts; }; - const createIncidentContact = async (userId: string, orgId: string, email: string) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.IncidentAccount - ); + const createIncidentContact = async (userId: string, orgId: string, email: string, actorOrgId?: string) => { + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.IncidentAccount); const doesIncidentContactExist = await incidentContactDAL.findOne(orgId, { email }); if (doesIncidentContactExist) { throw new BadRequestError({ @@ -438,12 +455,9 @@ export const orgServiceFactory = ({ return incidentContact; }; - const deleteIncidentContact = async (userId: string, orgId: string, id: string) => { - const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.IncidentAccount - ); + const deleteIncidentContact = async (userId: string, orgId: string, id: string, actorOrgId?: string) => { + const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.IncidentAccount); const incidentContact = await incidentContactDAL.deleteById(id, orgId); return incidentContact; @@ -455,10 +469,11 @@ export const orgServiceFactory = ({ findAllOrganizationOfUser, inviteUserToOrganization, verifyUserToOrg, - updateOrgName, + updateOrg, createOrganization, deleteOrganizationById, deleteOrgMembership, + findAllWorkspaces, updateOrgMembership, // incident contacts findIncidentContacts, diff --git a/backend/src/services/org/org-types.ts b/backend/src/services/org/org-types.ts index 6456e5de1..01b3c8e37 100644 --- a/backend/src/services/org/org-types.ts +++ b/backend/src/services/org/org-types.ts @@ -1,19 +1,26 @@ +import { TOrgPermission } from "@app/lib/types"; + +import { ActorType } from "../auth/auth-type"; + export type TUpdateOrgMembershipDTO = { userId: string; orgId: string; membershipId: string; role: string; + actorOrgId?: string; }; export type TDeleteOrgMembershipDTO = { userId: string; orgId: string; membershipId: string; + actorOrgId?: string; }; export type TInviteUserToOrgDTO = { userId: string; orgId: string; + actorOrgId?: string; inviteeEmail: string; }; @@ -22,3 +29,14 @@ export type TVerifyUserToOrgDTO = { orgId: string; code: string; }; + +export type TFindAllWorkspacesDTO = { + actor: ActorType; + actorId: string; + actorOrgId?: string; + orgId: string; +}; + +export type TUpdateOrgDTO = { + data: Partial<{ name: string; slug: string; authEnforced: boolean }>; +} & TOrgPermission; diff --git a/backend/src/services/project-bot/project-bot-dal.ts b/backend/src/services/project-bot/project-bot-dal.ts index 39a8628b8..7f342f0ae 100644 --- a/backend/src/services/project-bot/project-bot-dal.ts +++ b/backend/src/services/project-bot/project-bot-dal.ts @@ -15,11 +15,7 @@ export const projectBotDALFactory = (db: TDbClient) => { const bot = await (tx || db)(TableName.ProjectBot) .where(filter) .leftJoin(TableName.Users, `${TableName.ProjectBot}.senderId`, `${TableName.Users}.id`) - .leftJoin( - TableName.UserEncryptionKey, - `${TableName.UserEncryptionKey}.userId`, - `${TableName.Users}.id` - ) + .leftJoin(TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id`) .select(selectAllTableCols(TableName.ProjectBot)) .select(db.ref("publicKey").withSchema(TableName.UserEncryptionKey).as("senderPubKey")) .first(); diff --git a/backend/src/services/project-bot/project-bot-service.ts b/backend/src/services/project-bot/project-bot-service.ts index 8a3fd19d9..5ead160f2 100644 --- a/backend/src/services/project-bot/project-bot-service.ts +++ b/backend/src/services/project-bot/project-bot-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { decryptAsymmetric, @@ -28,10 +25,7 @@ type TProjectBotServiceFactoryDep = { export type TProjectBotServiceFactory = ReturnType; -export const projectBotServiceFactory = ({ - projectBotDAL, - permissionService -}: TProjectBotServiceFactoryDep) => { +export const projectBotServiceFactory = ({ projectBotDAL, permissionService }: TProjectBotServiceFactoryDep) => { const getBotKey = async (projectId: string) => { const appCfg = getConfig(); const encryptionKey = appCfg.ENCRYPTION_KEY; @@ -43,7 +37,7 @@ export const projectBotServiceFactory = ({ if (!bot.encryptedProjectKeyNonce || !bot.encryptedProjectKey) throw new BadRequestError({ message: "Encryption key missing" }); - if (rootEncryptionKey && bot.keyEncoding === SecretKeyEncoding.BASE64) { + if (rootEncryptionKey && (bot.keyEncoding as SecretKeyEncoding) === SecretKeyEncoding.BASE64) { const privateKeyBot = decryptSymmetric({ iv: bot.iv, tag: bot.tag, @@ -57,7 +51,7 @@ export const projectBotServiceFactory = ({ publicKey: bot.sender.publicKey }); } - if (encryptionKey && bot.keyEncoding === SecretKeyEncoding.UTF8) { + if (encryptionKey && (bot.keyEncoding as SecretKeyEncoding) === SecretKeyEncoding.UTF8) { const privateKeyBot = decryptSymmetric128BitHexKeyUTF8({ iv: bot.iv, tag: bot.tag, @@ -77,12 +71,9 @@ export const projectBotServiceFactory = ({ }); }; - const findBotByProjectId = async ({ actorId, actor, projectId }: TProjectPermission) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const findBotByProjectId = async ({ actorId, actor, actorOrgId, projectId }: TProjectPermission) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const appCfg = getConfig(); const bot = await projectBotDAL.transaction(async (tx) => { @@ -108,10 +99,7 @@ export const projectBotServiceFactory = ({ ); } if (appCfg.ENCRYPTION_KEY) { - const { iv, tag, ciphertext } = encryptSymmetric128BitHexKeyUTF8( - privateKey, - appCfg.ENCRYPTION_KEY - ); + const { iv, tag, ciphertext } = encryptSymmetric128BitHexKeyUTF8(privateKey, appCfg.ENCRYPTION_KEY); return projectBotDAL.create( { name: "Infisical Bot", @@ -132,25 +120,12 @@ export const projectBotServiceFactory = ({ return bot; }; - const setBotActiveState = async ({ - actor, - botId, - botKey, - actorId, - isActive - }: TSetActiveStateDTO) => { + const setBotActiveState = async ({ actor, botId, botKey, actorId, actorOrgId, isActive }: TSetActiveStateDTO) => { const bot = await projectBotDAL.findById(botId); if (!bot) throw new BadRequestError({ message: "Bot not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - bot.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, bot.projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); if (isActive) { if (!botKey?.nonce || !botKey?.encryptedKey) { diff --git a/backend/src/services/project-env/project-env-dal.ts b/backend/src/services/project-env/project-env-dal.ts index c74e8803c..42a234298 100644 --- a/backend/src/services/project-env/project-env-dal.ts +++ b/backend/src/services/project-env/project-env-dal.ts @@ -12,9 +12,7 @@ export const projectEnvDALFactory = (db: TDbClient) => { const findBySlugs = async (projectId: string, env: string[], tx?: Knex) => { try { - const envs = await (tx || db)(TableName.Environment) - .where("projectId", projectId) - .whereIn("slug", env); + const envs = await (tx || db)(TableName.Environment).where("projectId", projectId).whereIn("slug", env); return envs; } catch (error) { throw new DatabaseError({ error, name: "Find by slugs" }); @@ -26,17 +24,12 @@ export const projectEnvDALFactory = (db: TDbClient) => { const findLastEnvPosition = async (projectId: string, tx?: Knex) => { const lastPos = await (tx || db)(TableName.Environment) .where({ projectId }) - .max({ position: "position" }) + .max("position", { as: "position" }) .first(); return lastPos?.position || 0; }; - const updateAllPosition = async ( - projectId: string, - pos: number, - targetPos: number, - tx?: Knex - ) => { + const updateAllPosition = async (projectId: string, pos: number, targetPos: number, tx?: Knex) => { try { if (targetPos === -1) { // this means delete diff --git a/backend/src/services/project-env/project-env-service.ts b/backend/src/services/project-env/project-env-service.ts index d95bb7950..6ebb3a3d6 100644 --- a/backend/src/services/project-env/project-env-service.ts +++ b/backend/src/services/project-env/project-env-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TProjectDALFactory } from "../project/project-dal"; @@ -30,12 +27,9 @@ export const projectEnvServiceFactory = ({ projectDAL, folderDAL }: TProjectEnvServiceFactoryDep) => { - const createEnvironment = async ({ projectId, actorId, actor, name, slug }: TCreateEnvDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Environments - ); + const createEnvironment = async ({ projectId, actorId, actor, actorOrgId, name, slug }: TCreateEnvDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Environments); const envs = await projectEnvDAL.find({ projectId }); const existingEnv = envs.find(({ slug: envSlug }) => envSlug === slug); @@ -70,21 +64,19 @@ export const projectEnvServiceFactory = ({ slug, actor, actorId, + actorOrgId, name, id, position }: TUpdateEnvDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Environments - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments); const oldEnv = await projectEnvDAL.findOne({ id, projectId }); if (!oldEnv) throw new BadRequestError({ message: "Environment not found" }); if (slug) { - const existingEnv = await projectEnvDAL.findOne({ slug }); + const existingEnv = await projectEnvDAL.findOne({ slug, projectId }); if (existingEnv && existingEnv.id !== id) { throw new BadRequestError({ message: "Environment with slug already exist", @@ -102,12 +94,9 @@ export const projectEnvServiceFactory = ({ return { environment: env, old: oldEnv }; }; - const deleteEnvironment = async ({ projectId, actor, actorId, id }: TDeleteEnvDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Environments - ); + const deleteEnvironment = async ({ projectId, actor, actorId, actorOrgId, id }: TDeleteEnvDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments); const env = await projectEnvDAL.transaction(async (tx) => { const [doc] = await projectEnvDAL.delete({ id, projectId }, tx); diff --git a/backend/src/services/project-key/project-key-dal.ts b/backend/src/services/project-key/project-key-dal.ts index b183a933b..7423a48de 100644 --- a/backend/src/services/project-key/project-key-dal.ts +++ b/backend/src/services/project-key/project-key-dal.ts @@ -1,7 +1,7 @@ import { TDbClient } from "@app/db"; import { TableName, TProjectKeys } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { ormify } from "@app/lib/knex"; +import { ormify, selectAllTableCols } from "@app/lib/knex"; export type TProjectKeyDALFactory = ReturnType; @@ -11,25 +11,19 @@ export const projectKeyDALFactory = (db: TDbClient) => { const findLatestProjectKey = async ( userId: string, projectId: string - ): Promise => { + ): Promise<(TProjectKeys & { sender: { publicKey: string } }) | undefined> => { try { const projectKey = await db(TableName.ProjectKeys) - .where({ projectId, receiverId: userId }) .join(TableName.Users, `${TableName.ProjectKeys}.senderId`, `${TableName.Users}.id`) - .join( - TableName.UserEncryptionKey, - `${TableName.UserEncryptionKey}.userId`, - `${TableName.Users}.id` - ) + .join(TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id`) + .where({ projectId, receiverId: userId }) .orderBy("createdAt", "desc", "last") - .select(`${TableName.ProjectKeys}.*`, `${TableName.UserEncryptionKey}.publicKey`) + .select(selectAllTableCols(TableName.ProjectKeys)) + .select(db.ref("publicKey").withSchema(TableName.UserEncryptionKey)) .first(); if (projectKey) { - projectKey.sender = { - publicKey: projectKey.publicKey - }; + return { ...projectKey, sender: { publicKey: projectKey.publicKey } }; } - return projectKey; } catch (error) { throw new DatabaseError({ error, name: "Find latest project key" }); } @@ -40,11 +34,7 @@ export const projectKeyDALFactory = (db: TDbClient) => { const pubKeys = await db(TableName.ProjectMembership) .where({ projectId }) .join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) - .join( - TableName.UserEncryptionKey, - `${TableName.Users}.id`, - `${TableName.UserEncryptionKey}.userId` - ) + .join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`) .select("userId", "publicKey"); return pubKeys; } catch (error) { diff --git a/backend/src/services/project-key/project-key-service.ts b/backend/src/services/project-key/project-key-service.ts index 76694217f..fa77760a4 100644 --- a/backend/src/services/project-key/project-key-service.ts +++ b/backend/src/services/project-key/project-key-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; @@ -28,15 +25,13 @@ export const projectKeyServiceFactory = ({ receiverId, actor, actorId, + actorOrgId, projectId, nonce, encryptedKey }: TUploadProjectKeyDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Member - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); const receiverMembership = await projectMembershipDAL.findOne({ userId: receiverId, @@ -51,18 +46,15 @@ export const projectKeyServiceFactory = ({ await projectKeyDAL.create({ projectId, receiverId, encryptedKey, nonce, senderId: actorId }); }; - const getLatestProjectKey = async ({ actorId, projectId, actor }: TGetLatestProjectKeyDTO) => { - await permissionService.getProjectPermission(actor, actorId, projectId); + const getLatestProjectKey = async ({ actorId, projectId, actor, actorOrgId }: TGetLatestProjectKeyDTO) => { + await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); const latestKey = await projectKeyDAL.findLatestProjectKey(actorId, projectId); return latestKey; }; - const getProjectPublicKeys = async ({ actor, actorId, projectId }: TGetLatestProjectKeyDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Member - ); + const getProjectPublicKeys = async ({ actor, actorId, actorOrgId, projectId }: TGetLatestProjectKeyDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member); return projectKeyDAL.findAllProjectUserPubKeys(projectId); }; diff --git a/backend/src/services/project-membership/project-membership-dal.ts b/backend/src/services/project-membership/project-membership-dal.ts index 86cfa55b4..22b9937a9 100644 --- a/backend/src/services/project-membership/project-membership-dal.ts +++ b/backend/src/services/project-membership/project-membership-dal.ts @@ -1,5 +1,5 @@ import { TDbClient } from "@app/db"; -import { TableName } from "@app/db/schemas"; +import { TableName, TUserEncryptionKeys } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; @@ -14,7 +14,7 @@ export const projectMembershipDALFactory = (db: TDbClient) => { const members = await db(TableName.ProjectMembership) .where({ projectId }) .join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) - .join( + .join( TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id` @@ -25,10 +25,10 @@ export const projectMembershipDALFactory = (db: TDbClient) => { db.ref("role").withSchema(TableName.ProjectMembership), db.ref("roleId").withSchema(TableName.ProjectMembership), db.ref("email").withSchema(TableName.Users), + db.ref("publicKey").withSchema(TableName.UserEncryptionKey), db.ref("firstName").withSchema(TableName.Users), db.ref("lastName").withSchema(TableName.Users), - db.ref("id").withSchema(TableName.Users).as("userId"), - db.ref("publicKey").withSchema(TableName.UserEncryptionKey) + db.ref("id").withSchema(TableName.Users).as("userId") ); return members.map(({ email, firstName, lastName, publicKey, ...data }) => ({ ...data, diff --git a/backend/src/services/project-membership/project-membership-service.ts b/backend/src/services/project-membership/project-membership-service.ts index d2b4901a0..6b2123e5e 100644 --- a/backend/src/services/project-membership/project-membership-service.ts +++ b/backend/src/services/project-membership/project-membership-service.ts @@ -3,10 +3,7 @@ import { ForbiddenError } from "@casl/ability"; import { OrgMembershipStatus, ProjectMembershipRole, TableName } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError } from "@app/lib/errors"; import { groupBy } from "@app/lib/fn"; @@ -51,27 +48,16 @@ export const projectMembershipServiceFactory = ({ projectKeyDAL, licenseService }: TProjectMembershipServiceFactoryDep) => { - const getProjectMemberships = async ({ actorId, actor, projectId }: TGetProjectMembershipDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Member - ); + const getProjectMemberships = async ({ actorId, actor, actorOrgId, projectId }: TGetProjectMembershipDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member); return projectMembershipDAL.findAllProjectMembers(projectId); }; - const inviteUserToProject = async ({ - actorId, - actor, - projectId, - email - }: TInviteUserToProjectDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Member - ); + const inviteUserToProject = async ({ actorId, actor, actorOrgId, projectId, email }: TInviteUserToProjectDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Member); const invitee = await userDAL.findOne({ email }); if (!invitee || !invitee.isAccepted) @@ -126,37 +112,25 @@ export const projectMembershipServiceFactory = ({ return { invitee, latestKey }; }; - const addUsersToProject = async ({ - projectId, - actorId, - actor, - members - }: TAddUsersToWorkspaceDTO) => { + const addUsersToProject = async ({ projectId, actorId, actor, actorOrgId, members }: TAddUsersToWorkspaceDTO) => { const project = await projectDAL.findById(projectId); if (!project) throw new BadRequestError({ message: "Project not found" }); - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Member - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Member); const orgMembers = await orgDAL.findMembership({ orgId: project.orgId, $in: { - [`${TableName.OrgMembership}.id` as "id"]: members.map( - ({ orgMembershipId }) => orgMembershipId - ) + [`${TableName.OrgMembership}.id` as "id"]: members.map(({ orgMembershipId }) => orgMembershipId) } }); - if (orgMembers.length !== members.length) - throw new BadRequestError({ message: "Some users are not part of org" }); + if (orgMembers.length !== members.length) throw new BadRequestError({ message: "Some users are not part of org" }); const existingMembers = await projectMembershipDAL.find({ projectId, $in: { userId: orgMembers.map(({ userId }) => userId).filter(Boolean) as string[] } }); - if (existingMembers.length) - throw new BadRequestError({ message: "Some users are already part of project" }); + if (existingMembers.length) throw new BadRequestError({ message: "Some users are already part of project" }); await projectMembershipDAL.transaction(async (tx) => { await projectMembershipDAL.insertMany( @@ -184,7 +158,7 @@ export const projectMembershipServiceFactory = ({ await smtpService.sendMail({ template: SmtpTemplates.WorkspaceInvite, subjectLine: "Infisical workspace invitation", - recipients: orgMembers.map(({ email }) => email).filter(Boolean) as string[], + recipients: orgMembers.map(({ email }) => email).filter(Boolean), substitutions: { inviterFirstName: sender.firstName, inviterEmail: sender.email, @@ -198,29 +172,23 @@ export const projectMembershipServiceFactory = ({ const updateProjectMembership = async ({ actorId, actor, + actorOrgId, projectId, membershipId, role }: TUpdateProjectMembershipDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Member - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); - const isCustomRole = !Object.values(ProjectMembershipRole).includes( - role as ProjectMembershipRole - ); + const isCustomRole = !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole); if (isCustomRole) { const customRole = await projectRoleDAL.findOne({ slug: role, projectId }); - if (!customRole) - throw new BadRequestError({ name: "Update project membership", message: "Role not found" }); + if (!customRole) throw new BadRequestError({ name: "Update project membership", message: "Role not found" }); const project = await projectDAL.findById(customRole.projectId); const plan = await licenseService.getPlan(project.orgId); if (!plan?.rbac) throw new BadRequestError({ - message: - "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." + message: "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." }); const [membership] = await projectMembershipDAL.update( @@ -233,30 +201,22 @@ export const projectMembershipServiceFactory = ({ return membership; } - const [membership] = await projectMembershipDAL.update( - { id: membershipId, projectId }, - { role, roleId: null } - ); + const [membership] = await projectMembershipDAL.update({ id: membershipId, projectId }, { role, roleId: null }); return membership; }; const deleteProjectMembership = async ({ actorId, actor, + actorOrgId, projectId, membershipId }: TDeleteProjectMembershipDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Member - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Member); const membership = await projectMembershipDAL.transaction(async (tx) => { - const [deletedMembership] = await projectMembershipDAL.delete( - { projectId, id: membershipId }, - tx - ); + const [deletedMembership] = await projectMembershipDAL.delete({ projectId, id: membershipId }, tx); await projectKeyDAL.delete({ receiverId: deletedMembership.userId, projectId }, tx); return deletedMembership; }); diff --git a/backend/src/services/project-role/project-role-service.ts b/backend/src/services/project-role/project-role-service.ts index 8e86b45fb..7da98b314 100644 --- a/backend/src/services/project-role/project-role-service.ts +++ b/backend/src/services/project-role/project-role-service.ts @@ -18,29 +18,21 @@ import { TProjectRoleDALFactory } from "./project-role-dal"; type TProjectRoleServiceFactoryDep = { projectRoleDAL: TProjectRoleDALFactory; - permissionService: Pick< - TPermissionServiceFactory, - "getProjectPermission" | "getUserProjectPermission" - >; + permissionService: Pick; }; export type TProjectRoleServiceFactory = ReturnType; -export const projectRoleServiceFactory = ({ - projectRoleDAL, - permissionService -}: TProjectRoleServiceFactoryDep) => { +export const projectRoleServiceFactory = ({ projectRoleDAL, permissionService }: TProjectRoleServiceFactoryDep) => { const createRole = async ( actor: ActorType, actorId: string, projectId: string, - data: Omit + data: Omit, + actorOrgId?: string ) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Role - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Role); const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId }); if (existingRole) throw new BadRequestError({ name: "Create Role", message: "Duplicate role" }); const role = await projectRoleDAL.create({ @@ -56,13 +48,11 @@ export const projectRoleServiceFactory = ({ actorId: string, projectId: string, roleId: string, - data: Omit + data: Omit, + actorOrgId?: string ) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Role - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Role); if (data?.slug) { const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId }); if (existingRole && existingRole.id !== roleId) @@ -80,25 +70,20 @@ export const projectRoleServiceFactory = ({ actor: ActorType, actorId: string, projectId: string, - roleId: string + roleId: string, + actorOrgId?: string ) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Role - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Role); const [deletedRole] = await projectRoleDAL.delete({ id: roleId, projectId }); if (!deleteRole) throw new BadRequestError({ message: "Role not found", name: "Update role" }); return deletedRole; }; - const listRoles = async (actor: ActorType, actorId: string, projectId: string) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Role - ); + const listRoles = async (actor: ActorType, actorId: string, projectId: string, actorOrgId?: string) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role); const customRoles = await projectRoleDAL.find({ projectId }); const roles = [ { @@ -150,11 +135,8 @@ export const projectRoleServiceFactory = ({ return roles; }; - const getUserPermission = async (userId: string, projectId: string) => { - const { permission, membership } = await permissionService.getUserProjectPermission( - userId, - projectId - ); + const getUserPermission = async (userId: string, projectId: string, actorOrgId?: string) => { + const { permission, membership } = await permissionService.getUserProjectPermission(userId, projectId, actorOrgId); return { permissions: packRules(permission.rules), membership }; }; diff --git a/backend/src/services/project/project-dal.ts b/backend/src/services/project/project-dal.ts index a82fbc3af..44ba57481 100644 --- a/backend/src/services/project/project-dal.ts +++ b/backend/src/services/project/project-dal.ts @@ -12,16 +12,8 @@ export const projectDALFactory = (db: TDbClient) => { try { const workspaces = await db(TableName.ProjectMembership) .where({ userId }) - .join( - TableName.Project, - `${TableName.ProjectMembership}.projectId`, - `${TableName.Project}.id` - ) - .leftJoin( - TableName.Environment, - `${TableName.Environment}.projectId`, - `${TableName.Project}.id` - ) + .join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) + .leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) .select( selectAllTableCols(TableName.Project), db.ref("id").withSchema(TableName.Project).as("_id"), @@ -29,8 +21,12 @@ export const projectDALFactory = (db: TDbClient) => { db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("name").withSchema(TableName.Environment).as("envName") ) - .orderBy("createdAt", "asc", "last"); - return sqlNestRelationships({ + .orderBy([ + { column: `${TableName.Project}.name`, order: "asc" }, + { column: `${TableName.Environment}.position`, order: "asc" } + ]); + + const nestedWorkspaces = sqlNestRelationships({ data: workspaces, key: "id", parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }), @@ -46,32 +42,75 @@ export const projectDALFactory = (db: TDbClient) => { } ] }); + + return nestedWorkspaces.map((workspace) => ({ + ...workspace, + organization: workspace.orgId + })); } catch (error) { throw new DatabaseError({ error, name: "Find all projects" }); } }; + const findAllProjectsByIdentity = async (identityId: string) => { + try { + const workspaces = await db(TableName.IdentityProjectMembership) + .where({ identityId }) + .join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`) + .leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) + .select( + selectAllTableCols(TableName.Project), + db.ref("id").withSchema(TableName.Project).as("_id"), + db.ref("id").withSchema(TableName.Environment).as("envId"), + db.ref("slug").withSchema(TableName.Environment).as("envSlug"), + db.ref("name").withSchema(TableName.Environment).as("envName") + ) + .orderBy("createdAt", "asc", "last"); + + const nestedWorkspaces = sqlNestRelationships({ + data: workspaces, + key: "id", + parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }), + childrenMapper: [ + { + key: "envId", + label: "environments" as const, + mapper: ({ envId: id, envSlug: slug, envName: name }) => ({ + id, + slug, + name + }) + } + ] + }); + + // We need to add the organization field, as it's required for one of our API endpoint responses. + return nestedWorkspaces.map((workspace) => ({ + ...workspace, + organization: workspace.orgId + })); + } catch (error) { + throw new DatabaseError({ error, name: "Find all projects by identity" }); + } + }; + const findProjectById = async (id: string) => { try { const workspaces = await db(TableName.ProjectMembership) .where(`${TableName.Project}.id`, id) - .join( - TableName.Project, - `${TableName.ProjectMembership}.projectId`, - `${TableName.Project}.id` - ) - .join( - TableName.Environment, - `${TableName.Environment}.projectId`, - `${TableName.Project}.id` - ) + .join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) + .join(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) .select( selectAllTableCols(TableName.Project), db.ref("id").withSchema(TableName.Project).as("_id"), db.ref("id").withSchema(TableName.Environment).as("envId"), db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("name").withSchema(TableName.Environment).as("envName") - ); + ) + .orderBy([ + { column: `${TableName.Project}.name`, order: "asc" }, + { column: `${TableName.Environment}.position`, order: "asc" } + ]); return sqlNestRelationships({ data: workspaces, key: "id", @@ -96,6 +135,7 @@ export const projectDALFactory = (db: TDbClient) => { return { ...projectOrm, findAllProjects, + findAllProjectsByIdentity, findProjectById }; }; diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index df1168578..387c9bd78 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -3,15 +3,9 @@ import slugify from "@sindresorhus/slugify"; import { ProjectMembershipRole } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { createSecretBlindIndex } from "@app/lib/crypto"; import { BadRequestError } from "@app/lib/errors"; @@ -54,12 +48,9 @@ export const projectServiceFactory = ({ /* * Create workspace. Make user the admin * */ - const createProject = async ({ orgId, actor, actorId, workspaceName }: TCreateProjectDTO) => { - const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Workspace - ); + const createProject = async ({ orgId, actor, actorId, actorOrgId, workspaceName }: TCreateProjectDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace); const appCfg = getConfig(); const blindIndex = createSecretBlindIndex(appCfg.ROOT_ENCRYPTION_KEY, appCfg.ENCRYPTION_KEY); @@ -69,8 +60,7 @@ export const projectServiceFactory = ({ // case: limit imposed on number of workspaces allowed // case: number of workspaces used exceeds the number of workspaces allowed throw new BadRequestError({ - message: - "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces." + message: "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces." }); } @@ -116,12 +106,9 @@ export const projectServiceFactory = ({ return newProject; }; - const deleteProject = async ({ actor, actorId, projectId }: TDeleteProjectDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Project - ); + const deleteProject = async ({ actor, actorId, actorOrgId, projectId }: TDeleteProjectDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project); const deletedProject = await projectDAL.deleteById(projectId); return deletedProject; @@ -132,8 +119,8 @@ export const projectServiceFactory = ({ return workspaces; }; - const getAProject = async ({ actorId, projectId, actor }: TGetProjectDTO) => { - await permissionService.getProjectPermission(actor, actorId, projectId); + const getAProject = async ({ actorId, actorOrgId, projectId, actor }: TGetProjectDTO) => { + await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); return projectDAL.findProjectById(projectId); }; @@ -141,29 +128,19 @@ export const projectServiceFactory = ({ projectId, actor, actorId, + actorOrgId, autoCapitalization }: TGetProjectDTO & { autoCapitalization: boolean }) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Settings - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); const updatedProject = await projectDAL.updateById(projectId, { autoCapitalization }); return updatedProject; }; - const updateName = async ({ - projectId, - actor, - actorId, - name - }: TGetProjectDTO & { name: string }) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Settings - ); + const updateName = async ({ projectId, actor, actorId, actorOrgId, name }: TGetProjectDTO & { name: string }) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); const updatedProject = await projectDAL.updateById(projectId, { name }); return updatedProject; diff --git a/backend/src/services/project/project-types.ts b/backend/src/services/project/project-types.ts index 2b8c5e908..2ffea1117 100644 --- a/backend/src/services/project/project-types.ts +++ b/backend/src/services/project/project-types.ts @@ -3,6 +3,7 @@ import { ActorType } from "../auth/auth-type"; export type TCreateProjectDTO = { actor: ActorType; actorId: string; + actorOrgId?: string; orgId: string; workspaceName: string; }; @@ -10,11 +11,13 @@ export type TCreateProjectDTO = { export type TDeleteProjectDTO = { actor: ActorType; actorId: string; + actorOrgId?: string; projectId: string; }; export type TGetProjectDTO = { actor: ActorType; actorId: string; + actorOrgId?: string; projectId: string; }; diff --git a/backend/src/services/secret-blind-index/secret-blind-index-dal.ts b/backend/src/services/secret-blind-index/secret-blind-index-dal.ts index c508728af..8fa60cde7 100644 --- a/backend/src/services/secret-blind-index/secret-blind-index-dal.ts +++ b/backend/src/services/secret-blind-index/secret-blind-index-dal.ts @@ -13,20 +13,12 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => { const countOfSecretsWithNullSecretBlindIndex = async (projectId: string, tx?: Knex) => { try { const doc = await (tx || db)(TableName.Secret) - .leftJoin( - TableName.SecretFolder, - `${TableName.SecretFolder}.id`, - `${TableName.Secret}.folderId` - ) - .leftJoin( - TableName.Environment, - `${TableName.Environment}.id`, - `${TableName.SecretFolder}.envId` - ) + .leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.Secret}.folderId`) + .leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`) .where({ projectId }) .whereNull("secretBlindIndex") - .count(`${TableName.Secret}.id`); - return (doc as any)?.[0]?.count || 0; + .count(`${TableName.Secret}.id` as "id"); + return doc?.[0]?.count || 0; } catch (error) { throw new DatabaseError({ error, name: "CountOfSecretWillNullSecretBlindIndex" }); } @@ -35,16 +27,8 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => { const findAllSecretsByProjectId = async (projectId: string, tx?: Knex) => { try { const docs = await (tx || db)(TableName.Secret) - .leftJoin( - TableName.SecretFolder, - `${TableName.SecretFolder}.id`, - `${TableName.Secret}.folderId` - ) - .leftJoin( - TableName.Environment, - `${TableName.Environment}.id`, - `${TableName.SecretFolder}.envId` - ) + .leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.Secret}.folderId`) + .leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`) .where({ projectId }) .whereNull("secretBlindIndex") .select(selectAllTableCols(TableName.Secret)) @@ -61,16 +45,8 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => { const findSecretsByProjectId = async (projectId: string, secretIds: string[], tx?: Knex) => { try { const docs = await (tx || db)(TableName.Secret) - .leftJoin( - TableName.SecretFolder, - `${TableName.SecretFolder}.id`, - `${TableName.Secret}.folderId` - ) - .leftJoin( - TableName.Environment, - `${TableName.Environment}.id`, - `${TableName.SecretFolder}.envId` - ) + .leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.Secret}.folderId`) + .leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`) .where({ projectId }) .whereIn(`${TableName.Secret}.id`, secretIds) .whereNull("secretBlindIndex") diff --git a/backend/src/services/secret-blind-index/secret-blind-index-service.ts b/backend/src/services/secret-blind-index/secret-blind-index-service.ts index 215e54992..da7a44df2 100644 --- a/backend/src/services/secret-blind-index/secret-blind-index-service.ts +++ b/backend/src/services/secret-blind-index/secret-blind-index-service.ts @@ -27,12 +27,10 @@ export const secretBlindIndexServiceFactory = ({ const getSecretBlindIndexStatus = async ({ actor, projectId, - actorId + actorId, + actorOrgId }: TGetProjectBlindIndexStatusDTO) => { - const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId); - if (membership?.role !== ProjectMembershipRole.Admin) { - throw new UnauthorizedError({ message: "User must be admin" }); - } + await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); const secretCount = await secretBlindIndexDAL.countOfSecretsWithNullSecretBlindIndex(projectId); return Number(secretCount); @@ -52,23 +50,22 @@ export const secretBlindIndexServiceFactory = ({ projectId, actor, actorId, + actorOrgId, secretsToUpdate }: TUpdateProjectSecretNameDTO) => { - const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); if (membership?.role !== ProjectMembershipRole.Admin) { throw new UnauthorizedError({ message: "User must be admin" }); } const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); const secrets = await secretBlindIndexDAL.findSecretsByProjectId( projectId, secretsToUpdate.map(({ secretId }) => secretId) ); - if (secrets.length !== secretsToUpdate.length) - throw new BadRequestError({ message: "Secret not found" }); + if (secrets.length !== secretsToUpdate.length) throw new BadRequestError({ message: "Secret not found" }); const operations = await Promise.all( secretsToUpdate.map(async ({ secretName, secretId: id }) => { diff --git a/backend/src/services/secret-folder/secret-folder-dal.ts b/backend/src/services/secret-folder/secret-folder-dal.ts index 6129cfa9d..023d039ca 100644 --- a/backend/src/services/secret-folder/secret-folder-dal.ts +++ b/backend/src/services/secret-folder/secret-folder-dal.ts @@ -1,14 +1,9 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { - TableName, - TProjectEnvironments, - TSecretFolders, - TSecretFoldersUpdate -} from "@app/db/schemas"; +import { TableName, TProjectEnvironments, TSecretFolders, TSecretFoldersUpdate } from "@app/db/schemas"; import { BadRequestError, DatabaseError } from "@app/lib/errors"; -import { groupBy } from "@app/lib/fn"; +import { groupBy, removeTrailingSlash } from "@app/lib/fn"; import { ormify, selectAllTableCols } from "@app/lib/knex"; export const validateFolderName = (folderName: string) => { @@ -16,14 +11,10 @@ export const validateFolderName = (folderName: string) => { return validNameRegex.test(folderName); }; -const sqlFindMultipleFolderByEnvPathQuery = ( - db: Knex, - query: Array<{ envId: string; secretPath: string }> -) => { +const sqlFindMultipleFolderByEnvPathQuery = (db: Knex, query: Array<{ envId: string; secretPath: string }>) => { // this is removing an trailing slash like /folder1/folder2/ -> /folder1/folder2 const formatedQuery = query.map(({ envId, secretPath }) => { - const formatedPath = - secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath; + const formatedPath = secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath; const segments = formatedPath.split("/").filter(Boolean); if (segments.some((segment) => !validateFolderName(segment))) { throw new BadRequestError({ message: "Invalid folder name" }); @@ -42,7 +33,7 @@ const sqlFindMultipleFolderByEnvPathQuery = ( // Thus each node has connection to parent node // for a given path from root we recursively reach to the leaf path or till we get null // the below query is the base case where we select root folder which has parent folder id as null - baseQb + void baseQb .select({ depth: 1, // latestFolderVerId: db.raw("NULL::uuid"), @@ -57,49 +48,44 @@ const sqlFindMultipleFolderByEnvPathQuery = ( formatedQuery.map(({ envId }) => envId) ) .select(selectAllTableCols(TableName.SecretFolder)) - .union((qb) => - // for here on we keep going to next child node. - // we also keep a measure of depth then we check the depth matches the array path segment and folder name - // that is at depth 1 for a path /folder1/folder2 -> the name should be folder1 - qb - .select({ - depth: db.raw("parent.depth + 1"), - path: db.raw( - "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)" - ) - }) - .select(selectAllTableCols(TableName.SecretFolder)) - .where((wb) => - formatedQuery.map(({ secretPath }) => - wb.orWhereRaw( - `depth = array_position(ARRAY[${secretPath - .map(() => "?") - .join(",")}]::varchar[], ${TableName.SecretFolder}.name,depth)`, - [...secretPath] + .union( + (qb) => + // for here on we keep going to next child node. + // we also keep a measure of depth then we check the depth matches the array path segment and folder name + // that is at depth 1 for a path /folder1/folder2 -> the name should be folder1 + void qb + .select({ + depth: db.raw("parent.depth + 1"), + path: db.raw( + "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)" + ) + }) + .select(selectAllTableCols(TableName.SecretFolder)) + .where((wb) => + formatedQuery.map(({ secretPath }) => + wb.orWhereRaw( + `depth = array_position(ARRAY[${secretPath.map(() => "?").join(",")}]::varchar[], ${ + TableName.SecretFolder + }.name,depth)`, + [...secretPath] + ) ) ) - ) - .from(TableName.SecretFolder) - .join("parent", (bd) => - bd - .on("parent.id", `${TableName.SecretFolder}.parentId`) - .andOn("parent.envId", `${TableName.SecretFolder}.envId`) - ) + .from(TableName.SecretFolder) + .join("parent", (bd) => + bd + .on("parent.id", `${TableName.SecretFolder}.parentId`) + .andOn("parent.envId", `${TableName.SecretFolder}.envId`) + ) ); }) .select("*") .from("parent"); }; -const sqlFindFolderByPathQuery = ( - db: Knex, - projectId: string, - environment: string, - secretPath: string -) => { +const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environment: string, secretPath: string) => { // this is removing an trailing slash like /folder1/folder2/ -> /folder1/folder2 - const formatedPath = - secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath; + const formatedPath = secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath; // next goal to sanitize saw the raw sql query is safe // for this we ensure folder name contains only string and - nothing else const pathSegments = formatedPath.split("/").filter(Boolean); @@ -113,52 +99,45 @@ const sqlFindFolderByPathQuery = ( // Thus each node has connection to parent node // for a given path from root we recursively reach to the leaf path or till we get null // the below query is the base case where we select root folder which has parent folder id as null - baseQb + void baseQb .select({ depth: 1, // latestFolderVerId: db.raw("NULL::uuid"), path: db.raw("'/'") }) .from(TableName.SecretFolder) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .where({ projectId, parentId: null }) .where(`${TableName.Environment}.slug`, environment) .select(selectAllTableCols(TableName.SecretFolder)) - .union((qb) => - // for here on we keep going to next child node. - // we also keep a measure of depth then we check the depth matches the array path segment and folder name - // that is at depth 1 for a path /folder1/folder2 -> the name should be folder1 - qb - .select({ - depth: db.raw("parent.depth + 1"), - path: db.raw( - "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)" + .union( + (qb) => + // for here on we keep going to next child node. + // we also keep a measure of depth then we check the depth matches the array path segment and folder name + // that is at depth 1 for a path /folder1/folder2 -> the name should be folder1 + void qb + .select({ + depth: db.raw("parent.depth + 1"), + path: db.raw( + "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)" + ) + }) + .select(selectAllTableCols(TableName.SecretFolder)) + .whereRaw( + `depth = array_position(ARRAY[${pathSegments + .map(() => "?") + .join(",")}]::varchar[], secret_folders.name,depth)`, + [...pathSegments] ) - }) - .select(selectAllTableCols(TableName.SecretFolder)) - .whereRaw( - `depth = array_position(ARRAY[${pathSegments - .map(() => "?") - .join(",")}]::varchar[], secret_folders.name,depth)`, - [...pathSegments] - ) - .from(TableName.SecretFolder) - .join("parent", "parent.id", `${TableName.SecretFolder}.parentId`) + .from(TableName.SecretFolder) + .join("parent", "parent.id", `${TableName.SecretFolder}.parentId`) ); }) .from("parent") - .leftJoin( - TableName.Environment, - `${TableName.Environment}.id`, - "parent.envId" - ) + .leftJoin(TableName.Environment, `${TableName.Environment}.id`, "parent.envId") .select< TSecretFolders & { depth: number; @@ -183,43 +162,38 @@ const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: str // first remember our folders are connected as a link list or known as adjacency list // Thus each node has connection to parent node // we first find the folder given in folder id - baseQb + void baseQb .from(TableName.SecretFolder) .select(selectAllTableCols(TableName.SecretFolder)) .select({ // this is for root condition // if the given folder id is root folder id then intial path is set as / instead of /root // if not root folder the path here will be / - path: db.raw( - `CONCAT('/', (CASE WHEN "parentId" is NULL THEN '' ELSE ${TableName.SecretFolder}.name END))` - ), + path: db.raw(`CONCAT('/', (CASE WHEN "parentId" is NULL THEN '' ELSE ${TableName.SecretFolder}.name END))`), child: db.raw("NULL::uuid") }) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .where({ projectId }) .whereIn(`${TableName.SecretFolder}.id`, folderIds) - .union((qb) => - // then we keep going up - // until parent id is null - qb - .select(selectAllTableCols(TableName.SecretFolder)) - .select({ - // then we join join this folder name behind previous as we are going from child to parent - // the root folder check is used to avoid last / and also root name in folders - path: db.raw( - `CONCAT( CASE + .union( + (qb) => + // then we keep going up + // until parent id is null + void qb + .select(selectAllTableCols(TableName.SecretFolder)) + .select({ + // then we join join this folder name behind previous as we are going from child to parent + // the root folder check is used to avoid last / and also root name in folders + path: db.raw( + `CONCAT( CASE WHEN ${TableName.SecretFolder}."parentId" is NULL THEN '' ELSE CONCAT('/', secret_folders.name) END, parent.path )` - ), - child: db.raw("COALESCE(parent.child, parent.id)") - }) - .from(TableName.SecretFolder) - .join("parent", "parent.parentId", `${TableName.SecretFolder}.id`) + ), + child: db.raw("COALESCE(parent.child, parent.id)") + }) + .from(TableName.SecretFolder) + .join("parent", "parent.parentId", `${TableName.SecretFolder}.id`) ); }) .select("*") @@ -231,17 +205,12 @@ export const ROOT_FOLDER_NAME = "root"; export const secretFolderDALFactory = (db: TDbClient) => { const secretFolderOrm = ormify(db, TableName.SecretFolder); - const findBySecretPath = async ( - projectId: string, - environment: string, - path: string, - tx?: Knex - ) => { + const findBySecretPath = async (projectId: string, environment: string, path: string, tx?: Knex) => { try { - const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, path) + const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, removeTrailingSlash(path)) .orderBy("depth", "desc") .first(); - if (folder && folder.path !== path) { + if (folder && folder.path !== removeTrailingSlash(path)) { return; } if (!folder) return; @@ -255,14 +224,9 @@ export const secretFolderDALFactory = (db: TDbClient) => { // used in folder creation // even if its the original given /path1/path2 // it will stop automatically at /path2 - const findClosestFolder = async ( - projectId: string, - environment: string, - path: string, - tx?: Knex - ) => { + const findClosestFolder = async (projectId: string, environment: string, path: string, tx?: Knex) => { try { - const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, path) + const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, removeTrailingSlash(path)) .orderBy("depth", "desc") .first(); if (!folder) return; @@ -273,17 +237,15 @@ export const secretFolderDALFactory = (db: TDbClient) => { } }; - const findByManySecretPath = async ( - query: Array<{ envId: string; secretPath: string }>, - tx?: Knex - ) => { + const findByManySecretPath = async (query: Array<{ envId: string; secretPath: string }>, tx?: Knex) => { try { - const folders = await sqlFindMultipleFolderByEnvPathQuery(tx || db, query); - return query.map(({ envId, secretPath }) => - folders.find( - ({ path: targetPath, envId: targetEnvId }) => - targetPath === secretPath && targetEnvId === envId - ) + const formatedQuery = query.map(({ secretPath, envId }) => ({ + envId, + secretPath: removeTrailingSlash(secretPath) + })); + const folders = await sqlFindMultipleFolderByEnvPathQuery(tx || db, formatedQuery); + return formatedQuery.map(({ envId, secretPath }) => + folders.find(({ path: targetPath, envId: targetEnvId }) => targetPath === secretPath && targetEnvId === envId) ); } catch (error) { throw new DatabaseError({ error, name: "FindByManySecretPath" }); @@ -322,11 +284,7 @@ export const secretFolderDALFactory = (db: TDbClient) => { try { const folder = await (tx || db)(TableName.SecretFolder) .where({ [`${TableName.SecretFolder}.id` as "id"]: id }) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .select(selectAllTableCols(TableName.SecretFolder)) .select( db.ref("id").withSchema(TableName.Environment).as("envId"), diff --git a/backend/src/services/secret-folder/secret-folder-service.ts b/backend/src/services/secret-folder/secret-folder-service.ts index a9e7474c8..53d2a9fdf 100644 --- a/backend/src/services/secret-folder/secret-folder-service.ts +++ b/backend/src/services/secret-folder/secret-folder-service.ts @@ -4,21 +4,13 @@ import { v4 as uuidv4 } from "uuid"; import { TSecretFoldersInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { BadRequestError } from "@app/lib/errors"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TSecretFolderDALFactory } from "./secret-folder-dal"; -import { - TCreateFolderDTO, - TDeleteFolderDTO, - TGetFolderDTO, - TUpdateFolderDTO -} from "./secret-folder-types"; +import { TCreateFolderDTO, TDeleteFolderDTO, TGetFolderDTO, TUpdateFolderDTO } from "./secret-folder-types"; import { TSecretFolderVersionDALFactory } from "./secret-folder-version-dal"; type TSecretFolderServiceFactoryDep = { @@ -42,19 +34,19 @@ export const secretFolderServiceFactory = ({ projectId, actor, actorId, + actorOrgId, name, environment, path: secretPath }: TCreateFolderDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); - if (!env) - throw new BadRequestError({ message: "Environment not found", name: "Create folder" }); + if (!env) throw new BadRequestError({ message: "Environment not found", name: "Create folder" }); const folder = await folderDAL.transaction(async (tx) => { // the logic is simple we need to avoid creating same folder in same path multiple times @@ -62,12 +54,7 @@ export const secretFolderServiceFactory = ({ // so we do a tricky move. we try to find the to be created folder path if that is exactly match return that // else we get some path before that then we will start creating remaining folder const pathWithFolder = path.join(secretPath, name); - const parentFolder = await folderDAL.findClosestFolder( - projectId, - environment, - pathWithFolder, - tx - ); + const parentFolder = await folderDAL.findClosestFolder(projectId, environment, pathWithFolder, tx); // no folder found is not possible root should be their if (!parentFolder) throw new BadRequestError({ message: "Secret path not found" }); // exact folder @@ -78,24 +65,19 @@ export const secretFolderServiceFactory = ({ // this is upsert folder in a path // we are not taking snapshots of this because // snapshot will be removed from automatic for all commits to user click or cron based - const missingSegment = secretPath - .substring(parentFolder.path.length) - .split("/") - .filter(Boolean); + const missingSegment = secretPath.substring(parentFolder.path.length).split("/").filter(Boolean); if (missingSegment.length) { - const newFolders: Array = missingSegment.map( - (segment) => { - const newFolder = { - name: segment, - parentId: parentFolderId, - id: uuidv4(), - envId: env.id, - version: 1 - }; - parentFolderId = newFolder.id; - return newFolder; - } - ); + const newFolders: Array = missingSegment.map((segment) => { + const newFolder = { + name: segment, + parentId: parentFolderId, + id: uuidv4(), + envId: env.id, + version: 1 + }; + parentFolderId = newFolder.id; + return newFolder; + }); parentFolderId = newFolders.at(-1)?.id as string; const docs = await folderDAL.insertMany(newFolders, tx); await folderVersionDAL.insertMany( @@ -110,10 +92,7 @@ export const secretFolderServiceFactory = ({ } } - const doc = await folderDAL.create( - { name, envId: env.id, version: 1, parentId: parentFolderId }, - tx - ); + const doc = await folderDAL.create({ name, envId: env.id, version: 1, parentId: parentFolderId }, tx); await folderVersionDAL.create( { name: doc.name, @@ -134,12 +113,13 @@ export const secretFolderServiceFactory = ({ projectId, actor, actorId, + actorOrgId, name, environment, path: secretPath, id }: TUpdateFolderDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) @@ -149,8 +129,7 @@ export const secretFolderServiceFactory = ({ if (!parentFolder) throw new BadRequestError({ message: "Secret path not found" }); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); - if (!env) - throw new BadRequestError({ message: "Environment not found", name: "Update folder" }); + if (!env) throw new BadRequestError({ message: "Environment not found", name: "Update folder" }); const folder = await folderDAL .findOne({ envId: env.id, id, parentId: parentFolder.id }) // now folder api accepts id based change @@ -160,11 +139,7 @@ export const secretFolderServiceFactory = ({ if (!folder) throw new BadRequestError({ message: "Folder not found" }); const newFolder = await folderDAL.transaction(async (tx) => { - const [doc] = await folderDAL.update( - { envId: env.id, id: folder.id, parentId: parentFolder.id }, - { name }, - tx - ); + const [doc] = await folderDAL.update({ envId: env.id, id: folder.id, parentId: parentFolder.id }, { name }, tx); await folderVersionDAL.create( { name: doc.name, @@ -186,19 +161,19 @@ export const secretFolderServiceFactory = ({ projectId, actor, actorId, + actorOrgId, environment, path: secretPath, id }: TDeleteFolderDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); - if (!env) - throw new BadRequestError({ message: "Environment not found", name: "Create folder" }); + if (!env) throw new BadRequestError({ message: "Environment not found", name: "Create folder" }); const folder = await folderDAL.transaction(async (tx) => { const parentFolder = await folderDAL.findBySecretPath(projectId, environment, secretPath, tx); @@ -217,12 +192,13 @@ export const secretFolderServiceFactory = ({ projectId, actor, actorId, + actorOrgId, environment, path: secretPath }: TGetFolderDTO) => { // folder list is allowed to be read by anyone // permission to check does user has access - await permissionService.getProjectPermission(actor, actorId, projectId); + await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); if (!env) throw new BadRequestError({ message: "Environment not found", name: "get folders" }); diff --git a/backend/src/services/secret-folder/secret-folder-version-dal.ts b/backend/src/services/secret-folder/secret-folder-version-dal.ts index b610de359..f133308cf 100644 --- a/backend/src/services/secret-folder/secret-folder-version-dal.ts +++ b/backend/src/services/secret-folder/secret-folder-version-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TSecretFolderVersions } from "@app/db/schemas"; +import { TableName, TSecretFolderVersions } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols } from "@app/lib/knex"; @@ -14,11 +14,7 @@ export const secretFolderVersionDALFactory = (db: TDbClient) => { const findLatestVersionByFolderId = async (folderId: string, tx?: Knex) => { try { const docs = await (tx || db)(TableName.SecretFolderVersion) - .join( - TableName.SecretFolder, - `${TableName.SecretFolderVersion}.folderId`, - `${TableName.SecretFolder}.id` - ) + .join(TableName.SecretFolder, `${TableName.SecretFolderVersion}.folderId`, `${TableName.SecretFolder}.id`) .where({ parentId: folderId }) .join( (tx || db)(TableName.SecretFolderVersion) @@ -42,9 +38,7 @@ export const secretFolderVersionDALFactory = (db: TDbClient) => { const findLatestFolderVersions = async (folderIds: string[], tx?: Knex) => { try { - const docs: Array = await (tx || db)( - TableName.SecretFolderVersion - ) + const docs: Array = await (tx || db)(TableName.SecretFolderVersion) .whereIn("folderId", folderIds) .join( (tx || db)(TableName.SecretFolderVersion) diff --git a/backend/src/services/secret-import/secret-import-dal.ts b/backend/src/services/secret-import/secret-import-dal.ts index 0acbd7426..f9c6f1be7 100644 --- a/backend/src/services/secret-import/secret-import-dal.ts +++ b/backend/src/services/secret-import/secret-import-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TSecretImports } from "@app/db/schemas"; +import { TableName, TSecretImports } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; @@ -15,7 +15,7 @@ export const secretImportDALFactory = (db: TDbClient) => { const findLastImportPosition = async (folderId: string, tx?: Knex) => { const lastPos = await (tx || db)(TableName.SecretImport) .where({ folderId }) - .max({ position: "position" }) + .max("position", { as: "position" }) .first(); return lastPos?.position || 0; }; @@ -53,11 +53,7 @@ export const secretImportDALFactory = (db: TDbClient) => { try { const docs = await (tx || db)(TableName.SecretImport) .where(filter) - .join( - TableName.Environment, - `${TableName.SecretImport}.importEnv`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretImport}.importEnv`, `${TableName.Environment}.id`) .select( db.ref("*").withSchema(TableName.SecretImport) as unknown as keyof TSecretImports, db.ref("slug").withSchema(TableName.Environment), diff --git a/backend/src/services/secret-import/secret-import-fns.ts b/backend/src/services/secret-import/secret-import-fns.ts index 913f6da5b..1fa55f214 100644 --- a/backend/src/services/secret-import/secret-import-fns.ts +++ b/backend/src/services/secret-import/secret-import-fns.ts @@ -25,10 +25,15 @@ export const fnSecretsFromImports = async ({ if (!folderIds.length) { return []; } - const importedSecrets = await secretDAL.find({ - $in: { folderId: folderIds }, - type: SecretType.Shared - }); + const importedSecrets = await secretDAL.find( + { + $in: { folderId: folderIds }, + type: SecretType.Shared + }, + { + sort: [["id", "asc"]] + } + ); const importedSecsGroupByFolderId = groupBy(importedSecrets, (i) => i.folderId); return allowedImports.map(({ importPath, importEnv }, i) => ({ @@ -37,7 +42,12 @@ export const fnSecretsFromImports = async ({ environmentInfo: importEnv, folderId: importedFolders?.[i]?.id, secrets: importedFolders?.[i]?.id - ? importedSecsGroupByFolderId[importedFolders?.[i]?.id as string] + ? importedSecsGroupByFolderId[importedFolders?.[i]?.id as string].map((item) => ({ + ...item, + environment: importEnv.slug, + workspace: "", // This field should not be used, it's only here to keep the older Python SDK versions backwards compatible with the new Postgres backend. + _id: item.id // The old Python SDK depends on the _id field being returned. We return this to keep the older Python SDK versions backwards compatible with the new Postgres backend. + })) : [] })); }; diff --git a/backend/src/services/secret-import/secret-import-service.ts b/backend/src/services/secret-import/secret-import-service.ts index b4ba45d01..a519c7820 100644 --- a/backend/src/services/secret-import/secret-import-service.ts +++ b/backend/src/services/secret-import/secret-import-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; @@ -44,10 +41,11 @@ export const secretImportServiceFactory = ({ data, actor, actorId, + actorOrgId, projectId, path }: TCreateSecretImportDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); // check if user has permission to import into destination path ForbiddenError.from(permission).throwUnlessCan( @@ -69,8 +67,7 @@ export const secretImportServiceFactory = ({ // TODO(akhilmhdh-pg): updated permission check add here const [importEnv] = await projectEnvDAL.findBySlugs(projectId, [data.environment]); - if (!importEnv) - throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); + if (!importEnv) throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); const secImport = await secretImportDAL.transaction(async (tx) => { const lastPos = await secretImportDAL.findLastImportPosition(folder.id, tx); @@ -94,10 +91,11 @@ export const secretImportServiceFactory = ({ projectId, actor, actorId, + actorOrgId, data, id }: TUpdateSecretImportDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -112,8 +110,7 @@ export const secretImportServiceFactory = ({ const importedEnv = data.environment // this is get env information of new one or old one ? (await projectEnvDAL.findBySlugs(projectId, [data.environment]))?.[0] : await projectEnvDAL.findById(secImpDoc.importEnv); - if (!importedEnv) - throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); + if (!importedEnv) throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); const updatedSecImport = await secretImportDAL.transaction(async (tx) => { const secImp = await secretImportDAL.findOne({ folderId: folder.id, id }); @@ -141,9 +138,10 @@ export const secretImportServiceFactory = ({ projectId, actor, actorId, + actorOrgId, id }: TDeleteSecretImportDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -154,26 +152,18 @@ export const secretImportServiceFactory = ({ const secImport = await secretImportDAL.transaction(async (tx) => { const [doc] = await secretImportDAL.delete({ folderId: folder.id, id }, tx); - if (!doc) - throw new BadRequestError({ name: "Sec imp del", message: "Secret import doc not found" }); + if (!doc) throw new BadRequestError({ name: "Sec imp del", message: "Secret import doc not found" }); await secretImportDAL.updateAllPosition(folder.id, doc.position, -1, tx); const importEnv = await projectEnvDAL.findById(doc.importEnv); - if (!importEnv) - throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); + if (!importEnv) throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); return { ...doc, importEnv }; }); return secImport; }; - const getImports = async ({ - path, - environment, - projectId, - actor, - actorId - }: TGetSecretImportsDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const getImports = async ({ path, environment, projectId, actor, actorId, actorOrgId }: TGetSecretImportsDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -191,9 +181,10 @@ export const secretImportServiceFactory = ({ environment, projectId, actor, - actorId + actorId, + actorOrgId }: TGetSecretsFromImportDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) diff --git a/backend/src/services/secret-tag/secret-tag-dal.ts b/backend/src/services/secret-tag/secret-tag-dal.ts index 2e6b38c02..f1ae2424a 100644 --- a/backend/src/services/secret-tag/secret-tag-dal.ts +++ b/backend/src/services/secret-tag/secret-tag-dal.ts @@ -23,11 +23,7 @@ export const secretTagDALFactory = (db: TDbClient) => { const deleteTagsManySecret = async (projectId: string, secretIds: string[], tx?: Knex) => { try { const tags = await (tx || db)(TableName.JnSecretTag) - .join( - TableName.SecretTag, - `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, - `${TableName.SecretTag}.id` - ) + .join(TableName.SecretTag, `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id`) .where("projectId", projectId) .whereIn(`${TableName.Secret}Id`, secretIds) .delete() diff --git a/backend/src/services/secret-tag/secret-tag-service.ts b/backend/src/services/secret-tag/secret-tag-service.ts index 8b097e595..62ebd8a23 100644 --- a/backend/src/services/secret-tag/secret-tag-service.ts +++ b/backend/src/services/secret-tag/secret-tag-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TSecretTagDALFactory } from "./secret-tag-dal"; @@ -17,16 +14,10 @@ type TSecretTagServiceFactoryDep = { export type TSecretTagServiceFactory = ReturnType; -export const secretTagServiceFactory = ({ - secretTagDAL, - permissionService -}: TSecretTagServiceFactoryDep) => { - const createTag = async ({ name, slug, actor, color, actorId, projectId }: TCreateTagDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Tags - ); +export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSecretTagServiceFactoryDep) => { + const createTag = async ({ name, slug, actor, color, actorId, actorOrgId, projectId }: TCreateTagDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Tags); const existingTag = await secretTagDAL.findOne({ slug }); if (existingTag) throw new BadRequestError({ message: "Tag already exist" }); @@ -41,32 +32,22 @@ export const secretTagServiceFactory = ({ return newTag; }; - const deleteTag = async ({ actorId, actor, id }: TDeleteTagDTO) => { + const deleteTag = async ({ actorId, actor, actorOrgId, id }: TDeleteTagDTO) => { const tag = await secretTagDAL.findById(id); if (!tag) throw new BadRequestError({ message: "Tag doesn't exist" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - tag.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Tags - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, tag.projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags); const deletedTag = await secretTagDAL.deleteById(tag.id); return deletedTag; }; - const getProjectTags = async ({ actor, actorId, projectId }: TListProjectTagsDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Tags - ); + const getProjectTags = async ({ actor, actorId, actorOrgId, projectId }: TListProjectTagsDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); - const tags = await secretTagDAL.find({ projectId }); + const tags = await secretTagDAL.find({ projectId }, { sort: [["createdAt", "asc"]] }); return tags; }; diff --git a/backend/src/services/secret/secret-dal.ts b/backend/src/services/secret/secret-dal.ts index fbd5f446c..ba65033cb 100644 --- a/backend/src/services/secret/secret-dal.ts +++ b/backend/src/services/secret/secret-dal.ts @@ -11,17 +11,9 @@ export type TSecretDALFactory = ReturnType; export const secretDALFactory = (db: TDbClient) => { const secretOrm = ormify(db, TableName.Secret); - const update = async ( - filter: Partial, - data: Omit, - tx?: Knex - ) => { + const update = async (filter: Partial, data: Omit, tx?: Knex) => { try { - const sec = await (tx || db)(TableName.Secret) - .where(filter) - .update(data) - .increment("version", 1) - .returning("*"); + const sec = await (tx || db)(TableName.Secret).where(filter).update(data).increment("version", 1).returning("*"); return sec; } catch (error) { throw new DatabaseError({ error, name: "update secret" }); @@ -30,10 +22,7 @@ export const secretDALFactory = (db: TDbClient) => { // the idea is to use postgres specific function // insert with id this will cause a conflict then merge the data - const bulkUpdate = async ( - data: Array<{ filter: Partial; data: TSecretsUpdate }>, - tx?: Knex - ) => { + const bulkUpdate = async (data: Array<{ filter: Partial; data: TSecretsUpdate }>, tx?: Knex) => { try { const secs = await Promise.all( data.map(async ({ filter, data: updateData }) => { @@ -63,7 +52,7 @@ export const secretDALFactory = (db: TDbClient) => { .where({ folderId }) .where((bd) => { data.forEach((el) => { - bd.orWhere({ + void bd.orWhere({ secretBlindIndex: el.blindIndex, type: el.type, ...(el.type === SecretType.Personal ? { userId } : {}) @@ -89,27 +78,20 @@ export const secretDALFactory = (db: TDbClient) => { const secs = await (tx || db)(TableName.Secret) .where({ folderId }) .where((bd) => { - bd.whereNull("userId").orWhere({ userId: userId || null }); + void bd.whereNull("userId").orWhere({ userId: userId || null }); }) - .leftJoin( - TableName.JnSecretTag, - `${TableName.Secret}.id`, - `${TableName.JnSecretTag}.${TableName.Secret}Id` - ) - .leftJoin( - TableName.SecretTag, - `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, - `${TableName.SecretTag}.id` - ) + .leftJoin(TableName.JnSecretTag, `${TableName.Secret}.id`, `${TableName.JnSecretTag}.${TableName.Secret}Id`) + .leftJoin(TableName.SecretTag, `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id`) .select(selectAllTableCols(TableName.Secret)) .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")) - .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")); + .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")) + .orderBy("id", "asc"); const data = sqlNestRelationships({ data: secs, key: "id", - parentMapper: (el) => SecretsSchema.parse(el), + parentMapper: (el) => ({ _id: el.id, ...SecretsSchema.parse(el) }), childrenMapper: [ { key: "tagId", @@ -144,7 +126,7 @@ export const secretDALFactory = (db: TDbClient) => { if (el.type === SecretType.Personal && !userId) { throw new BadRequestError({ message: "Missing personal user id" }); } - bd.orWhere({ + void bd.orWhere({ secretBlindIndex: el.blindIndex, type: el.type, userId: el.type === SecretType.Personal ? userId : null diff --git a/backend/src/services/secret/secret-fns.ts b/backend/src/services/secret/secret-fns.ts index 9abaa8424..0f6caa248 100644 --- a/backend/src/services/secret/secret-fns.ts +++ b/backend/src/services/secret/secret-fns.ts @@ -8,10 +8,7 @@ import { buildSecretBlindIndexFromName, decryptSymmetric128BitHexKeyUTF8 } from import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretDALFactory } from "./secret-dal"; -export const generateSecretBlindIndexBySalt = async ( - secretName: string, - secretBlindIndexDoc: TSecretBlindIndexes -) => { +export const generateSecretBlindIndexBySalt = async (secretName: string, secretBlindIndexDoc: TSecretBlindIndexes) => { const appCfg = getConfig(); const secretBlindIndex = await buildSecretBlindIndexFromName({ secretName, @@ -32,12 +29,7 @@ type TInterpolateSecretArg = { folderDAL: Pick; }; -export const interpolateSecrets = ({ - projectId, - secretEncKey, - secretDAL, - folderDAL -}: TInterpolateSecretArg) => { +export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderDAL }: TInterpolateSecretArg) => { const fetchSecretsCrossEnv = () => { const fetchCache: Record> = {}; @@ -197,10 +189,7 @@ export const interpolateSecrets = ({ return expandSecrets; }; -export const decryptSecretRaw = ( - secret: TSecrets & { workspace: string; environment: string }, - key: string -) => { +export const decryptSecretRaw = (secret: TSecrets & { workspace: string; environment: string }, key: string) => { const secretKey = decryptSymmetric128BitHexKeyUTF8({ ciphertext: secret.secretKeyCiphertext, iv: secret.secretKeyIV, diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index 71b2444ae..b797b7caf 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -23,11 +23,7 @@ import { TWebhookDALFactory } from "../webhook/webhook-dal"; import { fnTriggerWebhook } from "../webhook/webhook-fns"; import { TSecretDALFactory } from "./secret-dal"; import { interpolateSecrets } from "./secret-fns"; -import { - TCreateSecretReminderDTO, - THandleReminderDTO, - TRemoveSecretReminderDTO -} from "./secret-types"; +import { TCreateSecretReminderDTO, THandleReminderDTO, TRemoveSecretReminderDTO } from "./secret-types"; export type TSecretQueueFactory = ReturnType; @@ -105,20 +101,13 @@ export const secretQueueFactory = ({ QueueJobs.SecretReminder, { // on prod it this will be in days, in development this will be second - every: - appCfg.NODE_ENV === "development" - ? secondsToMillis(dto.repeatDays) - : daysToMillisecond(dto.repeatDays) + every: appCfg.NODE_ENV === "development" ? secondsToMillis(dto.repeatDays) : daysToMillisecond(dto.repeatDays) }, `reminder-${dto.secretId}` ); }; - const addSecretReminder = async ({ - oldSecret, - newSecret, - projectId - }: TCreateSecretReminderDTO) => { + const addSecretReminder = async ({ oldSecret, newSecret, projectId }: TCreateSecretReminderDTO) => { try { const appCfg = getConfig(); @@ -179,8 +168,7 @@ export const secretQueueFactory = ({ if (newSecret.type !== "personal" && secretReminderRepeatDays !== undefined) { if ( - (secretReminderRepeatDays && - oldSecret.secretReminderRepeatDays !== secretReminderRepeatDays) || + (secretReminderRepeatDays && oldSecret.secretReminderRepeatDays !== secretReminderRepeatDays) || (secretReminderNote && oldSecret.secretReminderNote !== secretReminderNote) ) { await addSecretReminder({ @@ -212,10 +200,7 @@ export const secretQueueFactory = ({ secretDAL, folderDAL }); - const content: Record< - string, - { value: string; comment?: string; skipMultilineEncoding?: boolean } - > = {}; + const content: Record = {}; importedSecrets.forEach(({ secrets: secs }) => { secs.forEach((secret) => { @@ -294,8 +279,7 @@ export const secretQueueFactory = ({ const integrations = await integrationDAL.findByProjectIdV2(projectId, environment); const toBeSyncedIntegrations = integrations.filter( - ({ secretPath: integrationSecPath, isActive }) => - isActive && isSamePath(secretPath, integrationSecPath) + ({ secretPath: integrationSecPath, isActive }) => isActive && isSamePath(secretPath, integrationSecPath) ); if (!integrations.length) return; @@ -309,16 +293,10 @@ export const secretQueueFactory = ({ }; const botKey = await projectBotService.getBotKey(projectId); - const { accessToken, accessId } = await integrationAuthService.getIntegrationAccessToken( - integrationAuth, - botKey - ); - const secrets = await getIntegrationSecrets( - { environment, projectId, secretPath, folderId: folder.id }, - botKey - ); + const { accessToken, accessId } = await integrationAuthService.getIntegrationAccessToken(integrationAuth, botKey); + const secrets = await getIntegrationSecrets({ environment, projectId, secretPath, folderId: folder.id }, botKey); const suffixedSecrets: typeof secrets = {}; - const metadata = integration.metadata as Record; + const metadata = integration.metadata as Record; if (metadata) { Object.keys(secrets).forEach((key) => { const prefix = metadata?.secretPrefix || ""; @@ -353,25 +331,19 @@ export const secretQueueFactory = ({ const project = await projectDAL.findById(projectId); if (!organization) { - logger.info( - `secretReminderQueue.process: [secretDocument=${data.secretId}] no organization found` - ); + logger.info(`secretReminderQueue.process: [secretDocument=${data.secretId}] no organization found`); return; } if (!project) { - logger.info( - `secretReminderQueue.process: [secretDocument=${data.secretId}] no project found` - ); + logger.info(`secretReminderQueue.process: [secretDocument=${data.secretId}] no project found`); return; } const projectMembers = await projectMembershipDAL.findAllProjectMembers(projectId); if (!projectMembers || !projectMembers.length) { - logger.info( - `secretReminderQueue.process: [secretDocument=${data.secretId}] no project members found` - ); + logger.info(`secretReminderQueue.process: [secretDocument=${data.secretId}] no project members found`); return; } diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index 029db815b..ff9dce9a8 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -1,17 +1,8 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { - SecretEncryptionAlgo, - SecretKeyEncoding, - SecretsSchema, - SecretType, - TableName -} from "@app/db/schemas"; +import { SecretEncryptionAlgo, SecretKeyEncoding, SecretsSchema, SecretType, TableName } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { getConfig } from "@app/lib/config/env"; import { buildSecretBlindIndexFromName, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; @@ -46,7 +37,6 @@ import { TGetSecretsDTO, TGetSecretsRawDTO, TGetSecretVersionsDTO, - TListSecretVersionDTO, TUpdateBulkSecretDTO, TUpdateSecretDTO, TUpdateSecretRawDTO @@ -58,17 +48,11 @@ type TSecretServiceFactoryDep = { secretDAL: TSecretDALFactory; secretTagDAL: TSecretTagDALFactory; secretVersionDAL: TSecretVersionDALFactory; - folderDAL: Pick< - TSecretFolderDALFactory, - "findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" - >; + folderDAL: Pick; secretBlindIndexDAL: TSecretBlindIndexDALFactory; permissionService: Pick; snapshotService: Pick; - secretQueueService: Pick< - TSecretQueueFactory, - "syncSecrets" | "handleSecretReminder" | "removeSecretReminder" - >; + secretQueueService: Pick; projectBotService: Pick; secretImportDAL: Pick; secretVersionTagDAL: Pick; @@ -93,8 +77,7 @@ export const secretServiceFactory = ({ const appCfg = getConfig(); const secretBlindIndexDoc = await secretBlindIndexDAL.findOne({ projectId }); - if (!secretBlindIndexDoc) - throw new BadRequestError({ message: "Blind index not found", name: "Create secret" }); + if (!secretBlindIndexDoc) throw new BadRequestError({ message: "Blind index not found", name: "Create secret" }); const secretBlindIndex = await buildSecretBlindIndexFromName({ secretName, @@ -116,18 +99,18 @@ export const secretServiceFactory = ({ inputSecrets.map(({ tags, ...el }) => ({ ...el, folderId })), tx ); - const newSecretGroupByBlindIndex = groupBy(newSecrets, (item) => item.secretBlindIndex); + const newSecretGroupByBlindIndex = groupBy(newSecrets, (item) => item.secretBlindIndex as string); const newSecretTags = inputSecrets.flatMap(({ tags: secretTags = [], secretBlindIndex }) => secretTags.map((tag) => ({ [`${TableName.SecretTag}Id` as const]: tag, - [`${TableName.Secret}Id` as const]: newSecretGroupByBlindIndex[secretBlindIndex][0].id + [`${TableName.Secret}Id` as const]: newSecretGroupByBlindIndex[secretBlindIndex as string][0].id })) ); const secretVersions = await secretVersionDAL.insertMany( inputSecrets.map(({ tags, ...el }) => ({ ...el, folderId, - secretId: newSecretGroupByBlindIndex[el.secretBlindIndex][0].id + secretId: newSecretGroupByBlindIndex[el.secretBlindIndex as string][0].id })), tx ); @@ -141,15 +124,10 @@ export const secretServiceFactory = ({ await secretVersionTagDAL.insertMany(newSecretVersionTags, tx); } - return newSecrets; + return newSecrets.map((secret) => ({ ...secret, _id: secret.id })); }; - const fnSecretBulkUpdate = async ({ - tx, - inputSecrets, - folderId, - projectId - }: TFnSecretBulkUpdate) => { + const fnSecretBulkUpdate = async ({ tx, inputSecrets, folderId, projectId }: TFnSecretBulkUpdate) => { const newSecrets = await secretDAL.bulkUpdate( inputSecrets.map(({ filter, data: { tags, ...data } }) => ({ filter: { ...filter, folderId }, @@ -190,15 +168,10 @@ export const secretServiceFactory = ({ } } - return newSecrets; + return newSecrets.map((secret) => ({ ...secret, _id: secret.id })); }; - const fnSecretBulkDelete = async ({ - folderId, - inputSecrets, - tx, - actorId - }: TFnSecretBulkDelete) => { + const fnSecretBulkDelete = async ({ folderId, inputSecrets, tx, actorId }: TFnSecretBulkDelete) => { const deletedSecrets = await secretDAL.deleteMany( inputSecrets.map(({ type, secretBlindIndex }) => ({ blindIndex: secretBlindIndex, @@ -241,9 +214,7 @@ export const secretServiceFactory = ({ }: TFnSecretBlindIndexCheck) => { const blindIndex2KeyName: Record = {}; // used at audit log point const keyName2BlindIndex = await Promise.all( - inputSecrets.map(({ secretName }) => - generateSecretBlindIndexBySalt(secretName, blindIndexCfg) - ) + inputSecrets.map(({ secretName }) => generateSecretBlindIndexBySalt(secretName, blindIndexCfg)) ).then((blindIndexes) => blindIndexes.reduce>((prev, curr, i) => { // eslint-disable-next-line @@ -252,6 +223,7 @@ export const secretServiceFactory = ({ return prev; }, {}) ); + if (inputSecrets.some(({ type }) => type === SecretType.Personal) && !userId) { throw new BadRequestError({ message: "Missing user id for personal secret" }); } @@ -268,18 +240,16 @@ export const secretServiceFactory = ({ if (isNew) { if (secrets.length) throw new BadRequestError({ message: "Secret already exist" }); } else if (secrets.length !== inputSecrets.length) - throw new BadRequestError({ message: "Secret not found" }); + throw new BadRequestError({ + message: `Secret not found: blind index ${JSON.stringify(keyName2BlindIndex)}` + }); return { blindIndex2KeyName, keyName2BlindIndex, secrets }; }; // this is used when secret blind index already exist // mainly for secret approval - const fnSecretBlindIndexCheckV2 = async ({ - inputSecrets, - folderId, - userId - }: TFnSecretBlindIndexCheckV2) => { + const fnSecretBlindIndexCheckV2 = async ({ inputSecrets, folderId, userId }: TFnSecretBlindIndexCheckV2) => { if (inputSecrets.some(({ type }) => type === SecretType.Personal) && !userId) { throw new BadRequestError({ message: "Missing user id for personal secret" }); } @@ -291,7 +261,7 @@ export const secretServiceFactory = ({ })), userId ); - const secsGroupedByBlindIndex = groupBy(secrets, (i) => i.secretBlindIndex); + const secsGroupedByBlindIndex = groupBy(secrets, (i) => i.secretBlindIndex as string); return { secsGroupedByBlindIndex, secrets }; }; @@ -300,11 +270,12 @@ export const secretServiceFactory = ({ path, actor, actorId, + actorOrgId, environment, projectId, ...inputSecret }: TCreateSecretDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -315,8 +286,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); if (ActorType.USER !== actor && inputSecret.type === SecretType.Personal) { throw new BadRequestError({ message: "Must be user to create personal secret" }); @@ -345,11 +315,8 @@ export const secretServiceFactory = ({ // validate tags // fetch all tags and if not same count throw error meaning one was invalid tags - const tags = inputSecret.tags - ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) - : []; - if ((inputSecret.tags || []).length !== tags.length) - throw new BadRequestError({ message: "Tag not found" }); + const tags = inputSecret.tags ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) : []; + if ((inputSecret.tags || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" }); const { secretName, type, ...el } = inputSecret; const secret = await secretDAL.transaction((tx) => @@ -381,11 +348,12 @@ export const secretServiceFactory = ({ path, actor, actorId, + actorOrgId, environment, projectId, ...inputSecret }: TUpdateSecretDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -396,8 +364,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); if (ActorType.USER !== actor && inputSecret.type === SecretType.Personal) { throw new BadRequestError({ message: "Must be user to create personal secret" }); @@ -434,11 +401,8 @@ export const secretServiceFactory = ({ projectId }); - const tags = inputSecret.tags - ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) - : []; - if ((inputSecret.tags || []).length !== tags.length) - throw new BadRequestError({ message: "Tag not found" }); + const tags = inputSecret.tags ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) : []; + if ((inputSecret.tags || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" }); const { secretName, ...el } = inputSecret; const updatedSecret = await secretDAL.transaction(async (tx) => @@ -484,11 +448,12 @@ export const secretServiceFactory = ({ path, actor, actorId, + actorOrgId, environment, projectId, ...inputSecret }: TDeleteSecretDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -499,8 +464,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); if (ActorType.USER !== actor && inputSecret.type === SecretType.Personal) { throw new BadRequestError({ message: "Must be user to create personal secret" }); @@ -532,7 +496,7 @@ export const secretServiceFactory = ({ await secretQueueService.syncSecrets({ secretPath: path, projectId, environment }); // TODO(akhilmhdh-pg): licence check, posthog service and snapshot - return { ...deletedSecret[0], workspace: projectId, environment }; + return { ...deletedSecret[0], _id: deletedSecret[0].id, workspace: projectId, environment }; }; const getSecrets = async ({ @@ -541,9 +505,10 @@ export const secretServiceFactory = ({ environment, projectId, actor, + actorOrgId, includeImports }: TGetSecretsDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -584,6 +549,7 @@ export const secretServiceFactory = ({ const getSecretByName = async ({ actorId, actor, + actorOrgId, projectId, environment, path, @@ -592,7 +558,7 @@ export const secretServiceFactory = ({ version, includeImports }: TGetASecretDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -603,18 +569,30 @@ export const secretServiceFactory = ({ const secretBlindIndex = await interalGenSecBlindIndexByName(projectId, secretName); - const secret = await (typeof version !== undefined + // Case: The old python SDK uses incorrect logic https://github.com/Infisical/infisical-python/blob/main/infisical/client/infisicalclient.py#L89. + // Fetch secrets using service tokens cannot fetch personal secrets, only shared. + // The mongo backend used to correct this mistake, this line also adds it to current backend + // Mongo backend check: https://github.com/Infisical/infisical-mongo/blob/main/backend/src/helpers/secrets.ts#L658 + let secretType = type; + if (actor === ActorType.SERVICE) { + logger.info( + `secretServiceFactory: overriding secret type for service token [projectId=${projectId}] [factoryFunctionName=getSecretByName]` + ); + secretType = SecretType.Shared; + } + + const secret = await (version === undefined ? secretDAL.findOne({ folderId, - type, - userId: type === SecretType.Personal ? actorId : null, + type: secretType, + userId: secretType === SecretType.Personal ? actorId : null, secretBlindIndex }) : secretVersionDAL .findOne({ folderId, - type, - userId: type === SecretType.Personal ? actorId : null, + type: secretType, + userId: secretType === SecretType.Personal ? actorId : null, secretBlindIndex }) .then((el) => SecretsSchema.parse({ ...el, id: el.secretId }))); @@ -661,11 +639,12 @@ export const secretServiceFactory = ({ path, actor, actorId, + actorOrgId, environment, projectId, secrets: inputSecrets }: TCreateBulkSecretDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -676,8 +655,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({ inputSecrets, @@ -716,11 +694,12 @@ export const secretServiceFactory = ({ path, actor, actorId, + actorOrgId, environment, projectId, secrets: inputSecrets }: TUpdateBulkSecretDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -731,8 +710,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({ inputSecrets, @@ -789,9 +767,10 @@ export const secretServiceFactory = ({ environment, projectId, actor, - actorId + actorId, + actorOrgId }: TDeleteBulkSecretDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -802,8 +781,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({ inputSecrets, @@ -831,52 +809,24 @@ export const secretServiceFactory = ({ return secretsDeleted; }; - const listSecretVersionsBySecretId = async ({ - actorId, - actor, - limit, - offset, - secretId - }: TListSecretVersionDTO) => { - const secret = await secretDAL.findById(secretId); - if (!secret) throw new BadRequestError({ message: "Failed to find secret" }); - - const folder = await folderDAL.findById(secret.folderId); - if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - folder.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); - - const secretVersions = await secretVersionDAL.find( - { secretId }, - { limit, offset, sort: [["createdAt", "desc"]] } - ); - return secretVersions; - }; - const getSecretsRaw = async ({ projectId, path, actor, actorId, + actorOrgId, environment, includeImports }: TGetSecretsRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const { secrets, imports } = await getSecrets({ actorId, projectId, environment, actor, + actorOrgId, path, includeImports }); @@ -899,19 +849,20 @@ export const secretServiceFactory = ({ environment, projectId, actorId, + actorOrgId, secretName, includeImports, version }: TGetASecretRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const secret = await getSecretByName({ actorId, projectId, environment, actor, + actorOrgId, path, secretName, type, @@ -927,6 +878,7 @@ export const secretServiceFactory = ({ projectId, environment, actor, + actorOrgId, type, secretPath, secretValue, @@ -934,8 +886,7 @@ export const secretServiceFactory = ({ skipMultilineEncoding }: TCreateSecretRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretName, botKey); const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey); @@ -949,6 +900,7 @@ export const secretServiceFactory = ({ path: secretPath, actor, actorId, + actorOrgId, secretKeyCiphertext: secretKeyEncrypted.ciphertext, secretKeyIV: secretKeyEncrypted.iv, secretKeyTag: secretKeyEncrypted.tag, @@ -973,14 +925,14 @@ export const secretServiceFactory = ({ projectId, environment, actor, + actorOrgId, type, secretPath, secretValue, skipMultilineEncoding }: TUpdateSecretRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey); @@ -992,6 +944,7 @@ export const secretServiceFactory = ({ path: secretPath, actor, actorId, + actorOrgId, secretValueCiphertext: secretValueEncrypted.ciphertext, secretValueIV: secretValueEncrypted.iv, secretValueTag: secretValueEncrypted.tag, @@ -1010,12 +963,12 @@ export const secretServiceFactory = ({ projectId, environment, actor, + actorOrgId, type, secretPath }: TDeleteSecretRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const secret = await deleteSecret({ secretName, @@ -1024,7 +977,8 @@ export const secretServiceFactory = ({ type, path: secretPath, actor, - actorId + actorId, + actorOrgId }); await snapshotService.performSnapshot(secret.folderId); @@ -1036,6 +990,7 @@ export const secretServiceFactory = ({ const getSecretVersions = async ({ actorId, actor, + actorOrgId, limit = 20, offset = 0, secretId @@ -1046,20 +1001,10 @@ export const secretServiceFactory = ({ const folder = await folderDAL.findById(secret.folderId); if (!folder) throw new BadRequestError({ message: "Failed to find secret" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - folder.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, folder.projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); - const secretVersions = await secretVersionDAL.find( - { secretId }, - { offset, limit, sort: [["createdAt", "desc"]] } - ); + const secretVersions = await secretVersionDAL.find({ secretId }, { offset, limit, sort: [["createdAt", "desc"]] }); return secretVersions; }; @@ -1077,7 +1022,6 @@ export const secretServiceFactory = ({ createSecretRaw, updateSecretRaw, deleteSecretRaw, - listSecretVersionsBySecretId, getSecretVersions, // external services function fnSecretBulkDelete, diff --git a/backend/src/services/secret/secret-types.ts b/backend/src/services/secret/secret-types.ts index f5fdf32a2..50f678172 100644 --- a/backend/src/services/secret/secret-types.ts +++ b/backend/src/services/secret/secret-types.ts @@ -1,20 +1,11 @@ import { Knex } from "knex"; -import { - SecretType, - TSecretBlindIndexes, - TSecrets, - TSecretsInsert, - TSecretsUpdate -} from "@app/db/schemas"; +import { SecretType, TSecretBlindIndexes, TSecrets, TSecretsInsert, TSecretsUpdate } from "@app/db/schemas"; import { TProjectPermission } from "@app/lib/types"; type TPartialSecret = Pick; -type TPartialInputSecret = Pick< - TSecrets, - "type" | "secretReminderNote" | "secretReminderRepeatDays" | "id" ->; +type TPartialInputSecret = Pick; export type TCreateSecretDTO = { secretName: string; @@ -137,12 +128,6 @@ export type TDeleteBulkSecretDTO = { }>; } & TProjectPermission; -export type TListSecretVersionDTO = { - secretId: string; - offset?: number; - limit?: number; -} & Omit; - export type TGetSecretsRawDTO = { path: string; environment: string; diff --git a/backend/src/services/secret/secret-version-dal.ts b/backend/src/services/secret/secret-version-dal.ts index 339b8346b..7a6695e18 100644 --- a/backend/src/services/secret/secret-version-dal.ts +++ b/backend/src/services/secret/secret-version-dal.ts @@ -38,17 +38,11 @@ export const secretVersionDALFactory = (db: TDbClient) => { const findLatestVersionMany = async (folderId: string, secretIds: string[], tx?: Knex) => { try { - const docs: Array = await (tx || db)( - TableName.SecretVersion - ) + const docs: Array = await (tx || db)(TableName.SecretVersion) .where("folderId", folderId) .whereIn(`${TableName.SecretVersion}.secretId`, secretIds) .join( - (tx || db)(TableName.SecretVersion) - .groupBy("secretId") - .max("version") - .select("secretId") - .as("latestVersion"), + (tx || db)(TableName.SecretVersion).groupBy("secretId").max("version").select("secretId").as("latestVersion"), (bd) => { bd.on(`${TableName.SecretVersion}.secretId`, "latestVersion.secretId").andOn( `${TableName.SecretVersion}.version`, diff --git a/backend/src/services/service-token/service-token-service.ts b/backend/src/services/service-token/service-token-service.ts index 48ead50e6..76d33bd6b 100644 --- a/backend/src/services/service-token/service-token-service.ts +++ b/backend/src/services/service-token/service-token-service.ts @@ -4,15 +4,13 @@ import { ForbiddenError, subject } from "@casl/ability"; import bcrypt from "bcrypt"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { ActorType } from "../auth/auth-type"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; +import { TUserDALFactory } from "../user/user-dal"; import { TServiceTokenDALFactory } from "./service-token-dal"; import { TCreateServiceTokenDTO, @@ -23,6 +21,7 @@ import { type TServiceTokenServiceFactoryDep = { serviceTokenDAL: TServiceTokenDALFactory; + userDAL: TUserDALFactory; permissionService: Pick; projectEnvDAL: Pick; }; @@ -31,6 +30,7 @@ export type TServiceTokenServiceFactory = ReturnType { @@ -39,6 +39,7 @@ export const serviceTokenServiceFactory = ({ tag, name, actor, + actorOrgId, scopes, actorId, projectId, @@ -46,26 +47,22 @@ export const serviceTokenServiceFactory = ({ permissions, encryptedKey }: TCreateServiceTokenDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.ServiceTokens - ); - + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens); + scopes.forEach(({ environment, secretPath }) => { ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); - }) - + }); + const appCfg = getConfig(); // validates env const scopeEnvs = [...new Set(scopes.map(({ environment }) => environment))]; const inputEnvs = await projectEnvDAL.findBySlugs(projectId, scopeEnvs); - if (inputEnvs.length !== scopeEnvs.length) - throw new BadRequestError({ message: "Environment not found" }); + if (inputEnvs.length !== scopeEnvs.length) throw new BadRequestError({ message: "Environment not found" }); const secret = crypto.randomBytes(16).toString("hex"); const secretHash = await bcrypt.hash(secret, appCfg.SALT_ROUNDS); @@ -94,46 +91,39 @@ export const serviceTokenServiceFactory = ({ return { token, serviceToken }; }; - const deleteServiceToken = async ({ actorId, actor, id }: TDeleteServiceTokenDTO) => { + const deleteServiceToken = async ({ actorId, actor, actorOrgId, id }: TDeleteServiceTokenDTO) => { const serviceToken = await serviceTokenDAL.findById(id); if (!serviceToken) throw new BadRequestError({ message: "Token not found" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - serviceToken.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.ServiceTokens + serviceToken.projectId, + actorOrgId ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens); const deletedServiceToken = await serviceTokenDAL.deleteById(id); return deletedServiceToken; }; const getServiceToken = async ({ actor, actorId }: TGetServiceTokenInfoDTO) => { - if (actor !== ActorType.SERVICE) - throw new BadRequestError({ message: "Service token not found" }); + if (actor !== ActorType.SERVICE) throw new BadRequestError({ message: "Service token not found" }); const serviceToken = await serviceTokenDAL.findById(actorId); if (!serviceToken) throw new BadRequestError({ message: "Token not found" }); - return serviceToken; + const serviceTokenUser = await userDAL.findById(serviceToken.createdBy); + if (!serviceTokenUser) throw new BadRequestError({ message: "Service token user not found" }); + + return { serviceToken, user: serviceTokenUser }; }; - const getProjectServiceTokens = async ({ - actorId, - actor, - projectId - }: TProjectServiceTokensDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.ServiceTokens - ); + const getProjectServiceTokens = async ({ actorId, actor, actorOrgId, projectId }: TProjectServiceTokensDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens); - const tokens = await serviceTokenDAL.find({ projectId }); + const tokens = await serviceTokenDAL.find({ projectId }, { sort: [["createdAt", "desc"]] }); return tokens; }; diff --git a/backend/src/services/smtp/smtp-service.ts b/backend/src/services/smtp/smtp-service.ts index 37c5a98a9..142993ceb 100644 --- a/backend/src/services/smtp/smtp-service.ts +++ b/backend/src/services/smtp/smtp-service.ts @@ -41,8 +41,8 @@ export const getTlsOption = (host?: SmtpHost | string, secure?: boolean) => { if (!secure) return { secure: false }; if (!host) return { secure: true }; - if (host === SmtpHost.Sendgrid) { - return { secure: true, port: 465}; // more details here https://nodemailer.com/smtp/ + if ((host as SmtpHost) === SmtpHost.Sendgrid) { + return { secure: true, port: 465 }; // more details here https://nodemailer.com/smtp/ } if (host.includes("amazonaws.com")) { return { tls: { ciphers: "TLSv1.2" } }; diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts index a549cfac5..1144bd414 100644 --- a/backend/src/services/super-admin/super-admin-service.ts +++ b/backend/src/services/super-admin/super-admin-service.ts @@ -1,4 +1,5 @@ import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas"; +import { getConfig } from "@app/lib/config/env"; import { BadRequestError } from "@app/lib/errors"; import { TAuthLoginFactory } from "../auth/auth-login-service"; @@ -17,12 +18,8 @@ type TSuperAdminServiceFactoryDep = { export type TSuperAdminServiceFactory = ReturnType; -let serverCfg: Readonly; -export const getServerCfg = () => { - if (!serverCfg) - throw new BadRequestError({ name: "Get server cfg", message: "Server cfg not initialized" }); - return serverCfg; -}; +// eslint-disable-next-line +export let getServerCfg: () => Promise; export const superAdminServiceFactory = ({ serverCfgDAL, @@ -31,19 +28,18 @@ export const superAdminServiceFactory = ({ orgService }: TSuperAdminServiceFactoryDep) => { const initServerCfg = async () => { - serverCfg = await serverCfgDAL.findOne({}); - if (!serverCfg) { - const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true }); - serverCfg = newCfg; - return newCfg; - } - return serverCfg; + // TODO(akhilmhdh): bad pattern time less change this later to me itself + getServerCfg = () => serverCfgDAL.findOne({}); + + const serverCfg = await serverCfgDAL.findOne({}); + if (serverCfg) return; + const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true }); + return newCfg; }; const updateServerCfg = async (data: TSuperAdminUpdate) => { + const serverCfg = await getServerCfg(); const cfg = await serverCfgDAL.updateById(serverCfg.id, data); - serverCfg = cfg; - Object.freeze(serverCfg); return cfg; }; @@ -63,9 +59,9 @@ export const superAdminServiceFactory = ({ ip, userAgent }: TAdminSignUpDTO) => { + const appCfg = getConfig(); const existingUser = await userDAL.findOne({ email }); - if (existingUser) - throw new BadRequestError({ name: "Admin sign up", message: "User already exist" }); + if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exist" }); const userInfo = await userDAL.transaction(async (tx) => { const newUser = await userDAL.create( @@ -97,10 +93,18 @@ export const superAdminServiceFactory = ({ ); return { user: newUser, enc: userEnc }; }); - await orgService.createOrganization(userInfo.user.id, userInfo.user.email, "Admin Org"); + + const initialOrganizationName = appCfg.INITIAL_ORGANIZATION_NAME ?? "Admin Org"; + + await orgService.createOrganization(userInfo.user.id, userInfo.user.email, initialOrganizationName); await updateServerCfg({ initialized: true }); - const token = await authService.generateUserTokens(userInfo.user, ip, userAgent); + const token = await authService.generateUserTokens({ + user: userInfo.user, + ip, + userAgent, + organizationId: undefined + }); // TODO(akhilmhdh-pg): telemetry service return { token, user: userInfo }; }; diff --git a/backend/src/services/telemetry/telemetry-service.ts b/backend/src/services/telemetry/telemetry-service.ts index 82e912deb..c386abd95 100644 --- a/backend/src/services/telemetry/telemetry-service.ts +++ b/backend/src/services/telemetry/telemetry-service.ts @@ -51,7 +51,7 @@ To opt into telemetry, you can set "TELEMETRY_ENABLED=true" within the environme } }; - const sendPostHogEvents = async (event: TPostHogEvent) => { + const sendPostHogEvents = (event: TPostHogEvent) => { if (postHog) { postHog.capture({ event: event.event, diff --git a/backend/src/services/user/user-dal.ts b/backend/src/services/user/user-dal.ts index 8051cee28..0de490399 100644 --- a/backend/src/services/user/user-dal.ts +++ b/backend/src/services/user/user-dal.ts @@ -24,11 +24,7 @@ export const userDALFactory = (db: TDbClient) => { try { return await db(TableName.Users) .where({ email }) - .join( - TableName.UserEncryptionKey, - `${TableName.Users}.id`, - `${TableName.UserEncryptionKey}.userId` - ) + .join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`) .first(); } catch (error) { throw new DatabaseError({ error, name: "Find user enc by email" }); @@ -39,11 +35,7 @@ export const userDALFactory = (db: TDbClient) => { try { const user = await db(TableName.Users) .where(`${TableName.Users}.id`, userId) - .join( - TableName.UserEncryptionKey, - `${TableName.Users}.id`, - `${TableName.UserEncryptionKey}.userId` - ) + .join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`) .first(); if (user?.id) { // change to user id @@ -64,11 +56,7 @@ export const userDALFactory = (db: TDbClient) => { } }; - const updateUserEncryptionByUserId = async ( - userId: string, - data: TUserEncryptionKeysUpdate, - tx?: Knex - ) => { + const updateUserEncryptionByUserId = async (userId: string, data: TUserEncryptionKeysUpdate, tx?: Knex) => { try { const [userEnc] = await (tx || db)(TableName.UserEncryptionKey) .where({ userId }) @@ -86,10 +74,7 @@ export const userDALFactory = (db: TDbClient) => { tx?: Knex ) => { try { - const [userEnc] = await (tx - ? tx(TableName.UserEncryptionKey) - : db(TableName.UserEncryptionKey) - ) + const [userEnc] = await (tx ? tx(TableName.UserEncryptionKey) : db(TableName.UserEncryptionKey)) // if user insert make sure to pass all required data .insert({ userId, ...data } as TUserEncryptionKeys) .onConflict("userId") diff --git a/backend/src/services/user/user-service.ts b/backend/src/services/user/user-service.ts index 81c7ea9d2..eebfd958f 100644 --- a/backend/src/services/user/user-service.ts +++ b/backend/src/services/user/user-service.ts @@ -30,17 +30,6 @@ export const userServiceFactory = ({ userDAL }: TUserServiceFactoryDep) => { const user = await userDAL.findById(userId); if (!user) throw new BadRequestError({ name: "Update auth methods" }); - const hasSamlEnabled = user?.authMethods?.some((method) => - [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes( - method as AuthMethod - ) - ); - if (hasSamlEnabled) - throw new BadRequestError({ - name: "Update auth method", - message: "Failed to update auth methods due to SAML SSO " - }); - const updatedUser = await userDAL.updateById(userId, { authMethods }); return updatedUser; }; diff --git a/backend/src/services/webhook/webhook-dal.ts b/backend/src/services/webhook/webhook-dal.ts index 14abcac5a..c33d79fdb 100644 --- a/backend/src/services/webhook/webhook-dal.ts +++ b/backend/src/services/webhook/webhook-dal.ts @@ -63,21 +63,16 @@ export const webhookDALFactory = (db: TDbClient) => { } }; - const findAllWebhooks = async ( - projectId: string, - environment?: string, - secretPath?: string, - tx?: Knex - ) => { + const findAllWebhooks = async (projectId: string, environment?: string, secretPath?: string, tx?: Knex) => { try { const webhooks = await (tx || db)(TableName.Webhook) .where(`${TableName.Environment}.projectId`, projectId) .where((qb) => { if (environment) { - qb.where("slug", environment); + void qb.where("slug", environment); } if (secretPath) { - qb.where("secretPath", secretPath); + void qb.where("secretPath", secretPath); } }) .join(TableName.Environment, `${TableName.Webhook}.envId`, `${TableName.Environment}.id`) @@ -85,7 +80,8 @@ export const webhookDALFactory = (db: TDbClient) => { .select(db.ref("slug").withSchema(TableName.Environment).as("envSlug")) .select(db.ref("id").withSchema(TableName.Environment).as("envId")) .select(db.ref("projectId").withSchema(TableName.Environment)) - .select(selectAllTableCols(TableName.Webhook)); + .select(selectAllTableCols(TableName.Webhook)) + .orderBy(`${TableName.Webhook}.createdAt`, "asc"); return webhooks.map(({ envId, envSlug, envName, ...el }) => ({ ...el, @@ -103,9 +99,7 @@ export const webhookDALFactory = (db: TDbClient) => { const bulkUpdate = async (data: Array, tx?: Knex) => { try { - const queries = data.map(({ id, ...el }) => - (tx || db)(TableName.Webhook).where({ id }).update(el) - ); + const queries = data.map(({ id, ...el }) => (tx || db)(TableName.Webhook).where({ id }).update(el)); const docs = await Promise.all(queries); return docs; } catch (error) { diff --git a/backend/src/services/webhook/webhook-fns.ts b/backend/src/services/webhook/webhook-fns.ts index a43d1c969..35d2ba7fc 100644 --- a/backend/src/services/webhook/webhook-fns.ts +++ b/backend/src/services/webhook/webhook-fns.ts @@ -1,5 +1,6 @@ import crypto from "node:crypto"; +import { AxiosError } from "axios"; import picomatch from "picomatch"; import { SecretKeyEncoding, TWebhooks } from "@app/db/schemas"; @@ -43,10 +44,7 @@ export const triggerWebhookRequest = async ( }); } if (secretKey) { - const webhookSign = crypto - .createHmac("sha256", secretKey) - .update(JSON.stringify(payload)) - .digest("hex"); + const webhookSign = crypto.createHmac("sha256", secretKey).update(JSON.stringify(payload)).digest("hex"); headers["x-infisical-signature"] = `t=${payload.timestamp};${webhookSign}`; } } @@ -97,10 +95,7 @@ export const fnTriggerWebhook = async ({ logger.info("Secret webhook job started", { environment, secretPath, projectId }); const webhooksTriggered = await Promise.allSettled( toBeTriggeredHooks.map((hook) => - triggerWebhookRequest( - hook, - getWebhookPayload("secrets.modified", projectId, environment, secretPath) - ) + triggerWebhookRequest(hook, getWebhookPayload("secrets.modified", projectId, environment, secretPath)) ) ); // filter hooks by status @@ -111,7 +106,7 @@ export const fnTriggerWebhook = async ({ .filter(({ status }) => status === "rejected") .map((data, i) => ({ id: toBeTriggeredHooks[i].id, - error: data.status === "rejected" && data.reason.message + error: data.status === "rejected" ? (data.reason as AxiosError).message : "" })); await webhookDAL.transaction(async (tx) => { diff --git a/backend/src/services/webhook/webhook-service.ts b/backend/src/services/webhook/webhook-service.ts index e176cc865..c3919cc50 100644 --- a/backend/src/services/webhook/webhook-service.ts +++ b/backend/src/services/webhook/webhook-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { SecretEncryptionAlgo, SecretKeyEncoding, TWebhooksInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { encryptSymmetric, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { BadRequestError } from "@app/lib/errors"; @@ -29,25 +26,19 @@ type TWebhookServiceFactoryDep = { export type TWebhookServiceFactory = ReturnType; -export const webhookServiceFactory = ({ - webhookDAL, - projectEnvDAL, - permissionService -}: TWebhookServiceFactoryDep) => { +export const webhookServiceFactory = ({ webhookDAL, projectEnvDAL, permissionService }: TWebhookServiceFactoryDep) => { const createWebhook = async ({ actor, actorId, + actorOrgId, projectId, webhookUrl, environment, secretPath, webhookSecretKey }: TCreateWebhookDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Webhooks - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); if (!env) throw new BadRequestError({ message: "Env not found" }); @@ -69,10 +60,7 @@ export const webhookServiceFactory = ({ insertDoc.algorithm = SecretEncryptionAlgo.AES_256_GCM; insertDoc.keyEncoding = SecretKeyEncoding.BASE64; } else if (encryptionKey) { - const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8( - webhookSecretKey, - encryptionKey - ); + const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8(webhookSecretKey, encryptionKey); insertDoc.encryptedSecretKey = ciphertext; insertDoc.iv = iv; insertDoc.tag = tag; @@ -85,55 +73,34 @@ export const webhookServiceFactory = ({ return { ...webhook, projectId, environment: env }; }; - const updateWebhook = async ({ actorId, actor, id, isDisabled }: TUpdateWebhookDTO) => { + const updateWebhook = async ({ actorId, actor, actorOrgId, id, isDisabled }: TUpdateWebhookDTO) => { const webhook = await webhookDAL.findById(id); if (!webhook) throw new BadRequestError({ message: "Webhook not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - webhook.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Webhooks - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, webhook.projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks); const updatedWebhook = await webhookDAL.updateById(id, { isDisabled }); return { ...webhook, ...updatedWebhook }; }; - const deleteWebhook = async ({ id, actor, actorId }: TDeleteWebhookDTO) => { + const deleteWebhook = async ({ id, actor, actorId, actorOrgId }: TDeleteWebhookDTO) => { const webhook = await webhookDAL.findById(id); if (!webhook) throw new BadRequestError({ message: "Webhook not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - webhook.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Webhooks - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, webhook.projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks); const deletedWebhook = await webhookDAL.deleteById(id); return { ...webhook, ...deletedWebhook }; }; - const testWebhook = async ({ id, actor, actorId }: TTestWebhookDTO) => { + const testWebhook = async ({ id, actor, actorId, actorOrgId }: TTestWebhookDTO) => { const webhook = await webhookDAL.findById(id); if (!webhook) throw new BadRequestError({ message: "Webhook not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - webhook.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Webhooks - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, webhook.projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks); let webhookError: string | undefined; try { @@ -152,18 +119,9 @@ export const webhookServiceFactory = ({ return { ...webhook, ...updatedWebhook }; }; - const listWebhooks = async ({ - actorId, - actor, - projectId, - secretPath, - environment - }: TListWebhookDTO) => { - const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Webhooks - ); + const listWebhooks = async ({ actorId, actor, actorOrgId, projectId, secretPath, environment }: TListWebhookDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks); return webhookDAL.findAllWebhooks(projectId, environment, secretPath); }; diff --git a/backend/tsconfig.json b/backend/tsconfig.json index b31c5140e..fcf508922 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -22,11 +22,9 @@ "skipLibCheck": true, "baseUrl": ".", "paths": { - "@app/*": ["./src/*"], - "@lib/*": ["./src/lib/*"], - "@server/*": ["./src/server/*"] + "@app/*": ["./src/*"] } }, - "include": ["src/**/*", "scripts/**/*", "e2e-test/**/*","./.eslintrc.js"], + "include": ["src/**/*", "scripts/**/*", "e2e-test/**/*", "./.eslintrc.js", "./tsup.config.js"], "exclude": ["node_modules"] } diff --git a/backend/tsup.config.js b/backend/tsup.config.js index 2cc687afc..4e182b9d2 100644 --- a/backend/tsup.config.js +++ b/backend/tsup.config.js @@ -1,13 +1,73 @@ +/* eslint-disable */ +import path from "node:path"; + +import fs from "fs/promises"; +import { replaceTscAliasPaths } from "tsc-alias"; import { defineConfig } from "tsup"; +// Instead of using tsx or tsc for building, consider using tsup. +// TSX serves as an alternative to Node.js, allowing you to build directly on the Node.js runtime. +// Its functionality mirrors Node.js, with the only difference being the absence of a final build step. Production should ideally be launched with TSX. +// TSC is effective for creating a final build, but it requires manual copying of all static files such as handlebars, emails, etc. +// A significant challenge is the shift towards ESM, as more packages are adopting ESM. If the output is in CommonJS, it may lead to errors. +// The suggested configuration offers a balance, accommodating both ESM and CommonJS requirements. + export default defineConfig({ shims: true, + clean: true, + minify: false, + keepNames: true, + splitting: false, format: "esm", + // copy the files to output loader: { ".handlebars": "copy", - ".md": "copy" + ".md": "copy", + ".txt": "copy" }, external: ["../../../frontend/node_modules/next/dist/server/next-server.js"], outDir: "dist", - entry: ["./src"] + tsconfig: "./tsconfig.json", + entry: ["./src"], + sourceMap: true, + skipNodeModulesBundle: true, + esbuildPlugins: [ + { + // esm directory import are not allowed + // /folder1 should be explicitly imported as /folder1/index.ts + // this plugin will append it automatically on build time to all imports + name: "commonjs-esm-directory-import", + setup(build) { + build.onResolve({ filter: /.*/ }, async (args) => { + if (args.importer) { + if (args.kind === "import-statement") { + const isRelativePath = args.path.startsWith("."); + const absPath = isRelativePath + ? path.join(args.resolveDir, args.path) + : path.join(args.path.replace("@app", "./src")); + + const isFile = await fs + .stat(`${absPath}.ts`) + .then((el) => el.isFile) + .catch((err) => err.code === "ENOTDIR"); + + return { + path: isFile ? `${args.path}.mjs` : `${args.path}/index.mjs`, + external: true + }; + } + } + return undefined; + }); + } + } + ], + async onSuccess() { + // this will replace all tsconfig paths + await replaceTscAliasPaths({ + configFile: "tsconfig.json", + watch: false, + outDir: "dist" + }); + } }); diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 774cd5b02..f95810777 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,4 +1,4 @@ -version: '3' +version: "3.9" services: nginx: @@ -10,36 +10,84 @@ services: volumes: - ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - - frontend - backend - networks: - - infisical-dev + - frontend - backend: - container_name: infisical-dev-backend - restart: unless-stopped + db: + image: postgres:14-alpine + ports: + - "5432:5432" + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + POSTGRES_PASSWORD: infisical + POSTGRES_USER: infisical + POSTGRES_DB: infisical + + redis: + image: redis + container_name: infisical-dev-redis + environment: + - ALLOW_EMPTY_PASSWORD=yes + ports: + - 6379:6379 + volumes: + - redis_data:/data + + redis-commander: + container_name: infisical-dev-redis-commander + image: rediscommander/redis-commander + restart: always depends_on: - - mongo - - smtp-server - redis + environment: + - REDIS_HOSTS=local:redis:6379 + ports: + - "8085:8081" + + db-test: + profiles: ["test"] + image: postgres:14-alpine + ports: + - "5430:5432" + environment: + POSTGRES_PASSWORD: infisical + POSTGRES_USER: infisical + POSTGRES_DB: infisical-test + + db-migration: + container_name: infisical-db-migration + depends_on: + - db build: context: ./backend - dockerfile: Dockerfile - volumes: - - ./backend/src:/app/src - - ./backend/nodemon.json:/app/nodemon.json - - /app/node_modules - - ./backend/api-documentation.json:/app/api-documentation.json - - ./backend/swagger.ts:/app/swagger.ts - command: npm run dev + dockerfile: Dockerfile.dev env_file: .env + environment: + - DB_CONNECTION_URI=postgres://infisical:infisical@db/infisical?sslmode=disable + command: npm run migration:latest + + backend: + container_name: infisical-dev-api + build: + context: ./backend + dockerfile: Dockerfile.dev + depends_on: + db: + condition: service_started + redis: + condition: service_started + db-migration: + condition: service_completed_successfully + env_file: + - .env + ports: + - 4000:4000 environment: - NODE_ENV=development - - MONGO_URL=mongodb://root:example@mongo:27017/?authSource=admin - networks: - - infisical-dev - extra_hosts: - - "host.docker.internal:host-gateway" + - DB_CONNECTION_URI=postgres://infisical:infisical@db/infisical?sslmode=disable + volumes: + - ./backend/src:/app/src frontend: container_name: infisical-dev-frontend @@ -55,81 +103,31 @@ services: env_file: .env environment: - NEXT_PUBLIC_ENV=development - - INFISICAL_TELEMETRY_ENABLED=${TELEMETRY_ENABLED} - networks: - - infisical-dev + - INFISICAL_TELEMETRY_ENABLED=false - mongo: - image: mongo - container_name: infisical-dev-mongo + pgadmin: + image: dpage/pgadmin4 restart: always - env_file: .env environment: - - MONGO_INITDB_ROOT_USERNAME=root - - MONGO_INITDB_ROOT_PASSWORD=example - volumes: - - mongo-data:/data/db - networks: - - infisical-dev - - mongo-express: - container_name: infisical-dev-mongo-express - image: mongo-express - restart: always - depends_on: - - mongo - env_file: .env - environment: - - ME_CONFIG_MONGODB_ADMINUSERNAME=root - - ME_CONFIG_MONGODB_ADMINPASSWORD=example - - ME_CONFIG_MONGODB_URL=mongodb://root:example@mongo:27017/ + PGADMIN_DEFAULT_EMAIL: admin@example.com + PGADMIN_DEFAULT_PASSWORD: pass ports: - - 8081:8081 - networks: - - infisical-dev + - 5050:80 + depends_on: + - db smtp-server: container_name: infisical-dev-smtp-server image: lytrax/mailhog:latest # https://github.com/mailhog/MailHog/issues/353#issuecomment-821137362 restart: always logging: - driver: 'none' # disable saving logs + driver: "none" # disable saving logs ports: - 1025:1025 # SMTP server - 8025:8025 # Web UI - networks: - - infisical-dev - - redis: - image: redis - container_name: infisical-dev-redis - environment: - - ALLOW_EMPTY_PASSWORD=yes - ports: - - 6379:6379 - volumes: - - redis_data:/data - networks: - - infisical-dev - - redis-commander: - container_name: infisical-dev-redis-commander - image: rediscommander/redis-commander - restart: always - depends_on: - - redis - environment: - - REDIS_HOSTS=local:redis:6379 - ports: - - "8085:8081" - networks: - - infisical-dev volumes: - mongo-data: + postgres-data: driver: local redis_data: driver: local - -networks: - infisical-dev: diff --git a/docker-compose.pg.yml b/docker-compose.pg.yml deleted file mode 100644 index 353b6177b..000000000 --- a/docker-compose.pg.yml +++ /dev/null @@ -1,132 +0,0 @@ -version: "3.9" - -services: - nginx: - container_name: infisical-dev-nginx - image: nginx - restart: always - ports: - - 8080:80 - volumes: - - ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro - depends_on: - - backend - - frontend - - db: - image: postgres:14-alpine - ports: - - "5432:5432" - volumes: - - postgres-data:/var/lib/postgresql/data - environment: - POSTGRES_PASSWORD: infisical - POSTGRES_USER: infisical - POSTGRES_DB: infisical - - redis: - image: redis - container_name: infisical-dev-redis - environment: - - ALLOW_EMPTY_PASSWORD=yes - ports: - - 6379:6379 - volumes: - - redis_data:/data - - db-test: - profiles: ["test"] - image: postgres:14-alpine - ports: - - "5430:5432" - environment: - POSTGRES_PASSWORD: infisical - POSTGRES_USER: infisical - POSTGRES_DB: infisical-test - - backend: - container_name: infisical-dev-api - build: - context: ./backend - dockerfile: Dockerfile.dev - depends_on: - - db - env_file: - - .env - environment: - - NODE_ENV=development - - DB_CONNECTION_URI=postgres://infisical:infisical@db/infisical?sslmode=disable - volumes: - - ./backend/src:/app/src - - frontend: - container_name: infisical-dev-frontend - restart: unless-stopped - depends_on: - - backend - build: - context: ./frontend - dockerfile: Dockerfile.dev - volumes: - - ./frontend/src:/app/src/ # mounted whole src to avoid missing reload on new files - - ./frontend/public:/app/public - env_file: .env - environment: - - NEXT_PUBLIC_ENV=development - - INFISICAL_TELEMETRY_ENABLED=false - - pgadmin: - image: dpage/pgadmin4 - restart: always - environment: - PGADMIN_DEFAULT_EMAIL: admin@example.com - PGADMIN_DEFAULT_PASSWORD: pass - ports: - - 5050:80 - depends_on: - - db - - smtp-server: - container_name: infisical-dev-smtp-server - image: lytrax/mailhog:latest # https://github.com/mailhog/MailHog/issues/353#issuecomment-821137362 - restart: always - logging: - driver: "none" # disable saving logs - ports: - - 1025:1025 # SMTP server - - 8025:8025 # Web UI - - mongo: - image: mongo - container_name: infisical-dev-mongo - restart: always - env_file: .env - environment: - - MONGO_INITDB_ROOT_USERNAME=root - - MONGO_INITDB_ROOT_PASSWORD=example - volumes: - - mongo-data:/data/db - ports: - - 27017:27017 - - mongo-express: - container_name: infisical-dev-mongo-express - image: mongo-express - restart: always - depends_on: - - mongo - env_file: .env - environment: - - ME_CONFIG_MONGODB_ADMINUSERNAME=root - - ME_CONFIG_MONGODB_ADMINPASSWORD=example - - ME_CONFIG_MONGODB_URL=mongodb://root:example@mongo:27017/ - ports: - - 8081:8081 - -volumes: - postgres-data: - driver: local - redis_data: - driver: local - mongo-data: - driver: local diff --git a/docker-compose.yml b/docker-compose.prod.yml similarity index 54% rename from docker-compose.yml rename to docker-compose.prod.yml index c159a2175..5861a4a0e 100644 --- a/docker-compose.yml +++ b/docker-compose.prod.yml @@ -1,12 +1,27 @@ version: "3" services: + db-migration: + container_name: infisical-db-migration + depends_on: + - db + image: infisical/infisical:latest-postgres + env_file: .env + command: npm run migration:latest + networks: + - infisical + backend: container_name: infisical-backend restart: unless-stopped depends_on: - - mongo - image: infisical/infisical:latest + db: + condition: service_started + redis: + condition: service_started + db-migration: + condition: service_completed_successfully + image: infisical/infisical:latest-postgres env_file: .env ports: - 80:8080 @@ -28,21 +43,18 @@ services: volumes: - redis_data:/data - mongo: - container_name: infisical-mongo - image: mongo + db: + container_name: infisical-db + image: postgres:14-alpine restart: always env_file: .env - environment: - - MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME} - - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD} volumes: - - mongo-data:/data/db + - pg_data:/data/db networks: - infisical volumes: - mongo-data: + pg_data: driver: local redis_data: driver: local diff --git a/docs/api-reference/endpoints/environments/create.mdx b/docs/api-reference/endpoints/environments/create.mdx index 2527c613d..826dcce3d 100644 --- a/docs/api-reference/endpoints/environments/create.mdx +++ b/docs/api-reference/endpoints/environments/create.mdx @@ -1,4 +1,4 @@ --- title: "Create" -openapi: "POST /api/v2/workspace/{workspaceId}/environments" +openapi: "POST /api/v1/workspace/{workspaceId}/environments" --- diff --git a/docs/api-reference/endpoints/environments/delete.mdx b/docs/api-reference/endpoints/environments/delete.mdx index 944e42961..903e58d2a 100644 --- a/docs/api-reference/endpoints/environments/delete.mdx +++ b/docs/api-reference/endpoints/environments/delete.mdx @@ -1,4 +1,4 @@ --- title: "Delete" -openapi: "DELETE /api/v2/workspace/{workspaceId}/environments" ---- \ No newline at end of file +openapi: "DELETE /api/v1/workspace/{workspaceId}/environments/{id}" +--- diff --git a/docs/api-reference/endpoints/environments/update.mdx b/docs/api-reference/endpoints/environments/update.mdx index 291344d6c..f93968668 100644 --- a/docs/api-reference/endpoints/environments/update.mdx +++ b/docs/api-reference/endpoints/environments/update.mdx @@ -1,4 +1,4 @@ --- title: "Update" -openapi: "PUT /api/v2/workspace/{workspaceId}/environments" ---- \ No newline at end of file +openapi: "PATCH /api/v1/workspace/{workspaceId}/environments/{id}" +--- diff --git a/docs/api-reference/endpoints/folders/create.mdx b/docs/api-reference/endpoints/folders/create.mdx index 397f43cb5..e1ff3004a 100644 --- a/docs/api-reference/endpoints/folders/create.mdx +++ b/docs/api-reference/endpoints/folders/create.mdx @@ -1,4 +1,4 @@ --- title: "Create" -openapi: "POST /api/v1/folders/" ---- \ No newline at end of file +openapi: "POST /api/v1/folders" +--- diff --git a/docs/api-reference/endpoints/folders/delete.mdx b/docs/api-reference/endpoints/folders/delete.mdx index 0aacd66e2..dc73a41da 100644 --- a/docs/api-reference/endpoints/folders/delete.mdx +++ b/docs/api-reference/endpoints/folders/delete.mdx @@ -1,4 +1,4 @@ --- title: "Delete" -openapi: "DELETE /api/v1/folders/{folderName}" ---- \ No newline at end of file +openapi: "DELETE /api/v1/folders/{folderId}" +--- diff --git a/docs/api-reference/endpoints/folders/list.mdx b/docs/api-reference/endpoints/folders/list.mdx index c467c5975..f40f93273 100644 --- a/docs/api-reference/endpoints/folders/list.mdx +++ b/docs/api-reference/endpoints/folders/list.mdx @@ -1,4 +1,4 @@ --- title: "List" -openapi: "GET /api/v1/folders/" ---- \ No newline at end of file +openapi: "GET /api/v1/folders" +--- diff --git a/docs/api-reference/endpoints/folders/update.mdx b/docs/api-reference/endpoints/folders/update.mdx index 3ceae7fb6..c54778e94 100644 --- a/docs/api-reference/endpoints/folders/update.mdx +++ b/docs/api-reference/endpoints/folders/update.mdx @@ -1,4 +1,4 @@ --- title: "Update" -openapi: "PATCH /api/v1/folders/{folderName}" ---- \ No newline at end of file +openapi: "PATCH /api/v1/folders/{folderId}" +--- diff --git a/docs/api-reference/endpoints/identities/create.mdx b/docs/api-reference/endpoints/identities/create.mdx index 05a11521f..a6595f97a 100644 --- a/docs/api-reference/endpoints/identities/create.mdx +++ b/docs/api-reference/endpoints/identities/create.mdx @@ -1,4 +1,4 @@ --- title: "Create" -openapi: "POST /api/v1/identities/" ---- \ No newline at end of file +openapi: "POST /api/v1/identities" +--- diff --git a/docs/api-reference/endpoints/identities/delete.mdx b/docs/api-reference/endpoints/identities/delete.mdx index 07e79dfe9..5b6ed220a 100644 --- a/docs/api-reference/endpoints/identities/delete.mdx +++ b/docs/api-reference/endpoints/identities/delete.mdx @@ -1,4 +1,4 @@ --- title: "Delete" openapi: "DELETE /api/v1/identities/{identityId}" ---- \ No newline at end of file +--- diff --git a/docs/api-reference/endpoints/identities/update.mdx b/docs/api-reference/endpoints/identities/update.mdx index c0940467b..02d213181 100644 --- a/docs/api-reference/endpoints/identities/update.mdx +++ b/docs/api-reference/endpoints/identities/update.mdx @@ -1,4 +1,4 @@ --- title: "Update" openapi: "PATCH /api/v1/identities/{identityId}" ---- \ No newline at end of file +--- diff --git a/docs/api-reference/endpoints/organizations/list-identity-memberships.mdx b/docs/api-reference/endpoints/organizations/list-identity-memberships.mdx index 1929a4b59..5995184a5 100644 --- a/docs/api-reference/endpoints/organizations/list-identity-memberships.mdx +++ b/docs/api-reference/endpoints/organizations/list-identity-memberships.mdx @@ -1,4 +1,4 @@ --- title: "List Identity Memberships" -openapi: "GET /api/v2/organizations/{organizationId}/identity-memberships" ---- \ No newline at end of file +openapi: "GET /api/v2/organizations/{orgId}/identity-memberships" +--- diff --git a/docs/api-reference/endpoints/secret-imports/create.mdx b/docs/api-reference/endpoints/secret-imports/create.mdx index 2c823e528..3abfb320f 100644 --- a/docs/api-reference/endpoints/secret-imports/create.mdx +++ b/docs/api-reference/endpoints/secret-imports/create.mdx @@ -1,4 +1,4 @@ --- title: "Create" -openapi: "POST /api/v1/secret-imports/" ---- \ No newline at end of file +openapi: "POST /api/v1/secret-imports" +--- diff --git a/docs/api-reference/endpoints/secret-imports/delete.mdx b/docs/api-reference/endpoints/secret-imports/delete.mdx index c7da4f6d0..cfa5960b1 100644 --- a/docs/api-reference/endpoints/secret-imports/delete.mdx +++ b/docs/api-reference/endpoints/secret-imports/delete.mdx @@ -1,4 +1,4 @@ --- title: "Delete" -openapi: "DELETE /api/v1/secret-imports/{id}" ---- \ No newline at end of file +openapi: "DELETE /api/v1/secret-imports/{secretImportId}" +--- diff --git a/docs/api-reference/endpoints/secret-imports/list.mdx b/docs/api-reference/endpoints/secret-imports/list.mdx index 2de41b5d7..580d4be8d 100644 --- a/docs/api-reference/endpoints/secret-imports/list.mdx +++ b/docs/api-reference/endpoints/secret-imports/list.mdx @@ -1,4 +1,4 @@ --- title: "List" -openapi: "GET /api/v1/secret-imports/" ---- \ No newline at end of file +openapi: "GET /api/v1/secret-imports" +--- diff --git a/docs/api-reference/endpoints/secret-imports/update.mdx b/docs/api-reference/endpoints/secret-imports/update.mdx index 76c8a8feb..f21133223 100644 --- a/docs/api-reference/endpoints/secret-imports/update.mdx +++ b/docs/api-reference/endpoints/secret-imports/update.mdx @@ -1,4 +1,4 @@ --- title: "Update" -openapi: "PUT /api/v1/secret-imports/{id}" ---- \ No newline at end of file +openapi: "PATCH /api/v1/secret-imports/{secretImportId}" +--- diff --git a/docs/api-reference/endpoints/service-tokens/get.mdx b/docs/api-reference/endpoints/service-tokens/get.mdx index 5b2604282..593da8a92 100644 --- a/docs/api-reference/endpoints/service-tokens/get.mdx +++ b/docs/api-reference/endpoints/service-tokens/get.mdx @@ -1,6 +1,6 @@ --- title: "Get" -openapi: "GET /api/v2/service-token/" +openapi: "GET /api/v2/service-token" --- diff --git a/docs/api-reference/endpoints/workspaces/delete-identity-membership.mdx b/docs/api-reference/endpoints/workspaces/delete-identity-membership.mdx index 4621f9b50..e2b266626 100644 --- a/docs/api-reference/endpoints/workspaces/delete-identity-membership.mdx +++ b/docs/api-reference/endpoints/workspaces/delete-identity-membership.mdx @@ -1,4 +1,4 @@ --- title: "Delete Identity Membership" -openapi: "DELETE /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}" ---- \ No newline at end of file +openapi: "DELETE /api/v2/workspace/{projectId}/identity-memberships/{identityId}" +--- diff --git a/docs/api-reference/endpoints/workspaces/delete-membership.mdx b/docs/api-reference/endpoints/workspaces/delete-membership.mdx index e93b2415b..1995e4726 100644 --- a/docs/api-reference/endpoints/workspaces/delete-membership.mdx +++ b/docs/api-reference/endpoints/workspaces/delete-membership.mdx @@ -1,4 +1,4 @@ --- title: "Delete User Membership" -openapi: "DELETE /api/v2/workspace/{workspaceId}/memberships/{membershipId}" +openapi: "DELETE /api/v1/workspace/{workspaceId}/memberships/{membershipId}" --- diff --git a/docs/api-reference/endpoints/workspaces/list-identity-memberships.mdx b/docs/api-reference/endpoints/workspaces/list-identity-memberships.mdx index 45297efff..e5162e693 100644 --- a/docs/api-reference/endpoints/workspaces/list-identity-memberships.mdx +++ b/docs/api-reference/endpoints/workspaces/list-identity-memberships.mdx @@ -1,4 +1,4 @@ --- title: "List Identity Memberships" -openapi: "GET /api/v2/workspace/{workspaceId}/identity-memberships" ---- \ No newline at end of file +openapi: "GET /api/v2/workspace/{projectId}/identity-memberships" +--- diff --git a/docs/api-reference/endpoints/workspaces/memberships.mdx b/docs/api-reference/endpoints/workspaces/memberships.mdx index 386c8a089..3c4735f94 100644 --- a/docs/api-reference/endpoints/workspaces/memberships.mdx +++ b/docs/api-reference/endpoints/workspaces/memberships.mdx @@ -1,4 +1,4 @@ --- title: "Get User Memberships" -openapi: "GET /api/v2/workspace/{workspaceId}/memberships" +openapi: "GET /api/v1/workspace/{workspaceId}/memberships" --- diff --git a/docs/api-reference/endpoints/workspaces/rollback-snapshot.mdx b/docs/api-reference/endpoints/workspaces/rollback-snapshot.mdx index 8b648a400..527c861b2 100644 --- a/docs/api-reference/endpoints/workspaces/rollback-snapshot.mdx +++ b/docs/api-reference/endpoints/workspaces/rollback-snapshot.mdx @@ -1,4 +1,4 @@ --- title: "Roll Back to Snapshot" -openapi: "POST /api/v1/workspace/{workspaceId}/secret-snapshots/rollback" +openapi: "POST /api/v1/secret-snapshot/{secretSnapshotId}/rollback" --- diff --git a/docs/api-reference/endpoints/workspaces/update-identity-membership.mdx b/docs/api-reference/endpoints/workspaces/update-identity-membership.mdx index 398c7bc81..667cf7eb3 100644 --- a/docs/api-reference/endpoints/workspaces/update-identity-membership.mdx +++ b/docs/api-reference/endpoints/workspaces/update-identity-membership.mdx @@ -1,4 +1,4 @@ --- title: "Update Identity Membership" -openapi: "PATCH /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}" ---- \ No newline at end of file +openapi: "PATCH /api/v2/workspace/{projectId}/identity-memberships/{identityId}" +--- diff --git a/docs/api-reference/endpoints/workspaces/update-membership.mdx b/docs/api-reference/endpoints/workspaces/update-membership.mdx index f0ef15412..9a7aa600f 100644 --- a/docs/api-reference/endpoints/workspaces/update-membership.mdx +++ b/docs/api-reference/endpoints/workspaces/update-membership.mdx @@ -1,4 +1,4 @@ --- title: "Update User Membership" -openapi: "PATCH /api/v2/workspace/{workspaceId}/memberships/{membershipId}" +openapi: "PATCH /api/v1/workspace/{workspaceId}/memberships/{membershipId}" --- diff --git a/docs/cli/faq.mdx b/docs/cli/faq.mdx index 529ec70ab..cf95457c9 100644 --- a/docs/cli/faq.mdx +++ b/docs/cli/faq.mdx @@ -23,3 +23,12 @@ Yes. If you have previously retrieved secrets for a specific project and environ Yes. This is simply a configuration file and contains no sensitive data. + + + + Visit the Infisical website and navigate to a project of your choice. Once on the project page, access the **Project Settings** from the sidebar. Within the Project name section, click the "Copy Project ID" button for copying the current Project ID to clipboard, or simply obtain it from the URL of the current page. + + ``` + https://app.infisical.com/project//settings + ``` + diff --git a/docs/contributing/platform/backend/folder-structure.mdx b/docs/contributing/platform/backend/folder-structure.mdx new file mode 100644 index 000000000..abfe0f69d --- /dev/null +++ b/docs/contributing/platform/backend/folder-structure.mdx @@ -0,0 +1,82 @@ +--- +title: 'Backend folder structure' +--- + +``` +โ”œโ”€โ”€ scripts +โ”œโ”€โ”€ e2e-test +โ””โ”€โ”€ src/ + โ”œโ”€โ”€ @types/ + โ”‚ โ”œโ”€โ”€ knex.d.ts + โ”‚ โ””โ”€โ”€ fastify.d.ts + โ”œโ”€โ”€ db/ + โ”‚ โ”œโ”€โ”€ migrations + โ”‚ โ”œโ”€โ”€ schemas + โ”‚ โ””โ”€โ”€ seed + โ”œโ”€โ”€ lib/ + โ”‚ โ”œโ”€โ”€ fn + โ”‚ โ”œโ”€โ”€ date + โ”‚ โ””โ”€โ”€ config + โ”œโ”€โ”€ queue + โ”œโ”€โ”€ server/ + โ”‚ โ”œโ”€โ”€ routes/ + โ”‚ โ”‚ โ”œโ”€โ”€ v1 + โ”‚ โ”‚ โ””โ”€โ”€ v2 + โ”‚ โ”œโ”€โ”€ plugins + โ”‚ โ””โ”€โ”€ config + โ”œโ”€โ”€ services/ + โ”‚ โ”œโ”€โ”€ auth + โ”‚ โ”œโ”€โ”€ org + โ”‚ โ””โ”€โ”€ project/ + โ”‚ โ”œโ”€โ”€ project-service.ts + โ”‚ โ”œโ”€โ”€ project-types.ts + โ”‚ โ””โ”€โ”€ project-dal.ts + โ””โ”€โ”€ ee/ + โ”œโ”€โ”€ routes + โ””โ”€โ”€ services +``` + +### `backend/scripts` +Contains reusable scripts for backend automation, like running migrations and generating SQL schemas. + +### `backend/e2e-test` +Integration tests for the APIs. + +### `backend/src` +The source code of the backend. + +- `@types`: Type definitions for libraries like Fastify and Knex. +- `db`: Knex.js configuration for the database, including migration, seed files, and SQL type schemas. +- `lib`: Stateless, reusable functions used across the codebase. +- `queue`: Infisical's queue system based on BullMQ. + +### `src/server` + +- Scope anything related to Fastify/service here. +- Includes routes, Fastify plugins, and server configurations. +- The routes folder contains various versions of routes separated into v1, v2, etc. + +### `src/services` + +- Handles the core business logic for all operations. +- Follows the co-location principle: related components should be kept together. +- Each service component typically contains: + + 1. **dal**: Database Access Layer functions for database operations + 2. **service**: The service layer containing business logic. + 3. **type**: Type definitions used within the service component. + 4. **fns**: An optional component for sharing reusable functions related to the service. + 5. **queue**: An optional component for queue-specific logic, like `secret-queue.ts`. + +### `src/ee` + +Follows the same pattern as above, with the exception of a license change from MIT to Infisical Proprietary License. + +### Guidelines and Best Practices + +- All services are interconnected at `/src/server/routes/index.ts`, following the principle of simple dependency injection. +- Files should be named in dash-case. +- Avoid using classes in the codebase; opt for simple functions instead. +- All committed code must be properly linted using `npm run lint:fix` and type-checked with `npm run type:check`. +- Minimize shared logic between services as much as possible. +- Controllers within a router component should ideally call only one service layer, with exceptions for services like `audit-log` that require access to request object data. \ No newline at end of file diff --git a/docs/contributing/platform/backend/how-to-create-a-feature.mdx b/docs/contributing/platform/backend/how-to-create-a-feature.mdx new file mode 100644 index 000000000..e77313dab --- /dev/null +++ b/docs/contributing/platform/backend/how-to-create-a-feature.mdx @@ -0,0 +1,56 @@ +--- +title: "Backend development guide" +--- + +Suppose you're interested in implementing a new feature in Infisical's backend, let's call it "feature-x." Here are the general steps you should follow. + +## Database schema migration +In order to run [schema migrations](https://en.wikipedia.org/wiki/Schema_migration#:~:text=A%20schema%20migration%20is%20performed,some%20newer%20or%20older%20version) you need to expose your database connection string. Create a `.env.migration` file to set the database connection URI for migration scripts, or alternatively, export the `DB_CONNECTION_URI` environment variable. + +## Creating new database model +If your feature involves a change in the database, you need to first address this by generating the necessary database schemas. + +1. If you're adding a new table, update the `TableName` enum in `/src/db/schemas/models.ts` to include the new table name. +2. Create a new migration file by running `npm run migration:new` and give it a relevant name, such as `feature-x`. +3. Navigate to `/src/db/migrations/_.ts`. +4. Modify both the `up` and `down` functions to create or alter Postgres fields on migration up and to revert these changes on migration down, ensuring idempotency as outlined [here](https://github.com/graphile/migrate/blob/main/docs/idempotent-examples.md). + +### Generating TS Schemas + +While typically you would need to manually write TS types for Knex type-sense, we have automated this process: + +1. Start the server. +2. Run `npm run migration:latest` to apply all database changes. +3. Execute `npm run generate:schema` to automatically generate types and schemas using [zod](https://github.com/colinhacks/zod) in the `/src/db/schemas` folder. +4. Update the barrel export in `schema/index` and include the new tables in `/src/@types/knex.d.ts` to enable type-sensing in Knex.js. + +## Business Logic + +Once the database changes are in place, it's time to create the APIs for `feature-x`: + +1. Execute `npm run generate:component`. +2. Choose option 1 for the service component. +3. Name the service in dash-case, like `feature-x`. This will create a `feature-x` folder in `/src/services` containing three files. + 1. `feature-x-dal`: The Database Access Layer functions. + 2. `feature-x-service`: The service layer where all the business logic is handled. + 3. `feature-x-type`: The types used by `feature-x`. + +For reusable shared functions, set up a file named `feature-x-fns`. + +Use the custom Infisical function `ormify` in `src/lib/knex` for simple database operations within the DAL. + +## Connecting the Service Layer to the Server Layer + +Server-related logic is handled in `/src/server`. To connect the service layer to the server layer, we use Fastify plugins for dependency injection: + +1. Add the service type in the `fastify.d.ts` file under the `service` namespace of a FastifyServerInstance type. +2. In `/src/server/routes/index.ts`, instantiate the required dependencies for `feature-x`, such as the DAL and service layers, and then pass them to `fastify.register("service,{...dependencies})`. +3. This makes the service layer accessible within all routes under the Fastify service instance, accessed via `server.services..`. + +## Writing API Routes + +1. To create a route component, run `npm generate:component`. +2. Select option 3, type the router name in dash-case, and provide the version number. This will generate a router file in `src/server/routes/v/` + 1. Implement your logic to connect with the service layer as needed. + 2. Import the router component in the version folder's index.ts. For instance, if it's in v1, import it in `v1/index.ts`. + 3. Finally, register it under the appropriate prefix for access. \ No newline at end of file diff --git a/docs/documentation/platform/identities/overview.mdx b/docs/documentation/platform/identities/overview.mdx index d6366211f..7a4751487 100644 --- a/docs/documentation/platform/identities/overview.mdx +++ b/docs/documentation/platform/identities/overview.mdx @@ -4,7 +4,7 @@ description: "Programmatically interact with Infisical" --- - Currently, identities can only be used to make authenticated requests to the Infisical API and SDKs. They do not work with clients such as CLI, K8s Operator, Terraform Provider, etc. + Currently, identities can only be used to make authenticated requests to the Infisical API, SDKs, and Agent. They do not work with clients such as CLI, K8s Operator, Terraform Provider, etc. We will be releasing compatibility with it across clients in the coming quarter. @@ -50,4 +50,4 @@ Check out the following authentication method-specific guides for step-by-step i - The identity you are trying to read, update, or delete is more privileged than yourself. - The role you are trying to create an identity for or update an identity to is more privileged than yours. - \ No newline at end of file + diff --git a/docs/documentation/platform/role-based-access-controls.mdx b/docs/documentation/platform/role-based-access-controls.mdx index 198de29c5..ba774f0d1 100644 --- a/docs/documentation/platform/role-based-access-controls.mdx +++ b/docs/documentation/platform/role-based-access-controls.mdx @@ -1,6 +1,6 @@ --- title: "Role-based Access Controls" -description: "Infisical's Role-based Acccess Controls enable creating permissions for user and machine identities to restrict access to resources and the range of actions that can performed." +description: "Infisical's Role-based Access Controls enable creating permissions for user and machine identities to restrict access to resources and the range of actions that can be performed." --- ### General access controls diff --git a/docs/documentation/platform/sso/azure.mdx b/docs/documentation/platform/sso/azure.mdx index 20137c19b..0f644834a 100644 --- a/docs/documentation/platform/sso/azure.mdx +++ b/docs/documentation/platform/sso/azure.mdx @@ -91,10 +91,17 @@ description: "Configure Azure SAML for Infisical SSO" ![Azure SAML assignment](../../../images/sso/azure/assignment.png) - Enabling SAML SSO enforces all members in your organization to only be able to log into Infisical via Azure. + Enabling SAML SSO allows members in your organization to log into Infisical via Azure. ![Azure SAML assignment](../../../images/sso/azure/enable-saml.png) + + Enforcing SAML SSO ensures that members in your organization can only access Infisical + by logging into the organization via Azure. + + To enforce SAML SSO, you're required to test out the SAML connection by successfully authenticating at least one Azure user with Infisical; + Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO. + diff --git a/docs/documentation/platform/sso/jumpcloud.mdx b/docs/documentation/platform/sso/jumpcloud.mdx index e9ffb4f5e..2273a8852 100644 --- a/docs/documentation/platform/sso/jumpcloud.mdx +++ b/docs/documentation/platform/sso/jumpcloud.mdx @@ -71,10 +71,17 @@ description: "Configure JumpCloud SAML for Infisical SSO" ![JumpCloud SAML assignment](../../../images/sso/jumpcloud/assignment.png) - Enabling SAML SSO enforces all members in your organization to only be able to log into Infisical via JumpCloud. + Enabling SAML SSO allows members in your organization to log into Infisical via JumpCloud. ![JumpCloud SAML assignment](../../../images/sso/jumpcloud/enable-saml.png) + + Enforcing SAML SSO ensures that members in your organization can only access Infisical + by logging into the organization via JumpCloud. + + To enforce SAML SSO, you're required to test out the SAML connection by successfully authenticating at least one JumpCloud user with Infisical; + Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO. + diff --git a/docs/documentation/platform/sso/okta.mdx b/docs/documentation/platform/sso/okta.mdx index c07aca9ac..576bd769a 100644 --- a/docs/documentation/platform/sso/okta.mdx +++ b/docs/documentation/platform/sso/okta.mdx @@ -74,9 +74,16 @@ description: "Configure Okta SAML 2.0 for Infisical SSO" At this point, you have configured everything you need within the context of the Okta Admin Portal. - Enabling SAML SSO enforces all members in your organization to only be able to log into Infisical via Okta. + Enabling SAML SSO allows members in your organization to log into Infisical via Okta. - ![SAML Okta assignment](../../../images/sso/okta/enable-saml.png) + ![SAML Okta enable SAML](../../../images/sso/okta/enable-saml.png) + + + Enforcing SAML SSO ensures that members in your organization can only access Infisical + by logging into the organization via Okta. + + To enforce SAML SSO, you're required to test out the SAML connection by successfully authenticating at least one Okta user with Infisical; + Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO. diff --git a/docs/documentation/platform/token.mdx b/docs/documentation/platform/token.mdx index 31efdbce1..9e304de3d 100644 --- a/docs/documentation/platform/token.mdx +++ b/docs/documentation/platform/token.mdx @@ -8,7 +8,7 @@ Each service token can be provisioned scoped access to select environment(s) and ## Service Tokens -You can manage service tokens in Project Settings > Service Tokens. +You can manage service tokens in Access Control > Service Tokens (tab). ### Service Token (Current) @@ -25,7 +25,7 @@ of the token. ## Creating a service token -To create a service token, head to Project Settings > Service Tokens as shown below and press **Create token**. +To create a service token, head to Access Control > Service Tokens as shown below and press **Create token**. ![token add](../../images/project-token-old-add.png) diff --git a/docs/images/guides/agent-with-ecs/access-token-deposit.png b/docs/images/guides/agent-with-ecs/access-token-deposit.png new file mode 100644 index 000000000..8bf3b0450 Binary files /dev/null and b/docs/images/guides/agent-with-ecs/access-token-deposit.png differ diff --git a/docs/images/guides/agent-with-ecs/ecs-diagram.png b/docs/images/guides/agent-with-ecs/ecs-diagram.png new file mode 100644 index 000000000..ee159017c Binary files /dev/null and b/docs/images/guides/agent-with-ecs/ecs-diagram.png differ diff --git a/docs/images/guides/agent-with-ecs/file_browser_main.png b/docs/images/guides/agent-with-ecs/file_browser_main.png new file mode 100644 index 000000000..402583aa8 Binary files /dev/null and b/docs/images/guides/agent-with-ecs/file_browser_main.png differ diff --git a/docs/images/guides/agent-with-ecs/filebrowser_afterlogin.png b/docs/images/guides/agent-with-ecs/filebrowser_afterlogin.png new file mode 100644 index 000000000..b3caab8d3 Binary files /dev/null and b/docs/images/guides/agent-with-ecs/filebrowser_afterlogin.png differ diff --git a/docs/images/guides/agent-with-ecs/secrets-deposit.png b/docs/images/guides/agent-with-ecs/secrets-deposit.png new file mode 100644 index 000000000..19b3eedd7 Binary files /dev/null and b/docs/images/guides/agent-with-ecs/secrets-deposit.png differ diff --git a/docs/images/integrations/jenkins/jenkins_11.png b/docs/images/integrations/jenkins/jenkins_11.png index 577cca4c5..61f1e364f 100644 Binary files a/docs/images/integrations/jenkins/jenkins_11.png and b/docs/images/integrations/jenkins/jenkins_11.png differ diff --git a/docs/images/integrations/jenkins/jenkins_4.png b/docs/images/integrations/jenkins/jenkins_4.png index 1103da236..9e7360e97 100644 Binary files a/docs/images/integrations/jenkins/jenkins_4.png and b/docs/images/integrations/jenkins/jenkins_4.png differ diff --git a/docs/images/integrations/jenkins/jenkins_5.png b/docs/images/integrations/jenkins/jenkins_5.png index 824cacfff..9491df302 100644 Binary files a/docs/images/integrations/jenkins/jenkins_5.png and b/docs/images/integrations/jenkins/jenkins_5.png differ diff --git a/docs/images/integrations/jenkins/jenkins_9.png b/docs/images/integrations/jenkins/jenkins_9.png index 109d7305b..81bd4f2ba 100644 Binary files a/docs/images/integrations/jenkins/jenkins_9.png and b/docs/images/integrations/jenkins/jenkins_9.png differ diff --git a/docs/images/project-token-old-add.png b/docs/images/project-token-old-add.png index 960013a3f..c8d1b9e05 100644 Binary files a/docs/images/project-token-old-add.png and b/docs/images/project-token-old-add.png differ diff --git a/docs/integrations/cicd/jenkins.mdx b/docs/integrations/cicd/jenkins.mdx index 58c92d218..9184aef44 100644 --- a/docs/integrations/cicd/jenkins.mdx +++ b/docs/integrations/cicd/jenkins.mdx @@ -3,22 +3,32 @@ title: "Jenkins" description: "How to effectively and securely manage secrets in Jenkins using Infisical" --- +**Objective**: Fetch secrets from Infisical to Jenkins pipelines + +In this guide, we'll outline the steps to deliver secrets from Infisical to Jenkins via the Infisical CLI. +At a high level, the Infisical CLI will be executed within your build environment and use a service token to authenticate with Infisical. +This token must be added as a Jenkins Credential and then passed to the Infisical CLI as an environment variable, enabling it to access and retrieve secrets within your workflows. + Prerequisites: - Set up and add secrets to [Infisical](https://app.infisical.com). - You have a working Jenkins installation with the [credentials plugin](https://plugins.jenkins.io/credentials/) installed. -- You have the Infisical CLI installed on your Jenkins executor nodes or container images. +- You have the [Infisical CLI](/cli/overview) installed on your Jenkins executor nodes or container images. + ## Add Infisical Service Token to Jenkins -After setting up your project in Infisical and adding the Infisical CLI to container images, you will need to add the Infisical Service Token to Jenkins. Once you have generated the token, browse to **Manage Jenkins > Manage Credentials** in your Jenkins installation. +After setting up your project in Infisical and installing the Infisical CLI to the environment where your Jenkins builds will run, you will need to add the Infisical Service Token to Jenkins. + +To generate a Infisical service token, follow the guide [here](/documentation/platform/token). +Once you have generated the token, navigate to **Manage Jenkins > Manage Credentials** in your Jenkins instance. ![Jenkins step 1](../../images/integrations/jenkins/jenkins_1.png) Click on the credential store you want to store the Infisical Service Token in. In this case, we're using the default Jenkins global store. - Each of your projects will have a different INFISICAL_SERVICE_TOKEN though. + Each of your projects will have a different `INFISICAL_TOKEN`. As a result, it may make sense to spread these out into separate credential domains depending on your use case. @@ -28,18 +38,22 @@ Now, click Add Credentials. ![Jenkins step 3](../../images/integrations/jenkins/jenkins_3.png) -Choose **Secret text** from the **Kind** dropdown menu, paste the Infisical Service Token into the **Secret** field, enter `INFISICAL_SERVICE_TOKEN` into the **Description** field, and click **OK**. +Choose **Secret text** for the **Kind** option from the dropdown list and enter the Infisical Service Token in the **Secret** field. +Although the **ID** can be any value, we'll set it to `infisical-service-token` for the sake of this guide. +The description is optional and can be any text you prefer. + ![Jenkins step 4](../../images/integrations/jenkins/jenkins_4.png) -When you're done, you should have a credential similar to the one below: +When you're done, you should see a credential similar to the one below: ![Jenkins step 5](../../images/integrations/jenkins/jenkins_5.png) ## Use Infisical in a Freestyle Project -To use Infisical in a Freestyle Project job, you'll need to expose the credential you created above in an environment variable. First, click New Item from the dashboard navigation sidebar: +To fetch secrets with Infisical in a Freestyle Project job, you'll need to expose the credential you created above as an environment variable to the Infisical CLI. +To do so, first click **New Item** from the dashboard navigation sidebar: ![Jenkins step 6](../../images/integrations/jenkins/jenkins_6.png) @@ -51,7 +65,8 @@ Scroll down to the **Build Environment** section and enable the **Use secret tex ![Jenkins step 8](../../images/integrations/jenkins/jenkins_8.png) -Enter INFISICAL_SERVICE_TOKEN in the **Variable** field, select the **Specific credentials** option from the Credentials section and choose INFISICAL_SERVICE_TOKEN from the dropdown menu. +Enter `INFISICAL_TOKEN` in the **Variable** field then click the **Specific credentials** option from the Credentials section and select the credential you created earlier. +In this case, we saved it as `Infisical service token` so we'll choose that from the dropdown menu. ![Jenkins step 9](../../images/integrations/jenkins/jenkins_9.png) @@ -59,15 +74,16 @@ Scroll down to the **Build** section and choose **Execute shell** from the **Add ![Jenkins step 10](../../images/integrations/jenkins/jenkins_10.png) -In the command field, enter the following command and click **Save**: +In the command field, you can now use the Infisical CLI to fetch secrets. +The example command below will print the secrets using the service token passed as a credential. When done, click **Save**. ``` -infisical run -- printenv +infisical secrets --env=dev --path=/ ``` ![Jenkins step 11](../../images/integrations/jenkins/jenkins_11.png) -Finally, click **Build Now** from the navigation sidebar to test your new job. +Finally, click **Build Now** from the navigation sidebar to run your new job. Running into issues? Join Infisical's [community Slack](https://infisical.com/slack) for quick support. @@ -77,7 +93,8 @@ Finally, click **Build Now** from the navigation sidebar to test your new job. ## Use Infisical in a Jenkins Pipeline -To use Infisical in a Pipeline job, you'll need to expose the credential you created above as an environment variable. First, click **New Item** from the dashboard navigation sidebar: +To fetch secrets using Infisical in a Pipeline job, you'll need to expose the Jenkins credential you created above as an environment variable. +To do so, click **New Item** from the dashboard navigation sidebar: ![Jenkins step 6](../../images/integrations/jenkins/jenkins_6.png) @@ -92,31 +109,31 @@ pipeline { agent any environment { - INFISICAL_SERVICE_TOKEN = credentials('INFISICAL_SERVICE_TOKEN') + INFISICAL_TOKEN = credentials('infisical-service-token') } stages { stage('Run Infisical') { steps { - sh("infisical secrets") + sh("infisical secrets --env=dev --path=/") // doesn't work // sh("docker run --rm test-container infisical secrets") // works - // sh("docker run -e INFISICAL_SERVICE_TOKEN=${INFISICAL_SERVICE_TOKEN} --rm test-container infisical secrets") + // sh("docker run -e INFISICAL_TOKEN=${INFISICAL_TOKEN} --rm test-container infisical secrets --env=dev --path=/") // doesn't work // sh("docker-compose up -d") // works - // sh("INFISICAL_SERVICE_TOKEN=${INFISICAL_SERVICE_TOKEN} docker-compose up -d") + // sh("INFISICAL_TOKEN=${INFISICAL_TOKEN} docker-compose up -d") } } } } ``` -This is a very basic sample that you can work from. Jenkins injects the INFISICAL_SERVICE_TOKEN environment variable defined in the pipeline into the shell the commands execute with, but there are some situations where that won't pass through properly โ€“ notably if you're executing docker containers on the executor machine. The examples above should give you some idea for how that will work. - -Finally, click **Build Now** from the navigation sidebar to test your new job. +The example provided above serves as an initial guide. It shows how Jenkins adds the `INFISICAL_TOKEN` environment variable, which is configured in the pipeline, into the shell for executing commands. +There may be instances where this doesn't work as expected in the context of running Docker commands. +However, the list of working examples should provide some insight into how this can be handled properly. diff --git a/docs/integrations/platforms/ansible.mdx b/docs/integrations/platforms/ansible.mdx index efb9e4f63..2d524d55c 100644 --- a/docs/integrations/platforms/ansible.mdx +++ b/docs/integrations/platforms/ansible.mdx @@ -5,6 +5,19 @@ description: "How to use Infisical for secret management in Ansible" The documentation for using Infisical to manage secrets in Ansible is currently available [here](https://galaxy.ansible.com/ui/repo/published/infisical/vault/). - - Have any questions? Join Infisical's [community Slack](https://infisical.com/slack) for quick support. - +## Troubleshoot + + + If you get this Python error when you running the lookup plugin:- + + ``` + objc[72832]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug. + Fatal Python error: Aborted + ``` + + You will need to add this to your shell environment or ansible wrapper script:- + + ``` + export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES + ``` + diff --git a/docs/integrations/platforms/ecs-with-agent.mdx b/docs/integrations/platforms/ecs-with-agent.mdx new file mode 100644 index 000000000..bbb88ea64 --- /dev/null +++ b/docs/integrations/platforms/ecs-with-agent.mdx @@ -0,0 +1,287 @@ +--- +title: 'Amazon ECS' +description: "How to deliver secrets to Amazon Elastic Container Service" +--- + +![ecs diagram](/images/guides/agent-with-ecs/ecs-diagram.png) + +This guide will go over the steps needed to access secrets stored in Infisical from Amazon Elastic Container Service (ECS). + +At a high level, the steps involve setting up an ECS task with a [Infisical Agent](/infisical-agent/overview) as a sidecar container. This sidecar container uses [Universal Auth](/documentation/platform/identities/universal-auth) to authenticate with Infisical to fetch secrets/access tokens. +Once the secrets/access tokens are retrieved, they are then stored in a shared [Amazon Elastic File System](https://aws.amazon.com/efs/) (EFS) volume. This volume is then made accessible to your application and all of its replicas. + +This guide primarily focuses on integrating Infisical Cloud with Amazon ECS on AWS Fargate and Amazon EFS. +However, the principles and steps can be adapted for use with any instance of Infisical (on premise or cloud) and different ECS launch configurations. + +## Prerequisites +This guide requires the following prerequisites: +- Infisical account +- Git installed +- Terraform v1.0 or later installed +- Access to AWS credentials +- Understanding of [Infisical Agent](/infisical-agent/overview) + +## What we will deploy +For this demonstration, we'll deploy the [File Browser](https://github.com/filebrowser/filebrowser) application on our ECS cluster. +Although this guide focuses on File Browser, the principles outlined here can be applied to any application of your choice. + +File Browser plays a key role in this context because it enables us to view all files attached to a specific volume. +This feature is important for our demonstration, as it allows us to verify whether the Infisical agent is depositing the expected files into the designated file volume and if those files are accessible to the application. + + +Volumes that contain sensitive secrets should not be publicly accessible. The use of File Browser here is solely for demonstration and verification purposes. + + + +## Configure Authentication with Infisical +In order for the Infisical agent to fetch credentials from Infisical, we'll first need to authenticate with Infisical. +While Infisical supports various authentication methods, this guide focuses on using Universal Auth to authenticate the agent with Infisical. + +Follow the documentation to configure and generate a client id and client secret with Universal auth [here](/documentation/platform/identities/universal-auth). +Make sure to save these credentials somewhere handy because you'll need them soon. + +## Clone guide assets repository +To help you quickly deploy the example application, please clone the guide assets from this [Github repository](https://github.com/Infisical/infisical-guides.git). +This repository contains assets for all Infisical guides. The content for this guide can be found within a sub directory called `aws-ecs-with-agent`. +The guide will assume that `aws-ecs-with-agent` is your working directory going forward. + +## Deploy example application + +Before we can deploy our full application and its related infrastructure with Terraform, we'll need to first configure our Infisical agent. + +### Agent configuration overview +The agent config file defines what authentication method will be used when connecting with Infisical along with where the fetched secrets/access tokens should be saved to. + +Since the Infisical agent will be deployed as a sidecar, the agent configuration file and any secret template files will need to be encoded in base64. +This encoding step is necessary as it allows these files to be added into our Terraform configuration file without needing to upload them first. + +#### Secret template file +The Infisical agent accepts one or more optional template files. If provided, the agent will fetch secrets using the set authentication method and format the fetched secrets according to the given template file. + +For demonstration purposes, we will create the following secret template file. +This template will transform our secrets from Infisical project with the ID `62fd92aa8b63973fee23dec7`, in the `dev` environment, and secrets located in the path `/`, into a `KEY=VALUE` format. + + + Remember to update the project id, environment slug and secret path to one that exists within your Infisical project + + +```secrets.template secrets.template +{{- with secret "62fd92aa8b63973fee23dec7" "dev" "/" }} +{{- range . }} +{{ .Key }}={{ .Value }} +{{- end }} +{{- end }} +``` + +Next, we need encode this template file in `base64` so it can be set in the agent configuration file. + +```bash +cat secrets.template | base64 +Cnt7LSB3aXRoIHNlY3JldCAiMWVkMjk2MWQtNDM5NS00MmNlLTlkNzQtYjk2ZGQwYmYzMDg0IiAiZGV2IiAiLyIgfX0Ke3stIHJhbmdlIC4gfX0Ke3sgLktleSB9fT17eyAuVmFsdWUgfX0Ke3stIGVuZCB9fQp7ey0gZW5kIH19 +``` + +#### Full agent configuration file +This agent config file will connect with Infisical Cloud using Universal Auth and deposit access tokens at path `/infisical-agent/access-token` and render secrets to file `/infisical-agent/secrets`. + +You'll notice that instead of passing the path to the secret template file as we normally would, we set the base64 encoded template from the previous step under `base64-template-content` property. + +```yaml agent-config.yaml +infisical: + address: https://app.infisical.com + exit-after-auth: true +auth: + type: universal-auth + config: + remove_client_secret_on_read: false +sinks: + - type: file + config: + path: /infisical-agent/access-token +templates: + - base64-template-content: Cnt7LSB3aXRoIHNlY3JldCAiMWVkMjk2MWQtNDM5NS00MmNlLTlkNzQtYjk2ZGQwYmYzMDg0IiAiZGV2IiAiLyIgfX0Ke3stIHJhbmdlIC4gfX0Ke3sgLktleSB9fT17eyAuVmFsdWUgfX0Ke3stIGVuZCB9fQp7ey0gZW5kIH19 + destination-path: /infisical-agent/secrets +``` + +Again, we'll need to encode the full configuration file in `base64` so it can be easily delivered via Terraform. + +```bash +cat agent-config.yaml | base64 +aW5maXNpY2FsOgogIGFkZHJlc3M6IGh0dHBzOi8vYXBwLmluZmlzaWNhbC5jb20KICBleGl0LWFmdGVyLWF1dGg6IHRydWUKYXV0aDoKICB0eXBlOiB1bml2ZXJzYWwtYXV0aAogIGNvbmZpZzoKICAgIHJlbW92ZV9jbGllbnRfc2VjcmV0X29uX3JlYWQ6IGZhbHNlCnNpbmtzOgogIC0gdHlwZTogZmlsZQogICAgY29uZmlnOgogICAgICBwYXRoOiAvaW5maXNpY2FsLWFnZW50L2FjY2Vzcy10b2tlbgp0ZW1wbGF0ZXM6CiAgLSBiYXNlNjQtdGVtcGxhdGUtY29udGVudDogQ250N0xTQjNhWFJvSUhObFkzSmxkQ0FpTVdWa01qazJNV1F0TkRNNU5TMDBNbU5sTFRsa056UXRZamsyWkdRd1ltWXpNRGcwSWlBaVpHVjJJaUFpTHlJZ2ZYMEtlM3N0SUhKaGJtZGxJQzRnZlgwS2Uzc2dMa3RsZVNCOWZUMTdleUF1Vm1Gc2RXVWdmWDBLZTNzdElHVnVaQ0I5ZlFwN2V5MGdaVzVrSUgxOQogICAgZGVzdGluYXRpb24tcGF0aDogL2luZmlzaWNhbC1hZ2VudC9zZWNyZXRzCg== +``` + +## Add auth credentials & agent config +With the base64 encoded agent config file and Universal Auth credentials in hand, it's time to assign them as values in our Terraform config file. + +To configure these values, navigate to the `ecs.tf` file in your preferred code editor and assign values to `auth_client_id`, `auth_client_secret`, and `agent_config`. + +```hcl ecs.tf +...snip... +data "template_file" "cb_app" { + template = file("./templates/ecs/cb_app.json.tpl") + + vars = { + app_image = var.app_image + sidecar_image = var.sidecar_image + app_port = var.app_port + fargate_cpu = var.fargate_cpu + fargate_memory = var.fargate_memory + aws_region = var.aws_region + auth_client_id = "" + auth_client_secret = "" + agent_config = "" + } +} +...snip... +``` + + + To keep this guide simple, `auth_client_id`, `auth_client_secret` have been added directly into the ECS container definition. + However, in production, you should securely fetch these values from AWS Secrets Manager or AWS Parameter store and feed them directly to agent sidecar. + + +After these values have been set, they will be passed to the Infisical agent during startup through environment variables, as configured in the `infisical-sidecar` container below. + +```terraform templates/ecs/cb_app.json.tpl +[ +...snip... + { + "name": "infisical-sidecar", + "image": "${sidecar_image}", + "cpu": 1024, + "memory": 1024, + "networkMode": "bridge", + "command": ["agent"], + "essential": false, + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "/ecs/agent", + "awslogs-region": "${aws_region}", + "awslogs-stream-prefix": "ecs" + } + }, + "healthCheck": { + "command": ["CMD-SHELL", "agent", "--help"], + "interval": 30, + "timeout": 5, + "retries": 3, + "startPeriod": 0 + }, + "environment": [ + { + "name": "INFISICAL_UNIVERSAL_AUTH_CLIENT_ID", + "value": "${auth_client_id}" + }, + { + "name": "INFISICAL_UNIVERSAL_CLIENT_SECRET", + "value": "${auth_client_secret}" + }, + { + "name": "INFISICAL_AGENT_CONFIG_BASE64", + "value": "${agent_config}" + } + ], + "mountPoints": [ + { + "containerPath": "/infisical-agent", + "sourceVolume": "infisical-efs" + } + ] + } +] +``` + +In the above container definition, you'll notice that that the Infisical agent has a `mountPoints` defined. +This mount point is referencing to the already configured EFS volume as shown below. +`containerPath` is set to `/infisical-agent` because that is that the folder we have instructed the agent to deposit the credentials to. + +```hcl terraform/efs.tf +resource "aws_efs_file_system" "infisical_efs" { + tags = { + Name = "INFISICAL-ECS-EFS" + } +} + +resource "aws_efs_mount_target" "mount" { + count = length(aws_subnet.private.*.id) + file_system_id = aws_efs_file_system.infisical_efs.id + subnet_id = aws_subnet.private[count.index].id + security_groups = [aws_security_group.efs_sg.id] +} +``` + +## Configure AWS credentials +Because we'll be deploying the example file browser application to AWS via Terraform, you will need to obtain a set of `AWS Access Key` and `Secret Key`. +Once you have generated these credentials, export them to your terminal. + +1. Export the AWS Access Key ID: + + ```bash + export AWS_ACCESS_KEY_ID= + ``` + +2. Export the AWS Secret Access Key: + + ```bash + export AWS_SECRET_ACCESS_KEY= + ``` + +## Deploy terraform configuration +With the agent's sidecar configuration complete, we can now deploy our changes to AWS via Terraform. + +1. Change your directory to `terraform` +```sh +cd terraform +``` + +2. Initialize Terraform +``` +$ terraform init +``` + +3. Preview resources that will be created +``` +$ terraform plan +``` + +4. Trigger resource creation +```bash +$ terraform apply + +Do you want to perform these actions? + Terraform will perform the actions described above. + Only 'yes' will be accepted to approve. + + Enter a value: yes +``` + +```bash + +Apply complete! Resources: 1 added, 1 changed, 1 destroyed. + +Outputs: + +alb_hostname = "cb-load-balancer-1675475779.us-east-1.elb.amazonaws.com:8080" +``` + +Once the resources have been successfully deployed, Terrafrom will output the host address where the file browser application will be accessible. +It may take a few minutes for the application to become fully ready. + + +## Verify secrets/tokens in EFS volume +To verify that the agent is depositing access tokens and rendering secrets to the paths specified in the agent config, navigate to the web address from the previous step. +Once you visit the address, you'll be prompted to login. Enter the credentials shown below. + +![file browser main login page](/images/guides/agent-with-ecs/file_browser_main.png) + +Since our EFS volume is mounted to the path of the file browser application, we should see the access token and rendered secret file we defined via the agent config file. + +![file browswer dashbaord](/images/guides/agent-with-ecs/filebrowser_afterlogin.png) + +As expected, two files are present: `access-token` and `secrets`. +The `access-token` file should hold a valid `Bearer` token, which can be used to make HTTP requests to Infisical. +The `secrets` file should contain secrets, formatted according to the specifications in our secret template file (presented in key=value format). + +![file browser access token deposit](/images/guides/agent-with-ecs/access-token-deposit.png) + +![file browser secrets render](/images/guides/agent-with-ecs/secrets-deposit.png) \ No newline at end of file diff --git a/docs/mint.json b/docs/mint.json index 9e3701519..b67493aeb 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -1,9 +1,9 @@ { "name": "Infisical", - "basePath": "/docs", + "openapi": "https://app.infisical.com/api/docs/json", "logo": { - "dark": "/docs/logo/dark.svg", - "light": "/docs/logo/light.svg", + "dark": "/logo/dark.svg", + "light": "/logo/light.svg", "href": "https://infisical.com" }, "favicon": "/favicon.png", @@ -234,7 +234,8 @@ }, "integrations/platforms/kubernetes", "integrations/frameworks/terraform", - "integrations/platforms/ansible" + "integrations/platforms/ansible", + "integrations/platforms/ecs-with-agent" ] }, { @@ -463,7 +464,9 @@ { "group": "Contributing to platform", "pages": [ - "contributing/platform/developing" + "contributing/platform/developing", + "contributing/platform/backend/how-to-create-a-feature", + "contributing/platform/backend/folder-structure" ] }, { diff --git a/docs/self-hosting/configuration/envars.mdx b/docs/self-hosting/configuration/envars.mdx index 3ea1762e2..ed9f843a3 100644 --- a/docs/self-hosting/configuration/envars.mdx +++ b/docs/self-hosting/configuration/envars.mdx @@ -18,8 +18,8 @@ Other environment variables are listed below to increase the functionality of yo Must be a random 32 byte base64 string. Can be generated with `openssl rand -base64 32` - - Mongo connection string. *TLS based connection string is not yet supported + + Postgres database connection string. diff --git a/docs/self-hosting/deployment-options/docker-compose.mdx b/docs/self-hosting/deployment-options/docker-compose.mdx index 304fffbe3..771d0836f 100644 --- a/docs/self-hosting/deployment-options/docker-compose.mdx +++ b/docs/self-hosting/deployment-options/docker-compose.mdx @@ -2,53 +2,79 @@ title: "Docker Compose" description: "Run Infisical with Docker Compose template" --- +Install Infisical using Docker compose. This self hosting method contains all of the required components needed +to run a functional instance of Infisical. - - - ```bash - # Example in ubuntu - apt-get update - apt-get upgrade - apt install docker-compose - ``` - - - 2.1. Run the command below to download the `.env` file template. - - ```bash - wget -O .env https://raw.githubusercontent.com/Infisical/infisical/main/.env.example - ``` - - 2.2. Run the command below to download the docker compose template. - - ```bash - wget -O docker-compose.yml https://raw.githubusercontent.com/Infisical/infisical/main/docker-compose.yml - ``` - - 2.3. Run the command below to download the `nginx` config file. - - ```bash - mkdir nginx && wget -O ./nginx/default.conf https://raw.githubusercontent.com/Infisical/infisical/main/nginx/default.dev.conf - ``` - - - - Running Infisical requires a few environment variables to be set. - At minimum, Infisical requires that you set the variables `ENCRYPTION_KEY`, `AUTH_SECRET`, `MONGO_URL`, and `REDIS_URL` which you can read more about [here](/self-hosting/configuration/envars). +## Prerequisites +- [Docker](https://docs.docker.com/engine/install/) +- [Docker compose](https://docs.docker.com/compose/install/) - Tweak the `.env` accordingly. + +This Docker Compose configuration is not designed for high-availability production scenarios. +It includes just the essential components needed to set up an Infisical proof of concept (POC). +Additional configuration is required to enhance data redundancy and ensure higher availability for production environments. + - ```bash - nano .env - ``` - - - Finally, run the command below to get Infisical up and running (in detached mode). +## Verify prerequisites + To verify that Docker compose and Docker are installed on the machine where you plan to install Infisical, run the following commands. + Check for docker installation ```bash - docker-compose -f docker-compose.yml up -d + docker ``` - Your Infisical installation is complete and should be running on port `80` or `http://localhost:80`. - - \ No newline at end of file + Check for docker compose installation + ```bash + docker-compose + ``` + +## Download docker compose file +You can obtain the Infisical docker compose file by using a command-line downloader such as `wget` or `curl`. +If your system doesn't have either of these, you can use a equivalent command that works with your machine. + + + + ```bash + curl -o docker-compose.prod.yml https://raw.githubusercontent.com/Infisical/infisical/main/docker-compose.prod.yml + ``` + + + ```bash + wget -O docker-compose.prod.yml https://raw.githubusercontent.com/Infisical/infisical/main/docker-compose.prod.yml + ``` + + + +## Configure instance credentials +Infisical requires a set of credentials used for connecting to dependent services such as Postgres, Redis, etc. +The default credentials can be downloaded using the one of the commands listed below. + + + + ```bash + curl -o .env https://raw.githubusercontent.com/Infisical/infisical/main/.env.example + ``` + + + ```bash + wget -O .env https://raw.githubusercontent.com/Infisical/infisical/main/.env.example + ``` + + + +Once downloaded, the credentials file will be saved to your working directly as `.env` file. +View all available configurations [here](/self-hosting/configuration/envars). + + + The default .env file contains credentials that are intended solely for testing purposes. + For production use, please generate a new `ENCRYPTION_KEY` and `AUTH_SECRET`. Instructions to do so, can be found [here](/self-hosting/configuration/envars) + + +## Start Infisical +Run the command below to start Infisical and all related services. + +```bash +docker-compose -f docker-compose.prod.yml up +``` + +Your Infisical instance should now be running on port `80`. To access your instance, visit `http://localhost:80`. \ No newline at end of file diff --git a/docs/spec.yaml b/docs/spec.yaml deleted file mode 100644 index c3d050395..000000000 --- a/docs/spec.yaml +++ /dev/null @@ -1,5152 +0,0 @@ -openapi: 3.0.0 -info: - title: Infisical API - description: List of all available APIs that can be consumed - version: 1.0.0 -servers: - - url: https://app.infisical.com - description: Production server - - url: http://localhost:8080 - description: Local server -paths: - /api/v1/identities/: - post: - summary: Create identity - description: Create identity - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identity: - $ref: '#/components/schemas/Identity' - description: Details of the created identity - security: - - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: Name of entity to create - example: development - organizationId: - type: string - description: ID of organization where to create identity - example: dev-environment - role: - type: string - description: Role to assume for organization membership - example: no-access - required: - - name - - organizationId - - role - /api/v1/identities/{identityId}: - patch: - summary: Update identity - description: Update identity - parameters: - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity to update - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identity: - $ref: '#/components/schemas/Identity' - description: Details of the updated identity - security: - - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: Name of entity to update to - example: development - role: - type: string - description: Role to update to for organization membership - example: no-access - delete: - summary: Delete identity - description: Delete identity - parameters: - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identity: - $ref: '#/components/schemas/Identity' - description: Details of the deleted identity - security: - - bearerAuth: [] - /api/v1/secret/{secretId}/secret-versions: - get: - summary: Return secret versions - description: Return secret versions - parameters: - - name: secretId - in: path - required: true - schema: - type: string - description: ID of secret - - name: offset - description: Number of versions to skip - required: false - in: query - schema: - type: string - - name: limit - description: Maximum number of versions to return - required: false - in: query - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secretVersions: - type: array - items: - $ref: '#/components/schemas/SecretVersion' - description: Secret versions - security: - - apiKeyAuth: [] - /api/v1/secret/{secretId}/secret-versions/rollback: - post: - summary: Roll back secret to a version. - description: Roll back secret to a version. - parameters: - - name: secretId - in: path - required: true - schema: - type: string - description: ID of secret - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secret: - type: object - $ref: '#/components/schemas/Secret' - description: Secret rolled back to - security: - - apiKeyAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - version: - type: integer - description: Version of secret to roll back to - /api/v1/secret-snapshot/{secretSnapshotId}: - get: - description: '' - parameters: - - name: secretSnapshotId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/users/me/ip: - get: - description: '' - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/secret-snapshots: - get: - summary: Return project secret snapshot ids - description: Return project secret snapshots ids - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of project where to get secret snapshots for - - name: environment - description: Slug of environment where to get secret snapshots for - required: true - in: query - schema: - type: string - - name: directory - description: >- - Path where to get secret snapshots for like / or /foo/bar. Default - is / - required: false - in: query - schema: - type: string - - name: offset - description: Number of secret snapshots to skip - required: false - in: query - schema: - type: string - - name: limit - description: Maximum number of secret snapshots to return - required: false - in: query - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secretSnapshots: - type: array - items: - $ref: '#/components/schemas/SecretSnapshot' - description: Project secret snapshots - security: - - apiKeyAuth: [] - bearerAuth: [] - /api/v1/workspace/{workspaceId}/secret-snapshots/count: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/secret-snapshots/rollback: - post: - summary: >- - Roll back project secrets to those captured in a secret snapshot - version. - description: >- - Roll back project secrets to those captured in a secret snapshot - version. - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of project where to roll back - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secrets: - type: array - items: - $ref: '#/components/schemas/Secret' - description: Secrets rolled back to - security: - - apiKeyAuth: [] - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - environment: - type: string - description: Slug of environment where to roll back - directory: - type: string - description: Path where to roll back for like / or /foo/bar. Default is / - version: - type: integer - description: Version of secret snapshot to roll back to - /api/v1/workspace/{workspaceId}/audit-logs: - get: - summary: Return audit logs - description: Return audit logs - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of the workspace where to get folders from - - name: offset - description: Number of logs to skip before starting to return logs for pagination - required: false - in: query - schema: - type: string - - name: limit - description: Maximum number of logs to return for pagination - required: false - in: query - schema: - type: string - - name: startDate - description: Filter logs from this date in ISO-8601 format - required: false - in: query - schema: - type: string - - name: endDate - description: Filter logs till this date in ISO-8601 format - required: false - in: query - schema: - type: string - - name: eventType - description: >- - Filter by type of event such as get-secrets, get-secret, - create-secret, update-secret, delete-secret, etc. - required: false - in: query - schema: - type: string - - name: userAgentType - description: Filter by type of user agent such as web, cli, k8-operator, or other - required: false - in: query - schema: - type: string - - name: actor - description: Filter by actor such as user or service - required: false - in: query - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - auditLogs: - type: array - items: - $ref: '#/components/schemas/AuditLog' - description: List of audit log - security: - - apiKeyAuth: [] - /api/v1/workspace/{workspaceId}/audit-logs/filters/actors: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/trusted-ips: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}/trusted-ips/{trustedIpId}: - patch: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: trustedIpId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - delete: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: trustedIpId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/organizations/{organizationId}/plans/table: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/plan: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/session/trial: - post: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/plan/billing: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/plan/table: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/billing-details: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - patch: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/billing-details/payment-methods: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - post: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/billing-details/payment-methods/{pmtMethodId}: - delete: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - - name: pmtMethodId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/billing-details/tax-ids: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - post: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/billing-details/tax-ids/{taxId}: - delete: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - - name: taxId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/invoices: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organizations/{organizationId}/licenses: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/sso/redirect/saml2/{ssoIdentifier}: - get: - description: '' - parameters: - - name: ssoIdentifier - in: path - required: true - schema: - type: string - - name: callback_port - in: query - schema: - type: string - responses: - default: - description: '' - /api/v1/sso/saml2/{ssoIdentifier}: - post: - description: '' - parameters: - - name: ssoIdentifier - in: path - required: true - schema: - type: string - responses: - default: - description: '' - /api/v1/sso/config: - get: - description: '' - responses: - '200': - description: OK - post: - description: '' - responses: - '200': - description: OK - '400': - description: Bad Request - patch: - description: '' - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/cloud-products/: - get: - description: '' - responses: - '200': - description: OK - /api/v3/api-key/: - post: - description: '' - responses: - '200': - description: OK - /api/v3/api-key/{apiKeyDataId}: - patch: - description: '' - parameters: - - name: apiKeyDataId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: apiKeyDataId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret-rotation-providers/{workspaceId}: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret-rotations/: - post: - description: '' - responses: - '200': - description: OK - get: - description: '' - responses: - '200': - description: OK - /api/v1/secret-rotations/restart: - post: - description: '' - responses: - '200': - description: OK - /api/v1/secret-rotations/{id}: - delete: - description: '' - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/signup/email/signup: - post: - description: '' - responses: - '200': - description: OK - '403': - description: Forbidden - /api/v1/signup/email/verify: - post: - description: '' - responses: - '200': - description: OK - '403': - description: Forbidden - /api/v1/auth/token: - post: - description: '' - responses: - '200': - description: OK - /api/v1/auth/login1: - post: - description: '' - responses: - '200': - description: OK - /api/v1/auth/login2: - post: - description: '' - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/auth/logout: - post: - description: '' - responses: - '200': - description: OK - /api/v1/auth/checkAuth: - post: - description: '' - responses: - '200': - description: OK - /api/v1/auth/sessions: - delete: - description: '' - responses: - '200': - description: OK - /api/v1/auth/token/renew: - post: - summary: Renew access token - description: Renew access token - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - accessToken: - type: string - description: (Same) Access token after successful renewal - expiresIn: - type: number - description: TTL of access token in seconds - tokenType: - type: string - description: Type of access token (e.g. Bearer) - description: Access token and its details - requestBody: - content: - application/json: - schema: - type: object - properties: - accessToken: - type: string - description: Access token to renew - example: ... - /api/v1/auth/universal-auth/login: - post: - summary: Login with Universal Auth - description: Login with Universal Auth - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - accessToken: - type: string - description: Access token issued after successful login - expiresIn: - type: number - description: TTL of access token in seconds - tokenType: - type: string - description: Type of access token (e.g. Bearer) - description: Access token and its details - requestBody: - content: - application/json: - schema: - type: object - properties: - clientId: - type: string - description: Client ID for identity to login with Universal Auth - example: ... - clientSecret: - type: string - description: Client Secret for identity to login with Universal Auth - example: ... - /api/v1/auth/universal-auth/identities/{identityId}: - post: - summary: Attach Universal Auth configuration onto identity - description: Attach Universal Auth configuration onto identity - parameters: - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity to attach Universal Auth onto - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identityUniversalAuth: - $ref: '#/components/schemas/IdentityUniversalAuth' - description: Details of attached Universal Auth - '400': - description: Bad Request - security: - - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - clientSecretTrustedIps: - type: array - items: - type: object - properties: - ipAddress: - type: string - description: IP address to trust - default: 0.0.0.0/0 - description: >- - List of IPs or CIDR ranges that the Client Secret can be - used from together with the Client ID to get back an access - token. By default, Client Secrets are given the 0.0.0.0/0 - entry representing all possible IPv4 addresses. - example: ... - default: - - ipAddress: 0.0.0.0/0 - accessTokenTTL: - type: number - description: >- - The incremental lifetime for an acccess token in seconds; a - value of 0 implies an infinite incremental lifetime. - example: ... - default: 100 - accessTokenMaxTTL: - type: number - description: >- - The maximum lifetime for an acccess token in seconds; a - value of 0 implies an infinite maximum lifetime. - example: ... - default: 2592000 - accessTokenNumUsesLimit: - type: number - description: >- - The maximum number of times that an access token can be - used; a value of 0 implies infinite number of uses. - example: ... - default: 0 - accessTokenTrustedIps: - type: array - items: - type: object - properties: - ipAddress: - type: string - description: IP address to trust - default: 0.0.0.0/0 - description: >- - List of IPs or CIDR ranges that access tokens can be used - from. By default, each token is given the 0.0.0.0/0 entry - representing all possible IPv4 addresses. - example: ... - default: - - ipAddress: 0.0.0.0/0 - patch: - summary: Update Universal Auth configuration on identity - description: Update Universal Auth configuration on identity - parameters: - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity to update Universal Auth on - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identityUniversalAuth: - $ref: '#/components/schemas/IdentityUniversalAuth' - description: Details of updated Universal Auth - '400': - description: Bad Request - security: - - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - clientSecretTrustedIps: - type: array - items: - type: object - properties: - ipAddress: - type: string - description: IP address to trust - description: >- - List of IPs or CIDR ranges that the Client Secret can be - used from together with the Client ID to get back an access - token. By default, Client Secrets are given the 0.0.0.0/0 - entry representing all possible IPv4 addresses. - example: ... - accessTokenTTL: - type: number - description: >- - The incremental lifetime for an acccess token in seconds; a - value of 0 implies an infinite incremental lifetime. - example: ... - accessTokenMaxTTL: - type: number - description: >- - The maximum lifetime for an acccess token in seconds; a - value of 0 implies an infinite maximum lifetime. - example: ... - accessTokenNumUsesLimit: - type: number - description: >- - The maximum number of times that an access token can be - used; a value of 0 implies infinite number of uses. - example: ... - accessTokenTrustedIps: - type: array - items: - type: object - properties: - ipAddress: - type: string - description: IP address to trust - description: >- - List of IPs or CIDR ranges that access tokens can be used - from. By default, each token is given the 0.0.0.0/0 entry - representing all possible IPv4 addresses. - example: ... - get: - summary: Retrieve Universal Auth configuration on identity - description: Retrieve Universal Auth configuration on identity - parameters: - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity to retrieve Universal Auth on - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identityUniversalAuth: - $ref: '#/components/schemas/IdentityUniversalAuth' - description: Details of retrieved Universal Auth - security: - - bearerAuth: [] - /api/v1/auth/universal-auth/identities/{identityId}/client-secrets: - post: - summary: Create Universal Auth Client Secret for identity - description: Create Universal Auth Client Secret for identity - parameters: - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity to create Universal Auth Client Secret for - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - clientSecret: - type: string - description: The created Client Secret - clientSecretData: - $ref: '#/components/schemas/IdentityUniversalAuthClientSecretData' - description: Details of the created Client Secret - security: - - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - description: - type: string - description: A description for the Client Secret to create. - example: ... - ttl: - type: number - description: >- - The time-to-live for the Client Secret to create. By - default, the TTL will be set to 0 which implies that the - Client Secret will never expire; a value of 0 implies an - infinite lifetime. - example: ... - default: 0 - numUsesLimit: - type: number - description: >- - The maximum number of times that the Client Secret can be - used together with the Client ID to get back an access - token; a value of 0 implies infinite number of uses. - example: ... - default: 0 - get: - summary: List Universal Auth Client Secrets for identity - description: List Universal Auth Client Secrets for identity - parameters: - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity for which to get Client Secrets for - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - clientSecretData: - type: array - items: - $ref: >- - #/components/schemas/IdentityUniversalAuthClientSecretData - description: Details of the Client Secrets - security: - - bearerAuth: [] - /api/v1/auth/universal-auth/identities/{identityId}/client-secrets/{clientSecretId}/revoke: - post: - summary: Revoke Universal Auth Client Secret for identity - description: Revoke Universal Auth Client Secret for identity - parameters: - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity under which Client Secret was issued for - - name: clientSecretId - in: path - required: true - schema: - type: string - description: ID of Client Secret to revoke - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - clientSecretData: - $ref: '#/components/schemas/IdentityUniversalAuthClientSecretData' - description: Details of the revoked Client Secret - security: - - bearerAuth: [] - /api/v1/admin/config: - get: - description: '' - responses: - '200': - description: OK - patch: - description: '' - responses: - '200': - description: OK - /api/v1/admin/signup: - post: - description: '' - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - /api/v1/bot/{workspaceId}: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/bot/{botId}/active: - patch: - description: '' - parameters: - - name: botId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/user/: - get: - description: '' - responses: - '200': - description: OK - /api/v1/user-action/: - post: - description: '' - responses: - '200': - description: OK - get: - description: '' - responses: - '200': - description: OK - /api/v1/organization/: - get: - description: '' - responses: - '200': - description: OK - /api/v1/organization/{organizationId}: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organization/{organizationId}/users: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organization/{organizationId}/my-workspaces: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organization/{organizationId}/name: - patch: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organization/{organizationId}/incidentContactOrg: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - post: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organization/{organizationId}/customer-portal-session: - post: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/organization/{organizationId}/workspace-memberships: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/keys: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/users: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/: - get: - description: '' - responses: - '200': - description: OK - post: - description: '' - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/name: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/invite-signup: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/integrations: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/authorizations: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/workspace/{workspaceId}/service-tokens: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/membership-org/membershipOrg/{membershipOrgId}/change-role: - post: - description: '' - parameters: - - name: membershipOrgId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/membership-org/{membershipOrgId}: - delete: - description: '' - parameters: - - name: membershipOrgId - in: path - required: true - schema: - type: string - responses: - default: - description: '' - /api/v1/membership/{workspaceId}/connect: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/membership/{membershipId}: - delete: - description: '' - parameters: - - name: membershipId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/membership/{membershipId}/change-role: - post: - description: '' - parameters: - - name: membershipId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/key/{workspaceId}: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/key/{workspaceId}/latest: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/invite-org/signup: - post: - description: '' - parameters: - - name: host - in: header - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/invite-org/verify: - post: - description: '' - responses: - '200': - description: OK - /api/v1/secret/{workspaceId}: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - keys: - example: any - environment: - example: any - channel: - example: any - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: query - schema: - type: string - - name: channel - in: query - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret/{workspaceId}/service-token: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: query - schema: - type: string - - name: channel - in: query - schema: - type: string - responses: - '200': - description: OK - /api/v1/service-token/: - get: - description: '' - responses: - '200': - description: OK - post: - description: '' - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - example: any - workspaceId: - example: any - environment: - example: any - expiresIn: - example: any - publicKey: - example: any - encryptedKey: - example: any - nonce: - example: any - /api/v1/password/srp1: - post: - description: '' - responses: - '200': - description: OK - /api/v1/password/change-password: - post: - description: '' - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/password/email/password-reset: - post: - description: '' - responses: - '200': - description: OK - /api/v1/password/email/password-reset-verify: - post: - description: '' - responses: - '200': - description: OK - '403': - description: Forbidden - /api/v1/password/backup-private-key: - get: - description: '' - responses: - '200': - description: OK - post: - description: '' - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/password/password-reset: - post: - description: '' - responses: - '200': - description: OK - /api/v1/integration/: - post: - description: '' - responses: - '200': - description: OK - /api/v1/integration/{integrationId}: - patch: - description: '' - parameters: - - name: integrationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: integrationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration/manual-sync: - post: - description: '' - responses: - '200': - description: OK - /api/v1/integration-auth/integration-options: - get: - description: '' - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - delete: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/integration-auth/oauth-token: - post: - description: '' - responses: - '200': - description: OK - /api/v1/integration-auth/access-token: - post: - description: '' - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/apps: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/teams: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/vercel/branches: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/checkly/groups: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/qovery/orgs: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/qovery/projects: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/qovery/environments: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/qovery/apps: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/qovery/containers: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/qovery/jobs: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/railway/environments: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/railway/services: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/bitbucket/workspaces: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/northflank/secret-groups: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/integration-auth/{integrationAuthId}/teamcity/build-configs: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/folders/: - post: - summary: Create folder - description: Create folder - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - folder: - type: object - properties: - id: - type: string - description: ID of folder - example: someFolderId - name: - type: string - description: Name of folder - example: my_folder - version: - type: number - description: Version of folder - example: 1 - description: Details of created folder - '400': - description: >- - Bad Request. For example, 'Folder name cannot contain spaces. Only - underscore and dashes' - '401': - description: Unauthorized request. For example, 'Folder Permission Denied' - security: - - apiKeyAuth: [] - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - workspaceId: - type: string - description: ID of the workspace where to create folder - example: someWorkspaceId - environment: - type: string - description: Slug of environment where to create folder - example: production - folderName: - type: string - description: Name of folder to create - example: my_folder - directory: - type: string - description: Path where to create folder like / or /foo/bar. Default is / - example: /foo/bar - required: - - workspaceId - - environment - - folderName - get: - summary: Get folders - description: Get folders - parameters: - - name: workspaceId - description: ID of the workspace where to get folders from - required: true - in: query - schema: - type: string - - name: environment - description: Slug of environment where to get folders from - required: true - in: query - schema: - type: string - - name: directory - description: Path where to get fodlers from like / or /foo/bar. Default is / - required: false - in: query - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - folders: - type: array - items: - type: object - properties: - id: - type: string - example: someFolderId - name: - type: string - example: someFolderName - description: List of folders - '400': - description: Bad Request. For instance, 'The folder doesn't exist' - '401': - description: Unauthorized request. For example, 'Folder Permission Denied' - security: - - apiKeyAuth: [] - bearerAuth: [] - /api/v1/folders/{folderName}: - patch: - summary: Update folder - description: Update folder - parameters: - - name: folderName - in: path - required: true - schema: - type: string - description: Name of folder to update - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - description: Success message - example: Successfully updated folder - folder: - type: object - properties: - name: - type: string - description: Name of updated folder - example: updated_folder_name - id: - type: string - description: ID of created folder - example: abc123 - description: Details of the updated folder - '400': - description: >- - Bad Request. Reasons can include 'The folder doesn't exist' or - 'Folder name cannot contain spaces. Only underscore and dashes' - '401': - description: Unauthorized request. For example, 'Folder Permission Denied' - security: - - apiKeyAuth: [] - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - workspaceId: - type: string - description: ID of workspace where to update folder - example: someWorkspaceId - environment: - type: string - description: Slug of environment where to update folder - example: production - name: - type: string - description: Name of folder to update to - example: updated_folder_name - directory: - type: string - description: Path where to update folder like / or /foo/bar. Default is / - example: /foo/bar - required: - - workspaceId - - environment - - name - delete: - summary: Delete folder - description: Delete folder - parameters: - - name: folderName - in: path - required: true - schema: - type: string - description: Name of folder to delete - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - description: Success message - example: successfully deleted folders - folders: - type: array - items: - type: object - properties: - id: - type: string - description: ID of deleted folder - example: abc123 - name: - type: string - description: Name of deleted folder - example: someFolderName - description: List of IDs and names of deleted folders - '400': - description: Bad Request. Reasons can include 'The folder doesn't exist' - '401': - description: Unauthorized request. For example, 'Folder Permission Denied' - security: - - apiKeyAuth: [] - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - workspaceId: - type: string - description: ID of the workspace where to delete folder - example: someWorkspaceId - environment: - type: string - description: Slug of environment where to delete folder - example: production - directory: - type: string - description: Path where to delete folder like / or /foo/bar. Default is / - example: /foo/bar - required: - - workspaceId - - environment - /api/v1/secret-scanning/create-installation-session/organization/{organizationId}: - post: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret-scanning/link-installation: - post: - description: '' - responses: - '200': - description: OK - /api/v1/secret-scanning/installation-status/organization/{organizationId}: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret-scanning/organization/{organizationId}/risks: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret-scanning/organization/{organizationId}/risks/{riskId}/status: - post: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - - name: riskId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/webhooks/: - post: - description: '' - responses: - '200': - description: OK - get: - description: '' - responses: - '200': - description: OK - /api/v1/webhooks/{webhookId}: - patch: - description: '' - parameters: - - name: webhookId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: webhookId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/webhooks/{webhookId}/test: - post: - description: '' - parameters: - - name: webhookId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/secret-imports/: - post: - summary: Create secret import - description: Create secret import - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: successfully created secret import - description: Confirmation of secret import creation - '400': - description: Bad Request. For example, 'Secret import already exist' - '401': - description: Unauthorized request. For example, 'Folder Permission Denied' - '404': - description: Resource Not Found. For example, 'Failed to find folder' - requestBody: - content: - application/json: - schema: - type: object - properties: - workspaceId: - type: string - description: ID of workspace where to create secret import - example: someWorkspaceId - environment: - type: string - description: Slug of environment where to create secret import - example: dev - directory: - type: string - description: >- - Path where to create secret import like / or /foo/bar. - Default is / - example: /foo/bar - secretImport: - type: object - properties: - environment: - type: string - description: Slug of environment to import from - example: development - secretPath: - type: string - description: Path where to import from like / or /foo/bar. - example: /user/oauth - required: - - workspaceId - - environment - - directory - - secretImport - get: - summary: Get secret imports - description: Get secret imports - parameters: - - name: workspaceId - in: query - description: ID of workspace where to get secret imports from - required: true - example: workspace12345 - schema: - type: string - - name: environment - in: query - description: Slug of environment where to get secret imports from - required: true - example: production - schema: - type: string - - name: directory - in: query - description: >- - Path where to get secret imports from like / or /foo/bar. Default is - / - required: false - example: folder12345 - schema: - type: string - responses: - '200': - description: Successfully retrieved secret import - content: - application/json: - schema: - type: object - properties: - secretImport: - $ref: '#/components/schemas/SecretImport' - '401': - description: Unauthorized access due to invalid token or scope - '403': - description: Forbidden access due to insufficient permissions - /api/v1/secret-imports/{id}: - put: - summary: Update secret import - description: Update secret import - parameters: - - name: id - in: path - required: true - schema: - type: string - description: ID of secret import to update - example: import12345 - responses: - '200': - description: Successfully updated the secret import - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: successfully updated secret import - '400': - description: Bad Request - Import not found - '401': - description: Unauthorized access due to invalid token or scope - '403': - description: Forbidden access due to insufficient permissions - requestBody: - content: - application/json: - schema: - type: object - properties: - secretImports: - type: array - description: List of secret imports to update to - items: - type: object - properties: - environment: - type: string - description: Slug of environment to import from - example: dev - secretPath: - type: string - description: Path where to import secrets from like / or /foo/bar - example: /foo/bar - required: - - environment - - secretPath - required: - - secretImports - delete: - summary: Delete secret import - description: Delete secret import - parameters: - - name: id - in: path - required: true - schema: - type: string - description: >- - ID of parent secret import document from which to delete secret - import - example: 12345abcde - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - example: successfully delete secret import - description: Confirmation of secret import deletion - requestBody: - content: - application/json: - schema: - type: object - properties: - secretImportEnv: - type: string - description: Slug of environment of import to delete - example: someWorkspaceId - secretImportPath: - type: string - description: Path like / or /foo/bar of import to delete - example: production - required: - - id - - secretImportEnv - - secretImportPath - /api/v1/secret-imports/secrets: - get: - description: '' - responses: - '200': - description: OK - /api/v1/roles/: - post: - description: '' - responses: - '200': - description: OK - get: - description: '' - responses: - '200': - description: OK - /api/v1/roles/{id}: - patch: - description: '' - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/roles/organization/{orgId}/permissions: - get: - description: '' - parameters: - - name: orgId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/roles/workspace/{workspaceId}/permissions: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret-approvals/: - get: - description: '' - responses: - '200': - description: OK - post: - description: '' - responses: - '200': - description: OK - /api/v1/secret-approvals/board: - get: - description: '' - responses: - '200': - description: OK - /api/v1/secret-approvals/{id}: - patch: - description: '' - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/sso/redirect/google: - get: - description: '' - parameters: - - name: callback_port - in: query - schema: - type: string - responses: - default: - description: '' - /api/v1/sso/google: - get: - description: '' - responses: - default: - description: '' - /api/v1/sso/redirect/github: - get: - description: '' - parameters: - - name: callback_port - in: query - schema: - type: string - responses: - default: - description: '' - /api/v1/sso/github: - get: - description: '' - responses: - default: - description: '' - /api/v1/sso/redirect/gitlab: - get: - description: '' - parameters: - - name: callback_port - in: query - schema: - type: string - responses: - default: - description: '' - /api/v1/sso/gitlab: - get: - description: '' - responses: - default: - description: '' - /api/v1/secret-approval-requests/: - get: - description: '' - responses: - '200': - description: OK - /api/v1/secret-approval-requests/count: - get: - description: '' - responses: - '200': - description: OK - /api/v1/secret-approval-requests/{id}: - get: - description: '' - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret-approval-requests/{id}/merge: - post: - description: '' - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret-approval-requests/{id}/review: - post: - description: '' - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/secret-approval-requests/{id}/status: - post: - description: '' - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v2/signup/complete-account/signup: - post: - description: '' - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - '403': - description: Forbidden - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - firstName: - example: any - lastName: - example: any - protectedKey: - example: any - protectedKeyIV: - example: any - protectedKeyTag: - example: any - publicKey: - example: any - encryptedPrivateKey: - example: any - encryptedPrivateKeyIV: - example: any - encryptedPrivateKeyTag: - example: any - salt: - example: any - verifier: - example: any - organizationName: - example: any - /api/v2/signup/complete-account/invite: - post: - description: '' - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - '403': - description: Forbidden - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - firstName: - example: any - lastName: - example: any - protectedKey: - example: any - protectedKeyIV: - example: any - protectedKeyTag: - example: any - publicKey: - example: any - encryptedPrivateKey: - example: any - encryptedPrivateKeyIV: - example: any - encryptedPrivateKeyTag: - example: any - salt: - example: any - verifier: - example: any - /api/v2/auth/login1: - post: - description: '' - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - clientPublicKey: - example: any - /api/v2/auth/login2: - post: - description: '' - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - clientProof: - example: any - /api/v2/auth/mfa/send: - post: - description: '' - responses: - '200': - description: OK - /api/v2/auth/mfa/verify: - post: - description: '' - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - /api/v2/users/me/mfa: - patch: - description: '' - responses: - '200': - description: OK - /api/v2/users/me/name: - patch: - description: '' - responses: - '200': - description: OK - /api/v2/users/me/auth-methods: - put: - description: '' - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v2/users/me/organizations: - get: - summary: Return organizations that current user is part of - description: Return organizations that current user is part of - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - organizations: - type: array - items: - $ref: '#/components/schemas/Organization' - description: Organizations that user is part of - security: - - apiKeyAuth: [] - /api/v2/users/me/api-keys: - get: - description: '' - responses: - '200': - description: OK - post: - description: '' - responses: - '200': - description: OK - /api/v2/users/me/api-keys/{apiKeyDataId}: - delete: - description: '' - parameters: - - name: apiKeyDataId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v2/users/me/sessions: - get: - description: '' - responses: - '200': - description: OK - delete: - description: '' - responses: - '200': - description: OK - /api/v2/users/me: - get: - summary: Retrieve the current user on the request - description: Retrieve the current user on the request - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - user: - type: object - $ref: '#/components/schemas/CurrentUser' - description: Current user on request - security: - - apiKeyAuth: [] - delete: - description: '' - responses: - '200': - description: OK - /api/v2/organizations/{organizationId}/memberships: - get: - summary: Return organization user memberships - description: Return organization user memberships - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - description: ID of organization - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - memberships: - type: array - items: - $ref: '#/components/schemas/MembershipOrg' - description: Memberships of organization - security: - - apiKeyAuth: [] - bearerAuth: [] - /api/v2/organizations/{organizationId}/memberships/{membershipId}: - patch: - summary: Update organization user membership - description: Update organization user membership - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - description: ID of organization - - name: membershipId - in: path - required: true - schema: - type: string - description: ID of organization membership to update - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - membership: - $ref: '#/components/schemas/MembershipOrg' - description: Updated organization membership - '400': - description: Bad Request - security: - - apiKeyAuth: [] - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - role: - type: string - description: >- - Role of organization membership - either owner, admin, or - member - delete: - summary: Delete organization user membership - description: Delete organization user membership - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - description: ID of organization - - name: membershipId - in: path - required: true - schema: - type: string - description: ID of organization membership to delete - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - membership: - $ref: '#/components/schemas/MembershipOrg' - description: Deleted organization membership - security: - - apiKeyAuth: [] - bearerAuth: [] - /api/v2/organizations/{organizationId}/workspaces: - get: - summary: Return projects in organization that user is part of - description: Return projects in organization that user is part of - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - description: ID of organization - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - workspaces: - type: array - items: - $ref: '#/components/schemas/Project' - description: Projects of organization - security: - - apiKeyAuth: [] - bearerAuth: [] - /api/v2/organizations/: - post: - description: '' - responses: - '200': - description: OK - /api/v2/organizations/{organizationId}: - delete: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v2/organizations/{organizationId}/identity-memberships: - get: - summary: Return organization identity memberships - description: Return organization identity memberships - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - description: ID of organization - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identityMemberships: - type: array - items: - $ref: '#/components/schemas/IdentityMembershipOrg' - description: Identity memberships of organization - security: - - bearerAuth: [] - /api/v2/workspace/{workspaceId}/memberships: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - get: - summary: Return project user memberships - description: Return project user memberships - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of project - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - memberships: - type: array - items: - $ref: '#/components/schemas/Membership' - description: Memberships of project - security: - - apiKeyAuth: [] - bearerAuth: [] - /api/v2/workspace/{workspaceId}/environments: - post: - summary: Create environment - description: Create environment - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of workspace where to create environment - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - description: Sucess message - example: Successfully created environment - workspace: - type: string - description: ID of workspace where environment was created - example: abc123 - environment: - type: object - properties: - name: - type: string - description: Name of created environment - example: Staging - slug: - type: string - description: Slug of created environment - example: staging - description: Details of the created environment - '400': - description: Bad Request - security: - - apiKeyAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - environmentName: - type: string - description: Name of the environment to create - example: development - environmentSlug: - type: string - description: Slug of environment to create - example: dev-environment - required: - - environmentName - - environmentSlug - put: - summary: Update environment - description: Update environment - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of workspace where to update environment - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - description: Success message - example: Successfully update environment - workspace: - type: string - description: ID of workspace where environment was updated - example: abc123 - environment: - type: object - properties: - name: - type: string - description: Name of updated environment - example: Staging-Renamed - slug: - type: string - description: Slug of updated environment - example: staging-renamed - description: Details of the renamed environment - security: - - apiKeyAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - environmentName: - type: string - description: Name of environment to update to - example: Staging-Renamed - environmentSlug: - type: string - description: Slug of environment to update to - example: staging-renamed - oldEnvironmentSlug: - type: string - description: Current slug of environment - example: staging-old - required: - - environmentName - - environmentSlug - - oldEnvironmentSlug - patch: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - summary: Delete environment - description: Delete environment - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of workspace where to delete environment - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - description: Success message - example: Successfully deleted environment - workspace: - type: string - description: ID of workspace where environment was deleted - example: abc123 - environment: - type: string - description: Slug of deleted environment - example: dev - description: Response after deleting an environment from a workspace - security: - - apiKeyAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - environmentSlug: - type: string - description: Slug of environment to delete - example: dev - required: - - environmentSlug - /api/v2/workspace/{workspaceId}/tags: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v2/workspace/tags/{tagId}: - delete: - description: '' - parameters: - - name: tagId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v2/workspace/{workspaceId}/secrets: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - keys: - example: any - environment: - example: any - channel: - example: any - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: query - schema: - type: string - - name: channel - in: query - schema: - type: string - responses: - '200': - description: OK - /api/v2/workspace/{workspaceId}/encrypted-key: - get: - summary: Return encrypted project key - description: Return encrypted project key - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of project - responses: - '200': - description: OK - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ProjectKey' - description: Encrypted project key for the given project - security: - - apiKeyAuth: [] - /api/v2/workspace/{workspaceId}/service-token-data: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v2/workspace/{workspaceId}/memberships/{membershipId}: - patch: - summary: Update project user membership - description: Update project user membership - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of project - - name: membershipId - in: path - required: true - schema: - type: string - description: ID of project membership to update - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - membership: - $ref: '#/components/schemas/Membership' - description: Updated membership - security: - - apiKeyAuth: [] - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - role: - type: string - description: Role to update to for project membership - delete: - summary: Delete project user membership - description: Delete project user membership - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of project - - name: membershipId - in: path - required: true - schema: - type: string - description: ID of project membership to delete - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - membership: - $ref: '#/components/schemas/Membership' - description: Deleted membership - security: - - apiKeyAuth: [] - bearerAuth: [] - /api/v2/workspace/{workspaceId}/auto-capitalization: - patch: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v2/workspace/{workspaceId}/identity-memberships/{identityId}: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: identityId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - patch: - summary: Update project identity membership - description: Update project identity membership - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of project - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity whose membership to update in project - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identityMembership: - $ref: '#/components/schemas/IdentityMembership' - description: Updated identity membership - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - role: - type: string - description: Role to update to for identity project membership - delete: - summary: Delete project identity membership - description: Delete project identity membership - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of project - - name: identityId - in: path - required: true - schema: - type: string - description: ID of identity whose membership to delete in project - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identityMembership: - $ref: '#/components/schemas/IdentityMembership' - description: Deleted identity membership - security: - - bearerAuth: [] - /api/v2/workspace/{workspaceId}/identity-memberships: - get: - summary: Return project identity memberships - description: Return project identity memberships - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - description: ID of project - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - identityMemberships: - type: array - items: - $ref: '#/components/schemas/IdentityMembership' - description: Identity memberships of project - security: - - bearerAuth: [] - /api/v2/secret/batch-create/workspace/{workspaceId}/environment/{environment}: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - /api/v2/secret/workspace/{workspaceId}/environment/{environment}: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secret: - example: any - /api/v2/secret/workspace/{workspaceId}: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: query - schema: - type: string - responses: - '200': - description: OK - /api/v2/secret/{secretId}: - get: - description: '' - parameters: - - name: secretId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: secretId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v2/secret/batch/workspace/{workspaceId}/environment/{environmentName}: - delete: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environmentName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secretIds: - example: any - /api/v2/secret/batch-modify/workspace/{workspaceId}/environment/{environmentName}: - patch: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environmentName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - /api/v2/secret/workspace/{workspaceId}/environment/{environmentName}: - patch: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environmentName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secret: - example: any - /api/v2/secrets/batch: - post: - description: '' - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - /api/v2/secrets/: - post: - summary: Create new secret(s) - description: Create one or many secrets for a given project and environment. - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secrets: - type: array - items: - $ref: '#/components/schemas/Secret' - description: >- - Newly-created secrets for the given project and - environment - security: - - apiKeyAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - workspaceId: - type: string - description: ID of project - environment: - type: string - description: Environment within project - secrets: - $ref: '#/components/schemas/CreateSecret' - description: Secret(s) to create - object or array of objects - get: - summary: Read secrets - description: Read secrets from a project and environment - parameters: - - name: workspaceId - description: ID of project - required: true - in: query - schema: - type: string - - name: environment - description: Environment within project - required: true - in: query - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secrets: - type: array - items: - $ref: '#/components/schemas/Secret' - description: Secrets for the given project and environment - security: - - apiKeyAuth: [] - patch: - summary: Update secret(s) - description: Update secret(s) - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secrets: - type: array - items: - $ref: '#/components/schemas/Secret' - description: Updated secrets - security: - - apiKeyAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - secrets: - $ref: '#/components/schemas/UpdateSecret' - description: Secret(s) to update - object or array of objects - delete: - summary: Delete secret(s) - description: Delete one or many secrets by their ID(s) - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secrets: - type: array - items: - $ref: '#/components/schemas/Secret' - description: Deleted secrets - security: - - apiKeyAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - secretIds: - type: string - description: ID(s) of secrets - string or array of strings - /api/v2/service-token/: - get: - summary: Return Infisical Token data - description: Return Infisical Token data - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - serviceTokenData: - type: object - $ref: '#/components/schemas/ServiceTokenData' - description: Details of service token - security: - - bearerAuth: [] - post: - description: '' - responses: - '200': - description: OK - /api/v2/service-token/{serviceTokenDataId}: - delete: - description: '' - parameters: - - name: serviceTokenDataId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v3/auth/login1: - post: - description: '' - responses: - '200': - description: OK - /api/v3/auth/login2: - post: - description: '' - parameters: - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v3/secrets/raw: - get: - summary: List secrets - description: List secrets - parameters: - - name: workspaceId - description: ID of workspace where to get secrets from - required: true - in: query - schema: - type: string - - name: environment - description: Slug of environment where to get secrets from - required: true - in: query - schema: - type: string - - name: secretPath - description: Path where to update secret like / or /foo/bar. Default is / - required: false - in: query - schema: - type: string - - name: include_imports - description: Whether or not to include imported secrets. Default is false - required: false - in: query - schema: - type: boolean - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secrets: - type: array - items: - $ref: '#/components/schemas/RawSecret' - description: List of secrets - security: - - apiKeyAuth: [] - bearerAuth: [] - /api/v3/secrets/raw/{secretName}: - get: - summary: Get secret - description: Get secret - parameters: - - name: secretName - in: path - required: true - schema: - type: string - description: Name of secret to get - - name: workspaceId - description: ID of workspace where to get secret - required: true - in: query - schema: - type: string - - name: environment - description: Slug of environment where to get secret - required: true - in: query - schema: - type: string - - name: secretPath - description: Path where to update secret like / or /foo/bar. Default is / - required: false - in: query - schema: - type: string - - name: type - description: Type of secret to get; either shared or personal. Default is shared. - required: true - in: query - schema: - type: string - - name: include_imports - description: Whether or not to include imported secrets. Default is false - required: false - in: query - schema: - type: boolean - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secret: - $ref: '#/components/schemas/RawSecret' - security: - - apiKeyAuth: [] - bearerAuth: [] - post: - summary: Create secret - description: Create secret - parameters: - - name: secretName - in: path - required: true - schema: - type: string - description: Name of secret to create - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/RawSecret' - security: - - apiKeyAuth: [] - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - workspaceId: - type: string - description: ID of the workspace where to create secret - example: someWorkspaceId - environment: - type: string - description: Slug of environment where to create secret - example: dev - secretPath: - type: string - description: Path where to create secret. Default is / - example: /foo/bar - secretValue: - type: string - description: Value of secret to create - example: Some value - secretComment: - type: string - description: Comment for secret to create - example: Some comment - type: - type: string - description: >- - Type of secret to create; either shared or personal. Default - is shared. - example: shared - skipMultilineEncoding: - type: boolean - description: Convert multi line secrets into one line by wrapping - example: 'true' - required: - - workspaceId - - environment - - secretValue - patch: - summary: Update secret - description: Update secret - parameters: - - name: secretName - in: path - required: true - schema: - type: string - description: Name of secret to update - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/RawSecret' - security: - - apiKeyAuth: [] - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - workspaceId: - type: string - description: ID of the workspace where to update secret - example: someWorkspaceId - environment: - type: string - description: Slug of environment where to update secret - example: dev - secretPath: - type: string - description: Path where to update secret like / or /foo/bar. Default is / - example: /foo/bar - secretValue: - type: string - description: Value of secret to update to - example: Some value - type: - type: string - description: >- - Type of secret to update; either shared or personal. Default - is shared. - example: shared - skipMultilineEncoding: - type: boolean - description: Convert multi line secrets into one line by wrapping - example: 'true' - required: - - workspaceId - - environment - - secretValue - delete: - summary: Delete secret - description: Delete secret - parameters: - - name: secretName - in: path - required: true - schema: - type: string - description: Name of secret to delete - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - secret: - $ref: '#/components/schemas/RawSecret' - description: The deleted secret - security: - - apiKeyAuth: [] - bearerAuth: [] - requestBody: - content: - application/json: - schema: - type: object - properties: - workspaceId: - type: string - description: ID of workspace where to delete secret - example: someWorkspaceId - environment: - type: string - description: Slug of Environment where to delete secret - example: dev - secretPath: - type: string - description: Path where to delete secret. Default is / - example: /foo/bar - type: - type: string - description: >- - Type of secret to delete; either shared or personal. Default - is shared - example: shared - required: - - workspaceId - - environment - /api/v3/secrets/: - get: - description: '' - responses: - '200': - description: OK - /api/v3/secrets/batch: - post: - description: '' - responses: - '200': - description: OK - patch: - description: '' - responses: - '200': - description: OK - delete: - description: '' - responses: - '200': - description: OK - /api/v3/secrets/{secretName}: - post: - description: '' - parameters: - - name: secretName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - get: - description: '' - parameters: - - name: secretName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - patch: - description: '' - parameters: - - name: secretName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: secretName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v3/workspaces/{workspaceId}/secrets/blind-index-status: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v3/workspaces/{workspaceId}/secrets: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v3/workspaces/{workspaceId}/secrets/names: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v3/signup/complete-account/signup: - post: - description: '' - parameters: - - name: authorization - in: header - schema: - type: string - - name: user-agent - in: header - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - '403': - description: Forbidden - /api/v3/us/me/api-keys: - get: - description: '' - responses: - '200': - description: OK - /api/status: - get: - description: '' - responses: - '200': - description: OK -components: - schemas: - CurrentUser: - type: object - properties: - _id: - type: string - example: '' - email: - type: string - example: johndoe@gmail.com - firstName: - type: string - example: John - lastName: - type: string - example: Doe - publicKey: - type: string - example: johns_nacl_public_key - encryptedPrivateKey: - type: string - example: johns_enc_nacl_private_key - iv: - type: string - example: iv_of_enc_nacl_private_key - tag: - type: string - example: tag_of_enc_nacl_private_key - updatedAt: - type: string - example: '2023-01-13T14:16:12.210Z' - createdAt: - type: string - example: '2023-01-13T14:16:12.210Z' - Identity: - type: object - properties: - _id: - type: string - example: '' - name: - type: string - example: Machine 1 - authMethod: - type: string - example: universal-auth - IdentityUniversalAuth: - type: object - properties: - _id: - type: string - example: '' - identity: - type: string - example: '' - clientId: - type: string - example: ... - clientSecretTrustedIps: - type: array - items: - type: object - properties: - ipAddress: - type: string - example: 0.0.0.0 - type: - type: string - example: ipv4 - prefix: - type: string - example: '0' - accessTokenTTL: - type: number - example: 7200 - accessTokenMaxTTL: - type: number - example: 2592000 - accessTokenNumUsesLimit: - type: number - example: 0 - accessTokenTrustedIps: - type: array - items: - type: object - properties: - ipAddress: - type: string - example: 0.0.0.0 - type: - type: string - example: ipv4 - prefix: - type: string - example: '0' - IdentityUniversalAuthClientSecretData: - type: object - properties: - _id: - type: string - example: '' - identityUniversalAuth: - type: string - example: '' - isClientSecretRevoked: - type: boolean - example: false - description: - type: string - example: '' - clientSecretPrefix: - type: string - example: abc - clientSecretNumUses: - type: number - example: 0 - clientSecretNumUsesLimit: - type: number - example: 0 - clientSecretTTL: - type: number - example: 0 - createdAt: - type: string - example: '2023-01-13T14:16:12.210Z' - updatedAt: - type: string - example: '2023-01-13T14:16:12.210Z' - Membership: - type: object - properties: - user: - type: object - properties: - _id: - type: string - example: '' - email: - type: string - example: johndoe@gmail.com - firstName: - type: string - example: John - lastName: - type: string - example: Doe - publicKey: - type: string - example: johns_nacl_public_key - updatedAt: - type: string - example: '2023-01-13T14:16:12.210Z' - createdAt: - type: string - example: '2023-01-13T14:16:12.210Z' - workspace: - type: string - example: '' - role: - type: string - example: admin - MembershipOrg: - type: object - properties: - user: - type: object - properties: - _id: - type: string - example: '' - email: - type: string - example: johndoe@gmail.com - firstName: - type: string - example: John - lastName: - type: string - example: Doe - publicKey: - type: string - example: johns_nacl_public_key - updatedAt: - type: string - example: '2023-01-13T14:16:12.210Z' - createdAt: - type: string - example: '2023-01-13T14:16:12.210Z' - organization: - type: string - example: '' - role: - type: string - example: owner - status: - type: string - example: accepted - IdentityMembership: - type: object - properties: - identity: - type: object - properties: - _id: - type: string - example: '' - name: - type: string - example: Machine 1 - authMethod: - type: string - example: universal-auth - workspace: - type: string - example: '' - role: - type: string - example: member - IdentityMembershipOrg: - type: object - properties: - identity: - type: object - properties: - _id: - type: string - example: '' - name: - type: string - example: Machine 1 - authMethod: - type: string - example: universal-auth - organization: - type: string - example: '' - role: - type: string - example: member - status: - type: string - example: accepted - Organization: - type: object - properties: - _id: - type: string - example: '' - name: - type: string - example: Acme Corp. - customerId: - type: string - example: '' - Project: - type: object - properties: - name: - type: string - example: My Project - organization: - type: string - example: '' - environments: - type: array - items: - type: object - properties: - name: - type: string - example: development - slug: - type: string - example: dev - ProjectKey: - type: object - properties: - encryptedkey: - type: string - example: '' - nonce: - type: string - example: '' - sender: - type: object - properties: - publicKey: - type: string - example: senders_nacl_public_key - receiver: - type: string - example: '' - workspace: - type: string - example: '' - CreateSecret: - type: object - properties: - type: - type: string - example: shared - secretKeyCiphertext: - type: string - example: '' - secretKeyIV: - type: string - example: '' - secretKeyTag: - type: string - example: '' - secretValueCiphertext: - type: string - example: '' - secretValueIV: - type: string - example: '' - secretValueTag: - type: string - example: '' - secretCommentCiphertext: - type: string - example: '' - secretCommentIV: - type: string - example: '' - secretCommentTag: - type: string - example: '' - UpdateSecret: - type: object - properties: - id: - type: string - example: '' - secretKeyCiphertext: - type: string - example: '' - secretKeyIV: - type: string - example: '' - secretKeyTag: - type: string - example: '' - secretValueCiphertext: - type: string - example: '' - secretValueIV: - type: string - example: '' - secretValueTag: - type: string - example: '' - secretCommentCiphertext: - type: string - example: '' - secretCommentIV: - type: string - example: '' - secretCommentTag: - type: string - example: '' - Secret: - type: object - properties: - _id: - type: string - example: '' - version: - type: number - example: 1 - workspace: - type: string - example: '' - type: - type: string - example: shared - user: {} - secretKeyCiphertext: - type: string - example: '' - secretKeyIV: - type: string - example: '' - secretKeyTag: - type: string - example: '' - secretValueCiphertext: - type: string - example: '' - secretValueIV: - type: string - example: '' - secretValueTag: - type: string - example: '' - secretCommentCiphertext: - type: string - example: '' - secretCommentIV: - type: string - example: '' - secretCommentTag: - type: string - example: '' - updatedAt: - type: string - example: '2023-01-13T14:16:12.210Z' - createdAt: - type: string - example: '2023-01-13T14:16:12.210Z' - RawSecret: - type: object - properties: - _id: - type: string - example: abc123 - version: - type: number - example: 1 - workspace: - type: string - example: abc123 - environment: - type: string - example: dev - secretKey: - type: string - example: STRIPE_KEY - secretValue: - type: string - example: abc123 - secretComment: - type: string - example: Lorem ipsum - SecretImport: - type: object - properties: - _id: - type: string - example: '' - workspace: - type: string - example: abc123 - environment: - type: string - example: dev - folderId: - type: string - example: root - imports: - type: array - example: [] - items: {} - updatedAt: - type: string - example: '2023-01-13T14:16:12.210Z' - createdAt: - type: string - example: '2023-01-13T14:16:12.210Z' - Log: - type: object - properties: - _id: - type: string - example: '' - user: - type: object - properties: - _id: - type: string - example: '' - email: - type: string - example: johndoe@gmail.com - firstName: - type: string - example: John - lastName: - type: string - example: Doe - workspace: - type: string - example: '' - actionNames: - type: array - example: - - addSecrets - items: - type: string - actions: - type: array - items: - type: object - properties: - name: - type: string - example: addSecrets - user: - type: string - example: '' - workspace: - type: string - example: '' - payload: - type: array - items: - type: object - properties: - oldSecretVersion: - type: string - example: '' - newSecretVersion: - type: string - example: '' - channel: - type: string - example: cli - ipAddress: - type: string - example: 192.168.0.1 - updatedAt: - type: string - example: '2023-01-13T14:16:12.210Z' - createdAt: - type: string - example: '2023-01-13T14:16:12.210Z' - SecretSnapshot: - type: object - properties: - workspace: - type: string - example: '' - version: - type: number - example: 1 - secretVersions: - type: array - items: - type: object - properties: - _id: - type: string - example: '' - SecretVersion: - type: object - properties: - _id: - type: string - example: '' - secret: - type: string - example: '' - version: - type: number - example: 1 - workspace: - type: string - example: '' - type: - type: string - example: shared - user: - type: string - example: '' - environment: - type: string - example: dev - isDeleted: - type: string - example: '' - secretKeyCiphertext: - type: string - example: '' - secretKeyIV: - type: string - example: '' - secretKeyTag: - type: string - example: '' - secretValueCiphertext: - type: string - example: '' - secretValueIV: - type: string - example: '' - secretValueTag: - type: string - example: '' - ServiceTokenData: - type: object - properties: - _id: - type: string - example: '' - name: - type: string - example: '' - workspace: - type: string - example: '' - environment: - type: string - example: '' - user: - type: object - properties: - _id: - type: string - example: '' - firstName: - type: string - example: '' - lastName: - type: string - example: '' - expiresAt: - type: string - example: '2023-01-13T14:16:12.210Z' - encryptedKey: - type: string - example: '' - iv: - type: string - example: '' - tag: - type: string - example: '' - updatedAt: - type: string - example: '2023-01-13T14:16:12.210Z' - createdAt: - type: string - example: '2023-01-13T14:16:12.210Z' - AuditLog: - type: object - properties: - actor: - type: object - properties: - type: - type: string - example: '' - metadata: - type: object - properties: {} - organization: - type: string - example: '' - workspace: - type: string - example: '' - ipAddress: - type: string - example: '' - event: - type: object - properties: - type: - type: string - example: '' - metadata: - type: object - properties: {} - userAgent: - type: string - example: '' - userAgentType: - type: string - example: '' - expiresAt: - type: string - example: '' - securitySchemes: - bearerAuth: - type: http - scheme: bearer - bearerFormat: JWT - description: An access token in Infisical - apiKeyAuth: - type: apiKey - in: header - name: X-API-Key - description: An API Key in Infisical diff --git a/frontend/src/components/permissions/ProjectPermissionCan.tsx b/frontend/src/components/permissions/ProjectPermissionCan.tsx index aa64c33d7..f1af141f2 100644 --- a/frontend/src/components/permissions/ProjectPermissionCan.tsx +++ b/frontend/src/components/permissions/ProjectPermissionCan.tsx @@ -25,7 +25,7 @@ export const ProjectPermissionCan: FunctionComponent = ({ allowedLabel, ...props }) => { - const permission = useProjectPermission(); + const { permission } = useProjectPermission(); return ( {(isAllowed, ability) => { diff --git a/frontend/src/components/signup/DonwloadBackupPDFStep.tsx b/frontend/src/components/signup/DonwloadBackupPDFStep.tsx index e211c8494..73082af32 100644 --- a/frontend/src/components/signup/DonwloadBackupPDFStep.tsx +++ b/frontend/src/components/signup/DonwloadBackupPDFStep.tsx @@ -58,8 +58,8 @@ export default function DonwloadBackupPDFStep({ return (
-

- +

+ {t("signup.step4-message")}

diff --git a/frontend/src/components/signup/InitialSignupStep.tsx b/frontend/src/components/signup/InitialSignupStep.tsx index 7953ff0df..e5c23f333 100644 --- a/frontend/src/components/signup/InitialSignupStep.tsx +++ b/frontend/src/components/signup/InitialSignupStep.tsx @@ -1,9 +1,7 @@ import { useTranslation } from "react-i18next"; import Link from "next/link"; -import { useRouter } from "next/router"; import { faGithub, faGitlab, faGoogle } from "@fortawesome/free-brands-svg-icons"; import { faEnvelope } from "@fortawesome/free-regular-svg-icons"; -import { faLock } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Button } from "../v2"; @@ -14,7 +12,6 @@ export default function InitialSignupStep({ setIsSignupWithEmail: (value: boolean) => void; }) { const { t } = useTranslation(); - const router = useRouter(); return (
@@ -76,17 +73,6 @@ export default function InitialSignupStep({ Continue with Email
-
- -
{t("signup.create-policy")}
diff --git a/frontend/src/components/v2/SecretInput/SecretInput.tsx b/frontend/src/components/v2/SecretInput/SecretInput.tsx index 314dada0c..2dfafca74 100644 --- a/frontend/src/components/v2/SecretInput/SecretInput.tsx +++ b/frontend/src/components/v2/SecretInput/SecretInput.tsx @@ -1,43 +1,46 @@ /* eslint-disable react/no-danger */ import { forwardRef, TextareaHTMLAttributes } from "react"; -import sanitizeHtml, { DisallowedTagsModes } from "sanitize-html"; import { twMerge } from "tailwind-merge"; import { useToggle } from "@app/hooks"; -const REGEX = /\${([^}]+)}/g; +const REGEX = /(\${([^}]+)})/g; const replaceContentWithDot = (str: string) => { let finalStr = ""; for (let i = 0; i < str.length; i += 1) { const char = str.at(i); - finalStr += char === "\n" ? "\n" : "•"; + finalStr += char === "\n" ? "\n" : "*"; } return finalStr; }; -const sanitizeConf = { - allowedTags: ["span"], - disallowedTagsMode: "escape" as DisallowedTagsModes -}; - const syntaxHighlight = (content?: string | null, isVisible?: boolean) => { if (content === "") return "EMPTY"; if (!content) return "EMPTY"; if (!isVisible) return replaceContentWithDot(content); - const sanitizedContent = sanitizeHtml( - content.replaceAll("<", "<").replaceAll(">", ">"), - sanitizeConf - ); - const newContent = sanitizedContent.replace( - REGEX, - (_a, b) => - `${${b}}` - ); + let skipNext = false; + const formatedContent = content.split(REGEX).flatMap((el, i) => { + const isInterpolationSyntax = el.startsWith("${") && el.endsWith("}"); + if (isInterpolationSyntax) { + skipNext = true; + return ( + + ${{el.slice(2, -1)} + } + + ); + } + if (skipNext) { + skipNext = false; + return []; + } + return el; + }); // akhilmhdh: Dont remove this br. I am still clueless how this works but weirdly enough // when break is added a line break works properly - return `${newContent}
`; + return formatedContent.concat(
); }; type Props = TextareaHTMLAttributes & { @@ -59,18 +62,15 @@ export const SecretInput = forwardRef( return (
             
-              
+              
+                {syntaxHighlight(value, isVisible || isSecretFocused)}
+