Compare commits

..

8 Commits

Author SHA1 Message Date
Daniel Hougaard
f58ed1fbfb Update values.yaml 2025-03-21 03:58:50 +04:00
Daniel Hougaard
e0c2851a4f finished helm 2025-03-21 03:44:42 +04:00
Daniel Hougaard
fe44fa6a7e fixed values.yaml 2025-03-21 03:01:41 +04:00
Daniel Hougaard
3a3d8271bf fixed deployment.yaml 2025-03-21 02:49:20 +04:00
Daniel Hougaard
c40ca9b4c5 Fixed metrics-reader-rbac.yaml and proxy-rbac.yaml 2025-03-21 02:29:25 +04:00
Daniel Hougaard
ea3fe21955 no change needed for metrics-service.yaml 2025-03-21 02:26:34 +04:00
Daniel Hougaard
ae34a2f7fc fixed manager-rbac.yaml 2025-03-21 02:24:59 +04:00
Daniel Hougaard
c80f34c929 fixed crds 2025-03-21 02:24:38 +04:00
1261 changed files with 7179 additions and 46737 deletions

View File

@@ -1,27 +0,0 @@
name: Release K8 Operator Helm Chart
on:
workflow_dispatch:
jobs:
release-helm:
name: Release Helm Chart
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: v3.10.0
- name: Install python
uses: actions/setup-python@v4
- name: Install Cloudsmith CLI
run: pip install --upgrade cloudsmith-cli
- name: Build and push helm package to CloudSmith
run: cd helm-charts && sh upload-k8s-operator-cloudsmith.sh
env:
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}

View File

@@ -1,153 +1,132 @@
name: Build and release CLI
on:
workflow_dispatch:
workflow_dispatch:
push:
# run only against tags
tags:
- "infisical-cli/v*.*.*"
push:
# run only against tags
tags:
- "infisical-cli/v*.*.*"
permissions:
contents: write
contents: write
jobs:
cli-integration-tests:
name: Run tests before deployment
uses: ./.github/workflows/run-cli-tests.yml
secrets:
CLI_TESTS_UA_CLIENT_ID: ${{ secrets.CLI_TESTS_UA_CLIENT_ID }}
CLI_TESTS_UA_CLIENT_SECRET: ${{ secrets.CLI_TESTS_UA_CLIENT_SECRET }}
CLI_TESTS_SERVICE_TOKEN: ${{ secrets.CLI_TESTS_SERVICE_TOKEN }}
CLI_TESTS_PROJECT_ID: ${{ secrets.CLI_TESTS_PROJECT_ID }}
CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }}
CLI_TESTS_USER_EMAIL: ${{ secrets.CLI_TESTS_USER_EMAIL }}
CLI_TESTS_USER_PASSWORD: ${{ secrets.CLI_TESTS_USER_PASSWORD }}
CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE: ${{ secrets.CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE }}
cli-integration-tests:
name: Run tests before deployment
uses: ./.github/workflows/run-cli-tests.yml
secrets:
CLI_TESTS_UA_CLIENT_ID: ${{ secrets.CLI_TESTS_UA_CLIENT_ID }}
CLI_TESTS_UA_CLIENT_SECRET: ${{ secrets.CLI_TESTS_UA_CLIENT_SECRET }}
CLI_TESTS_SERVICE_TOKEN: ${{ secrets.CLI_TESTS_SERVICE_TOKEN }}
CLI_TESTS_PROJECT_ID: ${{ secrets.CLI_TESTS_PROJECT_ID }}
CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }}
CLI_TESTS_USER_EMAIL: ${{ secrets.CLI_TESTS_USER_EMAIL }}
CLI_TESTS_USER_PASSWORD: ${{ secrets.CLI_TESTS_USER_PASSWORD }}
CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE: ${{ secrets.CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE }}
npm-release:
runs-on: ubuntu-latest
env:
working-directory: ./npm
needs:
- cli-integration-tests
- goreleaser
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Extract version
run: |
VERSION=$(echo ${{ github.ref_name }} | sed 's/infisical-cli\/v//')
echo "Version extracted: $VERSION"
echo "CLI_VERSION=$VERSION" >> $GITHUB_ENV
- name: Print version
run: echo ${{ env.CLI_VERSION }}
- name: Setup Node
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 20
cache: "npm"
cache-dependency-path: ./npm/package-lock.json
- name: Install dependencies
working-directory: ${{ env.working-directory }}
run: npm install --ignore-scripts
- name: Set NPM version
working-directory: ${{ env.working-directory }}
run: npm version ${{ env.CLI_VERSION }} --allow-same-version --no-git-tag-version
- name: Setup NPM
working-directory: ${{ env.working-directory }}
run: |
echo 'registry="https://registry.npmjs.org/"' > ./.npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ./.npmrc
echo 'registry="https://registry.npmjs.org/"' > ~/.npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
npm-release:
runs-on: ubuntu-latest
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
working-directory: ./npm
needs:
- cli-integration-tests
- goreleaser
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Pack NPM
working-directory: ${{ env.working-directory }}
run: npm pack
- name: Extract version
run: |
VERSION=$(echo ${{ github.ref_name }} | sed 's/infisical-cli\/v//')
echo "Version extracted: $VERSION"
echo "CLI_VERSION=$VERSION" >> $GITHUB_ENV
- name: Publish NPM
working-directory: ${{ env.working-directory }}
run: npm publish --tarball=./infisical-sdk-${{github.ref_name}} --access public --registry=https://registry.npmjs.org/
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Print version
run: echo ${{ env.CLI_VERSION }}
goreleaser:
runs-on: ubuntu-latest
needs: [cli-integration-tests]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- run: git fetch --force --tags
- run: echo "Ref name ${{github.ref_name}}"
- uses: actions/setup-go@v3
with:
go-version: ">=1.19.3"
cache: true
cache-dependency-path: cli/go.sum
- name: Setup for libssl1.0-dev
run: |
echo 'deb http://security.ubuntu.com/ubuntu bionic-security main' | sudo tee -a /etc/apt/sources.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
sudo apt update
sudo apt-get install -y libssl1.0-dev
- name: OSXCross for CGO Support
run: |
mkdir ../../osxcross
git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target
- uses: goreleaser/goreleaser-action@v4
with:
distribution: goreleaser-pro
version: v1.26.2-pro
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}
POSTHOG_API_KEY_FOR_CLI: ${{ secrets.POSTHOG_API_KEY_FOR_CLI }}
FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
- uses: actions/setup-python@v4
- run: pip install --upgrade cloudsmith-cli
- uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252
with:
ruby-version: "3.3" # Not needed with a .ruby-version, .tool-versions or mise.toml
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Install deb-s3
run: gem install deb-s3
- name: Configure GPG Key
run: echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --batch --import
env:
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
GPG_SIGNING_KEY_PASSPHRASE: ${{ secrets.GPG_SIGNING_KEY_PASSPHRASE }}
- name: Publish to CloudSmith
run: sh cli/upload_to_cloudsmith.sh
env:
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
INFISICAL_CLI_S3_BUCKET: ${{ secrets.INFISICAL_CLI_S3_BUCKET }}
INFISICAL_CLI_REPO_SIGNING_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_SIGNING_KEY_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.INFISICAL_CLI_REPO_AWS_SECRET_ACCESS_KEY }}
- name: Invalidate Cloudfront cache
run: aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION_ID --paths '/deb/dists/stable/*'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.INFISICAL_CLI_REPO_AWS_SECRET_ACCESS_KEY }}
CLOUDFRONT_DISTRIBUTION_ID: ${{ secrets.INFISICAL_CLI_REPO_CLOUDFRONT_DISTRIBUTION_ID }}
- name: Setup Node
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: 20
cache: "npm"
cache-dependency-path: ./npm/package-lock.json
- name: Install dependencies
working-directory: ${{ env.working-directory }}
run: npm install --ignore-scripts
- name: Set NPM version
working-directory: ${{ env.working-directory }}
run: npm version ${{ env.CLI_VERSION }} --allow-same-version --no-git-tag-version
- name: Setup NPM
working-directory: ${{ env.working-directory }}
run: |
echo 'registry="https://registry.npmjs.org/"' > ./.npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ./.npmrc
echo 'registry="https://registry.npmjs.org/"' > ~/.npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Pack NPM
working-directory: ${{ env.working-directory }}
run: npm pack
- name: Publish NPM
working-directory: ${{ env.working-directory }}
run: npm publish --tarball=./infisical-sdk-${{github.ref_name}} --access public --registry=https://registry.npmjs.org/
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
goreleaser:
runs-on: ubuntu-latest
needs: [cli-integration-tests]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- run: git fetch --force --tags
- run: echo "Ref name ${{github.ref_name}}"
- uses: actions/setup-go@v3
with:
go-version: ">=1.19.3"
cache: true
cache-dependency-path: cli/go.sum
- name: Setup for libssl1.0-dev
run: |
echo 'deb http://security.ubuntu.com/ubuntu bionic-security main' | sudo tee -a /etc/apt/sources.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
sudo apt update
sudo apt-get install -y libssl1.0-dev
- name: OSXCross for CGO Support
run: |
mkdir ../../osxcross
git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target
- uses: goreleaser/goreleaser-action@v4
with:
distribution: goreleaser-pro
version: v1.26.2-pro
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}
POSTHOG_API_KEY_FOR_CLI: ${{ secrets.POSTHOG_API_KEY_FOR_CLI }}
FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
- uses: actions/setup-python@v4
- run: pip install --upgrade cloudsmith-cli
- name: Publish to CloudSmith
run: sh cli/upload_to_cloudsmith.sh
env:
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}

View File

@@ -1,107 +1,74 @@
name: Release K8 Operator Docker Image
name: Release image + Helm chart K8s Operator
on:
push:
tags:
- "infisical-k8-operator/v*.*.*"
permissions:
contents: write
pull-requests: write
push:
tags:
- "infisical-k8-operator/v*.*.*"
jobs:
release-image:
name: Generate Helm Chart PR
runs-on: ubuntu-latest
outputs:
pr_number: ${{ steps.create-pr.outputs.pull-request-number }}
steps:
- name: Extract version from tag
id: extract_version
run: echo "::set-output name=version::${GITHUB_REF_NAME#infisical-k8-operator/}"
release:
runs-on: ubuntu-latest
steps:
- name: Extract version from tag
id: extract_version
run: echo "::set-output name=version::${GITHUB_REF_NAME#infisical-k8-operator/}"
- name: Checkout code
uses: actions/checkout@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Checkout code
uses: actions/checkout@v2
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: v3.10.0
# Dependency for helm generation
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: v3.10.0
- name: Install python
uses: actions/setup-python@v4
- name: Generate Helm Chart
run: sh k8-operator/scripts/generate-helm.sh
- name: Update Helm Chart Version
run: sh k8-operator/scripts/update-version.sh ${{ steps.extract_version.outputs.version }}
# Dependency for helm generation
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: 1.21
- name: 🔧 Set up QEMU
uses: docker/setup-qemu-action@v1
# Install binaries for helm generation
- name: Install dependencies
working-directory: k8-operator
run: |
make helmify
make kustomize
make controller-gen
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Generate Helm Chart
working-directory: k8-operator
run: make helm
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Update Helm Chart Version
run: ./k8-operator/scripts/update-version.sh ${{ steps.extract_version.outputs.version }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: k8-operator
push: true
platforms: linux/amd64,linux/arm64
tags: |
infisical/kubernetes-operator:latest
infisical/kubernetes-operator:${{ steps.extract_version.outputs.version }}
- name: Install Cloudsmith CLI
run: pip install --upgrade cloudsmith-cli
- name: Debug - Check file changes
run: |
echo "Current git status:"
git status
echo ""
echo "Modified files:"
git diff --name-only
- name: Build and push helm package to Cloudsmith
run: cd helm-charts && sh upload-k8s-operator-cloudsmith.sh
env:
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
# If there is no diff, exit with error. Version should always be changed, so if there is no diff, something is wrong and we should exit.
if [ -z "$(git diff --name-only)" ]; then
echo "No helm changes or version changes. Invalid release detected, Exiting."
exit 1
fi
- name: Create Helm Chart PR
id: create-pr
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "Update Helm chart to version ${{ steps.extract_version.outputs.version }}"
committer: GitHub <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
branch: helm-update-${{ steps.extract_version.outputs.version }}
delete-branch: true
title: "Update Helm chart to version ${{ steps.extract_version.outputs.version }}"
body: |
This PR updates the Helm chart to version `${{ steps.extract_version.outputs.version }}`.
Additionally the helm chart has been updated to match the latest operator code changes.
Associated Release Workflow: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
Once you have approved this PR, you can trigger the helm release workflow manually.
base: main
- name: 🔧 Set up QEMU
uses: docker/setup-qemu-action@v1
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: k8-operator
push: true
platforms: linux/amd64,linux/arm64
tags: |
infisical/kubernetes-operator:latest
infisical/kubernetes-operator:${{ steps.extract_version.outputs.version }}
- name: Configure Git
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
- name: Commit and Push Helm Changes
run: |
git add .
git commit -m "Update Helm chart to version ${{ steps.extract_version.outputs.version }}" || echo "No changes to commit"
git push

View File

@@ -162,24 +162,6 @@ scoop:
description: "The official Infisical CLI"
license: MIT
winget:
- name: infisical
publisher: infisical
license: MIT
homepage: https://infisical.com
short_description: "The official Infisical CLI"
repository:
owner: infisical
name: winget-pkgs
branch: "infisical-{{.Version}}"
pull_request:
enabled: true
draft: false
base:
owner: microsoft
name: winget-pkgs
branch: master
aurs:
- name: infisical-bin
homepage: "https://infisical.com"

View File

@@ -8,19 +8,3 @@ frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/S
docs/mint.json:generic-api-key:651
backend/src/ee/services/hsm/hsm-service.ts:generic-api-key:134
docs/documentation/platform/audit-log-streams/audit-log-streams.mdx:generic-api-key:104
docs/cli/commands/bootstrap.mdx:jwt:86
docs/documentation/platform/audit-log-streams/audit-log-streams.mdx:generic-api-key:102
docs/self-hosting/guides/automated-bootstrapping.mdx:jwt:74
frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx:generic-api-key:72
k8-operator/config/samples/crd/pushsecret/source-secret-with-templating.yaml:private-key:11
k8-operator/config/samples/crd/pushsecret/push-secret-with-template.yaml:private-key:52
backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-types.ts:generic-api-key:125
frontend/src/components/permissions/AccessTree/nodes/RoleNode.tsx:generic-api-key:67
frontend/src/components/secret-rotations-v2/RotateSecretRotationV2Modal.tsx:generic-api-key:14
frontend/src/components/secret-rotations-v2/SecretRotationV2StatusBadge.tsx:generic-api-key:11
frontend/src/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewSecretRotationV2GeneratedCredentials.tsx:generic-api-key:23
frontend/src/hooks/api/secretRotationsV2/types/index.ts:generic-api-key:28
frontend/src/hooks/api/secretRotationsV2/types/index.ts:generic-api-key:65
frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretRotationListView/SecretRotationItem.tsx:generic-api-key:26
docs/documentation/platform/kms/overview.mdx:generic-api-key:281
docs/documentation/platform/kms/overview.mdx:generic-api-key:344

View File

@@ -50,7 +50,7 @@ We're on a mission to make security tooling more accessible to everyone, not jus
- **[Dashboard](https://infisical.com/docs/documentation/platform/project)**: Manage secrets across projects and environments (e.g. development, production, etc.) through a user-friendly interface.
- **[Native Integrations](https://infisical.com/docs/integrations/overview)**: Sync secrets to 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 use tools like [Terraform](https://infisical.com/docs/integrations/frameworks/terraform), [Ansible](https://infisical.com/docs/integrations/platforms/ansible), and more.
- **[Secret versioning](https://infisical.com/docs/documentation/platform/secret-versioning)** and **[Point-in-Time Recovery](https://infisical.com/docs/documentation/platform/pit-recovery)**: Keep track of every secret and project state; roll back when needed.
- **[Secret Rotation](https://infisical.com/docs/documentation/platform/secret-rotation/overview)**: Rotate secrets at regular intervals for services like [PostgreSQL](https://infisical.com/docs/documentation/platform/secret-rotation/postgres-credentials), [MySQL](https://infisical.com/docs/documentation/platform/secret-rotation/mysql), [AWS IAM](https://infisical.com/docs/documentation/platform/secret-rotation/aws-iam), and more.
- **[Secret Rotation](https://infisical.com/docs/documentation/platform/secret-rotation/overview)**: Rotate secrets at regular intervals for services like [PostgreSQL](https://infisical.com/docs/documentation/platform/secret-rotation/postgres), [MySQL](https://infisical.com/docs/documentation/platform/secret-rotation/mysql), [AWS IAM](https://infisical.com/docs/documentation/platform/secret-rotation/aws-iam), and more.
- **[Dynamic Secrets](https://infisical.com/docs/documentation/platform/dynamic-secrets/overview)**: Generate ephemeral secrets on-demand for services like [PostgreSQL](https://infisical.com/docs/documentation/platform/dynamic-secrets/postgresql), [MySQL](https://infisical.com/docs/documentation/platform/dynamic-secrets/mysql), [RabbitMQ](https://infisical.com/docs/documentation/platform/dynamic-secrets/rabbit-mq), and more.
- **[Secret Scanning and Leak Prevention](https://infisical.com/docs/cli/scanning-overview)**: Prevent secrets from leaking to git.
- **[Infisical Kubernetes Operator](https://infisical.com/docs/documentation/getting-started/kubernetes)**: Deliver secrets to your Kubernetes workloads and automatically reload deployments.

View File

@@ -8,8 +8,7 @@ RUN apt-get update && apt-get install -y \
python3 \
make \
g++ \
openssh-client \
openssl
openssh-client
# Install dependencies for TDS driver (required for SAP ASE dynamic secrets)
RUN apt-get install -y \

View File

@@ -19,7 +19,6 @@ RUN apt-get update && apt-get install -y \
make \
g++ \
openssh-client \
openssl \
curl \
pkg-config

View File

@@ -1,85 +0,0 @@
FROM node:20-slim
# ? Setup a test SoftHSM module. In production a real HSM is used.
ARG SOFTHSM2_VERSION=2.5.0
ENV SOFTHSM2_VERSION=${SOFTHSM2_VERSION} \
SOFTHSM2_SOURCES=/tmp/softhsm2
# Install build dependencies including python3 (required for pkcs11js and partially TDS driver)
RUN apt-get update && apt-get install -y \
build-essential \
autoconf \
automake \
git \
libtool \
libssl-dev \
python3 \
make \
g++ \
openssh-client \
curl \
pkg-config \
perl \
wget
# Install dependencies for TDS driver (required for SAP ASE dynamic secrets)
RUN apt-get install -y \
unixodbc \
unixodbc-dev \
freetds-dev \
freetds-bin \
tdsodbc
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nFileUsage = 1\n" > /etc/odbcinst.ini
# Build and install SoftHSM2
RUN git clone https://github.com/opendnssec/SoftHSMv2.git ${SOFTHSM2_SOURCES}
WORKDIR ${SOFTHSM2_SOURCES}
RUN git checkout ${SOFTHSM2_VERSION} -b ${SOFTHSM2_VERSION} \
&& sh autogen.sh \
&& ./configure --prefix=/usr/local --disable-gost \
&& make \
&& make install
WORKDIR /root
RUN rm -fr ${SOFTHSM2_SOURCES}
# Install pkcs11-tool
RUN apt-get install -y opensc
RUN mkdir -p /etc/softhsm2/tokens && \
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
WORKDIR /openssl-build
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
&& tar -xf openssl-3.1.2.tar.gz \
&& cd openssl-3.1.2 \
&& ./Configure enable-fips \
&& make \
&& make install_fips
# ? App setup
# Install Infisical CLI
RUN curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash && \
apt-get update && \
apt-get install -y infisical=0.8.1
WORKDIR /app
COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm install
COPY . .
ENV HOST=0.0.0.0
ENV OPENSSL_CONF=/app/nodejs.cnf
ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
ENV NODE_OPTIONS=--force-fips
CMD ["npm", "run", "dev:docker"]

View File

@@ -9,7 +9,6 @@ export const mockKeyStore = (): TKeyStoreFactory => {
store[key] = value;
return "OK";
},
setExpiry: async () => 0,
setItemWithExpiry: async (key, value) => {
store[key] = value;
return "OK";

View File

@@ -11,7 +11,6 @@ export const mockQueue = (): TQueueServiceFactory => {
job[name] = jobData;
},
queuePg: async () => {},
schedulePg: async () => {},
initialize: async () => {},
shutdown: async () => undefined,
stopRepeatableJob: async () => true,

View File

@@ -1,16 +0,0 @@
nodejs_conf = nodejs_init
.include /usr/local/ssl/fipsmodule.cnf
[nodejs_init]
providers = provider_sect
[provider_sect]
default = default_sect
fips = fips_sect
[default_sect]
activate = 1
[algorithm_sect]
default_properties = fips=yes

View File

@@ -104,7 +104,6 @@
"pkijs": "^3.2.4",
"posthog-node": "^3.6.2",
"probot": "^13.3.8",
"re2": "^1.21.4",
"safe-regex": "^2.1.1",
"scim-patch": "^0.8.3",
"scim2-parse-filter": "^0.2.10",
@@ -133,7 +132,7 @@
"@types/jsrp": "^0.2.6",
"@types/libsodium-wrappers": "^0.7.13",
"@types/lodash.isequal": "^4.5.8",
"@types/node": "^20.17.30",
"@types/node": "^20.9.5",
"@types/nodemailer": "^6.4.14",
"@types/passport-github": "^1.1.12",
"@types/passport-google-oauth20": "^2.0.14",
@@ -6861,79 +6860,6 @@
"node": ">= 8"
}
},
"node_modules/@npmcli/agent": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz",
"integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==",
"license": "ISC",
"dependencies": {
"agent-base": "^7.1.0",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.1",
"lru-cache": "^10.0.1",
"socks-proxy-agent": "^8.0.3"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/@npmcli/agent/node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/@npmcli/agent/node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/@npmcli/agent/node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@npmcli/agent/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"node_modules/@npmcli/fs": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz",
"integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==",
"license": "ISC",
"dependencies": {
"semver": "^7.3.5"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/@octokit/auth-app": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-7.1.1.tgz",
@@ -9827,12 +9753,11 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.17.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz",
"integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==",
"license": "MIT",
"version": "20.9.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.5.tgz",
"integrity": "sha512-Uq2xbNq0chGg+/WQEU0LJTSs/1nKxz6u1iemLcGomkSnKokbW1fbLqc3HOqCf2JP7KjlL4QkS7oZZTrOQHQYgQ==",
"dependencies": {
"undici-types": "~6.19.2"
"undici-types": "~5.26.4"
}
},
"node_modules/@types/node-fetch": {
@@ -11937,115 +11862,6 @@
"node": ">=8"
}
},
"node_modules/cacache": {
"version": "18.0.4",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz",
"integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==",
"license": "ISC",
"dependencies": {
"@npmcli/fs": "^3.1.0",
"fs-minipass": "^3.0.0",
"glob": "^10.2.2",
"lru-cache": "^10.0.1",
"minipass": "^7.0.3",
"minipass-collect": "^2.0.1",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.4",
"p-map": "^4.0.0",
"ssri": "^10.0.0",
"tar": "^6.1.11",
"unique-filename": "^3.0.0"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/cacache/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==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/cacache/node_modules/fs-minipass": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
"integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
"license": "ISC",
"dependencies": {
"minipass": "^7.0.3"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/cacache/node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^1.11.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/cacache/node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/cacache/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"node_modules/cacache/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/cacache/node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/call-bind": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
@@ -13025,16 +12841,6 @@
"node": ">= 0.8"
}
},
"node_modules/encoding": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"license": "MIT",
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.2"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -13067,21 +12873,6 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/err-code": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"license": "MIT"
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -13852,12 +13643,6 @@
"node": ">=0.10.0"
}
},
"node_modules/exponential-backoff": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz",
"integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==",
"license": "Apache-2.0"
},
"node_modules/express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
@@ -15435,12 +15220,6 @@
],
"license": "MIT"
},
"node_modules/http-cache-semantics": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
"license": "BSD-2-Clause"
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -15623,6 +15402,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"
}
@@ -15655,16 +15435,6 @@
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true
},
"node_modules/install-artifact-from-github": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/install-artifact-from-github/-/install-artifact-from-github-1.3.5.tgz",
"integrity": "sha512-gZHC7f/cJgXz7MXlHFBxPVMsvIbev1OQN1uKQYKVJDydGNm9oYf9JstbU4Atnh/eSvk41WtEovoRm+8IF686xg==",
"license": "BSD-3-Clause",
"bin": {
"install-from-cache": "bin/install-from-cache.js",
"save-to-github-cache": "bin/save-to-github-cache.js"
}
},
"node_modules/internal-slot": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz",
@@ -15747,19 +15517,6 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/ip-address": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
"integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
"license": "MIT",
"dependencies": {
"jsbn": "1.1.0",
"sprintf-js": "^1.1.3"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/ip-num": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/ip-num/-/ip-num-1.5.1.tgz",
@@ -15959,12 +15716,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-lambda": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
"integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
"license": "MIT"
},
"node_modules/is-negative-zero": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@@ -17108,38 +16859,6 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/make-fetch-happen": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz",
"integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==",
"license": "ISC",
"dependencies": {
"@npmcli/agent": "^2.0.0",
"cacache": "^18.0.0",
"http-cache-semantics": "^4.1.1",
"is-lambda": "^1.0.1",
"minipass": "^7.0.2",
"minipass-fetch": "^3.0.0",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.4",
"negotiator": "^0.6.3",
"proc-log": "^4.2.0",
"promise-retry": "^2.0.1",
"ssri": "^10.0.0"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/make-fetch-happen/node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -17313,125 +17032,6 @@
"node": ">=8"
}
},
"node_modules/minipass-collect": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz",
"integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==",
"license": "ISC",
"dependencies": {
"minipass": "^7.0.3"
},
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/minipass-collect/node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/minipass-fetch": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz",
"integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==",
"license": "MIT",
"dependencies": {
"minipass": "^7.0.3",
"minipass-sized": "^1.0.3",
"minizlib": "^2.1.2"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
},
"optionalDependencies": {
"encoding": "^0.1.13"
}
},
"node_modules/minipass-fetch/node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/minipass-flush": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
"integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
"license": "ISC",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/minipass-flush/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/minipass-pipeline": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
"integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
"license": "ISC",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/minipass-pipeline/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/minipass-sized": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
"integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
"license": "ISC",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/minipass-sized/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
@@ -17753,12 +17353,6 @@
"node": ">=12"
}
},
"node_modules/nan": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
@@ -17849,30 +17443,6 @@
}
}
},
"node_modules/node-gyp": {
"version": "10.3.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.3.1.tgz",
"integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==",
"license": "MIT",
"dependencies": {
"env-paths": "^2.2.0",
"exponential-backoff": "^3.1.1",
"glob": "^10.3.10",
"graceful-fs": "^4.2.6",
"make-fetch-happen": "^13.0.0",
"nopt": "^7.0.0",
"proc-log": "^4.1.0",
"semver": "^7.3.5",
"tar": "^6.2.1",
"which": "^4.0.0"
},
"bin": {
"node-gyp": "bin/node-gyp.js"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/node-gyp-build": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz",
@@ -17894,122 +17464,6 @@
"node-gyp-build-optional-packages-test": "build-test.js"
}
},
"node_modules/node-gyp/node_modules/abbrev": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
"integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
"license": "ISC",
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/node-gyp/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==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/node-gyp/node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^1.11.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/node-gyp/node_modules/isexe": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
"license": "ISC",
"engines": {
"node": ">=16"
}
},
"node_modules/node-gyp/node_modules/jackspeak": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
},
"optionalDependencies": {
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/node-gyp/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/node-gyp/node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/node-gyp/node_modules/nopt": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
"integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
"license": "ISC",
"dependencies": {
"abbrev": "^2.0.0"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/node-gyp/node_modules/which": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
"integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
"license": "ISC",
"dependencies": {
"isexe": "^3.1.1"
},
"bin": {
"node-which": "bin/which.js"
},
"engines": {
"node": "^16.13.0 || >=18.0.0"
}
},
"node_modules/node-releases": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
@@ -18670,21 +18124,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-map": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"license": "MIT",
"dependencies": {
"aggregate-error": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-queue": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
@@ -19762,15 +19201,6 @@
"real-require": "^0.2.0"
}
},
"node_modules/proc-log": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz",
"integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==",
"license": "ISC",
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@@ -19799,28 +19229,6 @@
"node": ">=0.4.0"
}
},
"node_modules/promise-retry": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
"integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
"license": "MIT",
"dependencies": {
"err-code": "^2.0.2",
"retry": "^0.12.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/promise-retry/node_modules/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/prompt-sync": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz",
@@ -20092,18 +19500,6 @@
"node": ">=0.10.0"
}
},
"node_modules/re2": {
"version": "1.21.4",
"resolved": "https://registry.npmjs.org/re2/-/re2-1.21.4.tgz",
"integrity": "sha512-MVIfXWJmsP28mRsSt8HeL750ifb8H5+oF2UDIxGaiJCr8fkMqhLZ7kcX9ADRk2dC8qeGKedB7UVYRfBVpEiLfA==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
"install-artifact-from-github": "^1.3.5",
"nan": "^2.20.0",
"node-gyp": "^10.2.0"
}
},
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
@@ -20685,6 +20081,11 @@
"undici-types": "~6.19.2"
}
},
"node_modules/scim-patch/node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
},
"node_modules/scim2-parse-filter": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/scim2-parse-filter/-/scim2-parse-filter-0.2.10.tgz",
@@ -21025,16 +20426,6 @@
"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==",
"license": "MIT",
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/smee-client": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/smee-client/-/smee-client-2.0.0.tgz",
@@ -21238,60 +20629,6 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/socks": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
"integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==",
"license": "MIT",
"dependencies": {
"ip-address": "^9.0.5",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks-proxy-agent": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"socks": "^2.8.3"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/socks-proxy-agent/node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/socks-proxy-agent/node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/sonic-boom": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz",
@@ -21347,27 +20684,6 @@
"node": ">= 0.6"
}
},
"node_modules/ssri": {
"version": "10.0.6",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz",
"integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==",
"license": "ISC",
"dependencies": {
"minipass": "^7.0.3"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/ssri/node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
@@ -23126,9 +22442,9 @@
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
@@ -23170,30 +22486,6 @@
"node": ">=4"
}
},
"node_modules/unique-filename": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz",
"integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==",
"license": "ISC",
"dependencies": {
"unique-slug": "^4.0.0"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/unique-slug": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz",
"integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==",
"license": "ISC",
"dependencies": {
"imurmurhash": "^0.1.4"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/universal-github-app-jwt": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.0.tgz",

View File

@@ -89,7 +89,7 @@
"@types/jsrp": "^0.2.6",
"@types/libsodium-wrappers": "^0.7.13",
"@types/lodash.isequal": "^4.5.8",
"@types/node": "^20.17.30",
"@types/node": "^20.9.5",
"@types/nodemailer": "^6.4.14",
"@types/passport-github": "^1.1.12",
"@types/passport-google-oauth20": "^2.0.14",
@@ -221,7 +221,6 @@
"pkijs": "^3.2.4",
"posthog-node": "^3.6.2",
"probot": "^13.3.8",
"re2": "^1.21.4",
"safe-regex": "^2.1.1",
"scim-patch": "^0.8.3",
"scim2-parse-filter": "^0.2.10",

View File

@@ -33,12 +33,10 @@ import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service";
import { TSecretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service";
import { TSecretRotationV2ServiceFactory } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-service";
import { TSecretScanningServiceFactory } from "@app/ee/services/secret-scanning/secret-scanning-service";
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { TSshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service";
import { TSshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service";
import { TSshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-service";
import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service";
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service";
@@ -136,7 +134,7 @@ declare module "fastify" {
rateLimits: RateLimitConfiguration;
// passport data
passportUser: {
isUserCompleted: boolean;
isUserCompleted: string;
providerAuthToken: string;
};
kmipUser: {
@@ -207,7 +205,6 @@ declare module "fastify" {
certificateTemplate: TCertificateTemplateServiceFactory;
sshCertificateAuthority: TSshCertificateAuthorityServiceFactory;
sshCertificateTemplate: TSshCertificateTemplateServiceFactory;
sshHost: TSshHostServiceFactory;
certificateAuthority: TCertificateAuthorityServiceFactory;
certificateAuthorityCrl: TCertificateAuthorityCrlServiceFactory;
certificateEst: TCertificateEstServiceFactory;
@@ -240,7 +237,6 @@ declare module "fastify" {
kmip: TKmipServiceFactory;
kmipOperation: TKmipOperationServiceFactory;
gateway: TGatewayServiceFactory;
secretRotationV2: TSecretRotationV2ServiceFactory;
};
// this is exclusive use for middlewares in which we need to inject data
// everywhere else access using service layer

View File

@@ -17,9 +17,6 @@ import {
TApiKeys,
TApiKeysInsert,
TApiKeysUpdate,
TAppConnections,
TAppConnectionsInsert,
TAppConnectionsUpdate,
TAuditLogs,
TAuditLogsInsert,
TAuditLogStreams,
@@ -68,9 +65,6 @@ import {
TDynamicSecrets,
TDynamicSecretsInsert,
TDynamicSecretsUpdate,
TExternalGroupOrgRoleMappings,
TExternalGroupOrgRoleMappingsInsert,
TExternalGroupOrgRoleMappingsUpdate,
TExternalKms,
TExternalKmsInsert,
TExternalKmsUpdate,
@@ -232,9 +226,6 @@ import {
TProjectSplitBackfillIds,
TProjectSplitBackfillIdsInsert,
TProjectSplitBackfillIdsUpdate,
TProjectSshConfigs,
TProjectSshConfigsInsert,
TProjectSshConfigsUpdate,
TProjectsUpdate,
TProjectTemplates,
TProjectTemplatesInsert,
@@ -308,12 +299,6 @@ import {
TSecretRotations,
TSecretRotationsInsert,
TSecretRotationsUpdate,
TSecretRotationsV2,
TSecretRotationsV2Insert,
TSecretRotationsV2Update,
TSecretRotationV2SecretMappings,
TSecretRotationV2SecretMappingsInsert,
TSecretRotationV2SecretMappingsUpdate,
TSecrets,
TSecretScanningGitRisks,
TSecretScanningGitRisksInsert,
@@ -335,27 +320,15 @@ import {
TSecretSnapshotsInsert,
TSecretSnapshotsUpdate,
TSecretsUpdate,
TSecretsV2,
TSecretsV2Insert,
TSecretsV2Update,
TSecretSyncs,
TSecretSyncsInsert,
TSecretSyncsUpdate,
TSecretTagJunction,
TSecretTagJunctionInsert,
TSecretTagJunctionUpdate,
TSecretTags,
TSecretTagsInsert,
TSecretTagsUpdate,
TSecretV2TagJunction,
TSecretV2TagJunctionInsert,
TSecretV2TagJunctionUpdate,
TSecretVersions,
TSecretVersionsInsert,
TSecretVersionsUpdate,
TSecretVersionsV2,
TSecretVersionsV2Insert,
TSecretVersionsV2Update,
TSecretVersionTagJunction,
TSecretVersionTagJunctionInsert,
TSecretVersionTagJunctionUpdate,
@@ -383,15 +356,6 @@ import {
TSshCertificateTemplates,
TSshCertificateTemplatesInsert,
TSshCertificateTemplatesUpdate,
TSshHostLoginUserMappings,
TSshHostLoginUserMappingsInsert,
TSshHostLoginUserMappingsUpdate,
TSshHostLoginUsers,
TSshHostLoginUsersInsert,
TSshHostLoginUsersUpdate,
TSshHosts,
TSshHostsInsert,
TSshHostsUpdate,
TSuperAdmin,
TSuperAdminInsert,
TSuperAdminUpdate,
@@ -423,6 +387,24 @@ import {
TWorkflowIntegrationsInsert,
TWorkflowIntegrationsUpdate
} from "@app/db/schemas";
import { TAppConnections, TAppConnectionsInsert, TAppConnectionsUpdate } from "@app/db/schemas/app-connections";
import {
TExternalGroupOrgRoleMappings,
TExternalGroupOrgRoleMappingsInsert,
TExternalGroupOrgRoleMappingsUpdate
} from "@app/db/schemas/external-group-org-role-mappings";
import { TSecretSyncs, TSecretSyncsInsert, TSecretSyncsUpdate } from "@app/db/schemas/secret-syncs";
import {
TSecretV2TagJunction,
TSecretV2TagJunctionInsert,
TSecretV2TagJunctionUpdate
} from "@app/db/schemas/secret-v2-tag-junction";
import {
TSecretVersionsV2,
TSecretVersionsV2Insert,
TSecretVersionsV2Update
} from "@app/db/schemas/secret-versions-v2";
import { TSecretsV2, TSecretsV2Insert, TSecretsV2Update } from "@app/db/schemas/secrets-v2";
declare module "knex" {
namespace Knex {
@@ -437,7 +419,6 @@ declare module "knex/types/tables" {
interface Tables {
[TableName.Users]: KnexOriginal.CompositeTableType<TUsers, TUsersInsert, TUsersUpdate>;
[TableName.Groups]: KnexOriginal.CompositeTableType<TGroups, TGroupsInsert, TGroupsUpdate>;
[TableName.SshHost]: KnexOriginal.CompositeTableType<TSshHosts, TSshHostsInsert, TSshHostsUpdate>;
[TableName.SshCertificateAuthority]: KnexOriginal.CompositeTableType<
TSshCertificateAuthorities,
TSshCertificateAuthoritiesInsert,
@@ -463,16 +444,6 @@ declare module "knex/types/tables" {
TSshCertificateBodiesInsert,
TSshCertificateBodiesUpdate
>;
[TableName.SshHostLoginUser]: KnexOriginal.CompositeTableType<
TSshHostLoginUsers,
TSshHostLoginUsersInsert,
TSshHostLoginUsersUpdate
>;
[TableName.SshHostLoginUserMapping]: KnexOriginal.CompositeTableType<
TSshHostLoginUserMappings,
TSshHostLoginUserMappingsInsert,
TSshHostLoginUserMappingsUpdate
>;
[TableName.CertificateAuthority]: KnexOriginal.CompositeTableType<
TCertificateAuthorities,
TCertificateAuthoritiesInsert,
@@ -577,11 +548,6 @@ declare module "knex/types/tables" {
[TableName.SuperAdmin]: KnexOriginal.CompositeTableType<TSuperAdmin, TSuperAdminInsert, TSuperAdminUpdate>;
[TableName.ApiKey]: KnexOriginal.CompositeTableType<TApiKeys, TApiKeysInsert, TApiKeysUpdate>;
[TableName.Project]: KnexOriginal.CompositeTableType<TProjects, TProjectsInsert, TProjectsUpdate>;
[TableName.ProjectSshConfig]: KnexOriginal.CompositeTableType<
TProjectSshConfigs,
TProjectSshConfigsInsert,
TProjectSshConfigsUpdate
>;
[TableName.ProjectMembership]: KnexOriginal.CompositeTableType<
TProjectMemberships,
TProjectMembershipsInsert,
@@ -984,15 +950,5 @@ declare module "knex/types/tables" {
TOrgGatewayConfigInsert,
TOrgGatewayConfigUpdate
>;
[TableName.SecretRotationV2]: KnexOriginal.CompositeTableType<
TSecretRotationsV2,
TSecretRotationsV2Insert,
TSecretRotationsV2Update
>;
[TableName.SecretRotationV2SecretMapping]: KnexOriginal.CompositeTableType<
TSecretRotationV2SecretMappings,
TSecretRotationV2SecretMappingsInsert,
TSecretRotationV2SecretMappingsUpdate
>;
}
}

View File

@@ -16,7 +16,7 @@ const createAuditLogPartition = async (knex: Knex, startDate: Date, endDate: Dat
const startDateStr = formatPartitionDate(startDate);
const endDateStr = formatPartitionDate(endDate);
const partitionName = `${TableName.AuditLog}_${startDateStr.replaceAll("-", "")}_${endDateStr.replaceAll("-", "")}`;
const partitionName = `${TableName.AuditLog}_${startDateStr.replace(/-/g, "")}_${endDateStr.replace(/-/g, "")}`;
await knex.schema.raw(
`CREATE TABLE ${partitionName} PARTITION OF ${TableName.AuditLog} FOR VALUES FROM ('${startDateStr}') TO ('${endDateStr}')`

View File

@@ -1,31 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.Organization, "shouldUseNewPrivilegeSystem"))) {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.boolean("shouldUseNewPrivilegeSystem");
t.string("privilegeUpgradeInitiatedByUsername");
t.dateTime("privilegeUpgradeInitiatedAt");
});
await knex(TableName.Organization).update({
shouldUseNewPrivilegeSystem: false
});
await knex.schema.alterTable(TableName.Organization, (t) => {
t.boolean("shouldUseNewPrivilegeSystem").defaultTo(true).notNullable().alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.Organization, "shouldUseNewPrivilegeSystem")) {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.dropColumn("shouldUseNewPrivilegeSystem");
t.dropColumn("privilegeUpgradeInitiatedByUsername");
t.dropColumn("privilegeUpgradeInitiatedAt");
});
}
}

View File

@@ -1,23 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
if (doesParentColumExist && doesNameColumnExist) {
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
t.index(["parentId", "name"]);
});
}
}
export async function down(knex: Knex): Promise<void> {
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
if (doesParentColumExist && doesNameColumnExist) {
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
t.dropIndex(["parentId", "name"]);
});
}
}

View File

@@ -1,19 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasReviewerJwtCol = await knex.schema.hasColumn(
TableName.IdentityKubernetesAuth,
"encryptedKubernetesTokenReviewerJwt"
);
if (hasReviewerJwtCol) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
t.binary("encryptedKubernetesTokenReviewerJwt").nullable().alter();
});
}
}
export async function down(): Promise<void> {
// we can't make it back to non nullable, it will fail
}

View File

@@ -1,29 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas/models";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.SecretApprovalPolicy, "allowedSelfApprovals"))) {
await knex.schema.alterTable(TableName.SecretApprovalPolicy, (t) => {
t.boolean("allowedSelfApprovals").notNullable().defaultTo(true);
});
}
if (!(await knex.schema.hasColumn(TableName.AccessApprovalPolicy, "allowedSelfApprovals"))) {
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (t) => {
t.boolean("allowedSelfApprovals").notNullable().defaultTo(true);
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.SecretApprovalPolicy, "allowedSelfApprovals")) {
await knex.schema.alterTable(TableName.SecretApprovalPolicy, (t) => {
t.dropColumn("allowedSelfApprovals");
});
}
if (await knex.schema.hasColumn(TableName.AccessApprovalPolicy, "allowedSelfApprovals")) {
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (t) => {
t.dropColumn("allowedSelfApprovals");
});
}
}

View File

@@ -1,19 +0,0 @@
import { Knex } from "knex";
import { TableName } from "@app/db/schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.AppConnection, "isPlatformManagedCredentials"))) {
await knex.schema.alterTable(TableName.AppConnection, (t) => {
t.boolean("isPlatformManagedCredentials").defaultTo(false);
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.AppConnection, "isPlatformManagedCredentials")) {
await knex.schema.alterTable(TableName.AppConnection, (t) => {
t.dropColumn("isPlatformManagedCredentials");
});
}
}

View File

@@ -1,58 +0,0 @@
import { Knex } from "knex";
import { TableName } from "@app/db/schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "@app/db/utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretRotationV2))) {
await knex.schema.createTable(TableName.SecretRotationV2, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name", 32).notNullable();
t.string("description");
t.string("type").notNullable();
t.jsonb("parameters").notNullable();
t.jsonb("secretsMapping").notNullable();
t.binary("encryptedGeneratedCredentials").notNullable();
t.boolean("isAutoRotationEnabled").notNullable().defaultTo(true);
t.integer("activeIndex").notNullable().defaultTo(0);
t.uuid("folderId").notNullable();
t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE");
t.uuid("connectionId").notNullable();
t.foreign("connectionId").references("id").inTable(TableName.AppConnection);
t.timestamps(true, true, true);
t.integer("rotationInterval").notNullable();
t.jsonb("rotateAtUtc").notNullable(); // { hours: number; minutes: number }
t.string("rotationStatus").notNullable();
t.datetime("lastRotationAttemptedAt").notNullable();
t.datetime("lastRotatedAt").notNullable();
t.binary("encryptedLastRotationMessage"); // we encrypt this because it may contain sensitive info (SQL errors showing credentials)
t.string("lastRotationJobId");
t.datetime("nextRotationAt");
t.boolean("isLastRotationManual").notNullable().defaultTo(true); // creation is considered a "manual" rotation
});
await createOnUpdateTrigger(knex, TableName.SecretRotationV2);
await knex.schema.alterTable(TableName.SecretRotationV2, (t) => {
t.unique(["folderId", "name"]);
});
}
if (!(await knex.schema.hasTable(TableName.SecretRotationV2SecretMapping))) {
await knex.schema.createTable(TableName.SecretRotationV2SecretMapping, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("secretId").notNullable();
// scott: this is deferred to block secret deletion but not prevent folder/environment/project deletion
// ie, if rotation is being deleted as well we permit it, otherwise throw
t.foreign("secretId").references("id").inTable(TableName.SecretV2).deferrable("deferred");
t.uuid("rotationId").notNullable();
t.foreign("rotationId").references("id").inTable(TableName.SecretRotationV2).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretRotationV2SecretMapping);
await knex.schema.dropTableIfExists(TableName.SecretRotationV2);
await dropOnUpdateTrigger(knex, TableName.SecretRotationV2);
}

View File

@@ -1,21 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasCol = await knex.schema.hasColumn(TableName.SecretFolder, "lastSecretModified");
if (!hasCol) {
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
t.datetime("lastSecretModified");
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasCol = await knex.schema.hasColumn(TableName.SecretFolder, "lastSecretModified");
if (hasCol) {
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
t.dropColumn("lastSecretModified");
});
}
}

View File

@@ -1,25 +0,0 @@
import { Knex } from "knex";
import { KmsKeyUsage } from "@app/services/kms/kms-types";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasKeyUsageColumn = await knex.schema.hasColumn(TableName.KmsKey, "keyUsage");
if (!hasKeyUsageColumn) {
await knex.schema.alterTable(TableName.KmsKey, (t) => {
t.string("keyUsage").notNullable().defaultTo(KmsKeyUsage.ENCRYPT_DECRYPT);
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasKeyUsageColumn = await knex.schema.hasColumn(TableName.KmsKey, "keyUsage");
if (hasKeyUsageColumn) {
await knex.schema.alterTable(TableName.KmsKey, (t) => {
t.dropColumn("keyUsage");
});
}
}

View File

@@ -1,32 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.SshCertificateAuthority, "keySource"))) {
await knex.schema.alterTable(TableName.SshCertificateAuthority, (t) => {
t.string("keySource");
});
// Backfilling the keySource to internal
await knex(TableName.SshCertificateAuthority).update({ keySource: "internal" });
await knex.schema.alterTable(TableName.SshCertificateAuthority, (t) => {
t.string("keySource").notNullable().alter();
});
}
if (await knex.schema.hasColumn(TableName.SshCertificate, "sshCaId")) {
await knex.schema.alterTable(TableName.SshCertificate, (t) => {
t.uuid("sshCaId").nullable().alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.SshCertificateAuthority, "keySource")) {
await knex.schema.alterTable(TableName.SshCertificateAuthority, (t) => {
t.dropColumn("keySource");
});
}
}

View File

@@ -1,93 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SshHost))) {
await knex.schema.createTable(TableName.SshHost, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.timestamps(true, true, true);
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.string("hostname").notNullable();
t.string("userCertTtl").notNullable();
t.string("hostCertTtl").notNullable();
t.uuid("userSshCaId").notNullable();
t.foreign("userSshCaId").references("id").inTable(TableName.SshCertificateAuthority).onDelete("CASCADE");
t.uuid("hostSshCaId").notNullable();
t.foreign("hostSshCaId").references("id").inTable(TableName.SshCertificateAuthority).onDelete("CASCADE");
t.unique(["projectId", "hostname"]);
});
await createOnUpdateTrigger(knex, TableName.SshHost);
}
if (!(await knex.schema.hasTable(TableName.SshHostLoginUser))) {
await knex.schema.createTable(TableName.SshHostLoginUser, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.timestamps(true, true, true);
t.uuid("sshHostId").notNullable();
t.foreign("sshHostId").references("id").inTable(TableName.SshHost).onDelete("CASCADE");
t.string("loginUser").notNullable(); // e.g. ubuntu, root, ec2-user, ...
t.unique(["sshHostId", "loginUser"]);
});
await createOnUpdateTrigger(knex, TableName.SshHostLoginUser);
}
if (!(await knex.schema.hasTable(TableName.SshHostLoginUserMapping))) {
await knex.schema.createTable(TableName.SshHostLoginUserMapping, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.timestamps(true, true, true);
t.uuid("sshHostLoginUserId").notNullable();
t.foreign("sshHostLoginUserId").references("id").inTable(TableName.SshHostLoginUser).onDelete("CASCADE");
t.uuid("userId").nullable();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.unique(["sshHostLoginUserId", "userId"]);
});
await createOnUpdateTrigger(knex, TableName.SshHostLoginUserMapping);
}
if (!(await knex.schema.hasTable(TableName.ProjectSshConfig))) {
// new table to store configuration for projects of type SSH (i.e. Infisical SSH)
await knex.schema.createTable(TableName.ProjectSshConfig, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.timestamps(true, true, true);
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.uuid("defaultUserSshCaId");
t.foreign("defaultUserSshCaId").references("id").inTable(TableName.SshCertificateAuthority).onDelete("CASCADE");
t.uuid("defaultHostSshCaId");
t.foreign("defaultHostSshCaId").references("id").inTable(TableName.SshCertificateAuthority).onDelete("CASCADE");
});
await createOnUpdateTrigger(knex, TableName.ProjectSshConfig);
}
const hasColumn = await knex.schema.hasColumn(TableName.SshCertificate, "sshHostId");
if (!hasColumn) {
await knex.schema.alterTable(TableName.SshCertificate, (t) => {
t.uuid("sshHostId").nullable();
t.foreign("sshHostId").references("id").inTable(TableName.SshHost).onDelete("SET NULL");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.ProjectSshConfig);
await dropOnUpdateTrigger(knex, TableName.ProjectSshConfig);
await knex.schema.dropTableIfExists(TableName.SshHostLoginUserMapping);
await dropOnUpdateTrigger(knex, TableName.SshHostLoginUserMapping);
await knex.schema.dropTableIfExists(TableName.SshHostLoginUser);
await dropOnUpdateTrigger(knex, TableName.SshHostLoginUser);
const hasColumn = await knex.schema.hasColumn(TableName.SshCertificate, "sshHostId");
if (hasColumn) {
await knex.schema.alterTable(TableName.SshCertificate, (t) => {
t.dropColumn("sshHostId");
});
}
await knex.schema.dropTableIfExists(TableName.SshHost);
await dropOnUpdateTrigger(knex, TableName.SshHost);
}

View File

@@ -1,20 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.ResourceMetadata, "dynamicSecretId"))) {
await knex.schema.alterTable(TableName.ResourceMetadata, (tb) => {
tb.uuid("dynamicSecretId");
tb.foreign("dynamicSecretId").references("id").inTable(TableName.DynamicSecret).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.ResourceMetadata, "dynamicSecretId")) {
await knex.schema.alterTable(TableName.ResourceMetadata, (tb) => {
tb.dropColumn("dynamicSecretId");
});
}
}

View File

@@ -1,21 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasCol = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "note");
if (!hasCol) {
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
t.string("note").nullable();
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasCol = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "note");
if (hasCol) {
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
t.dropColumn("note");
});
}
}

View File

@@ -1,27 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasCol = await knex.schema.hasColumn(TableName.ServiceToken, "expiryNotificationSent");
if (!hasCol) {
await knex.schema.alterTable(TableName.ServiceToken, (t) => {
t.boolean("expiryNotificationSent").defaultTo(false);
});
// Update only tokens where expiresAt is before current time
await knex(TableName.ServiceToken)
.whereRaw(`${TableName.ServiceToken}."expiresAt" < NOW()`)
.whereNotNull("expiresAt")
.update({ expiryNotificationSent: true });
}
}
export async function down(knex: Knex): Promise<void> {
const hasCol = await knex.schema.hasColumn(TableName.ServiceToken, "expiryNotificationSent");
if (hasCol) {
await knex.schema.alterTable(TableName.ServiceToken, (t) => {
t.dropColumn("expiryNotificationSent");
});
}
}

View File

@@ -1,21 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasCol = await knex.schema.hasColumn(TableName.Project, "hasDeleteProtection");
if (!hasCol) {
await knex.schema.alterTable(TableName.Project, (t) => {
t.boolean("hasDeleteProtection").defaultTo(false);
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasCol = await knex.schema.hasColumn(TableName.Project, "hasDeleteProtection");
if (hasCol) {
await knex.schema.alterTable(TableName.Project, (t) => {
t.dropColumn("hasDeleteProtection");
});
}
}

View File

@@ -1,15 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.string("altNames", 4096).alter();
});
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.string("altNames").alter(); // Defaults to varchar(255)
});
}

View File

@@ -1,15 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.KmipOrgServerCertificates, (t) => {
t.string("altNames", 4096).alter();
});
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.KmipOrgServerCertificates, (t) => {
t.string("altNames").alter(); // Defaults to varchar(255)
});
}

View File

@@ -1,21 +0,0 @@
import { Knex } from "knex";
import { OIDCJWTSignatureAlgorithm } from "@app/ee/services/oidc/oidc-config-types";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.OidcConfig, "jwtSignatureAlgorithm"))) {
await knex.schema.alterTable(TableName.OidcConfig, (t) => {
t.string("jwtSignatureAlgorithm").defaultTo(OIDCJWTSignatureAlgorithm.RS256).notNullable();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.OidcConfig, "jwtSignatureAlgorithm")) {
await knex.schema.alterTable(TableName.OidcConfig, (t) => {
t.dropColumn("jwtSignatureAlgorithm");
});
}
}

View File

@@ -1,19 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.Organization, "bypassOrgAuthEnabled"))) {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.boolean("bypassOrgAuthEnabled").defaultTo(false).notNullable();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.Organization, "bypassOrgAuthEnabled")) {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.dropColumn("bypassOrgAuthEnabled");
});
}
}

View File

@@ -16,8 +16,7 @@ export const AccessApprovalPoliciesSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
enforcementLevel: z.string().default("hard"),
deletedAt: z.date().nullable().optional(),
allowedSelfApprovals: z.boolean().default(true)
deletedAt: z.date().nullable().optional()
});
export type TAccessApprovalPolicies = z.infer<typeof AccessApprovalPoliciesSchema>;

View File

@@ -17,8 +17,7 @@ export const AccessApprovalRequestsSchema = z.object({
permissions: z.unknown(),
createdAt: z.date(),
updatedAt: z.date(),
requestedByUserId: z.string().uuid(),
note: z.string().nullable().optional()
requestedByUserId: z.string().uuid()
});
export type TAccessApprovalRequests = z.infer<typeof AccessApprovalRequestsSchema>;

View File

@@ -19,8 +19,7 @@ export const AppConnectionsSchema = z.object({
version: z.number().default(1),
orgId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
isPlatformManagedCredentials: z.boolean().default(false).nullable().optional()
updatedAt: z.date()
});
export type TAppConnections = z.infer<typeof AppConnectionsSchema>;

View File

@@ -28,7 +28,7 @@ export const IdentityKubernetesAuthsSchema = z.object({
allowedNamespaces: z.string(),
allowedNames: z.string(),
allowedAudience: z.string(),
encryptedKubernetesTokenReviewerJwt: zodBuffer.nullable().optional(),
encryptedKubernetesTokenReviewerJwt: zodBuffer,
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional()
});

View File

@@ -3,7 +3,6 @@ export * from "./access-approval-policies-approvers";
export * from "./access-approval-requests";
export * from "./access-approval-requests-reviewers";
export * from "./api-keys";
export * from "./app-connections";
export * from "./audit-log-streams";
export * from "./audit-logs";
export * from "./auth-token-sessions";
@@ -20,7 +19,6 @@ export * from "./certificate-templates";
export * from "./certificates";
export * from "./dynamic-secret-leases";
export * from "./dynamic-secrets";
export * from "./external-group-org-role-mappings";
export * from "./external-kms";
export * from "./gateways";
export * from "./git-app-install-sessions";
@@ -75,7 +73,6 @@ export * from "./project-memberships";
export * from "./project-roles";
export * from "./project-slack-configs";
export * from "./project-split-backfill-ids";
export * from "./project-ssh-configs";
export * from "./project-templates";
export * from "./project-user-additional-privilege";
export * from "./project-user-membership-roles";
@@ -100,16 +97,13 @@ export * from "./secret-references";
export * from "./secret-references-v2";
export * from "./secret-rotation-output-v2";
export * from "./secret-rotation-outputs";
export * from "./secret-rotation-v2-secret-mappings";
export * from "./secret-rotations";
export * from "./secret-rotations-v2";
export * from "./secret-scanning-git-risks";
export * from "./secret-sharing";
export * from "./secret-snapshot-folders";
export * from "./secret-snapshot-secrets";
export * from "./secret-snapshot-secrets-v2";
export * from "./secret-snapshots";
export * from "./secret-syncs";
export * from "./secret-tag-junction";
export * from "./secret-tags";
export * from "./secret-v2-tag-junction";
@@ -126,9 +120,6 @@ export * from "./ssh-certificate-authority-secrets";
export * from "./ssh-certificate-bodies";
export * from "./ssh-certificate-templates";
export * from "./ssh-certificates";
export * from "./ssh-host-login-user-mappings";
export * from "./ssh-host-login-users";
export * from "./ssh-hosts";
export * from "./super-admin";
export * from "./totp-configs";
export * from "./trusted-ips";

View File

@@ -16,8 +16,7 @@ export const KmsKeysSchema = z.object({
name: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
projectId: z.string().nullable().optional(),
keyUsage: z.string().default("encrypt-decrypt")
projectId: z.string().nullable().optional()
});
export type TKmsKeys = z.infer<typeof KmsKeysSchema>;

View File

@@ -2,9 +2,6 @@ import { z } from "zod";
export enum TableName {
Users = "users",
SshHost = "ssh_hosts",
SshHostLoginUser = "ssh_host_login_users",
SshHostLoginUserMapping = "ssh_host_login_user_mappings",
SshCertificateAuthority = "ssh_certificate_authorities",
SshCertificateAuthoritySecret = "ssh_certificate_authority_secrets",
SshCertificateTemplate = "ssh_certificate_templates",
@@ -41,7 +38,6 @@ export enum TableName {
SuperAdmin = "super_admin",
RateLimit = "rate_limit",
ApiKey = "api_keys",
ProjectSshConfig = "project_ssh_configs",
Project = "projects",
ProjectBot = "project_bots",
Environment = "project_environments",
@@ -144,9 +140,7 @@ export enum TableName {
KmipClient = "kmip_clients",
KmipOrgConfig = "kmip_org_configs",
KmipOrgServerCertificates = "kmip_org_server_certificates",
KmipClientCertificates = "kmip_client_certificates",
SecretRotationV2 = "secret_rotations_v2",
SecretRotationV2SecretMapping = "secret_rotation_v2_secret_mappings"
KmipClientCertificates = "kmip_client_certificates"
}
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
@@ -239,8 +233,3 @@ export enum ActionProjectType {
// project operations that happen on all types
Any = "any"
}
export enum SortDirection {
ASC = "asc",
DESC = "desc"
}

View File

@@ -30,10 +30,9 @@ export const OidcConfigsSchema = z.object({
updatedAt: z.date(),
orgId: z.string().uuid(),
lastUsed: z.date().nullable().optional(),
encryptedOidcClientId: zodBuffer,
encryptedOidcClientSecret: zodBuffer,
manageGroupMemberships: z.boolean().default(false),
jwtSignatureAlgorithm: z.string().default("RS256")
encryptedOidcClientId: zodBuffer,
encryptedOidcClientSecret: zodBuffer
});
export type TOidcConfigs = z.infer<typeof OidcConfigsSchema>;

View File

@@ -23,11 +23,7 @@ export const OrganizationsSchema = z.object({
defaultMembershipRole: z.string().default("member"),
enforceMfa: z.boolean().default(false),
selectedMfaMethod: z.string().nullable().optional(),
allowSecretSharingOutsideOrganization: z.boolean().default(true).nullable().optional(),
shouldUseNewPrivilegeSystem: z.boolean().default(true),
privilegeUpgradeInitiatedByUsername: z.string().nullable().optional(),
privilegeUpgradeInitiatedAt: z.date().nullable().optional(),
bypassOrgAuthEnabled: z.boolean().default(false)
allowSecretSharingOutsideOrganization: z.boolean().default(true).nullable().optional()
});
export type TOrganizations = z.infer<typeof OrganizationsSchema>;

View File

@@ -1,21 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const ProjectSshConfigsSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
projectId: z.string(),
defaultUserSshCaId: z.string().uuid().nullable().optional(),
defaultHostSshCaId: z.string().uuid().nullable().optional()
});
export type TProjectSshConfigs = z.infer<typeof ProjectSshConfigsSchema>;
export type TProjectSshConfigsInsert = Omit<z.input<typeof ProjectSshConfigsSchema>, TImmutableDBKeys>;
export type TProjectSshConfigsUpdate = Partial<Omit<z.input<typeof ProjectSshConfigsSchema>, TImmutableDBKeys>>;

View File

@@ -26,8 +26,7 @@ export const ProjectsSchema = z.object({
kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional(),
description: z.string().nullable().optional(),
type: z.string(),
enforceCapitalization: z.boolean().default(false),
hasDeleteProtection: z.boolean().default(true).nullable().optional()
enforceCapitalization: z.boolean().default(false)
});
export type TProjects = z.infer<typeof ProjectsSchema>;

View File

@@ -16,8 +16,7 @@ export const ResourceMetadataSchema = z.object({
identityId: z.string().uuid().nullable().optional(),
secretId: z.string().uuid().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date(),
dynamicSecretId: z.string().uuid().nullable().optional()
updatedAt: z.date()
});
export type TResourceMetadata = z.infer<typeof ResourceMetadataSchema>;

View File

@@ -16,8 +16,7 @@ export const SecretApprovalPoliciesSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
enforcementLevel: z.string().default("hard"),
deletedAt: z.date().nullable().optional(),
allowedSelfApprovals: z.boolean().default(true)
deletedAt: z.date().nullable().optional()
});
export type TSecretApprovalPolicies = z.infer<typeof SecretApprovalPoliciesSchema>;

View File

@@ -16,8 +16,7 @@ export const SecretFoldersSchema = z.object({
envId: z.string().uuid(),
parentId: z.string().uuid().nullable().optional(),
isReserved: z.boolean().default(false).nullable().optional(),
description: z.string().nullable().optional(),
lastSecretModified: z.date().nullable().optional()
description: z.string().nullable().optional()
});
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;

View File

@@ -1,23 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SecretRotationV2SecretMappingsSchema = z.object({
id: z.string().uuid(),
secretId: z.string().uuid(),
rotationId: z.string().uuid()
});
export type TSecretRotationV2SecretMappings = z.infer<typeof SecretRotationV2SecretMappingsSchema>;
export type TSecretRotationV2SecretMappingsInsert = Omit<
z.input<typeof SecretRotationV2SecretMappingsSchema>,
TImmutableDBKeys
>;
export type TSecretRotationV2SecretMappingsUpdate = Partial<
Omit<z.input<typeof SecretRotationV2SecretMappingsSchema>, TImmutableDBKeys>
>;

View File

@@ -1,39 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const SecretRotationsV2Schema = z.object({
id: z.string().uuid(),
name: z.string(),
description: z.string().nullable().optional(),
type: z.string(),
parameters: z.unknown(),
secretsMapping: z.unknown(),
encryptedGeneratedCredentials: zodBuffer,
isAutoRotationEnabled: z.boolean().default(true),
activeIndex: z.number().default(0),
folderId: z.string().uuid(),
connectionId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
rotationInterval: z.number(),
rotateAtUtc: z.unknown(),
rotationStatus: z.string(),
lastRotationAttemptedAt: z.date(),
lastRotatedAt: z.date(),
encryptedLastRotationMessage: zodBuffer.nullable().optional(),
lastRotationJobId: z.string().nullable().optional(),
nextRotationAt: z.date().nullable().optional(),
isLastRotationManual: z.boolean().default(true)
});
export type TSecretRotationsV2 = z.infer<typeof SecretRotationsV2Schema>;
export type TSecretRotationsV2Insert = Omit<z.input<typeof SecretRotationsV2Schema>, TImmutableDBKeys>;
export type TSecretRotationsV2Update = Partial<Omit<z.input<typeof SecretRotationsV2Schema>, TImmutableDBKeys>>;

View File

@@ -21,8 +21,7 @@ export const ServiceTokensSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
createdBy: z.string(),
projectId: z.string(),
expiryNotificationSent: z.boolean().default(false).nullable().optional()
projectId: z.string()
});
export type TServiceTokens = z.infer<typeof ServiceTokensSchema>;

View File

@@ -14,8 +14,7 @@ export const SshCertificateAuthoritiesSchema = z.object({
projectId: z.string(),
status: z.string(),
friendlyName: z.string(),
keyAlgorithm: z.string(),
keySource: z.string()
keyAlgorithm: z.string()
});
export type TSshCertificateAuthorities = z.infer<typeof SshCertificateAuthoritiesSchema>;

View File

@@ -11,15 +11,14 @@ export const SshCertificatesSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
sshCaId: z.string().uuid().nullable().optional(),
sshCaId: z.string().uuid(),
sshCertificateTemplateId: z.string().uuid().nullable().optional(),
serialNumber: z.string(),
certType: z.string(),
principals: z.string().array(),
keyId: z.string(),
notBefore: z.date(),
notAfter: z.date(),
sshHostId: z.string().uuid().nullable().optional()
notAfter: z.date()
});
export type TSshCertificates = z.infer<typeof SshCertificatesSchema>;

View File

@@ -1,22 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SshHostLoginUserMappingsSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
sshHostLoginUserId: z.string().uuid(),
userId: z.string().uuid().nullable().optional()
});
export type TSshHostLoginUserMappings = z.infer<typeof SshHostLoginUserMappingsSchema>;
export type TSshHostLoginUserMappingsInsert = Omit<z.input<typeof SshHostLoginUserMappingsSchema>, TImmutableDBKeys>;
export type TSshHostLoginUserMappingsUpdate = Partial<
Omit<z.input<typeof SshHostLoginUserMappingsSchema>, TImmutableDBKeys>
>;

View File

@@ -1,20 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SshHostLoginUsersSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
sshHostId: z.string().uuid(),
loginUser: z.string()
});
export type TSshHostLoginUsers = z.infer<typeof SshHostLoginUsersSchema>;
export type TSshHostLoginUsersInsert = Omit<z.input<typeof SshHostLoginUsersSchema>, TImmutableDBKeys>;
export type TSshHostLoginUsersUpdate = Partial<Omit<z.input<typeof SshHostLoginUsersSchema>, TImmutableDBKeys>>;

View File

@@ -1,24 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SshHostsSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
projectId: z.string(),
hostname: z.string(),
userCertTtl: z.string(),
hostCertTtl: z.string(),
userSshCaId: z.string().uuid(),
hostSshCaId: z.string().uuid()
});
export type TSshHosts = z.infer<typeof SshHostsSchema>;
export type TSshHostsInsert = Omit<z.input<typeof SshHostsSchema>, TImmutableDBKeys>;
export type TSshHostsUpdate = Partial<Omit<z.input<typeof SshHostsSchema>, TImmutableDBKeys>>;

View File

@@ -16,7 +16,7 @@ export const registerCertificateEstRouter = async (server: FastifyZodProvider) =
// for CSRs sent in PEM, we leave them as is
// for CSRs sent in base64, we preprocess them to remove new lines and spaces
if (!csrBody.includes("BEGIN CERTIFICATE REQUEST")) {
csrBody = csrBody.replaceAll("\n", "").replaceAll(" ", "");
csrBody = csrBody.replace(/\n/g, "").replace(/ /g, "");
}
done(null, csrBody);

View File

@@ -29,8 +29,7 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
.array()
.min(1, { message: "At least one approver should be provided" }),
approvals: z.number().min(1).default(1),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
allowedSelfApprovals: z.boolean().default(true)
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
}),
response: {
200: z.object({
@@ -148,8 +147,7 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
.array()
.min(1, { message: "At least one approver should be provided" }),
approvals: z.number().min(1).optional(),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
allowedSelfApprovals: z.boolean().default(true)
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
}),
response: {
200: z.object({

View File

@@ -22,8 +22,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
body: z.object({
permissions: z.any().array(),
isTemporary: z.boolean(),
temporaryRange: z.string().optional(),
note: z.string().max(255).optional()
temporaryRange: z.string().optional()
}),
querystring: z.object({
projectSlug: z.string().trim()
@@ -44,8 +43,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
actorOrgId: req.permission.orgId,
projectSlug: req.query.projectSlug,
temporaryRange: req.body.temporaryRange,
isTemporary: req.body.isTemporary,
note: req.body.note
isTemporary: req.body.isTemporary
});
return { approval: request };
}
@@ -112,8 +110,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
secretPath: z.string().nullish(),
envId: z.string(),
enforcementLevel: z.string(),
deletedAt: z.date().nullish(),
allowedSelfApprovals: z.boolean()
deletedAt: z.date().nullish()
}),
reviewers: z
.object({

View File

@@ -11,7 +11,6 @@ import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
server.route({
@@ -49,8 +48,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
.nullable(),
path: z.string().describe(DYNAMIC_SECRETS.CREATE.path).trim().default("/").transform(removeTrailingSlash),
environmentSlug: z.string().describe(DYNAMIC_SECRETS.CREATE.environmentSlug).min(1),
name: slugSchema({ min: 1, max: 64, field: "Name" }).describe(DYNAMIC_SECRETS.CREATE.name),
metadata: ResourceMetadataSchema.optional()
name: slugSchema({ min: 1, max: 64, field: "Name" }).describe(DYNAMIC_SECRETS.CREATE.name)
}),
response: {
200: z.object({
@@ -145,8 +143,7 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
})
.nullable(),
newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional(),
metadata: ResourceMetadataSchema.optional()
newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional()
})
}),
response: {
@@ -241,7 +238,6 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
name: req.params.name,
...req.query
});
return { dynamicSecret: dynamicSecretCfg };
}
});

View File

@@ -32,7 +32,6 @@ import { registerSnapshotRouter } from "./snapshot-router";
import { registerSshCaRouter } from "./ssh-certificate-authority-router";
import { registerSshCertRouter } from "./ssh-certificate-router";
import { registerSshCertificateTemplateRouter } from "./ssh-certificate-template-router";
import { registerSshHostRouter } from "./ssh-host-router";
import { registerTrustedIpRouter } from "./trusted-ip-router";
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
@@ -83,7 +82,6 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
await sshRouter.register(registerSshCaRouter, { prefix: "/ca" });
await sshRouter.register(registerSshCertRouter, { prefix: "/certificates" });
await sshRouter.register(registerSshCertificateTemplateRouter, { prefix: "/certificate-templates" });
await sshRouter.register(registerSshHostRouter, { prefix: "/hosts" });
},
{ prefix: "/ssh" }
);

View File

@@ -2,7 +2,7 @@ import z from "zod";
import { KmsKeysSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { SymmetricKeyAlgorithm } from "@app/lib/crypto/cipher";
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -74,7 +74,7 @@ export const registerKmipSpecRouter = async (server: FastifyZodProvider) => {
schema: {
description: "KMIP endpoint for creating managed objects",
body: z.object({
algorithm: z.nativeEnum(SymmetricKeyAlgorithm)
algorithm: z.nativeEnum(SymmetricEncryption)
}),
response: {
200: KmsKeysSchema
@@ -433,7 +433,7 @@ export const registerKmipSpecRouter = async (server: FastifyZodProvider) => {
body: z.object({
key: z.string(),
name: z.string(),
algorithm: z.nativeEnum(SymmetricKeyAlgorithm)
algorithm: z.nativeEnum(SymmetricEncryption)
}),
response: {
200: z.object({

View File

@@ -61,8 +61,8 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
if (ldapConfig.groupSearchBase) {
const groupFilter = "(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))";
const groupSearchFilter = (ldapConfig.groupSearchFilter || groupFilter)
.replaceAll("{{.Username}}", user.uid)
.replaceAll("{{.UserDN}}", user.dn);
.replace(/{{\.Username}}/g, user.uid)
.replace(/{{\.UserDN}}/g, user.dn);
if (!isValidLdapFilter(groupSearchFilter)) {
throw new Error("Generated LDAP search filter is invalid.");

View File

@@ -12,7 +12,7 @@ import RedisStore from "connect-redis";
import { z } from "zod";
import { OidcConfigsSchema } from "@app/db/schemas";
import { OIDCConfigurationType, OIDCJWTSignatureAlgorithm } from "@app/ee/services/oidc/oidc-config-types";
import { OIDCConfigurationType } from "@app/ee/services/oidc/oidc-config-types";
import { getConfig } from "@app/lib/config/env";
import { authRateLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -30,8 +30,7 @@ const SanitizedOidcConfigSchema = OidcConfigsSchema.pick({
orgId: true,
isActive: true,
allowedEmailDomains: true,
manageGroupMemberships: true,
jwtSignatureAlgorithm: true
manageGroupMemberships: true
});
export const registerOidcRouter = async (server: FastifyZodProvider) => {
@@ -137,12 +136,11 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
url: "/login/error",
method: "GET",
handler: async (req, res) => {
const failureMessage = req.session.get<any>("messages");
await req.session.destroy();
return res.status(500).send({
error: "Authentication error",
details: failureMessage ?? req.query
details: req.query
});
}
});
@@ -171,8 +169,7 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
isActive: true,
orgId: true,
allowedEmailDomains: true,
manageGroupMemberships: true,
jwtSignatureAlgorithm: true
manageGroupMemberships: true
}).extend({
clientId: z.string(),
clientSecret: z.string()
@@ -227,8 +224,7 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
clientId: z.string().trim(),
clientSecret: z.string().trim(),
isActive: z.boolean(),
manageGroupMemberships: z.boolean().optional(),
jwtSignatureAlgorithm: z.nativeEnum(OIDCJWTSignatureAlgorithm).optional()
manageGroupMemberships: z.boolean().optional()
})
.partial()
.merge(z.object({ orgSlug: z.string() })),
@@ -295,11 +291,7 @@ export const registerOidcRouter = async (server: FastifyZodProvider) => {
clientSecret: z.string().trim(),
isActive: z.boolean(),
orgSlug: z.string().trim(),
manageGroupMemberships: z.boolean().optional().default(false),
jwtSignatureAlgorithm: z
.nativeEnum(OIDCJWTSignatureAlgorithm)
.optional()
.default(OIDCJWTSignatureAlgorithm.RS256)
manageGroupMemberships: z.boolean().optional().default(false)
})
.superRefine((data, ctx) => {
if (data.configurationType === OIDCConfigurationType.CUSTOM) {

View File

@@ -223,18 +223,12 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
samlConfigId: z.string().trim()
})
},
preValidation: passport.authenticate(
"saml",
{
session: false
},
async (req, res, err, user) => {
if (err) {
throw new BadRequestError({ message: `Saml authentication failed. ${err?.message}`, error: err });
}
req.passportUser = user as { isUserCompleted: boolean; providerAuthToken: string };
}
) as any, // this is due to zod type difference
preValidation: passport.authenticate("saml", {
session: false,
failureFlash: true,
failureRedirect: "/login/provider/error"
// this is due to zod type difference
}) as any,
handler: (req, res) => {
if (req.passportUser.isUserCompleted) {
return res.redirect(

View File

@@ -35,8 +35,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
.array()
.min(1, { message: "At least one approver should be provided" }),
approvals: z.number().min(1).default(1),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
allowedSelfApprovals: z.boolean().default(true)
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
}),
response: {
200: z.object({
@@ -86,8 +85,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
.nullable()
.transform((val) => (val ? removeTrailingSlash(val) : val))
.transform((val) => (val === "" ? "/" : val)),
enforcementLevel: z.nativeEnum(EnforcementLevel).optional(),
allowedSelfApprovals: z.boolean().default(true)
enforcementLevel: z.nativeEnum(EnforcementLevel).optional()
}),
response: {
200: z.object({

View File

@@ -49,8 +49,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
.array(),
secretPath: z.string().optional().nullable(),
enforcementLevel: z.string(),
deletedAt: z.date().nullish(),
allowedSelfApprovals: z.boolean()
deletedAt: z.date().nullish()
}),
committerUser: approvalRequestUser,
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
@@ -268,8 +267,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
approvers: approvalRequestUser.array(),
secretPath: z.string().optional().nullable(),
enforcementLevel: z.string(),
deletedAt: z.date().nullish(),
allowedSelfApprovals: z.boolean()
deletedAt: z.date().nullish()
}),
environment: z.string(),
statusChangedByUser: approvalRequestUser.optional(),
@@ -277,10 +275,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
reviewers: approvalRequestUser.extend({ status: z.string(), comment: z.string().optional() }).array(),
secretPath: z.string(),
commits: secretRawSchema
.omit({ _id: true, environment: true, workspace: true, type: true, version: true, secretValue: true })
.omit({ _id: true, environment: true, workspace: true, type: true, version: true })
.extend({
secretValue: z.string().optional(),
isRotatedSecret: z.boolean().optional(),
op: z.string(),
tags: SanitizedTagSchema.array().optional(),
secretMetadata: ResourceMetadataSchema.nullish(),

View File

@@ -23,8 +23,7 @@ export const registerSecretRotationProviderRouter = async (server: FastifyZodPro
title: z.string(),
image: z.string().optional(),
description: z.string().optional(),
template: z.any(),
isDeprecated: z.boolean().optional()
template: z.any()
})
.array()
})

View File

@@ -33,8 +33,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
.extend({
secretValueHidden: z.boolean(),
secretId: z.string(),
tags: SanitizedTagSchema.array(),
isRotatedSecret: z.boolean().optional()
tags: SanitizedTagSchema.array()
})
.array(),
folderVersion: z.object({ id: z.string(), name: z.string() }).array(),

View File

@@ -1,15 +1,14 @@
import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { normalizeSshPrivateKey } from "@app/ee/services/ssh/ssh-certificate-authority-fns";
import { sanitizedSshCa } from "@app/ee/services/ssh/ssh-certificate-authority-schema";
import { SshCaKeySource, SshCaStatus } from "@app/ee/services/ssh/ssh-certificate-authority-types";
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
import { SshCaStatus } from "@app/ee/services/ssh/ssh-certificate-authority-types";
import { sanitizedSshCertificateTemplate } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-schema";
import { SSH_CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
export const registerSshCaRouter = async (server: FastifyZodProvider) => {
server.route({
@@ -21,34 +20,14 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Create SSH CA",
body: z
.object({
projectId: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.projectId),
friendlyName: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.friendlyName),
keyAlgorithm: z
.nativeEnum(SshCertKeyAlgorithm)
.default(SshCertKeyAlgorithm.ED25519)
.describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.keyAlgorithm),
publicKey: z.string().trim().optional().describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.publicKey),
privateKey: z
.string()
.trim()
.optional()
.transform((val) => (val ? normalizeSshPrivateKey(val) : undefined))
.describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.privateKey),
keySource: z
.nativeEnum(SshCaKeySource)
.default(SshCaKeySource.INTERNAL)
.describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.keySource)
})
.refine((data) => data.keySource === SshCaKeySource.INTERNAL || (!!data.publicKey && !!data.privateKey), {
message: "publicKey and privateKey are required when keySource is external",
path: ["publicKey"]
})
.refine((data) => data.keySource === SshCaKeySource.EXTERNAL || !!data.keyAlgorithm, {
message: "keyAlgorithm is required when keySource is internal",
path: ["keyAlgorithm"]
}),
body: z.object({
projectId: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.projectId),
friendlyName: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.friendlyName),
keyAlgorithm: z
.nativeEnum(CertKeyAlgorithm)
.default(CertKeyAlgorithm.RSA_2048)
.describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.keyAlgorithm)
}),
response: {
200: z.object({
ca: sanitizedSshCa.extend({

View File

@@ -2,14 +2,12 @@ import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
import { SSH_CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms";
import { writeLimit } from "@app/server/config/rateLimiter";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
export const registerSshCertRouter = async (server: FastifyZodProvider) => {
server.route({
@@ -75,16 +73,6 @@ export const registerSshCertRouter = async (server: FastifyZodProvider) => {
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.SignSshKey,
distinctId: getTelemetryDistinctId(req),
properties: {
certificateTemplateId: req.body.certificateTemplateId,
principals: req.body.principals,
...req.auditLogInfo
}
});
return {
serialNumber,
signedKey: signedPublicKey
@@ -108,8 +96,8 @@ export const registerSshCertRouter = async (server: FastifyZodProvider) => {
.min(1)
.describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.certificateTemplateId),
keyAlgorithm: z
.nativeEnum(SshCertKeyAlgorithm)
.default(SshCertKeyAlgorithm.ED25519)
.nativeEnum(CertKeyAlgorithm)
.default(CertKeyAlgorithm.RSA_2048)
.describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.keyAlgorithm),
certType: z
.nativeEnum(SshCertType)
@@ -133,7 +121,7 @@ export const registerSshCertRouter = async (server: FastifyZodProvider) => {
privateKey: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.privateKey),
publicKey: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.publicKey),
keyAlgorithm: z
.nativeEnum(SshCertKeyAlgorithm)
.nativeEnum(CertKeyAlgorithm)
.describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.keyAlgorithm)
})
}
@@ -164,16 +152,6 @@ export const registerSshCertRouter = async (server: FastifyZodProvider) => {
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.IssueSshCreds,
distinctId: getTelemetryDistinctId(req),
properties: {
certificateTemplateId: req.body.certificateTemplateId,
principals: req.body.principals,
...req.auditLogInfo
}
});
return {
serialNumber,
signedKey: signedPublicKey,

View File

@@ -92,8 +92,8 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
allowHostCertificates: z.boolean().describe(SSH_CERTIFICATE_TEMPLATES.CREATE.allowHostCertificates),
allowCustomKeyIds: z.boolean().describe(SSH_CERTIFICATE_TEMPLATES.CREATE.allowCustomKeyIds)
})
.refine((data) => ms(data.maxTTL) >= ms(data.ttl), {
message: "Max TLL must be greater than or equal to TTL",
.refine((data) => ms(data.maxTTL) > ms(data.ttl), {
message: "Max TLL must be greater than TTL",
path: ["maxTTL"]
}),
response: {

View File

@@ -1,444 +0,0 @@
import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
import { loginMappingSchema, sanitizedSshHost } from "@app/ee/services/ssh-host/ssh-host-schema";
import { isValidHostname } from "@app/ee/services/ssh-host/ssh-host-validators";
import { SSH_HOSTS } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms";
import { publicSshCaLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
export const registerSshHostRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
schema: {
response: {
200: z.array(
sanitizedSshHost.extend({
loginMappings: z.array(loginMappingSchema)
})
)
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const hosts = await server.services.sshHost.listSshHosts({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
return hosts;
}
});
server.route({
method: "GET",
url: "/:sshHostId",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
sshHostId: z.string().describe(SSH_HOSTS.GET.sshHostId)
}),
response: {
200: sanitizedSshHost.extend({
loginMappings: z.array(loginMappingSchema)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const host = await server.services.sshHost.getSshHost({
sshHostId: req.params.sshHostId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: host.projectId,
event: {
type: EventType.GET_SSH_HOST,
metadata: {
sshHostId: host.id,
hostname: host.hostname
}
}
});
return host;
}
});
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
description: "Add an SSH Host",
body: z.object({
projectId: z.string().describe(SSH_HOSTS.CREATE.projectId),
hostname: z
.string()
.min(1)
.refine((v) => isValidHostname(v), {
message: "Hostname must be a valid hostname"
})
.describe(SSH_HOSTS.CREATE.hostname),
userCertTtl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.default("8h")
.describe(SSH_HOSTS.CREATE.userCertTtl),
hostCertTtl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.default("1y")
.describe(SSH_HOSTS.CREATE.hostCertTtl),
loginMappings: z.array(loginMappingSchema).default([]).describe(SSH_HOSTS.CREATE.loginMappings),
userSshCaId: z.string().describe(SSH_HOSTS.CREATE.userSshCaId).optional(),
hostSshCaId: z.string().describe(SSH_HOSTS.CREATE.hostSshCaId).optional()
}),
response: {
200: sanitizedSshHost.extend({
loginMappings: z.array(loginMappingSchema)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const host = await server.services.sshHost.createSshHost({
...req.body,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: host.projectId,
event: {
type: EventType.CREATE_SSH_HOST,
metadata: {
sshHostId: host.id,
hostname: host.hostname,
userCertTtl: host.userCertTtl,
hostCertTtl: host.hostCertTtl,
loginMappings: host.loginMappings,
userSshCaId: host.userSshCaId,
hostSshCaId: host.hostSshCaId
}
}
});
return host;
}
});
server.route({
method: "PATCH",
url: "/:sshHostId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Update SSH Host",
params: z.object({
sshHostId: z.string().trim().describe(SSH_HOSTS.UPDATE.sshHostId)
}),
body: z.object({
hostname: z
.string()
.min(1)
.refine((v) => isValidHostname(v), {
message: "Hostname must be a valid hostname"
})
.optional()
.describe(SSH_HOSTS.UPDATE.hostname),
userCertTtl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.optional()
.describe(SSH_HOSTS.UPDATE.userCertTtl),
hostCertTtl: z
.string()
.refine((val) => ms(val) > 0, "TTL must be a positive number")
.optional()
.describe(SSH_HOSTS.UPDATE.hostCertTtl),
loginMappings: z.array(loginMappingSchema).optional().describe(SSH_HOSTS.UPDATE.loginMappings)
}),
response: {
200: sanitizedSshHost.extend({
loginMappings: z.array(loginMappingSchema)
})
}
},
handler: async (req) => {
const host = await server.services.sshHost.updateSshHost({
sshHostId: req.params.sshHostId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: host.projectId,
event: {
type: EventType.UPDATE_SSH_HOST,
metadata: {
sshHostId: host.id,
hostname: host.hostname,
userCertTtl: host.userCertTtl,
hostCertTtl: host.hostCertTtl,
loginMappings: host.loginMappings,
userSshCaId: host.userSshCaId,
hostSshCaId: host.hostSshCaId
}
}
});
return host;
}
});
server.route({
method: "DELETE",
url: "/:sshHostId",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
sshHostId: z.string().describe(SSH_HOSTS.DELETE.sshHostId)
}),
response: {
200: sanitizedSshHost.extend({
loginMappings: z.array(loginMappingSchema)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const host = await server.services.sshHost.deleteSshHost({
sshHostId: req.params.sshHostId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: host.projectId,
event: {
type: EventType.DELETE_SSH_HOST,
metadata: {
sshHostId: host.id,
hostname: host.hostname
}
}
});
return host;
}
});
server.route({
method: "POST",
url: "/:sshHostId/issue-user-cert",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
description: "Issue SSH certificate for user",
params: z.object({
sshHostId: z.string().describe(SSH_HOSTS.ISSUE_SSH_CREDENTIALS.sshHostId)
}),
body: z.object({
loginUser: z.string().describe(SSH_HOSTS.ISSUE_SSH_CREDENTIALS.loginUser)
}),
response: {
200: z.object({
serialNumber: z.string().describe(SSH_HOSTS.ISSUE_SSH_CREDENTIALS.serialNumber),
signedKey: z.string().describe(SSH_HOSTS.ISSUE_SSH_CREDENTIALS.signedKey),
privateKey: z.string().describe(SSH_HOSTS.ISSUE_SSH_CREDENTIALS.privateKey),
publicKey: z.string().describe(SSH_HOSTS.ISSUE_SSH_CREDENTIALS.publicKey),
keyAlgorithm: z.nativeEnum(SshCertKeyAlgorithm).describe(SSH_HOSTS.ISSUE_SSH_CREDENTIALS.keyAlgorithm)
})
}
},
handler: async (req) => {
const { serialNumber, signedPublicKey, privateKey, publicKey, keyAlgorithm, host, principals } =
await server.services.sshHost.issueSshHostUserCert({
sshHostId: req.params.sshHostId,
loginUser: req.body.loginUser,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.ISSUE_SSH_HOST_USER_CERT,
metadata: {
sshHostId: req.params.sshHostId,
hostname: host.hostname,
loginUser: req.body.loginUser,
principals,
ttl: host.userCertTtl
}
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.IssueSshHostUserCert,
distinctId: getTelemetryDistinctId(req),
properties: {
sshHostId: req.params.sshHostId,
hostname: host.hostname,
principals,
...req.auditLogInfo
}
});
return {
serialNumber,
signedKey: signedPublicKey,
privateKey,
publicKey,
keyAlgorithm
};
}
});
server.route({
method: "POST",
url: "/:sshHostId/issue-host-cert",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Issue SSH certificate for host",
params: z.object({
sshHostId: z.string().describe(SSH_HOSTS.ISSUE_HOST_CERT.sshHostId)
}),
body: z.object({
publicKey: z.string().describe(SSH_HOSTS.ISSUE_HOST_CERT.publicKey)
}),
response: {
200: z.object({
serialNumber: z.string().describe(SSH_HOSTS.ISSUE_HOST_CERT.serialNumber),
signedKey: z.string().describe(SSH_HOSTS.ISSUE_HOST_CERT.signedKey)
})
}
},
handler: async (req) => {
const { host, principals, serialNumber, signedPublicKey } = await server.services.sshHost.issueSshHostHostCert({
sshHostId: req.params.sshHostId,
publicKey: req.body.publicKey,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.ISSUE_SSH_HOST_HOST_CERT,
metadata: {
sshHostId: req.params.sshHostId,
hostname: host.hostname,
principals,
serialNumber,
ttl: host.hostCertTtl
}
}
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.IssueSshHostHostCert,
distinctId: getTelemetryDistinctId(req),
properties: {
sshHostId: req.params.sshHostId,
hostname: host.hostname,
principals,
...req.auditLogInfo
}
});
return {
serialNumber,
signedKey: signedPublicKey
};
}
});
server.route({
method: "GET",
url: "/:sshHostId/user-ca-public-key",
config: {
rateLimit: publicSshCaLimit
},
schema: {
description: "Get public key of the user SSH CA linked to the host",
params: z.object({
sshHostId: z.string().trim().describe(SSH_HOSTS.GET_USER_CA_PUBLIC_KEY.sshHostId)
}),
response: {
200: z.string().describe(SSH_HOSTS.GET_USER_CA_PUBLIC_KEY.publicKey)
}
},
handler: async (req) => {
const publicKey = await server.services.sshHost.getSshHostUserCaPk(req.params.sshHostId);
return publicKey;
}
});
server.route({
method: "GET",
url: "/:sshHostId/host-ca-public-key",
config: {
rateLimit: publicSshCaLimit
},
schema: {
description: "Get public key of the host SSH CA linked to the host",
params: z.object({
sshHostId: z.string().trim().describe(SSH_HOSTS.GET_HOST_CA_PUBLIC_KEY.sshHostId)
}),
response: {
200: z.string().describe(SSH_HOSTS.GET_HOST_CA_PUBLIC_KEY.publicKey)
}
},
handler: async (req) => {
const publicKey = await server.services.sshHost.getSshHostHostCaPk(req.params.sshHostId);
return publicKey;
}
});
};

View File

@@ -1,8 +1,3 @@
import {
registerSecretRotationV2Router,
SECRET_ROTATION_REGISTER_ROUTER_MAP
} from "@app/ee/routes/v2/secret-rotation-v2-routers";
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
import { registerProjectRoleRouter } from "./project-role-router";
@@ -18,17 +13,4 @@ export const registerV2EERoutes = async (server: FastifyZodProvider) => {
await server.register(registerIdentityProjectAdditionalPrivilegeRouter, {
prefix: "/identity-project-additional-privilege"
});
await server.register(
async (secretRotationV2Router) => {
// register generic secret rotation endpoints
await secretRotationV2Router.register(registerSecretRotationV2Router);
// register service specific secret rotation endpoints (secret-rotations/postgres-credentials, etc.)
for await (const [type, router] of Object.entries(SECRET_ROTATION_REGISTER_ROUTER_MAP)) {
await secretRotationV2Router.register(router, { prefix: `/${type}` });
}
},
{ prefix: "/secret-rotations" }
);
};

View File

@@ -1,19 +0,0 @@
import {
Auth0ClientSecretRotationGeneratedCredentialsSchema,
Auth0ClientSecretRotationSchema,
CreateAuth0ClientSecretRotationSchema,
UpdateAuth0ClientSecretRotationSchema
} from "@app/ee/services/secret-rotation-v2/auth0-client-secret";
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
export const registerAuth0ClientSecretRotationRouter = async (server: FastifyZodProvider) =>
registerSecretRotationEndpoints({
type: SecretRotation.Auth0ClientSecret,
server,
responseSchema: Auth0ClientSecretRotationSchema,
createSchema: CreateAuth0ClientSecretRotationSchema,
updateSchema: UpdateAuth0ClientSecretRotationSchema,
generatedCredentialsSchema: Auth0ClientSecretRotationGeneratedCredentialsSchema
});

View File

@@ -1,16 +0,0 @@
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
import { registerAuth0ClientSecretRotationRouter } from "./auth0-client-secret-rotation-router";
import { registerMsSqlCredentialsRotationRouter } from "./mssql-credentials-rotation-router";
import { registerPostgresCredentialsRotationRouter } from "./postgres-credentials-rotation-router";
export * from "./secret-rotation-v2-router";
export const SECRET_ROTATION_REGISTER_ROUTER_MAP: Record<
SecretRotation,
(server: FastifyZodProvider) => Promise<void>
> = {
[SecretRotation.PostgresCredentials]: registerPostgresCredentialsRotationRouter,
[SecretRotation.MsSqlCredentials]: registerMsSqlCredentialsRotationRouter,
[SecretRotation.Auth0ClientSecret]: registerAuth0ClientSecretRotationRouter
};

View File

@@ -1,19 +0,0 @@
import {
CreateMsSqlCredentialsRotationSchema,
MsSqlCredentialsRotationSchema,
UpdateMsSqlCredentialsRotationSchema
} from "@app/ee/services/secret-rotation-v2/mssql-credentials";
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
import { SqlCredentialsRotationGeneratedCredentialsSchema } from "@app/ee/services/secret-rotation-v2/shared/sql-credentials";
import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
export const registerMsSqlCredentialsRotationRouter = async (server: FastifyZodProvider) =>
registerSecretRotationEndpoints({
type: SecretRotation.MsSqlCredentials,
server,
responseSchema: MsSqlCredentialsRotationSchema,
createSchema: CreateMsSqlCredentialsRotationSchema,
updateSchema: UpdateMsSqlCredentialsRotationSchema,
generatedCredentialsSchema: SqlCredentialsRotationGeneratedCredentialsSchema
});

View File

@@ -1,19 +0,0 @@
import {
CreatePostgresCredentialsRotationSchema,
PostgresCredentialsRotationSchema,
UpdatePostgresCredentialsRotationSchema
} from "@app/ee/services/secret-rotation-v2/postgres-credentials";
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
import { SqlCredentialsRotationGeneratedCredentialsSchema } from "@app/ee/services/secret-rotation-v2/shared/sql-credentials";
import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
export const registerPostgresCredentialsRotationRouter = async (server: FastifyZodProvider) =>
registerSecretRotationEndpoints({
type: SecretRotation.PostgresCredentials,
server,
responseSchema: PostgresCredentialsRotationSchema,
createSchema: CreatePostgresCredentialsRotationSchema,
updateSchema: UpdatePostgresCredentialsRotationSchema,
generatedCredentialsSchema: SqlCredentialsRotationGeneratedCredentialsSchema
});

View File

@@ -1,429 +0,0 @@
import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
import { SECRET_ROTATION_NAME_MAP } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-maps";
import {
TRotateAtUtc,
TSecretRotationV2,
TSecretRotationV2GeneratedCredentials,
TSecretRotationV2Input
} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types";
import { SecretRotations } from "@app/lib/api-docs";
import { startsWithVowel } from "@app/lib/fn";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSecretRotationEndpoints = <
T extends TSecretRotationV2,
I extends TSecretRotationV2Input,
C extends TSecretRotationV2GeneratedCredentials
>({
server,
type,
createSchema,
updateSchema,
responseSchema,
generatedCredentialsSchema
}: {
type: SecretRotation;
server: FastifyZodProvider;
createSchema: z.ZodType<{
name: string;
environment: string;
secretPath: string;
projectId: string;
connectionId: string;
parameters: I["parameters"];
secretsMapping: I["secretsMapping"];
description?: string | null;
isAutoRotationEnabled?: boolean;
rotationInterval: number;
rotateAtUtc?: TRotateAtUtc;
}>;
updateSchema: z.ZodType<{
connectionId?: string;
name?: string;
environment?: string;
secretPath?: string;
parameters?: I["parameters"];
secretsMapping?: I["secretsMapping"];
description?: string | null;
isAutoRotationEnabled?: boolean;
rotationInterval?: number;
rotateAtUtc?: TRotateAtUtc;
}>;
responseSchema: z.ZodTypeAny;
generatedCredentialsSchema: z.ZodTypeAny;
}) => {
const rotationType = SECRET_ROTATION_NAME_MAP[type];
server.route({
method: "GET",
url: `/`,
config: {
rateLimit: readLimit
},
schema: {
description: `List the ${rotationType} Rotations for the specified project.`,
querystring: z.object({
projectId: z.string().trim().min(1, "Project ID required").describe(SecretRotations.LIST(type).projectId)
}),
response: {
200: z.object({ secretRotations: responseSchema.array() })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
query: { projectId }
} = req;
const secretRotations = (await server.services.secretRotationV2.listSecretRotationsByProjectId(
{ projectId, type },
req.permission
)) as T[];
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.GET_SECRET_ROTATIONS,
metadata: {
type,
count: secretRotations.length,
rotationIds: secretRotations.map((rotation) => rotation.id)
}
}
});
return { secretRotations };
}
});
server.route({
method: "GET",
url: "/:rotationId",
config: {
rateLimit: readLimit
},
schema: {
description: `Get the specified ${rotationType} Rotation by ID.`,
params: z.object({
rotationId: z.string().uuid().describe(SecretRotations.GET_BY_ID(type).rotationId)
}),
response: {
200: z.object({ secretRotation: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { rotationId } = req.params;
const secretRotation = (await server.services.secretRotationV2.findSecretRotationById(
{ rotationId, type },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: secretRotation.projectId,
event: {
type: EventType.GET_SECRET_ROTATION,
metadata: {
rotationId,
type,
secretPath: secretRotation.folder.path,
environment: secretRotation.environment.slug
}
}
});
return { secretRotation };
}
});
server.route({
method: "GET",
url: `/rotation-name/:rotationName`,
config: {
rateLimit: readLimit
},
schema: {
description: `Get the specified ${rotationType} Rotation by name, secret path, environment and project ID.`,
params: z.object({
rotationName: z
.string()
.trim()
.min(1, "Rotation name required")
.describe(SecretRotations.GET_BY_NAME(type).rotationName)
}),
querystring: z.object({
projectId: z
.string()
.trim()
.min(1, "Project ID required")
.describe(SecretRotations.GET_BY_NAME(type).projectId),
secretPath: z
.string()
.trim()
.min(1, "Secret path required")
.describe(SecretRotations.GET_BY_NAME(type).secretPath),
environment: z
.string()
.trim()
.min(1, "Environment required")
.describe(SecretRotations.GET_BY_NAME(type).environment)
}),
response: {
200: z.object({ secretRotation: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { rotationName } = req.params;
const { projectId, secretPath, environment } = req.query;
const secretRotation = (await server.services.secretRotationV2.findSecretRotationByName(
{ rotationName, projectId, type, secretPath, environment },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.GET_SECRET_ROTATION,
metadata: {
rotationId: secretRotation.id,
type,
secretPath,
environment
}
}
});
return { secretRotation };
}
});
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
description: `Create ${
startsWithVowel(rotationType) ? "an" : "a"
} ${rotationType} Rotation for the specified project.`,
body: createSchema,
response: {
200: z.object({ secretRotation: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const secretRotation = (await server.services.secretRotationV2.createSecretRotation(
{ ...req.body, type },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: secretRotation.projectId,
event: {
type: EventType.CREATE_SECRET_ROTATION,
metadata: {
rotationId: secretRotation.id,
type,
...req.body
}
}
});
return { secretRotation };
}
});
server.route({
method: "PATCH",
url: "/:rotationId",
config: {
rateLimit: writeLimit
},
schema: {
description: `Update the specified ${rotationType} Rotation.`,
params: z.object({
rotationId: z.string().uuid().describe(SecretRotations.UPDATE(type).rotationId)
}),
body: updateSchema,
response: {
200: z.object({ secretRotation: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { rotationId } = req.params;
const secretRotation = (await server.services.secretRotationV2.updateSecretRotation(
{ ...req.body, rotationId, type },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: secretRotation.projectId,
event: {
type: EventType.UPDATE_SECRET_ROTATION,
metadata: {
rotationId,
type,
...req.body
}
}
});
return { secretRotation };
}
});
server.route({
method: "DELETE",
url: `/:rotationId`,
config: {
rateLimit: writeLimit
},
schema: {
description: `Delete the specified ${rotationType} Rotation.`,
params: z.object({
rotationId: z.string().uuid().describe(SecretRotations.DELETE(type).rotationId)
}),
querystring: z.object({
deleteSecrets: z
.enum(["true", "false"])
.transform((value) => value === "true")
.describe(SecretRotations.DELETE(type).deleteSecrets),
revokeGeneratedCredentials: z
.enum(["true", "false"])
.transform((value) => value === "true")
.describe(SecretRotations.DELETE(type).revokeGeneratedCredentials)
}),
response: {
200: z.object({ secretRotation: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { rotationId } = req.params;
const { deleteSecrets, revokeGeneratedCredentials } = req.query;
const secretRotation = (await server.services.secretRotationV2.deleteSecretRotation(
{ type, rotationId, deleteSecrets, revokeGeneratedCredentials },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: secretRotation.projectId,
event: {
type: EventType.DELETE_SECRET_ROTATION,
metadata: {
type,
rotationId,
deleteSecrets,
revokeGeneratedCredentials
}
}
});
return { secretRotation };
}
});
server.route({
method: "GET",
url: "/:rotationId/generated-credentials",
config: {
rateLimit: readLimit
},
schema: {
description: `Get the generated credentials for the specified ${rotationType} Rotation.`,
params: z.object({
rotationId: z.string().uuid().describe(SecretRotations.GET_GENERATED_CREDENTIALS_BY_ID(type).rotationId)
}),
response: {
200: z.object({
generatedCredentials: generatedCredentialsSchema,
activeIndex: z.number(),
rotationId: z.string().uuid(),
type: z.literal(type)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { rotationId } = req.params;
const {
generatedCredentials,
secretRotation: { activeIndex, projectId, folder, environment }
} = await server.services.secretRotationV2.findSecretRotationGeneratedCredentialsById(
{
rotationId,
type
},
req.permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.GET_SECRET_ROTATION_GENERATED_CREDENTIALS,
metadata: {
type,
rotationId,
secretPath: folder.path,
environment: environment.slug
}
}
});
return { generatedCredentials: generatedCredentials as C, activeIndex, rotationId, type };
}
});
server.route({
method: "POST",
url: "/:rotationId/rotate-secrets",
config: {
rateLimit: writeLimit
},
schema: {
description: `Rotate the generated credentials for the specified ${rotationType} Rotation.`,
params: z.object({
rotationId: z.string().uuid().describe(SecretRotations.ROTATE(type).rotationId)
}),
response: {
200: z.object({ secretRotation: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { rotationId } = req.params;
const secretRotation = (await server.services.secretRotationV2.rotateSecretRotation(
{
rotationId,
type,
auditLogInfo: req.auditLogInfo
},
req.permission
)) as T;
return { secretRotation };
}
});
};

View File

@@ -1,83 +0,0 @@
import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { Auth0ClientSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/auth0-client-secret";
import { MsSqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mssql-credentials";
import { PostgresCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/postgres-credentials";
import { SecretRotationV2Schema } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema";
import { SecretRotations } from "@app/lib/api-docs";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
const SecretRotationV2OptionsSchema = z.discriminatedUnion("type", [
PostgresCredentialsRotationListItemSchema,
MsSqlCredentialsRotationListItemSchema,
Auth0ClientSecretRotationListItemSchema
]);
export const registerSecretRotationV2Router = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/options",
config: {
rateLimit: readLimit
},
schema: {
description: "List the available Secret Rotation Options.",
response: {
200: z.object({
secretRotationOptions: SecretRotationV2OptionsSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: () => {
const secretRotationOptions = server.services.secretRotationV2.listSecretRotationOptions();
return { secretRotationOptions };
}
});
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
schema: {
description: "List all the Secret Rotations for the specified project.",
querystring: z.object({
projectId: z.string().trim().min(1, "Project ID required").describe(SecretRotations.LIST().projectId)
}),
response: {
200: z.object({ secretRotations: SecretRotationV2Schema.array() })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
query: { projectId },
permission
} = req;
const secretRotations = await server.services.secretRotationV2.listSecretRotationsByProjectId(
{ projectId },
permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.GET_SECRET_ROTATIONS,
metadata: {
rotationIds: secretRotations.map((sync) => sync.id),
count: secretRotations.length
}
}
});
return { secretRotations };
}
});
};

View File

@@ -65,8 +65,7 @@ export const accessApprovalPolicyServiceFactory = ({
approvers,
projectSlug,
environment,
enforcementLevel,
allowedSelfApprovals
enforcementLevel
}: TCreateAccessApprovalPolicy) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
@@ -154,8 +153,7 @@ export const accessApprovalPolicyServiceFactory = ({
approvals,
secretPath,
name,
enforcementLevel,
allowedSelfApprovals
enforcementLevel
},
tx
);
@@ -218,8 +216,7 @@ export const accessApprovalPolicyServiceFactory = ({
actorOrgId,
actorAuthMethod,
approvals,
enforcementLevel,
allowedSelfApprovals
enforcementLevel
}: TUpdateAccessApprovalPolicy) => {
const groupApprovers = approvers
.filter((approver) => approver.type === ApproverType.Group)
@@ -265,8 +262,7 @@ export const accessApprovalPolicyServiceFactory = ({
approvals,
secretPath,
name,
enforcementLevel,
allowedSelfApprovals
enforcementLevel
},
tx
);

View File

@@ -26,7 +26,6 @@ export type TCreateAccessApprovalPolicy = {
projectSlug: string;
name: string;
enforcementLevel: EnforcementLevel;
allowedSelfApprovals: boolean;
} & Omit<TProjectPermission, "projectId">;
export type TUpdateAccessApprovalPolicy = {
@@ -36,7 +35,6 @@ export type TUpdateAccessApprovalPolicy = {
secretPath?: string;
name?: string;
enforcementLevel?: EnforcementLevel;
allowedSelfApprovals: boolean;
} & Omit<TProjectPermission, "projectId">;
export type TDeleteAccessApprovalPolicy = {

View File

@@ -61,7 +61,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
db.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
db.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
db.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"),
db.ref("allowedSelfApprovals").withSchema(TableName.AccessApprovalPolicy).as("policyAllowedSelfApprovals"),
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId"),
db.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt")
)
@@ -120,7 +119,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
approvals: doc.policyApprovals,
secretPath: doc.policySecretPath,
enforcementLevel: doc.policyEnforcementLevel,
allowedSelfApprovals: doc.policyAllowedSelfApprovals,
envId: doc.policyEnvId,
deletedAt: doc.policyDeletedAt
},
@@ -256,7 +254,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
tx.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
tx.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"),
tx.ref("allowedSelfApprovals").withSchema(TableName.AccessApprovalPolicy).as("policyAllowedSelfApprovals"),
tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
tx.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt")
);
@@ -278,7 +275,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
approvals: el.policyApprovals,
secretPath: el.policySecretPath,
enforcementLevel: el.policyEnforcementLevel,
allowedSelfApprovals: el.policyAllowedSelfApprovals,
deletedAt: el.policyDeletedAt
},
requestedByUser: {

View File

@@ -94,8 +94,7 @@ export const accessApprovalRequestServiceFactory = ({
actor,
actorOrgId,
actorAuthMethod,
projectSlug,
note
projectSlug
}: TCreateAccessApprovalRequestDTO) => {
const cfg = getConfig();
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
@@ -210,8 +209,7 @@ export const accessApprovalRequestServiceFactory = ({
requestedByUserId: actorId,
temporaryRange: temporaryRange || null,
permissions: JSON.stringify(requestedPermissions),
isTemporary,
note: note || null
isTemporary
},
tx
);
@@ -234,8 +232,7 @@ export const accessApprovalRequestServiceFactory = ({
secretPath,
environment: envSlug,
permissions: accessTypes,
approvalUrl,
note
approvalUrl
}
}
});
@@ -255,8 +252,7 @@ export const accessApprovalRequestServiceFactory = ({
secretPath,
environment: envSlug,
permissions: accessTypes,
approvalUrl,
note
approvalUrl
},
template: SmtpTemplates.AccessApprovalRequest
});
@@ -324,11 +320,6 @@ export const accessApprovalRequestServiceFactory = ({
message: "The policy associated with this access request has been deleted."
});
}
if (!policy.allowedSelfApprovals && actorId === accessApprovalRequest.requestedByUserId) {
throw new BadRequestError({
message: "Failed to review access approval request. Users are not authorized to review their own request."
});
}
const { membership, hasRole } = await permissionService.getProjectPermission({
actor,

View File

@@ -24,7 +24,6 @@ export type TCreateAccessApprovalRequestDTO = {
permissions: unknown;
isTemporary: boolean;
temporaryRange?: string;
note?: string;
} & Omit<TProjectPermission, "projectId">;
export type TListApprovalRequestsDTO = {

View File

@@ -45,6 +45,7 @@ export const auditLogStreamServiceFactory = ({
}: TCreateAuditLogStreamDTO) => {
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID attached to authentication token" });
const appCfg = getConfig();
const plan = await licenseService.getPlan(actorOrgId);
if (!plan.auditLogStreams) {
throw new BadRequestError({
@@ -61,8 +62,9 @@ export const auditLogStreamServiceFactory = ({
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
const appCfg = getConfig();
if (appCfg.isCloud) await blockLocalAndPrivateIpAddresses(url);
if (appCfg.isCloud) {
blockLocalAndPrivateIpAddresses(url);
}
const totalStreams = await auditLogStreamDAL.find({ orgId: actorOrgId });
if (totalStreams.length >= plan.auditLogStreamLimit) {
@@ -133,8 +135,9 @@ export const auditLogStreamServiceFactory = ({
const { orgId } = logStream;
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
const appCfg = getConfig();
if (url && appCfg.isCloud) await blockLocalAndPrivateIpAddresses(url);
if (url && appCfg.isCloud) blockLocalAndPrivateIpAddresses(url);
// testing connection first
const streamHeaders: RawAxiosRequestHeaders = { "Content-Type": "application/json" };

View File

@@ -9,14 +9,13 @@ import { logger } from "@app/lib/logger";
import { QueueName } from "@app/queue";
import { ActorType } from "@app/services/auth/auth-type";
import { EventType, filterableSecretEvents } from "./audit-log-types";
import { EventType } from "./audit-log-types";
export type TAuditLogDALFactory = ReturnType<typeof auditLogDALFactory>;
type TFindQuery = {
actor?: string;
projectId?: string;
environment?: string;
orgId?: string;
eventType?: string;
startDate?: string;
@@ -33,7 +32,6 @@ export const auditLogDALFactory = (db: TDbClient) => {
{
orgId,
projectId,
environment,
userAgentType,
startDate,
endDate,
@@ -42,14 +40,12 @@ export const auditLogDALFactory = (db: TDbClient) => {
actorId,
actorType,
secretPath,
secretKey,
eventType,
eventMetadata
}: Omit<TFindQuery, "actor" | "eventType"> & {
actorId?: string;
actorType?: ActorType;
secretPath?: string;
secretKey?: string;
eventType?: EventType[];
eventMetadata?: Record<string, string>;
},
@@ -94,29 +90,8 @@ export const auditLogDALFactory = (db: TDbClient) => {
});
}
const eventIsSecretType = !eventType?.length || eventType.some((event) => filterableSecretEvents.includes(event));
// We only want to filter for environment/secretPath/secretKey if the user is either checking for all event types
// ? Note(daniel): use the `eventMetadata" @> ?::jsonb` approach to properly use our GIN index
if (projectId && eventIsSecretType) {
if (environment || secretPath) {
// Handle both environment and secret path together to only use the GIN index once
void sqlQuery.whereRaw(`"eventMetadata" @> ?::jsonb`, [
JSON.stringify({
...(environment && { environment }),
...(secretPath && { secretPath })
})
]);
}
// Handle secret key separately to include the OR condition
if (secretKey) {
void sqlQuery.whereRaw(
`("eventMetadata" @> ?::jsonb
OR "eventMetadata"->'secrets' @> ?::jsonb)`,
[JSON.stringify({ secretKey }), JSON.stringify([{ secretKey }])]
);
}
if (projectId && secretPath) {
void sqlQuery.whereRaw(`"eventMetadata" @> jsonb_build_object('secretPath', ?::text)`, [secretPath]);
}
// Filter by actor type

View File

@@ -63,8 +63,6 @@ export const auditLogServiceFactory = ({
actorType: filter.actorType,
eventMetadata: filter.eventMetadata,
secretPath: filter.secretPath,
secretKey: filter.secretKey,
environment: filter.environment,
...(filter.projectId ? { projectId: filter.projectId } : { orgId: actorOrgId })
});

View File

@@ -2,18 +2,9 @@ import {
TCreateProjectTemplateDTO,
TUpdateProjectTemplateDTO
} from "@app/ee/services/project-template/project-template-types";
import { SecretRotation, SecretRotationStatus } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
import {
TCreateSecretRotationV2DTO,
TDeleteSecretRotationV2DTO,
TSecretRotationV2Raw,
TUpdateSecretRotationV2DTO
} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types";
import { SshCaStatus, SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
import { SshCertTemplateStatus } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-types";
import { SymmetricKeyAlgorithm } from "@app/lib/crypto/cipher";
import { AsymmetricKeyAlgorithm, SigningAlgorithm } from "@app/lib/crypto/sign/types";
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
import { TProjectPermission } from "@app/lib/types";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
@@ -42,11 +33,9 @@ export type TListProjectAuditLogDTO = {
endDate?: string;
startDate?: string;
projectId?: string;
environment?: string;
auditLogActorId?: string;
actorType?: ActorType;
secretPath?: string;
secretKey?: string;
eventMetadata?: Record<string, string>;
};
} & Omit<TProjectPermission, "projectId">;
@@ -65,8 +54,6 @@ export type TCreateAuditLogDTO = {
projectId?: string;
} & BaseAuthData;
export type AuditLogInfo = Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">;
interface BaseAuthData {
ipAddress?: string;
userAgent?: string;
@@ -191,12 +178,6 @@ export enum EventType {
UPDATE_SSH_CERTIFICATE_TEMPLATE = "update-ssh-certificate-template",
DELETE_SSH_CERTIFICATE_TEMPLATE = "delete-ssh-certificate-template",
GET_SSH_CERTIFICATE_TEMPLATE = "get-ssh-certificate-template",
CREATE_SSH_HOST = "create-ssh-host",
UPDATE_SSH_HOST = "update-ssh-host",
DELETE_SSH_HOST = "delete-ssh-host",
GET_SSH_HOST = "get-ssh-host",
ISSUE_SSH_HOST_USER_CERT = "issue-ssh-host-user-cert",
ISSUE_SSH_HOST_HOST_CERT = "issue-ssh-host-host-cert",
CREATE_CA = "create-certificate-authority",
GET_CA = "get-certificate-authority",
UPDATE_CA = "update-certificate-authority",
@@ -256,11 +237,6 @@ export enum EventType {
GET_CMEK = "get-cmek",
CMEK_ENCRYPT = "cmek-encrypt",
CMEK_DECRYPT = "cmek-decrypt",
CMEK_SIGN = "cmek-sign",
CMEK_VERIFY = "cmek-verify",
CMEK_LIST_SIGNING_ALGORITHMS = "cmek-list-signing-algorithms",
CMEK_GET_PUBLIC_KEY = "cmek-get-public-key",
UPDATE_EXTERNAL_GROUP_ORG_ROLE_MAPPINGS = "update-external-group-org-role-mapping",
GET_EXTERNAL_GROUP_ORG_ROLE_MAPPINGS = "get-external-group-org-role-mapping",
GET_PROJECT_TEMPLATES = "get-project-templates",
@@ -307,29 +283,9 @@ export enum EventType {
KMIP_OPERATION_ACTIVATE = "kmip-operation-activate",
KMIP_OPERATION_REVOKE = "kmip-operation-revoke",
KMIP_OPERATION_LOCATE = "kmip-operation-locate",
KMIP_OPERATION_REGISTER = "kmip-operation-register",
GET_SECRET_ROTATIONS = "get-secret-rotations",
GET_SECRET_ROTATION = "get-secret-rotation",
GET_SECRET_ROTATION_GENERATED_CREDENTIALS = "get-secret-rotation-generated-credentials",
CREATE_SECRET_ROTATION = "create-secret-rotation",
UPDATE_SECRET_ROTATION = "update-secret-rotation",
DELETE_SECRET_ROTATION = "delete-secret-rotation",
SECRET_ROTATION_ROTATE_SECRETS = "secret-rotation-rotate-secrets",
PROJECT_ACCESS_REQUEST = "project-access-request"
KMIP_OPERATION_REGISTER = "kmip-operation-register"
}
export const filterableSecretEvents: EventType[] = [
EventType.GET_SECRET,
EventType.DELETE_SECRETS,
EventType.CREATE_SECRETS,
EventType.UPDATE_SECRETS,
EventType.CREATE_SECRET,
EventType.UPDATE_SECRET,
EventType.DELETE_SECRET
];
interface UserActorMetadata {
userId: string;
email?: string | null;
@@ -1012,7 +968,6 @@ interface LoginIdentityOidcAuthEvent {
identityId: string;
identityOidcAuthId: string;
identityAccessTokenId: string;
oidcClaimsReceived: Record<string, unknown>;
};
}
@@ -1390,7 +1345,7 @@ interface IssueSshCreds {
type: EventType.ISSUE_SSH_CREDS;
metadata: {
certificateTemplateId: string;
keyAlgorithm: SshCertKeyAlgorithm;
keyAlgorithm: CertKeyAlgorithm;
certType: SshCertType;
principals: string[];
ttl: string;
@@ -1486,80 +1441,6 @@ interface DeleteSshCertificateTemplate {
};
}
interface CreateSshHost {
type: EventType.CREATE_SSH_HOST;
metadata: {
sshHostId: string;
hostname: string;
userCertTtl: string;
hostCertTtl: string;
loginMappings: {
loginUser: string;
allowedPrincipals: {
usernames: string[];
};
}[];
userSshCaId: string;
hostSshCaId: string;
};
}
interface UpdateSshHost {
type: EventType.UPDATE_SSH_HOST;
metadata: {
sshHostId: string;
hostname?: string;
userCertTtl?: string;
hostCertTtl?: string;
loginMappings?: {
loginUser: string;
allowedPrincipals: {
usernames: string[];
};
}[];
userSshCaId?: string;
hostSshCaId?: string;
};
}
interface DeleteSshHost {
type: EventType.DELETE_SSH_HOST;
metadata: {
sshHostId: string;
hostname: string;
};
}
interface GetSshHost {
type: EventType.GET_SSH_HOST;
metadata: {
sshHostId: string;
hostname: string;
};
}
interface IssueSshHostUserCert {
type: EventType.ISSUE_SSH_HOST_USER_CERT;
metadata: {
sshHostId: string;
hostname: string;
loginUser: string;
principals: string[];
ttl: string;
};
}
interface IssueSshHostHostCert {
type: EventType.ISSUE_SSH_HOST_HOST_CERT;
metadata: {
sshHostId: string;
hostname: string;
serialNumber: string;
principals: string[];
ttl: string;
};
}
interface CreateCa {
type: EventType.CREATE_CA;
metadata: {
@@ -2003,7 +1884,7 @@ interface CreateCmekEvent {
keyId: string;
name: string;
description?: string;
encryptionAlgorithm: SymmetricKeyAlgorithm | AsymmetricKeyAlgorithm;
encryptionAlgorithm: SymmetricEncryption;
};
}
@@ -2051,39 +1932,6 @@ interface CmekDecryptEvent {
};
}
interface CmekSignEvent {
type: EventType.CMEK_SIGN;
metadata: {
keyId: string;
signingAlgorithm: SigningAlgorithm;
signature: string;
};
}
interface CmekVerifyEvent {
type: EventType.CMEK_VERIFY;
metadata: {
keyId: string;
signingAlgorithm: SigningAlgorithm;
signature: string;
signatureValid: boolean;
};
}
interface CmekListSigningAlgorithmsEvent {
type: EventType.CMEK_LIST_SIGNING_ALGORITHMS;
metadata: {
keyId: string;
};
}
interface CmekGetPublicKeyEvent {
type: EventType.CMEK_GET_PUBLIC_KEY;
metadata: {
keyId: string;
};
}
interface GetExternalGroupOrgRoleMappingsEvent {
type: EventType.GET_EXTERNAL_GROUP_ORG_ROLE_MAPPINGS;
metadata?: Record<string, never>; // not needed, based off orgId
@@ -2416,15 +2264,6 @@ interface KmipOperationRegisterEvent {
};
}
interface ProjectAccessRequestEvent {
type: EventType.PROJECT_ACCESS_REQUEST;
metadata: {
projectId: string;
requesterId: string;
requesterEmail: string;
};
}
interface SetupKmipEvent {
type: EventType.SETUP_KMIP;
metadata: {
@@ -2450,63 +2289,6 @@ interface RegisterKmipServerEvent {
};
}
interface GetSecretRotationsEvent {
type: EventType.GET_SECRET_ROTATIONS;
metadata: {
type?: SecretRotation;
count: number;
rotationIds: string[];
secretPath?: string;
environment?: string;
};
}
interface GetSecretRotationEvent {
type: EventType.GET_SECRET_ROTATION;
metadata: {
type: SecretRotation;
rotationId: string;
secretPath: string;
environment: string;
};
}
interface GetSecretRotationCredentialsEvent {
type: EventType.GET_SECRET_ROTATION_GENERATED_CREDENTIALS;
metadata: {
type: SecretRotation;
rotationId: string;
secretPath: string;
environment: string;
};
}
interface CreateSecretRotationEvent {
type: EventType.CREATE_SECRET_ROTATION;
metadata: Omit<TCreateSecretRotationV2DTO, "projectId"> & { rotationId: string };
}
interface UpdateSecretRotationEvent {
type: EventType.UPDATE_SECRET_ROTATION;
metadata: TUpdateSecretRotationV2DTO;
}
interface DeleteSecretRotationEvent {
type: EventType.DELETE_SECRET_ROTATION;
metadata: TDeleteSecretRotationV2DTO;
}
interface RotateSecretRotationEvent {
type: EventType.SECRET_ROTATION_ROTATE_SECRETS;
metadata: Pick<TSecretRotationV2Raw, "parameters" | "secretsMapping" | "type" | "connectionId" | "folderId"> & {
status: SecretRotationStatus;
rotationId: string;
jobId?: string | undefined;
occurredAt: Date;
message?: string | null | undefined;
};
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
@@ -2613,12 +2395,6 @@ export type Event =
| UpdateSshCertificateTemplate
| GetSshCertificateTemplate
| DeleteSshCertificateTemplate
| CreateSshHost
| UpdateSshHost
| DeleteSshHost
| GetSshHost
| IssueSshHostUserCert
| IssueSshHostHostCert
| CreateCa
| GetCa
| UpdateCa
@@ -2678,10 +2454,6 @@ export type Event =
| GetCmeksEvent
| CmekEncryptEvent
| CmekDecryptEvent
| CmekSignEvent
| CmekVerifyEvent
| CmekListSigningAlgorithmsEvent
| CmekGetPublicKeyEvent
| GetExternalGroupOrgRoleMappingsEvent
| UpdateExternalGroupOrgRoleMappingsEvent
| GetProjectTemplatesEvent
@@ -2726,13 +2498,5 @@ export type Event =
| KmipOperationRevokeEvent
| KmipOperationLocateEvent
| KmipOperationRegisterEvent
| ProjectAccessRequestEvent
| CreateSecretRequestEvent
| SecretApprovalRequestReview
| GetSecretRotationsEvent
| GetSecretRotationEvent
| GetSecretRotationCredentialsEvent
| CreateSecretRotationEvent
| UpdateSecretRotationEvent
| DeleteSecretRotationEvent
| RotateSecretRotationEvent;
| SecretApprovalRequestReview;

View File

@@ -1,6 +1,5 @@
import * as x509 from "@peculiar/x509";
import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
import { BadRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { isCertChainValid } from "@app/services/certificate/certificate-fns";
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
@@ -68,7 +67,9 @@ export const certificateEstServiceFactory = ({
const certTemplate = await certificateTemplateDAL.findById(certificateTemplateId);
const leafCertificate = extractX509CertFromChain(decodeURIComponent(sslClientCert))?.[0];
const leafCertificate = decodeURIComponent(sslClientCert).match(
/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g
)?.[0];
if (!leafCertificate) {
throw new UnauthorizedError({ message: "Missing client certificate" });
@@ -87,7 +88,10 @@ export const certificateEstServiceFactory = ({
const verifiedChains = await Promise.all(
caCertChains.map((chain) => {
const caCert = new x509.X509Certificate(chain.certificate);
const caChain = extractX509CertFromChain(chain.certificateChain)?.map((c) => new x509.X509Certificate(c)) || [];
const caChain =
chain.certificateChain
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
?.map((c) => new x509.X509Certificate(c)) || [];
return isCertChainValid([cert, caCert, ...caChain]);
})
@@ -168,15 +172,19 @@ export const certificateEstServiceFactory = ({
}
if (!estConfig.disableBootstrapCertValidation) {
const caCerts = extractX509CertFromChain(estConfig.caChain)?.map((cert) => {
return new x509.X509Certificate(cert);
});
const caCerts = estConfig.caChain
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
?.map((cert) => {
return new x509.X509Certificate(cert);
});
if (!caCerts) {
throw new BadRequestError({ message: "Failed to parse certificate chain" });
}
const leafCertificate = extractX509CertFromChain(decodeURIComponent(sslClientCert))?.[0];
const leafCertificate = decodeURIComponent(sslClientCert).match(
/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g
)?.[0];
if (!leafCertificate) {
throw new BadRequestError({ message: "Missing client certificate" });
@@ -242,7 +250,13 @@ export const certificateEstServiceFactory = ({
kmsService
});
const certificates = extractX509CertFromChain(caCertChain).map((cert) => new x509.X509Certificate(cert));
const certificates = caCertChain
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
?.map((cert) => new x509.X509Certificate(cert));
if (!certificates) {
throw new BadRequestError({ message: "Failed to parse certificate chain" });
}
const caCertificate = new x509.X509Certificate(caCert);
return convertRawCertsToPkcs7([caCertificate.rawData, ...certificates.map((cert) => cert.rawData)]);

View File

@@ -78,6 +78,10 @@ export const dynamicSecretLeaseServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
@@ -98,15 +102,6 @@ export const dynamicSecretLeaseServiceFactory = ({
message: `Dynamic secret with name '${name}' in folder with path '${path}' not found`
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecretCfg.metadata
})
);
const totalLeasesTaken = await dynamicSecretLeaseDAL.countLeasesForDynamicSecret(dynamicSecretCfg.id);
if (totalLeasesTaken >= appCfg.MAX_LEASE_LIMIT)
throw new BadRequestError({ message: `Max lease limit reached. Limit: ${appCfg.MAX_LEASE_LIMIT}` });
@@ -164,6 +159,10 @@ export const dynamicSecretLeaseServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
@@ -184,29 +183,11 @@ export const dynamicSecretLeaseServiceFactory = ({
});
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
if (!dynamicSecretLease || dynamicSecretLease.dynamicSecret.folderId !== folder.id) {
if (!dynamicSecretLease) {
throw new NotFoundError({ message: `Dynamic secret lease with ID '${leaseId}' not found` });
}
const dynamicSecretCfg = await dynamicSecretDAL.findOne({
id: dynamicSecretLease.dynamicSecretId,
folderId: folder.id
});
if (!dynamicSecretCfg)
throw new NotFoundError({
message: `Dynamic secret with ID '${dynamicSecretLease.dynamicSecretId}' not found`
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecretCfg.metadata
})
);
const dynamicSecretCfg = dynamicSecretLease.dynamicSecret;
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const decryptedStoredInput = JSON.parse(
secretManagerDecryptor({ cipherTextBlob: Buffer.from(dynamicSecretCfg.encryptedInput) }).toString()
@@ -258,6 +239,10 @@ export const dynamicSecretLeaseServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
@@ -271,28 +256,10 @@ export const dynamicSecretLeaseServiceFactory = ({
});
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
if (!dynamicSecretLease || dynamicSecretLease.dynamicSecret.folderId !== folder.id)
if (!dynamicSecretLease)
throw new NotFoundError({ message: `Dynamic secret lease with ID '${leaseId}' not found` });
const dynamicSecretCfg = await dynamicSecretDAL.findOne({
id: dynamicSecretLease.dynamicSecretId,
folderId: folder.id
});
if (!dynamicSecretCfg)
throw new NotFoundError({
message: `Dynamic secret with ID '${dynamicSecretLease.dynamicSecretId}' not found`
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecretCfg.metadata
})
);
const dynamicSecretCfg = dynamicSecretLease.dynamicSecret;
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const decryptedStoredInput = JSON.parse(
secretManagerDecryptor({ cipherTextBlob: Buffer.from(dynamicSecretCfg.encryptedInput) }).toString()
@@ -342,6 +309,10 @@ export const dynamicSecretLeaseServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder)
@@ -355,15 +326,6 @@ export const dynamicSecretLeaseServiceFactory = ({
message: `Dynamic secret with name '${name}' in folder with path '${path}' not found`
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecretCfg.metadata
})
);
const dynamicSecretLeases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id });
return dynamicSecretLeases;
};
@@ -390,6 +352,10 @@ export const dynamicSecretLeaseServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new NotFoundError({ message: `Folder with path '${path}' not found` });
@@ -398,25 +364,6 @@ export const dynamicSecretLeaseServiceFactory = ({
if (!dynamicSecretLease)
throw new NotFoundError({ message: `Dynamic secret lease with ID '${leaseId}' not found` });
const dynamicSecretCfg = await dynamicSecretDAL.findOne({
id: dynamicSecretLease.dynamicSecretId,
folderId: folder.id
});
if (!dynamicSecretCfg)
throw new NotFoundError({
message: `Dynamic secret with ID '${dynamicSecretLease.dynamicSecretId}' not found`
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.Lease,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecretCfg.metadata
})
);
return dynamicSecretLease;
};

View File

@@ -1,17 +1,9 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { TableName, TDynamicSecrets } from "@app/db/schemas";
import { TableName } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import {
buildFindFilter,
ormify,
prependTableNameToFindFilter,
selectAllTableCols,
sqlNestRelationships,
TFindFilter,
TFindOpt
} from "@app/lib/knex";
import { ormify, selectAllTableCols } from "@app/lib/knex";
import { OrderByDirection } from "@app/lib/types";
import { SecretsOrderBy } from "@app/services/secret/secret-types";
@@ -20,86 +12,6 @@ export type TDynamicSecretDALFactory = ReturnType<typeof dynamicSecretDALFactory
export const dynamicSecretDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.DynamicSecret);
const findOne = async (filter: TFindFilter<TDynamicSecrets>, tx?: Knex) => {
const query = (tx || db.replicaNode())(TableName.DynamicSecret)
.leftJoin(
TableName.ResourceMetadata,
`${TableName.ResourceMetadata}.dynamicSecretId`,
`${TableName.DynamicSecret}.id`
)
.select(selectAllTableCols(TableName.DynamicSecret))
.select(
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
)
.where(prependTableNameToFindFilter(TableName.DynamicSecret, filter));
const docs = sqlNestRelationships({
data: await query,
key: "id",
parentMapper: (el) => el,
childrenMapper: [
{
key: "metadataId",
label: "metadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
}
]
});
return docs[0];
};
const findWithMetadata = async (
filter: TFindFilter<TDynamicSecrets>,
{ offset, limit, sort, tx }: TFindOpt<TDynamicSecrets> = {}
) => {
const query = (tx || db.replicaNode())(TableName.DynamicSecret)
.leftJoin(
TableName.ResourceMetadata,
`${TableName.ResourceMetadata}.dynamicSecretId`,
`${TableName.DynamicSecret}.id`
)
.select(selectAllTableCols(TableName.DynamicSecret))
.select(
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
)
// eslint-disable-next-line @typescript-eslint/no-misused-promises
.where(buildFindFilter(filter));
if (limit) void query.limit(limit);
if (offset) void query.offset(offset);
if (sort) {
void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })));
}
const docs = sqlNestRelationships({
data: await query,
key: "id",
parentMapper: (el) => el,
childrenMapper: [
{
key: "metadataId",
label: "metadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
}
]
});
return docs;
};
// find dynamic secrets for multiple environments (folder IDs are cross env, thus need to rank for pagination)
const listDynamicSecretsByFolderIds = async (
{
@@ -127,27 +39,18 @@ export const dynamicSecretDALFactory = (db: TDbClient) => {
void bd.whereILike(`${TableName.DynamicSecret}.name`, `%${search}%`);
}
})
.leftJoin(
TableName.ResourceMetadata,
`${TableName.ResourceMetadata}.dynamicSecretId`,
`${TableName.DynamicSecret}.id`
)
.leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.DynamicSecret}.folderId`)
.leftJoin(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
.select(
selectAllTableCols(TableName.DynamicSecret),
db.ref("slug").withSchema(TableName.Environment).as("environment"),
db.raw(`DENSE_RANK() OVER (ORDER BY ${TableName.DynamicSecret}."name" ${orderDirection}) as rank`),
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
db.raw(`DENSE_RANK() OVER (ORDER BY ${TableName.DynamicSecret}."name" ${orderDirection}) as rank`)
)
.orderBy(`${TableName.DynamicSecret}.${orderBy}`, orderDirection);
let queryWithLimit;
if (limit) {
const rankOffset = offset + 1;
queryWithLimit = (tx || db.replicaNode())
return await (tx || db)
.with("w", query)
.select("*")
.from<Awaited<typeof query>[number]>("w")
@@ -155,22 +58,7 @@ export const dynamicSecretDALFactory = (db: TDbClient) => {
.andWhere("w.rank", "<", rankOffset + limit);
}
const dynamicSecrets = sqlNestRelationships({
data: await (queryWithLimit || query),
key: "id",
parentMapper: (el) => el,
childrenMapper: [
{
key: "metadataId",
label: "metadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
}
]
});
const dynamicSecrets = await query;
return dynamicSecrets;
} catch (error) {
@@ -178,5 +66,5 @@ export const dynamicSecretDALFactory = (db: TDbClient) => {
}
};
return { ...orm, listDynamicSecretsByFolderIds, findOne, findWithMetadata };
return { ...orm, listDynamicSecretsByFolderIds };
};

View File

@@ -1,53 +1,31 @@
import dns from "node:dns/promises";
import net from "node:net";
import crypto from "node:crypto";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors";
import { isPrivateIp } from "@app/lib/ip/ipRange";
import { getDbConnectionHost } from "@app/lib/knex";
export const verifyHostInputValidity = async (host: string, isGateway = false) => {
export const verifyHostInputValidity = (host: string, isGateway = false) => {
const appCfg = getConfig();
const dbHost = appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI);
// no need for validation when it's dev
if (appCfg.NODE_ENV === "development") return;
if (appCfg.isDevelopmentMode) return [host];
if (host === "host.docker.internal") throw new BadRequestError({ message: "Invalid db host" });
const reservedHosts = [appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI)].concat(
(appCfg.DB_READ_REPLICAS || []).map((el) => getDbConnectionHost(el.DB_CONNECTION_URI)),
getDbConnectionHost(appCfg.REDIS_URL),
getDbConnectionHost(appCfg.AUDIT_LOGS_DB_CONNECTION_URI)
);
if (
appCfg.isCloud &&
!isGateway &&
// localhost
// internal ips
(host.match(/^10\.\d+\.\d+\.\d+/) || host.match(/^192\.168\.\d+\.\d+/))
)
throw new BadRequestError({ message: "Invalid db host" });
// get host db ip
const exclusiveIps: string[] = [];
for await (const el of reservedHosts) {
if (el) {
if (net.isIPv4(el)) {
exclusiveIps.push(el);
} else {
const resolvedIps = await dns.resolve4(el);
exclusiveIps.push(...resolvedIps);
}
}
if (
host === "localhost" ||
host === "127.0.0.1" ||
(dbHost?.length === host.length && crypto.timingSafeEqual(Buffer.from(dbHost || ""), Buffer.from(host)))
) {
throw new BadRequestError({ message: "Invalid db host" });
}
const normalizedHost = host.split(":")[0];
const inputHostIps: string[] = [];
if (net.isIPv4(host)) {
inputHostIps.push(host);
} else {
if (normalizedHost === "localhost" || normalizedHost === "host.docker.internal") {
throw new BadRequestError({ message: "Invalid db host" });
}
const resolvedIps = await dns.resolve4(host);
inputHostIps.push(...resolvedIps);
}
if (!isGateway && !(appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP || appCfg.ALLOW_INTERNAL_IP_CONNECTIONS)) {
const isInternalIp = inputHostIps.some((el) => isPrivateIp(el));
if (isInternalIp) throw new BadRequestError({ message: "Invalid db host" });
}
const isAppUsedIps = inputHostIps.some((el) => exclusiveIps.includes(el));
if (isAppUsedIps) throw new BadRequestError({ message: "Invalid db host" });
return inputHostIps;
};

View File

@@ -12,7 +12,6 @@ import { OrderByDirection, OrgServiceActor } from "@app/lib/types";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TResourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal";
@@ -47,7 +46,6 @@ type TDynamicSecretServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
projectGatewayDAL: Pick<TProjectGatewayDALFactory, "findOne">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
};
export type TDynamicSecretServiceFactory = ReturnType<typeof dynamicSecretServiceFactory>;
@@ -62,8 +60,7 @@ export const dynamicSecretServiceFactory = ({
dynamicSecretQueueService,
projectDAL,
kmsService,
projectGatewayDAL,
resourceMetadataDAL
projectGatewayDAL
}: TDynamicSecretServiceFactoryDep) => {
const create = async ({
path,
@@ -76,8 +73,7 @@ export const dynamicSecretServiceFactory = ({
projectSlug,
actorOrgId,
defaultTTL,
actorAuthMethod,
metadata
actorAuthMethod
}: TCreateDynamicSecretDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
@@ -91,10 +87,9 @@ export const dynamicSecretServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.CreateRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path, metadata })
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const plan = await licenseService.getPlan(actorOrgId);
@@ -136,36 +131,16 @@ export const dynamicSecretServiceFactory = ({
projectId
});
const dynamicSecretCfg = await dynamicSecretDAL.transaction(async (tx) => {
const cfg = await dynamicSecretDAL.create(
{
type: provider.type,
version: 1,
encryptedInput: secretManagerEncryptor({ plainText: Buffer.from(JSON.stringify(inputs)) }).cipherTextBlob,
maxTTL,
defaultTTL,
folderId: folder.id,
name,
projectGatewayId: selectedGatewayId
},
tx
);
if (metadata) {
await resourceMetadataDAL.insertMany(
metadata.map(({ key, value }) => ({
key,
value,
dynamicSecretId: cfg.id,
orgId: actorOrgId
})),
tx
);
}
return cfg;
const dynamicSecretCfg = await dynamicSecretDAL.create({
type: provider.type,
version: 1,
encryptedInput: secretManagerEncryptor({ plainText: Buffer.from(JSON.stringify(inputs)) }).cipherTextBlob,
maxTTL,
defaultTTL,
folderId: folder.id,
name,
projectGatewayId: selectedGatewayId
});
return dynamicSecretCfg;
};
@@ -181,8 +156,7 @@ export const dynamicSecretServiceFactory = ({
actorId,
newName,
actorOrgId,
actorAuthMethod,
metadata
actorAuthMethod
}: TUpdateDynamicSecretDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
@@ -197,6 +171,10 @@ export const dynamicSecretServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.EditRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
@@ -215,27 +193,6 @@ export const dynamicSecretServiceFactory = ({
message: `Dynamic secret with name '${name}' in folder '${folder.path}' not found`
});
}
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.EditRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecretCfg.metadata
})
);
if (metadata) {
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.EditRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata
})
);
}
if (newName) {
const existingDynamicSecret = await dynamicSecretDAL.findOne({ name: newName, folderId: folder.id });
if (existingDynamicSecret)
@@ -274,41 +231,14 @@ export const dynamicSecretServiceFactory = ({
const isConnected = await selectedProvider.validateConnection(newInput);
if (!isConnected) throw new BadRequestError({ message: "Provider connection failed" });
const updatedDynamicCfg = await dynamicSecretDAL.transaction(async (tx) => {
const cfg = await dynamicSecretDAL.updateById(
dynamicSecretCfg.id,
{
encryptedInput: secretManagerEncryptor({ plainText: Buffer.from(JSON.stringify(updatedInput)) })
.cipherTextBlob,
maxTTL,
defaultTTL,
name: newName ?? name,
status: null,
projectGatewayId: selectedGatewayId
},
tx
);
if (metadata) {
await resourceMetadataDAL.delete(
{
dynamicSecretId: cfg.id
},
tx
);
await resourceMetadataDAL.insertMany(
metadata.map(({ key, value }) => ({
key,
value,
dynamicSecretId: cfg.id,
orgId: actorOrgId
})),
tx
);
}
return cfg;
const updatedDynamicCfg = await dynamicSecretDAL.updateById(dynamicSecretCfg.id, {
encryptedInput: secretManagerEncryptor({ plainText: Buffer.from(JSON.stringify(updatedInput)) }).cipherTextBlob,
maxTTL,
defaultTTL,
name: newName ?? name,
status: null,
statusDetails: null,
projectGatewayId: selectedGatewayId
});
return updatedDynamicCfg;
@@ -338,6 +268,10 @@ export const dynamicSecretServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder)
@@ -348,15 +282,6 @@ export const dynamicSecretServiceFactory = ({
throw new NotFoundError({ message: `Dynamic secret with name '${name}' in folder '${folder.path}' not found` });
}
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecretCfg.metadata
})
);
const leases = await dynamicSecretLeaseDAL.find({ dynamicSecretId: dynamicSecretCfg.id });
// when not forced we check with the external system to first remove the things
// we introduce a forced concept because consider the external lease got deleted by some other external like a human or another system
@@ -404,6 +329,14 @@ export const dynamicSecretServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.ReadRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.EditRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder)
@@ -413,25 +346,6 @@ export const dynamicSecretServiceFactory = ({
if (!dynamicSecretCfg) {
throw new NotFoundError({ message: `Dynamic secret with name '${name} in folder '${path}' not found` });
}
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.ReadRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecretCfg.metadata
})
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.EditRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecretCfg.metadata
})
);
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
projectId
@@ -442,7 +356,6 @@ export const dynamicSecretServiceFactory = ({
) as object;
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
const providerInputs = (await selectedProvider.validateProviderInputs(decryptedStoredInput)) as object;
return { ...dynamicSecretCfg, inputs: providerInputs };
};
@@ -513,7 +426,7 @@ export const dynamicSecretServiceFactory = ({
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.ReadRootCredential,
ProjectPermissionSub.DynamicSecrets
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
@@ -560,12 +473,16 @@ export const dynamicSecretServiceFactory = ({
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.ReadRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
);
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder)
throw new NotFoundError({ message: `Folder with path '${path}' in environment '${environmentSlug}' not found` });
const dynamicSecretCfg = await dynamicSecretDAL.findWithMetadata(
const dynamicSecretCfg = await dynamicSecretDAL.find(
{ folderId: folder.id, $search: search ? { name: `%${search}%` } : undefined },
{
limit,
@@ -573,17 +490,7 @@ export const dynamicSecretServiceFactory = ({
sort: orderBy ? [[orderBy, orderDirection]] : undefined
}
);
return dynamicSecretCfg.filter((dynamicSecret) => {
return permission.can(
ProjectPermissionDynamicSecretActions.ReadRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: environmentSlug,
secretPath: path,
metadata: dynamicSecret.metadata
})
);
});
return dynamicSecretCfg;
};
const listDynamicSecretsByFolderIds = async (
@@ -635,14 +542,24 @@ export const dynamicSecretServiceFactory = ({
isInternal,
...params
}: TListDynamicSecretsMultiEnvDTO) => {
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
if (!isInternal) {
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
});
// verify user has access to each env in request
environmentSlugs.forEach((environmentSlug) =>
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionDynamicSecretActions.ReadRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
)
);
}
const folders = await folderDAL.findBySecretPathMultiEnv(projectId, environmentSlugs, path);
if (!folders.length)
@@ -655,16 +572,7 @@ export const dynamicSecretServiceFactory = ({
...params
});
return dynamicSecretCfg.filter((dynamicSecret) => {
return permission.can(
ProjectPermissionDynamicSecretActions.ReadRootCredential,
subject(ProjectPermissionSub.DynamicSecrets, {
environment: dynamicSecret.environment,
secretPath: path,
metadata: dynamicSecret.metadata
})
);
});
return dynamicSecretCfg;
};
const fetchAzureEntraIdUsers = async ({

View File

@@ -1,7 +1,6 @@
import { z } from "zod";
import { OrderByDirection, TProjectPermission } from "@app/lib/types";
import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema";
import { SecretsOrderBy } from "@app/services/secret/secret-types";
import { DynamicSecretProviderSchema } from "./providers/models";
@@ -21,7 +20,6 @@ export type TCreateDynamicSecretDTO = {
environmentSlug: string;
name: string;
projectSlug: string;
metadata?: ResourceMetadataDTO;
} & Omit<TProjectPermission, "projectId">;
export type TUpdateDynamicSecretDTO = {
@@ -33,7 +31,6 @@ export type TUpdateDynamicSecretDTO = {
environmentSlug: string;
inputs?: TProvider["inputs"];
projectSlug: string;
metadata?: ResourceMetadataDTO;
} & Omit<TProjectPermission, "projectId">;
export type TDeleteDynamicSecretDTO = {

View File

@@ -13,7 +13,6 @@ import { customAlphabet } from "nanoid";
import { z } from "zod";
import { BadRequestError } from "@app/lib/errors";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models";
@@ -145,14 +144,6 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
// We can't return the parsed statements here because we need to use the handlebars template to generate the username and password, before we can use the parsed statements.
CreateElastiCacheUserSchema.parse(JSON.parse(providerInputs.creationStatement));
DeleteElasticCacheUserSchema.parse(JSON.parse(providerInputs.revocationStatement));
validateHandlebarTemplate("AWS ElastiCache creation", providerInputs.creationStatement, {
allowedExpressions: (val) => ["username", "password", "expiration"].includes(val)
});
if (providerInputs.revocationStatement) {
validateHandlebarTemplate("AWS ElastiCache revoke", providerInputs.revocationStatement, {
allowedExpressions: (val) => ["username"].includes(val)
});
}
return providerInputs;
};

View File

@@ -3,10 +3,9 @@ import handlebars from "handlebars";
import { customAlphabet } from "nanoid";
import { z } from "zod";
import { BadRequestError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
import { verifyHostInputValidity } from "../dynamic-secret-fns";
import { DynamicSecretCassandraSchema, TDynamicProviderFns } from "./models";
const generatePassword = (size = 48) => {
@@ -21,28 +20,14 @@ const generateUsername = () => {
export const CassandraProvider = (): TDynamicProviderFns => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = await DynamicSecretCassandraSchema.parseAsync(inputs);
const hostIps = await Promise.all(
providerInputs.host
.split(",")
.filter(Boolean)
.map((el) => verifyHostInputValidity(el).then((ip) => ip[0]))
);
validateHandlebarTemplate("Cassandra creation", providerInputs.creationStatement, {
allowedExpressions: (val) => ["username", "password", "expiration", "keyspace"].includes(val)
});
if (providerInputs.renewStatement) {
validateHandlebarTemplate("Cassandra renew", providerInputs.renewStatement, {
allowedExpressions: (val) => ["username", "expiration", "keyspace"].includes(val)
});
if (providerInputs.host === "localhost" || providerInputs.host === "127.0.0.1") {
throw new BadRequestError({ message: "Invalid db host" });
}
validateHandlebarTemplate("Cassandra revoke", providerInputs.revocationStatement, {
allowedExpressions: (val) => ["username"].includes(val)
});
return { ...providerInputs, hostIps };
return providerInputs;
};
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretCassandraSchema> & { hostIps: string[] }) => {
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretCassandraSchema>) => {
const sslOptions = providerInputs.ca ? { rejectUnauthorized: false, ca: providerInputs.ca } : undefined;
const client = new cassandra.Client({
sslOptions,
@@ -55,7 +40,7 @@ export const CassandraProvider = (): TDynamicProviderFns => {
},
keyspace: providerInputs.keyspace,
localDataCenter: providerInputs?.localDataCenter,
contactPoints: providerInputs.hostIps
contactPoints: providerInputs.host.split(",").filter(Boolean)
});
return client;
};

View File

@@ -19,14 +19,15 @@ const generateUsername = () => {
export const ElasticSearchProvider = (): TDynamicProviderFns => {
const validateProviderInputs = async (inputs: unknown) => {
const providerInputs = await DynamicSecretElasticSearchSchema.parseAsync(inputs);
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
return { ...providerInputs, hostIp };
verifyHostInputValidity(providerInputs.host);
return providerInputs;
};
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretElasticSearchSchema> & { hostIp: string }) => {
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretElasticSearchSchema>) => {
const connection = new ElasticSearchClient({
node: {
url: new URL(`${providerInputs.hostIp}:${providerInputs.port}`),
url: new URL(`${providerInputs.host}:${providerInputs.port}`),
...(providerInputs.ca && {
ssl: {
rejectUnauthorized: false,

View File

@@ -2,7 +2,6 @@ import handlebars from "handlebars";
import ldapjs from "ldapjs";
import ldif from "ldif";
import { customAlphabet } from "nanoid";
import RE2 from "re2";
import { z } from "zod";
import { BadRequestError } from "@app/lib/errors";
@@ -195,8 +194,7 @@ export const LdapProvider = (): TDynamicProviderFns => {
const client = await $getClient(providerInputs);
if (providerInputs.credentialType === LdapCredentialType.Static) {
const dnRegex = new RE2("^dn:\\s*(.+)", "m");
const dnMatch = dnRegex.exec(providerInputs.rotationLdif);
const dnMatch = providerInputs.rotationLdif.match(/^dn:\s*(.+)/m);
if (dnMatch) {
const username = dnMatch[1];
@@ -240,8 +238,7 @@ export const LdapProvider = (): TDynamicProviderFns => {
const client = await $getClient(providerInputs);
if (providerInputs.credentialType === LdapCredentialType.Static) {
const dnRegex = new RE2("^dn:\\s*(.+)", "m");
const dnMatch = dnRegex.exec(providerInputs.rotationLdif);
const dnMatch = providerInputs.rotationLdif.match(/^dn:\s*(.+)/m);
if (dnMatch) {
const username = dnMatch[1];

Some files were not shown because too many files have changed in this diff Show More