Compare commits
131 Commits
patch-azur
...
daniel/aud
Author | SHA1 | Date | |
---|---|---|---|
|
17249d603b | ||
|
9bdff9c504 | ||
|
5d8f32b774 | ||
|
bb71f5eb7e | ||
|
30b431a255 | ||
|
fd32118685 | ||
|
8528f3fd2a | ||
|
eb358bcafd | ||
|
ffbd29c575 | ||
|
a74f0170da | ||
|
a0fad34a6d | ||
|
0b9d890a51 | ||
|
5ba507bc1c | ||
|
0ecc196e5d | ||
|
ddac9f7cc4 | ||
|
34354994d8 | ||
|
1576358805 | ||
|
e6103d2d3f | ||
|
8bf8bc77c9 | ||
|
3219723149 | ||
|
6d3793beff | ||
|
0df41f3391 | ||
|
1acac9d479 | ||
|
0cefd6f837 | ||
|
5e9dc0b98d | ||
|
f632847dc6 | ||
|
faa6d1cf40 | ||
|
7fb18870e3 | ||
|
ae841715e5 | ||
|
baac87c16a | ||
|
291d29ec41 | ||
|
b726187ba3 | ||
|
d98ff32b07 | ||
|
1fa510b32f | ||
|
c57f0d8120 | ||
|
00490f2cff | ||
|
ee58f538c0 | ||
|
0fa20f7839 | ||
|
40ef75d3bd | ||
|
26af13453c | ||
|
ad1f71883d | ||
|
2659ea7170 | ||
|
d2e3f504fd | ||
|
ca4151a34d | ||
|
d4bc104fd1 | ||
|
7e3a3fcdb0 | ||
|
7f67912d2f | ||
|
a7f4020c08 | ||
|
d2d89034ba | ||
|
2fc6c564c0 | ||
|
240b86c1d5 | ||
|
30d66cef89 | ||
|
b7d11444a9 | ||
|
0a6aef0afa | ||
|
0ac7ec460b | ||
|
808a901aee | ||
|
d5412f916f | ||
|
d0648ca596 | ||
|
3291f1f908 | ||
|
965d30bd03 | ||
|
68fe7c589a | ||
|
54377a44d3 | ||
|
8c902d7699 | ||
|
c25c84aeb3 | ||
|
4359eb7313 | ||
|
322536d738 | ||
|
6c5db3a187 | ||
|
a337e6badd | ||
|
524a97e9a6 | ||
|
c56f598115 | ||
|
19d32a1a3b | ||
|
7e5417a0eb | ||
|
afd6de27fe | ||
|
7781a6b7e7 | ||
|
b3b4e41d92 | ||
|
5225f5136a | ||
|
398adfaf76 | ||
|
d77c26fa38 | ||
|
ef7b81734a | ||
|
09b489a348 | ||
|
6b5c50def0 | ||
|
1f2d52176c | ||
|
7002e297c8 | ||
|
71864a131f | ||
|
9964d2ecaa | ||
|
3ebbaefc2a | ||
|
dd5c494bdb | ||
|
bace8af5a1 | ||
|
f56196b820 | ||
|
7042d73410 | ||
|
cb22ee315e | ||
|
701eb7cfc6 | ||
|
bf8df14b01 | ||
|
1ba8b6394b | ||
|
c442c8483a | ||
|
0435305a68 | ||
|
febf11f502 | ||
|
64fd15c32d | ||
|
a2c9494d52 | ||
|
18460e0678 | ||
|
3d03fece74 | ||
|
234e7eb9be | ||
|
04af313bf0 | ||
|
9b038ccc45 | ||
|
9beb384546 | ||
|
12ec9b4b4e | ||
|
96b8e7fda8 | ||
|
93b9108aa3 | ||
|
99017ea1ae | ||
|
f32588112e | ||
|
f9b0b6700a | ||
|
b45d9398f0 | ||
|
1d1140237f | ||
|
937560fd8d | ||
|
5f4b7b9ea7 | ||
|
7f6bc3ecfe | ||
|
d8cc000ad1 | ||
|
50ceedf39f | ||
|
550096e72b | ||
|
1190ca2d77 | ||
|
2fb60201bc | ||
|
e763a6f683 | ||
|
cb1b006118 | ||
|
356e7c5958 | ||
|
1a68765f15 | ||
|
ae07d38c19 | ||
|
025b4b8761 | ||
|
ef688efc8d | ||
|
8c98565715 | ||
|
e9358cd1d8 | ||
|
242595fceb |
8
.github/workflows/check-fe-ts-and-lint.yml
vendored
@@ -18,18 +18,18 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: ☁️ Checkout source
|
- name: ☁️ Checkout source
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: 🔧 Setup Node 16
|
- name: 🔧 Setup Node 20
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: "16"
|
node-version: "20"
|
||||||
cache: "npm"
|
cache: "npm"
|
||||||
cache-dependency-path: frontend/package-lock.json
|
cache-dependency-path: frontend/package-lock.json
|
||||||
- name: 📦 Install dependencies
|
- name: 📦 Install dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
- name: 🏗️ Run Type check
|
- name: 🏗️ Run Type check
|
||||||
run: npm run type:check
|
run: npm run type:check
|
||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
- name: 🏗️ Run Link check
|
- name: 🏗️ Run Link check
|
||||||
run: npm run lint:fix
|
run: npm run lint:fix
|
||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
|
6
.github/workflows/deployment-pipeline.yml
vendored
@@ -97,7 +97,7 @@ jobs:
|
|||||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||||
environment-variables: "LOG_LEVEL=info"
|
environment-variables: "LOG_LEVEL=info"
|
||||||
- name: Deploy to Amazon ECS service
|
- name: Deploy to Amazon ECS service
|
||||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
|
||||||
with:
|
with:
|
||||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||||
service: infisical-core-gamma-stage
|
service: infisical-core-gamma-stage
|
||||||
@@ -153,7 +153,7 @@ jobs:
|
|||||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||||
environment-variables: "LOG_LEVEL=info"
|
environment-variables: "LOG_LEVEL=info"
|
||||||
- name: Deploy to Amazon ECS service
|
- name: Deploy to Amazon ECS service
|
||||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
|
||||||
with:
|
with:
|
||||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||||
service: infisical-core-platform
|
service: infisical-core-platform
|
||||||
@@ -204,7 +204,7 @@ jobs:
|
|||||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||||
environment-variables: "LOG_LEVEL=info"
|
environment-variables: "LOG_LEVEL=info"
|
||||||
- name: Deploy to Amazon ECS service
|
- name: Deploy to Amazon ECS service
|
||||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
|
||||||
with:
|
with:
|
||||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||||
service: infisical-core-platform
|
service: infisical-core-platform
|
||||||
|
@@ -8,7 +8,7 @@ FROM node:20-slim AS base
|
|||||||
FROM base AS frontend-dependencies
|
FROM base AS frontend-dependencies
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY frontend/package.json frontend/package-lock.json frontend/next.config.js ./
|
COPY frontend/package.json frontend/package-lock.json ./
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
RUN npm ci --only-production --ignore-scripts
|
RUN npm ci --only-production --ignore-scripts
|
||||||
@@ -23,17 +23,16 @@ COPY --from=frontend-dependencies /app/node_modules ./node_modules
|
|||||||
COPY /frontend .
|
COPY /frontend .
|
||||||
|
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ENV NEXT_PUBLIC_ENV production
|
|
||||||
ARG POSTHOG_HOST
|
ARG POSTHOG_HOST
|
||||||
ENV NEXT_PUBLIC_POSTHOG_HOST $POSTHOG_HOST
|
ENV VITE_POSTHOG_HOST $POSTHOG_HOST
|
||||||
ARG POSTHOG_API_KEY
|
ARG POSTHOG_API_KEY
|
||||||
ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY
|
ENV VITE_POSTHOG_API_KEY $POSTHOG_API_KEY
|
||||||
ARG INTERCOM_ID
|
ARG INTERCOM_ID
|
||||||
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
|
ENV VITE_INTERCOM_ID $INTERCOM_ID
|
||||||
ARG INFISICAL_PLATFORM_VERSION
|
ARG INFISICAL_PLATFORM_VERSION
|
||||||
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
ENV VITE_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
||||||
ARG CAPTCHA_SITE_KEY
|
ARG CAPTCHA_SITE_KEY
|
||||||
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY $CAPTCHA_SITE_KEY
|
ENV VITE_CAPTCHA_SITE_KEY $CAPTCHA_SITE_KEY
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
@@ -44,20 +43,10 @@ WORKDIR /app
|
|||||||
|
|
||||||
RUN groupadd -r -g 1001 nodejs && useradd -r -u 1001 -g nodejs non-root-user
|
RUN groupadd -r -g 1001 nodejs && useradd -r -u 1001 -g nodejs non-root-user
|
||||||
|
|
||||||
RUN mkdir -p /app/.next/cache/images && chown non-root-user:nodejs /app/.next/cache/images
|
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/dist ./
|
||||||
VOLUME /app/.next/cache/images
|
|
||||||
|
|
||||||
COPY --chown=non-root-user:nodejs --chmod=555 frontend/scripts ./scripts
|
|
||||||
COPY --from=frontend-builder /app/public ./public
|
|
||||||
RUN chown non-root-user:nodejs ./public/data
|
|
||||||
|
|
||||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/standalone ./
|
|
||||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/static ./.next/static
|
|
||||||
|
|
||||||
USER non-root-user
|
USER non-root-user
|
||||||
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
|
||||||
|
|
||||||
##
|
##
|
||||||
## BACKEND
|
## BACKEND
|
||||||
##
|
##
|
||||||
@@ -137,7 +126,7 @@ RUN apt-get update && apt-get install -y \
|
|||||||
freetds-dev \
|
freetds-dev \
|
||||||
freetds-bin \
|
freetds-bin \
|
||||||
tdsodbc \
|
tdsodbc \
|
||||||
openssh \
|
openssh-client \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Configure ODBC in production
|
# Configure ODBC in production
|
||||||
@@ -160,14 +149,11 @@ RUN chmod u+rx /usr/sbin/update-ca-certificates
|
|||||||
|
|
||||||
## set pre baked keys
|
## set pre baked keys
|
||||||
ARG POSTHOG_API_KEY
|
ARG POSTHOG_API_KEY
|
||||||
ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
ENV POSTHOG_API_KEY=$POSTHOG_API_KEY
|
||||||
BAKED_NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY
|
|
||||||
ARG INTERCOM_ID=intercom-id
|
ARG INTERCOM_ID=intercom-id
|
||||||
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
|
ENV INTERCOM_ID=$INTERCOM_ID
|
||||||
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
|
|
||||||
ARG CAPTCHA_SITE_KEY
|
ARG CAPTCHA_SITE_KEY
|
||||||
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY \
|
ENV CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY
|
||||||
BAKED_NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY
|
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
|
|
||||||
@@ -192,4 +178,4 @@ EXPOSE 443
|
|||||||
|
|
||||||
USER non-root-user
|
USER non-root-user
|
||||||
|
|
||||||
CMD ["./standalone-entrypoint.sh"]
|
CMD ["./standalone-entrypoint.sh"]
|
||||||
|
@@ -12,7 +12,7 @@ RUN apk add --no-cache libc6-compat
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY frontend/package.json frontend/package-lock.json frontend/next.config.js ./
|
COPY frontend/package.json frontend/package-lock.json ./
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
RUN npm ci --only-production --ignore-scripts
|
RUN npm ci --only-production --ignore-scripts
|
||||||
@@ -27,17 +27,16 @@ COPY --from=frontend-dependencies /app/node_modules ./node_modules
|
|||||||
COPY /frontend .
|
COPY /frontend .
|
||||||
|
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ENV NEXT_PUBLIC_ENV production
|
|
||||||
ARG POSTHOG_HOST
|
ARG POSTHOG_HOST
|
||||||
ENV NEXT_PUBLIC_POSTHOG_HOST $POSTHOG_HOST
|
ENV VITE_POSTHOG_HOST $POSTHOG_HOST
|
||||||
ARG POSTHOG_API_KEY
|
ARG POSTHOG_API_KEY
|
||||||
ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY
|
ENV VITE_POSTHOG_API_KEY $POSTHOG_API_KEY
|
||||||
ARG INTERCOM_ID
|
ARG INTERCOM_ID
|
||||||
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
|
ENV VITE_INTERCOM_ID $INTERCOM_ID
|
||||||
ARG INFISICAL_PLATFORM_VERSION
|
ARG INFISICAL_PLATFORM_VERSION
|
||||||
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
ENV VITE_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
||||||
ARG CAPTCHA_SITE_KEY
|
ARG CAPTCHA_SITE_KEY
|
||||||
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY $CAPTCHA_SITE_KEY
|
ENV VITE_CAPTCHA_SITE_KEY $CAPTCHA_SITE_KEY
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
@@ -49,20 +48,10 @@ WORKDIR /app
|
|||||||
RUN addgroup --system --gid 1001 nodejs
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
RUN adduser --system --uid 1001 non-root-user
|
RUN adduser --system --uid 1001 non-root-user
|
||||||
|
|
||||||
RUN mkdir -p /app/.next/cache/images && chown non-root-user:nodejs /app/.next/cache/images
|
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/dist ./
|
||||||
VOLUME /app/.next/cache/images
|
|
||||||
|
|
||||||
COPY --chown=non-root-user:nodejs --chmod=555 frontend/scripts ./scripts
|
|
||||||
COPY --from=frontend-builder /app/public ./public
|
|
||||||
RUN chown non-root-user:nodejs ./public/data
|
|
||||||
|
|
||||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/standalone ./
|
|
||||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/static ./.next/static
|
|
||||||
|
|
||||||
USER non-root-user
|
USER non-root-user
|
||||||
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
|
||||||
|
|
||||||
##
|
##
|
||||||
## BACKEND
|
## BACKEND
|
||||||
##
|
##
|
||||||
@@ -159,14 +148,11 @@ RUN chmod u+rx /usr/sbin/update-ca-certificates
|
|||||||
|
|
||||||
## set pre baked keys
|
## set pre baked keys
|
||||||
ARG POSTHOG_API_KEY
|
ARG POSTHOG_API_KEY
|
||||||
ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
ENV POSTHOG_API_KEY=$POSTHOG_API_KEY
|
||||||
BAKED_NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY
|
|
||||||
ARG INTERCOM_ID=intercom-id
|
ARG INTERCOM_ID=intercom-id
|
||||||
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
|
ENV INTERCOM_ID=$INTERCOM_ID
|
||||||
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
|
|
||||||
ARG CAPTCHA_SITE_KEY
|
ARG CAPTCHA_SITE_KEY
|
||||||
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY \
|
ENV CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY
|
||||||
BAKED_NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY
|
|
||||||
|
|
||||||
|
|
||||||
COPY --from=backend-runner /app /backend
|
COPY --from=backend-runner /app /backend
|
||||||
@@ -189,4 +175,4 @@ EXPOSE 443
|
|||||||
|
|
||||||
USER non-root-user
|
USER non-root-user
|
||||||
|
|
||||||
CMD ["./standalone-entrypoint.sh"]
|
CMD ["./standalone-entrypoint.sh"]
|
||||||
|
128
backend/package-lock.json
generated
@@ -26,6 +26,7 @@
|
|||||||
"@fastify/rate-limit": "^9.0.0",
|
"@fastify/rate-limit": "^9.0.0",
|
||||||
"@fastify/request-context": "^5.1.0",
|
"@fastify/request-context": "^5.1.0",
|
||||||
"@fastify/session": "^10.7.0",
|
"@fastify/session": "^10.7.0",
|
||||||
|
"@fastify/static": "^7.0.4",
|
||||||
"@fastify/swagger": "^8.14.0",
|
"@fastify/swagger": "^8.14.0",
|
||||||
"@fastify/swagger-ui": "^2.1.0",
|
"@fastify/swagger-ui": "^2.1.0",
|
||||||
"@google-cloud/kms": "^4.5.0",
|
"@google-cloud/kms": "^4.5.0",
|
||||||
@@ -5406,6 +5407,7 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz",
|
||||||
"integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==",
|
"integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
@@ -5545,6 +5547,7 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz",
|
||||||
"integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==",
|
"integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lukeed/ms": "^2.0.1",
|
"@lukeed/ms": "^2.0.1",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
@@ -5563,16 +5566,85 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fastify/static": {
|
"node_modules/@fastify/static": {
|
||||||
"version": "6.12.0",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@fastify/static/-/static-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fastify/static/-/static-7.0.4.tgz",
|
||||||
"integrity": "sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==",
|
"integrity": "sha512-p2uKtaf8BMOZWLs6wu+Ihg7bWNBdjNgCwDza4MJtTqg+5ovKmcbgbR9Xs5/smZ1YISfzKOCNYmZV8LaCj+eJ1Q==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/accept-negotiator": "^1.0.0",
|
"@fastify/accept-negotiator": "^1.0.0",
|
||||||
"@fastify/send": "^2.0.0",
|
"@fastify/send": "^2.0.0",
|
||||||
"content-disposition": "^0.5.3",
|
"content-disposition": "^0.5.3",
|
||||||
"fastify-plugin": "^4.0.0",
|
"fastify-plugin": "^4.0.0",
|
||||||
"glob": "^8.0.1",
|
"fastq": "^1.17.0",
|
||||||
"p-limit": "^3.1.0"
|
"glob": "^10.3.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fastify/static/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/@fastify/static/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/@fastify/static/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/@fastify/static/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/@fastify/static/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/@fastify/swagger": {
|
"node_modules/@fastify/swagger": {
|
||||||
@@ -5599,6 +5671,20 @@
|
|||||||
"yaml": "^2.2.2"
|
"yaml": "^2.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fastify/swagger-ui/node_modules/@fastify/static": {
|
||||||
|
"version": "6.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fastify/static/-/static-6.12.0.tgz",
|
||||||
|
"integrity": "sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@fastify/accept-negotiator": "^1.0.0",
|
||||||
|
"@fastify/send": "^2.0.0",
|
||||||
|
"content-disposition": "^0.5.3",
|
||||||
|
"fastify-plugin": "^4.0.0",
|
||||||
|
"glob": "^8.0.1",
|
||||||
|
"p-limit": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@google-cloud/kms": {
|
"node_modules/@google-cloud/kms": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@google-cloud/kms/-/kms-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@google-cloud/kms/-/kms-4.5.0.tgz",
|
||||||
@@ -6062,9 +6148,10 @@
|
|||||||
"integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ=="
|
"integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@lukeed/ms": {
|
"node_modules/@lukeed/ms": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz",
|
||||||
"integrity": "sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==",
|
"integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -13879,9 +13966,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.21.1",
|
"version": "4.21.2",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
@@ -13903,7 +13990,7 @@
|
|||||||
"methods": "~1.1.2",
|
"methods": "~1.1.2",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"path-to-regexp": "0.1.10",
|
"path-to-regexp": "0.1.12",
|
||||||
"proxy-addr": "~2.0.7",
|
"proxy-addr": "~2.0.7",
|
||||||
"qs": "6.13.0",
|
"qs": "6.13.0",
|
||||||
"range-parser": "~1.2.1",
|
"range-parser": "~1.2.1",
|
||||||
@@ -13918,6 +14005,10 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.10.0"
|
"node": ">= 0.10.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express-session": {
|
"node_modules/express-session": {
|
||||||
@@ -17388,15 +17479,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.7",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ai"
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"nanoid": "bin/nanoid.cjs"
|
"nanoid": "bin/nanoid.cjs"
|
||||||
},
|
},
|
||||||
@@ -18383,9 +18475,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "0.1.10",
|
"version": "0.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||||
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
|
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/path-type": {
|
"node_modules/path-type": {
|
||||||
|
@@ -134,6 +134,7 @@
|
|||||||
"@fastify/rate-limit": "^9.0.0",
|
"@fastify/rate-limit": "^9.0.0",
|
||||||
"@fastify/request-context": "^5.1.0",
|
"@fastify/request-context": "^5.1.0",
|
||||||
"@fastify/session": "^10.7.0",
|
"@fastify/session": "^10.7.0",
|
||||||
|
"@fastify/static": "^7.0.4",
|
||||||
"@fastify/swagger": "^8.14.0",
|
"@fastify/swagger": "^8.14.0",
|
||||||
"@fastify/swagger-ui": "^2.1.0",
|
"@fastify/swagger-ui": "^2.1.0",
|
||||||
"@google-cloud/kms": "^4.5.0",
|
"@google-cloud/kms": "^4.5.0",
|
||||||
|
8
backend/src/@types/knex.d.ts
vendored
@@ -218,6 +218,9 @@ import {
|
|||||||
TRateLimit,
|
TRateLimit,
|
||||||
TRateLimitInsert,
|
TRateLimitInsert,
|
||||||
TRateLimitUpdate,
|
TRateLimitUpdate,
|
||||||
|
TResourceMetadata,
|
||||||
|
TResourceMetadataInsert,
|
||||||
|
TResourceMetadataUpdate,
|
||||||
TSamlConfigs,
|
TSamlConfigs,
|
||||||
TSamlConfigsInsert,
|
TSamlConfigsInsert,
|
||||||
TSamlConfigsUpdate,
|
TSamlConfigsUpdate,
|
||||||
@@ -887,6 +890,11 @@ declare module "knex/types/tables" {
|
|||||||
TProjectSplitBackfillIdsInsert,
|
TProjectSplitBackfillIdsInsert,
|
||||||
TProjectSplitBackfillIdsUpdate
|
TProjectSplitBackfillIdsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.ResourceMetadata]: KnexOriginal.CompositeTableType<
|
||||||
|
TResourceMetadata,
|
||||||
|
TResourceMetadataInsert,
|
||||||
|
TResourceMetadataUpdate
|
||||||
|
>;
|
||||||
[TableName.AppConnection]: KnexOriginal.CompositeTableType<
|
[TableName.AppConnection]: KnexOriginal.CompositeTableType<
|
||||||
TAppConnections,
|
TAppConnections,
|
||||||
TAppConnectionsInsert,
|
TAppConnectionsInsert,
|
||||||
|
@@ -0,0 +1,40 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.ResourceMetadata))) {
|
||||||
|
await knex.schema.createTable(TableName.ResourceMetadata, (tb) => {
|
||||||
|
tb.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
tb.string("key").notNullable();
|
||||||
|
tb.string("value", 1020).notNullable();
|
||||||
|
tb.uuid("orgId").notNullable();
|
||||||
|
tb.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
|
tb.uuid("userId");
|
||||||
|
tb.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
tb.uuid("identityId");
|
||||||
|
tb.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
||||||
|
tb.uuid("secretId");
|
||||||
|
tb.foreign("secretId").references("id").inTable(TableName.SecretV2).onDelete("CASCADE");
|
||||||
|
tb.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasSecretMetadataField = await knex.schema.hasColumn(TableName.SecretApprovalRequestSecretV2, "secretMetadata");
|
||||||
|
if (!hasSecretMetadataField) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequestSecretV2, (t) => {
|
||||||
|
t.jsonb("secretMetadata");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.ResourceMetadata);
|
||||||
|
|
||||||
|
const hasSecretMetadataField = await knex.schema.hasColumn(TableName.SecretApprovalRequestSecretV2, "secretMetadata");
|
||||||
|
if (hasSecretMetadataField) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequestSecretV2, (t) => {
|
||||||
|
t.dropColumn("secretMetadata");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -71,6 +71,7 @@ export * from "./project-user-additional-privilege";
|
|||||||
export * from "./project-user-membership-roles";
|
export * from "./project-user-membership-roles";
|
||||||
export * from "./projects";
|
export * from "./projects";
|
||||||
export * from "./rate-limit";
|
export * from "./rate-limit";
|
||||||
|
export * from "./resource-metadata";
|
||||||
export * from "./saml-configs";
|
export * from "./saml-configs";
|
||||||
export * from "./scim-tokens";
|
export * from "./scim-tokens";
|
||||||
export * from "./secret-approval-policies";
|
export * from "./secret-approval-policies";
|
||||||
|
@@ -80,6 +80,7 @@ export enum TableName {
|
|||||||
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
||||||
// used by both identity and users
|
// used by both identity and users
|
||||||
IdentityMetadata = "identity_metadata",
|
IdentityMetadata = "identity_metadata",
|
||||||
|
ResourceMetadata = "resource_metadata",
|
||||||
ScimToken = "scim_tokens",
|
ScimToken = "scim_tokens",
|
||||||
AccessApprovalPolicy = "access_approval_policies",
|
AccessApprovalPolicy = "access_approval_policies",
|
||||||
AccessApprovalPolicyApprover = "access_approval_policies_approvers",
|
AccessApprovalPolicyApprover = "access_approval_policies_approvers",
|
||||||
|
24
backend/src/db/schemas/resource-metadata.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// 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 ResourceMetadataSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
key: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
orgId: z.string().uuid(),
|
||||||
|
userId: z.string().uuid().nullable().optional(),
|
||||||
|
identityId: z.string().uuid().nullable().optional(),
|
||||||
|
secretId: z.string().uuid().nullable().optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TResourceMetadata = z.infer<typeof ResourceMetadataSchema>;
|
||||||
|
export type TResourceMetadataInsert = Omit<z.input<typeof ResourceMetadataSchema>, TImmutableDBKeys>;
|
||||||
|
export type TResourceMetadataUpdate = Partial<Omit<z.input<typeof ResourceMetadataSchema>, TImmutableDBKeys>>;
|
@@ -24,7 +24,8 @@ export const SecretApprovalRequestsSecretsV2Schema = z.object({
|
|||||||
requestId: z.string().uuid(),
|
requestId: z.string().uuid(),
|
||||||
op: z.string(),
|
op: z.string(),
|
||||||
secretId: z.string().uuid().nullable().optional(),
|
secretId: z.string().uuid().nullable().optional(),
|
||||||
secretVersion: z.string().uuid().nullable().optional()
|
secretVersion: z.string().uuid().nullable().optional(),
|
||||||
|
secretMetadata: z.unknown().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalRequestsSecretsV2 = z.infer<typeof SecretApprovalRequestsSecretsV2Schema>;
|
export type TSecretApprovalRequestsSecretsV2 = z.infer<typeof SecretApprovalRequestsSecretsV2Schema>;
|
||||||
|
@@ -84,7 +84,10 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
samlConfig.audience = `spn:${ssoConfig.issuer}`;
|
samlConfig.audience = `spn:${ssoConfig.issuer}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ssoConfig.authProvider === SamlProviders.GOOGLE_SAML) {
|
if (
|
||||||
|
ssoConfig.authProvider === SamlProviders.GOOGLE_SAML ||
|
||||||
|
ssoConfig.authProvider === SamlProviders.AUTH0_SAML
|
||||||
|
) {
|
||||||
samlConfig.wantAssertionsSigned = false;
|
samlConfig.wantAssertionsSigned = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +126,10 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
`email: ${email} firstName: ${profile.firstName as string}`
|
`email: ${email} firstName: ${profile.firstName as string}`
|
||||||
);
|
);
|
||||||
|
|
||||||
throw new Error("Invalid saml request. Missing email or first name");
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Missing email or first name. Please double check your SAML attribute mapping for the selected provider."
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const userMetadata = Object.keys(profile.attributes || {})
|
const userMetadata = Object.keys(profile.attributes || {})
|
||||||
|
@@ -12,6 +12,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
|
|
||||||
const approvalRequestUser = z.object({ userId: z.string().nullable().optional() }).merge(
|
const approvalRequestUser = z.object({ userId: z.string().nullable().optional() }).merge(
|
||||||
UsersSchema.pick({
|
UsersSchema.pick({
|
||||||
@@ -274,6 +275,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
.extend({
|
.extend({
|
||||||
op: z.string(),
|
op: z.string(),
|
||||||
tags: tagSchema,
|
tags: tagSchema,
|
||||||
|
secretMetadata: ResourceMetadataSchema.nullish(),
|
||||||
secret: z
|
secret: z
|
||||||
.object({
|
.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
@@ -291,7 +293,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
secretKey: z.string(),
|
secretKey: z.string(),
|
||||||
secretValue: z.string().optional(),
|
secretValue: z.string().optional(),
|
||||||
secretComment: z.string().optional(),
|
secretComment: z.string().optional(),
|
||||||
tags: tagSchema
|
tags: tagSchema,
|
||||||
|
secretMetadata: ResourceMetadataSchema.nullish()
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
})
|
})
|
||||||
|
@@ -213,7 +213,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`;
|
const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`;
|
||||||
const approvalUrl = `${cfg.SITE_URL}/project/${project.id}/approval`;
|
const approvalUrl = `${cfg.SITE_URL}/secret-manager/${project.id}/approval`;
|
||||||
|
|
||||||
await triggerSlackNotification({
|
await triggerSlackNotification({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
@@ -100,10 +100,10 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
// Filter by date range
|
// Filter by date range
|
||||||
if (startDate) {
|
if (startDate) {
|
||||||
void sqlQuery.where(`${TableName.AuditLog}.createdAt`, ">=", startDate);
|
void sqlQuery.whereRaw(`"${TableName.AuditLog}"."createdAt" >= ?::timestamptz`, [startDate]);
|
||||||
}
|
}
|
||||||
if (endDate) {
|
if (endDate) {
|
||||||
void sqlQuery.where(`${TableName.AuditLog}.createdAt`, "<=", endDate);
|
void sqlQuery.whereRaw(`"${TableName.AuditLog}"."createdAt" <= ?::timestamptz`, [endDate]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we timeout long running queries to prevent DB resource issues (2 minutes)
|
// we timeout long running queries to prevent DB resource issues (2 minutes)
|
||||||
|
@@ -246,8 +246,7 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getOrgPlan = async ({ orgId, actor, actorId, actorOrgId, actorAuthMethod, projectId }: TOrgPlanDTO) => {
|
const getOrgPlan = async ({ orgId, actor, actorId, actorOrgId, actorAuthMethod, projectId }: TOrgPlanDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
|
||||||
const plan = await getPlan(orgId, projectId);
|
const plan = await getPlan(orgId, projectId);
|
||||||
return plan;
|
return plan;
|
||||||
};
|
};
|
||||||
|
@@ -6,7 +6,8 @@ export enum SamlProviders {
|
|||||||
AZURE_SAML = "azure-saml",
|
AZURE_SAML = "azure-saml",
|
||||||
JUMPCLOUD_SAML = "jumpcloud-saml",
|
JUMPCLOUD_SAML = "jumpcloud-saml",
|
||||||
GOOGLE_SAML = "google-saml",
|
GOOGLE_SAML = "google-saml",
|
||||||
KEYCLOAK_SAML = "keycloak-saml"
|
KEYCLOAK_SAML = "keycloak-saml",
|
||||||
|
AUTH0_SAML = "auth0-saml"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TCreateSamlCfgDTO = {
|
export type TCreateSamlCfgDTO = {
|
||||||
|
@@ -36,7 +36,7 @@ export const sendApprovalEmailsFn = async ({
|
|||||||
firstName: reviewerUser.firstName,
|
firstName: reviewerUser.firstName,
|
||||||
projectName: project.name,
|
projectName: project.name,
|
||||||
organizationName: project.organization.name,
|
organizationName: project.organization.name,
|
||||||
approvalUrl: `${cfg.SITE_URL}/project/${project.id}/approval?requestId=${secretApprovalRequest.id}`
|
approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval?requestId=${secretApprovalRequest.id}`
|
||||||
},
|
},
|
||||||
template: SmtpTemplates.SecretApprovalRequestNeedsReview
|
template: SmtpTemplates.SecretApprovalRequestNeedsReview
|
||||||
});
|
});
|
||||||
|
@@ -256,6 +256,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretVersionV2Tag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretVersionV2Tag}.${TableName.SecretTag}Id`,
|
||||||
db.ref("id").withSchema("secVerTag")
|
db.ref("id").withSchema("secVerTag")
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
.select(selectAllTableCols(TableName.SecretApprovalRequestSecretV2))
|
.select(selectAllTableCols(TableName.SecretApprovalRequestSecretV2))
|
||||||
.select({
|
.select({
|
||||||
secVerTagId: "secVerTag.id",
|
secVerTagId: "secVerTag.id",
|
||||||
@@ -279,6 +280,11 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("key").withSchema(TableName.SecretVersionV2).as("secVerKey"),
|
db.ref("key").withSchema(TableName.SecretVersionV2).as("secVerKey"),
|
||||||
db.ref("encryptedValue").withSchema(TableName.SecretVersionV2).as("secVerValue"),
|
db.ref("encryptedValue").withSchema(TableName.SecretVersionV2).as("secVerValue"),
|
||||||
db.ref("encryptedComment").withSchema(TableName.SecretVersionV2).as("secVerComment")
|
db.ref("encryptedComment").withSchema(TableName.SecretVersionV2).as("secVerComment")
|
||||||
|
)
|
||||||
|
.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")
|
||||||
);
|
);
|
||||||
const formatedDoc = sqlNestRelationships({
|
const formatedDoc = sqlNestRelationships({
|
||||||
data: doc,
|
data: doc,
|
||||||
@@ -338,9 +344,19 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "metadataId",
|
||||||
|
label: "oldSecretMetadata" as const,
|
||||||
|
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||||
|
id: metadataId,
|
||||||
|
key: metadataKey,
|
||||||
|
value: metadataValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
return formatedDoc?.map(({ secret, secretVersion, ...el }) => ({
|
return formatedDoc?.map(({ secret, secretVersion, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
secret: secret?.[0],
|
secret: secret?.[0],
|
||||||
|
@@ -22,6 +22,8 @@ import { KmsDataKey } from "@app/services/kms/kms-types";
|
|||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||||
|
import { TResourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
|
||||||
|
import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import {
|
import {
|
||||||
decryptSecretWithBot,
|
decryptSecretWithBot,
|
||||||
@@ -91,6 +93,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
|||||||
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
||||||
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
||||||
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
|
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
|
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
smtpService: Pick<TSmtpService, "sendMail">;
|
||||||
userDAL: Pick<TUserDALFactory, "find" | "findOne" | "findById">;
|
userDAL: Pick<TUserDALFactory, "find" | "findOne" | "findById">;
|
||||||
@@ -138,7 +141,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretVersionTagV2BridgeDAL,
|
secretVersionTagV2BridgeDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
projectSlackConfigDAL
|
projectSlackConfigDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
}: TSecretApprovalRequestServiceFactoryDep) => {
|
||||||
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
@@ -241,6 +245,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
secretKey: el.key,
|
secretKey: el.key,
|
||||||
id: el.id,
|
id: el.id,
|
||||||
version: el.version,
|
version: el.version,
|
||||||
|
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
|
||||||
secretValue: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "",
|
secretValue: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "",
|
||||||
secretComment: el.encryptedComment
|
secretComment: el.encryptedComment
|
||||||
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
|
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
|
||||||
@@ -269,7 +274,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
secretComment: el.secretVersion.encryptedComment
|
secretComment: el.secretVersion.encryptedComment
|
||||||
? secretManagerDecryptor({ cipherTextBlob: el.secretVersion.encryptedComment }).toString()
|
? secretManagerDecryptor({ cipherTextBlob: el.secretVersion.encryptedComment }).toString()
|
||||||
: "",
|
: "",
|
||||||
tags: el.secretVersion.tags
|
tags: el.secretVersion.tags,
|
||||||
|
secretMetadata: el.oldSecretMetadata as ResourceMetadataDTO
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}));
|
}));
|
||||||
@@ -543,6 +549,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
? await fnSecretV2BridgeBulkInsert({
|
? await fnSecretV2BridgeBulkInsert({
|
||||||
tx,
|
tx,
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
inputSecrets: secretCreationCommits.map((el) => ({
|
inputSecrets: secretCreationCommits.map((el) => ({
|
||||||
tagIds: el?.tags.map(({ id }) => id),
|
tagIds: el?.tags.map(({ id }) => id),
|
||||||
version: 1,
|
version: 1,
|
||||||
@@ -550,6 +557,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
encryptedValue: el.encryptedValue,
|
encryptedValue: el.encryptedValue,
|
||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
key: el.key,
|
key: el.key,
|
||||||
|
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
|
||||||
references: el.encryptedValue
|
references: el.encryptedValue
|
||||||
? getAllSecretReferencesV2Bridge(
|
? getAllSecretReferencesV2Bridge(
|
||||||
secretManagerDecryptor({
|
secretManagerDecryptor({
|
||||||
@@ -559,6 +567,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
: [],
|
: [],
|
||||||
type: SecretType.Shared
|
type: SecretType.Shared
|
||||||
})),
|
})),
|
||||||
|
resourceMetadataDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
@@ -568,6 +577,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const updatedSecrets = secretUpdationCommits.length
|
const updatedSecrets = secretUpdationCommits.length
|
||||||
? await fnSecretV2BridgeBulkUpdate({
|
? await fnSecretV2BridgeBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
tx,
|
tx,
|
||||||
inputSecrets: secretUpdationCommits.map((el) => {
|
inputSecrets: secretUpdationCommits.map((el) => {
|
||||||
const encryptedValue =
|
const encryptedValue =
|
||||||
@@ -592,6 +602,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
key: el.key,
|
key: el.key,
|
||||||
tags: el?.tags.map(({ id }) => id),
|
tags: el?.tags.map(({ id }) => id),
|
||||||
|
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
|
||||||
...encryptedValue
|
...encryptedValue
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -599,7 +610,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL: secretVersionTagV2BridgeDAL
|
secretVersionTagDAL: secretVersionTagV2BridgeDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
const deletedSecret = secretDeletionCommits.length
|
const deletedSecret = secretDeletionCommits.length
|
||||||
@@ -824,6 +836,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
}
|
}
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretPath: folder.path,
|
secretPath: folder.path,
|
||||||
environmentSlug: folder.environmentSlug,
|
environmentSlug: folder.environmentSlug,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -852,7 +865,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
bypassReason,
|
bypassReason,
|
||||||
secretPath: policy.secretPath,
|
secretPath: policy.secretPath,
|
||||||
environment: env.name,
|
environment: env.name,
|
||||||
approvalUrl: `${cfg.SITE_URL}/project/${project.id}/approval`
|
approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval`
|
||||||
},
|
},
|
||||||
template: SmtpTemplates.AccessSecretRequestBypassed
|
template: SmtpTemplates.AccessSecretRequestBypassed
|
||||||
});
|
});
|
||||||
@@ -1208,6 +1221,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
),
|
),
|
||||||
skipMultilineEncoding: createdSecret.skipMultilineEncoding,
|
skipMultilineEncoding: createdSecret.skipMultilineEncoding,
|
||||||
key: createdSecret.secretKey,
|
key: createdSecret.secretKey,
|
||||||
|
secretMetadata: createdSecret.secretMetadata,
|
||||||
type: SecretType.Shared
|
type: SecretType.Shared
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
@@ -1263,12 +1277,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
reminderNote,
|
reminderNote,
|
||||||
secretComment,
|
secretComment,
|
||||||
metadata,
|
metadata,
|
||||||
skipMultilineEncoding
|
skipMultilineEncoding,
|
||||||
|
secretMetadata
|
||||||
}) => {
|
}) => {
|
||||||
const secretId = updatingSecretsGroupByKey[secretKey][0].id;
|
const secretId = updatingSecretsGroupByKey[secretKey][0].id;
|
||||||
if (tagIds?.length) commitTagIds[secretKey] = tagIds;
|
if (tagIds?.length) commitTagIds[secretKey] = tagIds;
|
||||||
return {
|
return {
|
||||||
...latestSecretVersions[secretId],
|
...latestSecretVersions[secretId],
|
||||||
|
secretMetadata,
|
||||||
key: newSecretName || secretKey,
|
key: newSecretName || secretKey,
|
||||||
encryptedComment: setKnexStringValue(
|
encryptedComment: setKnexStringValue(
|
||||||
secretComment,
|
secretComment,
|
||||||
@@ -1370,7 +1386,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
reminderRepeatDays,
|
reminderRepeatDays,
|
||||||
encryptedValue,
|
encryptedValue,
|
||||||
secretId,
|
secretId,
|
||||||
secretVersion
|
secretVersion,
|
||||||
|
secretMetadata
|
||||||
}) => ({
|
}) => ({
|
||||||
version,
|
version,
|
||||||
requestId: doc.id,
|
requestId: doc.id,
|
||||||
@@ -1383,7 +1400,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
reminderRepeatDays,
|
reminderRepeatDays,
|
||||||
reminderNote,
|
reminderNote,
|
||||||
encryptedComment,
|
encryptedComment,
|
||||||
key
|
key,
|
||||||
|
secretMetadata: JSON.stringify(secretMetadata)
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
tx
|
tx
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { TImmutableDBKeys, TSecretApprovalPolicies, TSecretApprovalRequestsSecrets } from "@app/db/schemas";
|
import { TImmutableDBKeys, TSecretApprovalPolicies, TSecretApprovalRequestsSecrets } from "@app/db/schemas";
|
||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
import { SecretOperations } from "@app/services/secret/secret-types";
|
import { SecretOperations } from "@app/services/secret/secret-types";
|
||||||
|
|
||||||
export enum RequestState {
|
export enum RequestState {
|
||||||
@@ -34,6 +35,7 @@ export type TApprovalCreateSecretV2Bridge = {
|
|||||||
reminderRepeatDays?: number | null;
|
reminderRepeatDays?: number | null;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
metadata?: Record<string, string>;
|
metadata?: Record<string, string>;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -13,6 +13,8 @@ import { ActorType } from "@app/services/auth/auth-type";
|
|||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||||
|
import { TResourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
|
||||||
|
import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns";
|
import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns";
|
||||||
import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue";
|
import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue";
|
||||||
@@ -56,6 +58,7 @@ type TSecretReplicationServiceFactoryDep = {
|
|||||||
>;
|
>;
|
||||||
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "find" | "insertMany">;
|
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "find" | "insertMany">;
|
||||||
secretVersionV2TagBridgeDAL: Pick<TSecretVersionV2TagDALFactory, "find" | "insertMany">;
|
secretVersionV2TagBridgeDAL: Pick<TSecretVersionV2TagDALFactory, "find" | "insertMany">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "replicateSecrets">;
|
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "replicateSecrets">;
|
||||||
queueService: Pick<TQueueServiceFactory, "start" | "listen" | "queue" | "stopJobById">;
|
queueService: Pick<TQueueServiceFactory, "start" | "listen" | "queue" | "stopJobById">;
|
||||||
secretApprovalPolicyService: Pick<TSecretApprovalPolicyServiceFactory, "getSecretApprovalPolicy">;
|
secretApprovalPolicyService: Pick<TSecretApprovalPolicyServiceFactory, "getSecretApprovalPolicy">;
|
||||||
@@ -121,7 +124,8 @@ export const secretReplicationServiceFactory = ({
|
|||||||
secretVersionV2TagBridgeDAL,
|
secretVersionV2TagBridgeDAL,
|
||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
kmsService
|
kmsService,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TSecretReplicationServiceFactoryDep) => {
|
}: TSecretReplicationServiceFactoryDep) => {
|
||||||
const $getReplicatedSecrets = (
|
const $getReplicatedSecrets = (
|
||||||
botKey: string,
|
botKey: string,
|
||||||
@@ -151,8 +155,10 @@ export const secretReplicationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const $getReplicatedSecretsV2 = (
|
const $getReplicatedSecretsV2 = (
|
||||||
localSecrets: (TSecretsV2 & { secretKey: string; secretValue?: string })[],
|
localSecrets: (TSecretsV2 & { secretKey: string; secretValue?: string; secretMetadata?: ResourceMetadataDTO })[],
|
||||||
importedSecrets: { secrets: (TSecretsV2 & { secretKey: string; secretValue?: string })[] }[]
|
importedSecrets: {
|
||||||
|
secrets: (TSecretsV2 & { secretKey: string; secretValue?: string; secretMetadata?: ResourceMetadataDTO })[];
|
||||||
|
}[]
|
||||||
) => {
|
) => {
|
||||||
const deDupe = new Set<string>();
|
const deDupe = new Set<string>();
|
||||||
const secrets = [...localSecrets];
|
const secrets = [...localSecrets];
|
||||||
@@ -178,6 +184,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
secretPath,
|
secretPath,
|
||||||
environmentSlug,
|
environmentSlug,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
pickOnlyImportIds,
|
pickOnlyImportIds,
|
||||||
@@ -222,6 +229,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
.map(({ folderId }) =>
|
.map(({ folderId }) =>
|
||||||
secretQueueService.replicateSecrets({
|
secretQueueService.replicateSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
secretPath: foldersGroupedById[folderId][0]?.path as string,
|
secretPath: foldersGroupedById[folderId][0]?.path as string,
|
||||||
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
|
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -267,6 +275,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString()
|
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString()
|
||||||
: undefined
|
: undefined
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const sourceSecrets = $getReplicatedSecretsV2(sourceDecryptedLocalSecrets, sourceImportedSecrets);
|
const sourceSecrets = $getReplicatedSecretsV2(sourceDecryptedLocalSecrets, sourceImportedSecrets);
|
||||||
const sourceSecretsGroupByKey = groupBy(sourceSecrets, (i) => i.key);
|
const sourceSecretsGroupByKey = groupBy(sourceSecrets, (i) => i.key);
|
||||||
|
|
||||||
@@ -333,13 +342,29 @@ export const secretReplicationServiceFactory = ({
|
|||||||
.map((el) => ({ ...el, operation: SecretOperations.Create })); // rewrite update ops to create
|
.map((el) => ({ ...el, operation: SecretOperations.Create })); // rewrite update ops to create
|
||||||
|
|
||||||
const locallyUpdatedSecrets = sourceSecrets
|
const locallyUpdatedSecrets = sourceSecrets
|
||||||
.filter(
|
.filter(({ key, secretKey, secretValue, secretMetadata }) => {
|
||||||
({ key, secretKey, secretValue }) =>
|
const sourceSecretMetadataJson = JSON.stringify(
|
||||||
|
(secretMetadata ?? []).map((entry) => ({
|
||||||
|
key: entry.key,
|
||||||
|
value: entry.value
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
const destinationSecretMetadataJson = JSON.stringify(
|
||||||
|
(destinationLocalSecretsGroupedByKey[key]?.[0]?.secretMetadata ?? []).map((entry) => ({
|
||||||
|
key: entry.key,
|
||||||
|
value: entry.value
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
destinationLocalSecretsGroupedByKey[key]?.[0] &&
|
destinationLocalSecretsGroupedByKey[key]?.[0] &&
|
||||||
// if key or value changed
|
// if key or value changed
|
||||||
(destinationLocalSecretsGroupedByKey[key]?.[0]?.secretKey !== secretKey ||
|
(destinationLocalSecretsGroupedByKey[key]?.[0]?.secretKey !== secretKey ||
|
||||||
destinationLocalSecretsGroupedByKey[key]?.[0]?.secretValue !== secretValue)
|
destinationLocalSecretsGroupedByKey[key]?.[0]?.secretValue !== secretValue ||
|
||||||
)
|
sourceSecretMetadataJson !== destinationSecretMetadataJson)
|
||||||
|
);
|
||||||
|
})
|
||||||
.map((el) => ({ ...el, operation: SecretOperations.Update })); // rewrite update ops to create
|
.map((el) => ({ ...el, operation: SecretOperations.Update })); // rewrite update ops to create
|
||||||
|
|
||||||
const locallyDeletedSecrets = destinationLocalSecrets
|
const locallyDeletedSecrets = destinationLocalSecrets
|
||||||
@@ -387,6 +412,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
op: operation,
|
op: operation,
|
||||||
requestId: approvalRequestDoc.id,
|
requestId: approvalRequestDoc.id,
|
||||||
metadata: doc.metadata,
|
metadata: doc.metadata,
|
||||||
|
secretMetadata: JSON.stringify(doc.secretMetadata),
|
||||||
key: doc.key,
|
key: doc.key,
|
||||||
encryptedValue: doc.encryptedValue,
|
encryptedValue: doc.encryptedValue,
|
||||||
encryptedComment: doc.encryptedComment,
|
encryptedComment: doc.encryptedComment,
|
||||||
@@ -406,10 +432,12 @@ export const secretReplicationServiceFactory = ({
|
|||||||
if (locallyCreatedSecrets.length) {
|
if (locallyCreatedSecrets.length) {
|
||||||
await fnSecretV2BridgeBulkInsert({
|
await fnSecretV2BridgeBulkInsert({
|
||||||
folderId: destinationReplicationFolderId,
|
folderId: destinationReplicationFolderId,
|
||||||
|
orgId,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
tx,
|
tx,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
|
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
|
||||||
inputSecrets: locallyCreatedSecrets.map((doc) => {
|
inputSecrets: locallyCreatedSecrets.map((doc) => {
|
||||||
return {
|
return {
|
||||||
@@ -419,6 +447,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
encryptedValue: doc.encryptedValue,
|
encryptedValue: doc.encryptedValue,
|
||||||
encryptedComment: doc.encryptedComment,
|
encryptedComment: doc.encryptedComment,
|
||||||
skipMultilineEncoding: doc.skipMultilineEncoding,
|
skipMultilineEncoding: doc.skipMultilineEncoding,
|
||||||
|
secretMetadata: doc.secretMetadata,
|
||||||
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
|
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@@ -426,10 +455,12 @@ export const secretReplicationServiceFactory = ({
|
|||||||
}
|
}
|
||||||
if (locallyUpdatedSecrets.length) {
|
if (locallyUpdatedSecrets.length) {
|
||||||
await fnSecretV2BridgeBulkUpdate({
|
await fnSecretV2BridgeBulkUpdate({
|
||||||
|
orgId,
|
||||||
folderId: destinationReplicationFolderId,
|
folderId: destinationReplicationFolderId,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
tx,
|
tx,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
|
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
|
||||||
inputSecrets: locallyUpdatedSecrets.map((doc) => {
|
inputSecrets: locallyUpdatedSecrets.map((doc) => {
|
||||||
@@ -445,6 +476,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
encryptedValue: doc.encryptedValue as Buffer,
|
encryptedValue: doc.encryptedValue as Buffer,
|
||||||
encryptedComment: doc.encryptedComment,
|
encryptedComment: doc.encryptedComment,
|
||||||
skipMultilineEncoding: doc.skipMultilineEncoding,
|
skipMultilineEncoding: doc.skipMultilineEncoding,
|
||||||
|
secretMetadata: doc.secretMetadata,
|
||||||
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
|
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -466,6 +498,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
|
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
secretPath: destinationFolder.path,
|
secretPath: destinationFolder.path,
|
||||||
environmentSlug: destinationFolder.environmentSlug,
|
environmentSlug: destinationFolder.environmentSlug,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -751,6 +784,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
|
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
secretPath: destinationFolder.path,
|
secretPath: destinationFolder.path,
|
||||||
environmentSlug: destinationFolder.environmentSlug,
|
environmentSlug: destinationFolder.environmentSlug,
|
||||||
actorId,
|
actorId,
|
||||||
|
@@ -1150,7 +1150,8 @@ export const INTEGRATION = {
|
|||||||
shouldMaskSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Masked'.",
|
shouldMaskSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Masked'.",
|
||||||
shouldProtectSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Protected'.",
|
shouldProtectSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Protected'.",
|
||||||
shouldEnableDelete: "The flag to enable deletion of secrets.",
|
shouldEnableDelete: "The flag to enable deletion of secrets.",
|
||||||
octopusDeployScopeValues: "Specifies the scope values to set on synced secrets to Octopus Deploy."
|
octopusDeployScopeValues: "Specifies the scope values to set on synced secrets to Octopus Deploy.",
|
||||||
|
metadataSyncMode: "The mode for syncing metadata to external system"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UPDATE: {
|
UPDATE: {
|
||||||
|
@@ -157,6 +157,8 @@ const envSchema = z
|
|||||||
INFISICAL_CLOUD: zodStrBool.default("false"),
|
INFISICAL_CLOUD: zodStrBool.default("false"),
|
||||||
MAINTENANCE_MODE: zodStrBool.default("false"),
|
MAINTENANCE_MODE: zodStrBool.default("false"),
|
||||||
CAPTCHA_SECRET: zpStr(z.string().optional()),
|
CAPTCHA_SECRET: zpStr(z.string().optional()),
|
||||||
|
CAPTCHA_SITE_KEY: zpStr(z.string().optional()),
|
||||||
|
INTERCOM_ID: zpStr(z.string().optional()),
|
||||||
|
|
||||||
// TELEMETRY
|
// TELEMETRY
|
||||||
OTEL_TELEMETRY_COLLECTION_ENABLED: zodStrBool.default("false"),
|
OTEL_TELEMETRY_COLLECTION_ENABLED: zodStrBool.default("false"),
|
||||||
|
@@ -27,10 +27,10 @@ import { globalRateLimiterCfg } from "./config/rateLimiter";
|
|||||||
import { addErrorsToResponseSchemas } from "./plugins/add-errors-to-response-schemas";
|
import { addErrorsToResponseSchemas } from "./plugins/add-errors-to-response-schemas";
|
||||||
import { apiMetrics } from "./plugins/api-metrics";
|
import { apiMetrics } from "./plugins/api-metrics";
|
||||||
import { fastifyErrHandler } from "./plugins/error-handler";
|
import { fastifyErrHandler } from "./plugins/error-handler";
|
||||||
import { registerExternalNextjs } from "./plugins/external-nextjs";
|
|
||||||
import { serializerCompiler, validatorCompiler, ZodTypeProvider } from "./plugins/fastify-zod";
|
import { serializerCompiler, validatorCompiler, ZodTypeProvider } from "./plugins/fastify-zod";
|
||||||
import { fastifyIp } from "./plugins/ip";
|
import { fastifyIp } from "./plugins/ip";
|
||||||
import { maintenanceMode } from "./plugins/maintenanceMode";
|
import { maintenanceMode } from "./plugins/maintenanceMode";
|
||||||
|
import { registerServeUI } from "./plugins/serve-ui";
|
||||||
import { fastifySwagger } from "./plugins/swagger";
|
import { fastifySwagger } from "./plugins/swagger";
|
||||||
import { registerRoutes } from "./routes";
|
import { registerRoutes } from "./routes";
|
||||||
|
|
||||||
@@ -120,13 +120,10 @@ export const main = async ({ db, hsmModule, auditLogDb, smtp, logger, queue, key
|
|||||||
|
|
||||||
await server.register(registerRoutes, { smtp, queue, db, auditLogDb, keyStore, hsmModule });
|
await server.register(registerRoutes, { smtp, queue, db, auditLogDb, keyStore, hsmModule });
|
||||||
|
|
||||||
if (appCfg.isProductionMode) {
|
await server.register(registerServeUI, {
|
||||||
await server.register(registerExternalNextjs, {
|
standaloneMode: appCfg.STANDALONE_MODE || IS_PACKAGED,
|
||||||
standaloneMode: appCfg.STANDALONE_MODE || IS_PACKAGED,
|
dir: path.join(__dirname, IS_PACKAGED ? "../../../" : "../../")
|
||||||
dir: path.join(__dirname, IS_PACKAGED ? "../../../" : "../../"),
|
});
|
||||||
port: appCfg.PORT
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await server.ready();
|
await server.ready();
|
||||||
server.swagger();
|
server.swagger();
|
||||||
|
@@ -1,76 +0,0 @@
|
|||||||
// this plugins allows to run infisical in standalone mode
|
|
||||||
// standalone mode = infisical backend and nextjs frontend in one server
|
|
||||||
// this way users don't need to deploy two things
|
|
||||||
import path from "node:path";
|
|
||||||
|
|
||||||
import { IS_PACKAGED } from "@app/lib/config/env";
|
|
||||||
|
|
||||||
// to enabled this u need to set standalone mode to true
|
|
||||||
export const registerExternalNextjs = async (
|
|
||||||
server: FastifyZodProvider,
|
|
||||||
{
|
|
||||||
standaloneMode,
|
|
||||||
dir,
|
|
||||||
port
|
|
||||||
}: {
|
|
||||||
standaloneMode?: boolean;
|
|
||||||
dir: string;
|
|
||||||
port: number;
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
if (standaloneMode) {
|
|
||||||
const frontendName = IS_PACKAGED ? "frontend" : "frontend-build";
|
|
||||||
const nextJsBuildPath = path.join(dir, frontendName);
|
|
||||||
|
|
||||||
const { default: conf } = (await import(
|
|
||||||
path.join(dir, `${frontendName}/.next/required-server-files.json`),
|
|
||||||
// @ts-expect-error type
|
|
||||||
{
|
|
||||||
assert: { type: "json" }
|
|
||||||
}
|
|
||||||
)) as { default: { config: string } };
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
let NextServer: any;
|
|
||||||
|
|
||||||
if (!IS_PACKAGED) {
|
|
||||||
/* eslint-disable */
|
|
||||||
const { default: nextServer } = (
|
|
||||||
await import(path.join(dir, `${frontendName}/node_modules/next/dist/server/next-server.js`))
|
|
||||||
).default;
|
|
||||||
|
|
||||||
NextServer = nextServer;
|
|
||||||
} else {
|
|
||||||
/* eslint-disable */
|
|
||||||
const nextServer = await import(path.join(dir, `${frontendName}/node_modules/next/dist/server/next-server.js`));
|
|
||||||
|
|
||||||
NextServer = nextServer.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextApp = new NextServer({
|
|
||||||
dev: false,
|
|
||||||
dir: nextJsBuildPath,
|
|
||||||
port,
|
|
||||||
conf: conf.config,
|
|
||||||
hostname: "local",
|
|
||||||
customServer: false
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: ["GET", "PUT", "PATCH", "POST", "DELETE"],
|
|
||||||
url: "/*",
|
|
||||||
schema: {
|
|
||||||
hide: true
|
|
||||||
},
|
|
||||||
handler: (req, res) =>
|
|
||||||
nextApp
|
|
||||||
.getRequestHandler()(req.raw, res.raw)
|
|
||||||
.then(() => {
|
|
||||||
res.hijack();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
server.addHook("onClose", () => nextApp.close());
|
|
||||||
await nextApp.prepare();
|
|
||||||
/* eslint-enable */
|
|
||||||
}
|
|
||||||
};
|
|
64
backend/src/server/plugins/serve-ui.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
import staticServe from "@fastify/static";
|
||||||
|
|
||||||
|
import { getConfig, IS_PACKAGED } from "@app/lib/config/env";
|
||||||
|
|
||||||
|
// to enabled this u need to set standalone mode to true
|
||||||
|
export const registerServeUI = async (
|
||||||
|
server: FastifyZodProvider,
|
||||||
|
{
|
||||||
|
standaloneMode,
|
||||||
|
dir
|
||||||
|
}: {
|
||||||
|
standaloneMode?: boolean;
|
||||||
|
dir: string;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
// use this only for frontend runtime static non-sensitive configuration in standalone mode
|
||||||
|
// that app needs before loading like posthog dsn key
|
||||||
|
// for most of the other usecase use server config
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/runtime-ui-env.js",
|
||||||
|
schema: {
|
||||||
|
hide: true
|
||||||
|
},
|
||||||
|
handler: (_req, res) => {
|
||||||
|
const appCfg = getConfig();
|
||||||
|
void res.type("application/javascript");
|
||||||
|
const config = {
|
||||||
|
CAPTCHA_SITE_KEY: appCfg.CAPTCHA_SITE_KEY,
|
||||||
|
POSTHOG_API_KEY: appCfg.POSTHOG_PROJECT_API_KEY,
|
||||||
|
INTERCOM_ID: appCfg.INTERCOM_ID,
|
||||||
|
TELEMETRY_CAPTURING_ENABLED: appCfg.TELEMETRY_ENABLED
|
||||||
|
};
|
||||||
|
const js = `window.__INFISICAL_RUNTIME_ENV__ = Object.freeze(${JSON.stringify(config)});`;
|
||||||
|
void res.send(js);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (standaloneMode) {
|
||||||
|
const frontendName = IS_PACKAGED ? "frontend" : "frontend-build";
|
||||||
|
const frontendPath = path.join(dir, frontendName);
|
||||||
|
await server.register(staticServe, {
|
||||||
|
root: frontendPath,
|
||||||
|
wildcard: false
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/*",
|
||||||
|
schema: {
|
||||||
|
hide: true
|
||||||
|
},
|
||||||
|
handler: (request, reply) => {
|
||||||
|
if (request.url.startsWith("/api")) {
|
||||||
|
reply.callNotFound();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void reply.sendFile("index.html");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@@ -181,6 +181,7 @@ import { projectUserMembershipRoleDALFactory } from "@app/services/project-membe
|
|||||||
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
||||||
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
||||||
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
|
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
|
||||||
|
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
|
||||||
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import { secretQueueFactory } from "@app/services/secret/secret-queue";
|
import { secretQueueFactory } from "@app/services/secret/secret-queue";
|
||||||
import { secretServiceFactory } from "@app/services/secret/secret-service";
|
import { secretServiceFactory } from "@app/services/secret/secret-service";
|
||||||
@@ -374,6 +375,7 @@ export const registerRoutes = async (
|
|||||||
const externalGroupOrgRoleMappingDAL = externalGroupOrgRoleMappingDALFactory(db);
|
const externalGroupOrgRoleMappingDAL = externalGroupOrgRoleMappingDALFactory(db);
|
||||||
|
|
||||||
const projectTemplateDAL = projectTemplateDALFactory(db);
|
const projectTemplateDAL = projectTemplateDALFactory(db);
|
||||||
|
const resourceMetadataDAL = resourceMetadataDALFactory(db);
|
||||||
|
|
||||||
const permissionService = permissionServiceFactory({
|
const permissionService = permissionServiceFactory({
|
||||||
permissionDAL,
|
permissionDAL,
|
||||||
@@ -854,7 +856,8 @@ export const registerRoutes = async (
|
|||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectUserMembershipRoleDAL,
|
projectUserMembershipRoleDAL,
|
||||||
orgService
|
orgService,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectService = projectServiceFactory({
|
const projectService = projectServiceFactory({
|
||||||
@@ -980,7 +983,8 @@ export const registerRoutes = async (
|
|||||||
secretApprovalPolicyService,
|
secretApprovalPolicyService,
|
||||||
secretApprovalRequestSecretDAL,
|
secretApprovalRequestSecretDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
snapshotService
|
snapshotService,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretApprovalRequestService = secretApprovalRequestServiceFactory({
|
const secretApprovalRequestService = secretApprovalRequestServiceFactory({
|
||||||
@@ -1007,7 +1011,8 @@ export const registerRoutes = async (
|
|||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
projectSlackConfigDAL
|
projectSlackConfigDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretService = secretServiceFactory({
|
const secretService = secretServiceFactory({
|
||||||
@@ -1086,8 +1091,10 @@ export const registerRoutes = async (
|
|||||||
kmsService,
|
kmsService,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
secretVersionV2TagBridgeDAL: secretVersionTagV2BridgeDAL,
|
secretVersionV2TagBridgeDAL: secretVersionTagV2BridgeDAL,
|
||||||
secretVersionV2BridgeDAL
|
secretVersionV2BridgeDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretRotationQueue = secretRotationQueueFactory({
|
const secretRotationQueue = secretRotationQueueFactory({
|
||||||
telemetryService,
|
telemetryService,
|
||||||
secretRotationDAL,
|
secretRotationDAL,
|
||||||
@@ -1339,7 +1346,8 @@ export const registerRoutes = async (
|
|||||||
folderDAL,
|
folderDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
queueService,
|
queueService,
|
||||||
secretV2BridgeService
|
secretV2BridgeService,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const migrationService = externalMigrationServiceFactory({
|
const migrationService = externalMigrationServiceFactory({
|
||||||
|
@@ -63,7 +63,8 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
|||||||
schema: {
|
schema: {
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
token: z.string()
|
token: z.string(),
|
||||||
|
organizationId: z.string().optional()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -115,7 +116,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
|
|||||||
{ expiresIn: appCfg.JWT_AUTH_LIFETIME }
|
{ expiresIn: appCfg.JWT_AUTH_LIFETIME }
|
||||||
);
|
);
|
||||||
|
|
||||||
return { token };
|
return { token, organizationId: decodedToken.organizationId };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@@ -17,6 +17,7 @@ import { getUserAgentType } from "@app/server/plugins/audit-log";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedDynamicSecretSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedDynamicSecretSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
import { SecretsOrderBy } from "@app/services/secret/secret-types";
|
import { SecretsOrderBy } from "@app/services/secret/secret-types";
|
||||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||||
|
|
||||||
@@ -116,6 +117,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
@@ -408,6 +410,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
@@ -693,6 +696,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
@@ -864,6 +868,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
|
@@ -331,12 +331,8 @@ export const registerSlackRouter = async (server: FastifyZodProvider) => {
|
|||||||
failureAsync: async () => {
|
failureAsync: async () => {
|
||||||
return res.redirect(appCfg.SITE_URL as string);
|
return res.redirect(appCfg.SITE_URL as string);
|
||||||
},
|
},
|
||||||
successAsync: async (installation) => {
|
successAsync: async () => {
|
||||||
const metadata = JSON.parse(installation.metadata || "") as {
|
return res.redirect(`${appCfg.SITE_URL}/organization/settings?selectedTab=workflow-integrations`);
|
||||||
orgId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
return res.redirect(`${appCfg.SITE_URL}/org/${metadata.orgId}/settings?selectedTab=workflow-integrations`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ import { getUserAgentType } from "@app/server/plugins/audit-log";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ProjectFilterType } from "@app/services/project/project-types";
|
import { ProjectFilterType } from "@app/services/project/project-types";
|
||||||
|
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
import { SecretOperations, SecretProtectionType } from "@app/services/secret/secret-types";
|
import { SecretOperations, SecretProtectionType } from "@app/services/secret/secret-types";
|
||||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||||
|
|
||||||
@@ -205,6 +206,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
@@ -220,7 +222,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretPath: z.string(),
|
secretPath: z.string(),
|
||||||
environment: z.string(),
|
environment: z.string(),
|
||||||
folderId: z.string().optional(),
|
folderId: z.string().optional(),
|
||||||
secrets: secretRawSchema.omit({ createdAt: true, updatedAt: true }).array()
|
secrets: secretRawSchema
|
||||||
|
.omit({ createdAt: true, updatedAt: true })
|
||||||
|
.extend({
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -348,7 +355,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
.extend({ name: z.string() })
|
.extend({ name: z.string() })
|
||||||
.array()
|
.array()
|
||||||
.optional()
|
.optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -450,6 +458,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
|
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
|
||||||
.describe(RAW_SECRETS.CREATE.secretValue),
|
.describe(RAW_SECRETS.CREATE.secretValue),
|
||||||
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds),
|
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds),
|
||||||
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
||||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type),
|
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type),
|
||||||
@@ -484,6 +493,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretValue: req.body.secretValue,
|
secretValue: req.body.secretValue,
|
||||||
skipMultilineEncoding: req.body.skipMultilineEncoding,
|
skipMultilineEncoding: req.body.skipMultilineEncoding,
|
||||||
secretComment: req.body.secretComment,
|
secretComment: req.body.secretComment,
|
||||||
|
secretMetadata: req.body.secretMetadata,
|
||||||
tagIds: req.body.tagIds,
|
tagIds: req.body.tagIds,
|
||||||
secretReminderNote: req.body.secretReminderNote,
|
secretReminderNote: req.body.secretReminderNote,
|
||||||
secretReminderRepeatDays: req.body.secretReminderRepeatDays
|
secretReminderRepeatDays: req.body.secretReminderRepeatDays
|
||||||
@@ -558,6 +568,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type),
|
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type),
|
||||||
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
|
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
|
||||||
metadata: z.record(z.string()).optional(),
|
metadata: z.record(z.string()).optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
|
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
|
||||||
secretReminderRepeatDays: z
|
secretReminderRepeatDays: z
|
||||||
.number()
|
.number()
|
||||||
@@ -595,8 +606,10 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretReminderNote: req.body.secretReminderNote,
|
secretReminderNote: req.body.secretReminderNote,
|
||||||
metadata: req.body.metadata,
|
metadata: req.body.metadata,
|
||||||
newSecretName: req.body.newSecretName,
|
newSecretName: req.body.newSecretName,
|
||||||
secretComment: req.body.secretComment
|
secretComment: req.body.secretComment,
|
||||||
|
secretMetadata: req.body.secretMetadata
|
||||||
});
|
});
|
||||||
|
|
||||||
if (secretOperation.type === SecretProtectionType.Approval) {
|
if (secretOperation.type === SecretProtectionType.Approval) {
|
||||||
return { approval: secretOperation.approval };
|
return { approval: secretOperation.approval };
|
||||||
}
|
}
|
||||||
@@ -1850,6 +1863,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
||||||
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
||||||
metadata: z.record(z.string()).optional(),
|
metadata: z.record(z.string()).optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds)
|
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds)
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
@@ -1952,6 +1966,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
newSecretName: z.string().min(1).optional().describe(RAW_SECRETS.UPDATE.newSecretName),
|
newSecretName: z.string().min(1).optional().describe(RAW_SECRETS.UPDATE.newSecretName),
|
||||||
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
|
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
|
||||||
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
|
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
secretReminderRepeatDays: z
|
secretReminderRepeatDays: z
|
||||||
.number()
|
.number()
|
||||||
.optional()
|
.optional()
|
||||||
|
@@ -16,6 +16,7 @@ import { TProjectDALFactory } from "../project/project-dal";
|
|||||||
import { TProjectServiceFactory } from "../project/project-service";
|
import { TProjectServiceFactory } from "../project/project-service";
|
||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
||||||
|
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||||
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
@@ -35,6 +36,8 @@ export type TImportDataIntoInfisicalDTO = {
|
|||||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "create">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "create">;
|
||||||
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany" | "create">;
|
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany" | "create">;
|
||||||
|
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
|
||||||
|
|
||||||
folderDAL: Pick<TSecretFolderDALFactory, "create" | "findBySecretPath" | "findById">;
|
folderDAL: Pick<TSecretFolderDALFactory, "create" | "findBySecretPath" | "findById">;
|
||||||
projectService: Pick<TProjectServiceFactory, "createProject">;
|
projectService: Pick<TProjectServiceFactory, "createProject">;
|
||||||
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
||||||
@@ -503,6 +506,7 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
input: { data, actor, actorId, actorOrgId, actorAuthMethod }
|
input: { data, actor, actorId, actorOrgId, actorAuthMethod }
|
||||||
}: TImportDataIntoInfisicalDTO) => {
|
}: TImportDataIntoInfisicalDTO) => {
|
||||||
// Import data to infisical
|
// Import data to infisical
|
||||||
@@ -762,6 +766,8 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
folderId: selectedFolder.id,
|
folderId: selectedFolder.id,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
|
@@ -8,6 +8,7 @@ import { TProjectDALFactory } from "../project/project-dal";
|
|||||||
import { TProjectServiceFactory } from "../project/project-service";
|
import { TProjectServiceFactory } from "../project/project-service";
|
||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
||||||
|
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||||
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
@@ -35,6 +36,8 @@ export type TExternalMigrationQueueFactoryDep = {
|
|||||||
projectService: Pick<TProjectServiceFactory, "createProject">;
|
projectService: Pick<TProjectServiceFactory, "createProject">;
|
||||||
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
||||||
secretV2BridgeService: Pick<TSecretV2BridgeServiceFactory, "createManySecret">;
|
secretV2BridgeService: Pick<TSecretV2BridgeServiceFactory, "createManySecret">;
|
||||||
|
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TExternalMigrationQueueFactory = ReturnType<typeof externalMigrationQueueFactory>;
|
export type TExternalMigrationQueueFactory = ReturnType<typeof externalMigrationQueueFactory>;
|
||||||
@@ -52,7 +55,8 @@ export const externalMigrationQueueFactory = ({
|
|||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
folderDAL
|
folderDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TExternalMigrationQueueFactoryDep) => {
|
}: TExternalMigrationQueueFactoryDep) => {
|
||||||
const startImport = async (dto: {
|
const startImport = async (dto: {
|
||||||
actorEmail: string;
|
actorEmail: string;
|
||||||
@@ -109,7 +113,8 @@ export const externalMigrationQueueFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
projectService,
|
projectService,
|
||||||
projectEnvService,
|
projectEnvService,
|
||||||
secretV2BridgeService
|
secretV2BridgeService,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
if (projectsNotImported.length) {
|
if (projectsNotImported.length) {
|
||||||
|
@@ -427,3 +427,8 @@ export const getIntegrationOptions = async () => {
|
|||||||
|
|
||||||
return INTEGRATION_OPTIONS;
|
return INTEGRATION_OPTIONS;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum IntegrationMetadataSyncMode {
|
||||||
|
CUSTOM = "custom",
|
||||||
|
SECRET_METADATA = "secret-metadata"
|
||||||
|
}
|
||||||
|
@@ -38,6 +38,7 @@ import { TCreateManySecretsRawFn, TUpdateManySecretsRawFn } from "@app/services/
|
|||||||
|
|
||||||
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
||||||
import { IntegrationMetadataSchema } from "../integration/integration-schema";
|
import { IntegrationMetadataSchema } from "../integration/integration-schema";
|
||||||
|
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
|
||||||
import { IntegrationAuthMetadataSchema } from "./integration-auth-schema";
|
import { IntegrationAuthMetadataSchema } from "./integration-auth-schema";
|
||||||
import {
|
import {
|
||||||
CircleCiScope,
|
CircleCiScope,
|
||||||
@@ -48,6 +49,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
IntegrationInitialSyncBehavior,
|
IntegrationInitialSyncBehavior,
|
||||||
IntegrationMappingBehavior,
|
IntegrationMappingBehavior,
|
||||||
|
IntegrationMetadataSyncMode,
|
||||||
Integrations,
|
Integrations,
|
||||||
IntegrationUrls
|
IntegrationUrls
|
||||||
} from "./integration-list";
|
} from "./integration-list";
|
||||||
@@ -305,10 +307,16 @@ const syncSecretsAzureAppConfig = async ({
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCompleteAzureAppConfigValues = async (url: string) => {
|
if (!integration.app || !integration.app.endsWith(".azconfig.io"))
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Invalid Azure App Configuration URL provided."
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCompleteAzureAppConfigValues = async (baseURL: string, url: string) => {
|
||||||
let result: AzureAppConfigKeyValue[] = [];
|
let result: AzureAppConfigKeyValue[] = [];
|
||||||
while (url) {
|
while (url) {
|
||||||
const res = await request.get(url, {
|
const res = await request.get(url, {
|
||||||
|
baseURL,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`
|
||||||
},
|
},
|
||||||
@@ -319,7 +327,7 @@ const syncSecretsAzureAppConfig = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
result = result.concat(res.data.items);
|
result = result.concat(res.data.items);
|
||||||
url = res.data.nextLink;
|
url = res.data?.["@nextLink"];
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -327,11 +335,13 @@ const syncSecretsAzureAppConfig = async ({
|
|||||||
|
|
||||||
const metadata = IntegrationMetadataSchema.parse(integration.metadata);
|
const metadata = IntegrationMetadataSchema.parse(integration.metadata);
|
||||||
|
|
||||||
const azureAppConfigValuesUrl = `${integration.app}/kv?api-version=2023-11-01&key=${metadata.secretPrefix}*${
|
const azureAppConfigValuesUrl = `/kv?api-version=2023-11-01&key=${metadata.secretPrefix}*${
|
||||||
metadata.azureLabel ? `&label=${metadata.azureLabel}` : "&label=%00"
|
metadata.azureLabel ? `&label=${metadata.azureLabel}` : "&label=%00"
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const azureAppConfigSecrets = (await getCompleteAzureAppConfigValues(azureAppConfigValuesUrl)).reduce(
|
const azureAppConfigSecrets = (
|
||||||
|
await getCompleteAzureAppConfigValues(integration.app, azureAppConfigValuesUrl)
|
||||||
|
).reduce(
|
||||||
(accum, entry) => {
|
(accum, entry) => {
|
||||||
accum[entry.key] = entry.value;
|
accum[entry.key] = entry.value;
|
||||||
|
|
||||||
@@ -1074,14 +1084,14 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
projectId
|
projectId
|
||||||
}: {
|
}: {
|
||||||
integration: TIntegrations;
|
integration: TIntegrations;
|
||||||
secrets: Record<string, { value: string; comment?: string }>;
|
secrets: Record<string, { value: string; comment?: string; secretMetadata?: ResourceMetadataDTO }>;
|
||||||
accessId: string | null;
|
accessId: string | null;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
awsAssumeRoleArn: string | null;
|
awsAssumeRoleArn: string | null;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const metadata = z.record(z.any()).parse(integration.metadata || {});
|
const metadata = IntegrationMetadataSchema.parse(integration.metadata || {});
|
||||||
|
|
||||||
if (!accessId && !awsAssumeRoleArn) {
|
if (!accessId && !awsAssumeRoleArn) {
|
||||||
throw new Error("AWS access ID/AWS Assume Role is required");
|
throw new Error("AWS access ID/AWS Assume Role is required");
|
||||||
@@ -1129,8 +1139,25 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
|
|
||||||
const processAwsSecret = async (
|
const processAwsSecret = async (
|
||||||
secretId: string,
|
secretId: string,
|
||||||
secretValue: Record<string, string | null | undefined> | string
|
secretValue: Record<string, string | null | undefined> | string,
|
||||||
|
secretMetadata?: ResourceMetadataDTO
|
||||||
) => {
|
) => {
|
||||||
|
const secretAWSTag = metadata.secretAWSTag as { key: string; value: string }[] | undefined;
|
||||||
|
const shouldTag =
|
||||||
|
(secretAWSTag && secretAWSTag.length) ||
|
||||||
|
(metadata.metadataSyncMode === IntegrationMetadataSyncMode.SECRET_METADATA &&
|
||||||
|
metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE);
|
||||||
|
const tagArray =
|
||||||
|
(metadata.metadataSyncMode === IntegrationMetadataSyncMode.SECRET_METADATA ? secretMetadata : secretAWSTag) ?? [];
|
||||||
|
|
||||||
|
const integrationTagObj = tagArray.reduce(
|
||||||
|
(acc, item) => {
|
||||||
|
acc[item.key] = item.value;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const awsSecretManagerSecret = await secretsManager.send(
|
const awsSecretManagerSecret = await secretsManager.send(
|
||||||
new GetSecretValueCommand({
|
new GetSecretValueCommand({
|
||||||
@@ -1159,15 +1186,14 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
} else {
|
} else {
|
||||||
await secretsManager.send(
|
await secretsManager.send(
|
||||||
new DeleteSecretCommand({
|
new DeleteSecretCommand({
|
||||||
SecretId: secretId
|
SecretId: secretId,
|
||||||
|
ForceDeleteWithoutRecovery: true
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const secretAWSTag = metadata.secretAWSTag as { key: string; value: string }[] | undefined;
|
if (shouldTag) {
|
||||||
|
|
||||||
if (secretAWSTag && secretAWSTag.length) {
|
|
||||||
const describedSecret = await secretsManager.send(
|
const describedSecret = await secretsManager.send(
|
||||||
// requires secretsmanager:DescribeSecret policy
|
// requires secretsmanager:DescribeSecret policy
|
||||||
new DescribeSecretCommand({
|
new DescribeSecretCommand({
|
||||||
@@ -1177,14 +1203,6 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
|
|
||||||
if (!describedSecret.Tags) return;
|
if (!describedSecret.Tags) return;
|
||||||
|
|
||||||
const integrationTagObj = secretAWSTag.reduce(
|
|
||||||
(acc, item) => {
|
|
||||||
acc[item.key] = item.value;
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{} as Record<string, string>
|
|
||||||
);
|
|
||||||
|
|
||||||
const awsTagObj = (describedSecret.Tags || []).reduce(
|
const awsTagObj = (describedSecret.Tags || []).reduce(
|
||||||
(acc, item) => {
|
(acc, item) => {
|
||||||
if (item.Key && item.Value) {
|
if (item.Key && item.Value) {
|
||||||
@@ -1216,7 +1234,7 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
secretAWSTag?.forEach((tag) => {
|
tagArray.forEach((tag) => {
|
||||||
if (!(tag.key in awsTagObj)) {
|
if (!(tag.key in awsTagObj)) {
|
||||||
// create tag in AWS secret manager
|
// create tag in AWS secret manager
|
||||||
tagsToUpdate.push({
|
tagsToUpdate.push({
|
||||||
@@ -1253,8 +1271,8 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
Name: secretId,
|
Name: secretId,
|
||||||
SecretString: typeof secretValue === "string" ? secretValue : JSON.stringify(secretValue),
|
SecretString: typeof secretValue === "string" ? secretValue : JSON.stringify(secretValue),
|
||||||
...(metadata.kmsKeyId && { KmsKeyId: metadata.kmsKeyId }),
|
...(metadata.kmsKeyId && { KmsKeyId: metadata.kmsKeyId }),
|
||||||
Tags: metadata.secretAWSTag
|
Tags: shouldTag
|
||||||
? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({
|
? tagArray.map((tag: { key: string; value: string }) => ({
|
||||||
Key: tag.key,
|
Key: tag.key,
|
||||||
Value: tag.value
|
Value: tag.value
|
||||||
}))
|
}))
|
||||||
@@ -1271,7 +1289,7 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
|
|
||||||
if (metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE) {
|
if (metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE) {
|
||||||
for await (const [key, value] of Object.entries(secrets)) {
|
for await (const [key, value] of Object.entries(secrets)) {
|
||||||
await processAwsSecret(key, value.value);
|
await processAwsSecret(key, value.value, value.secretMetadata);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await processAwsSecret(integration.app as string, getSecretKeyValuePair(secrets));
|
await processAwsSecret(integration.app as string, getSecretKeyValuePair(secrets));
|
||||||
@@ -4431,7 +4449,7 @@ export const syncIntegrationSecrets = async ({
|
|||||||
secretPath: string;
|
secretPath: string;
|
||||||
};
|
};
|
||||||
integrationAuth: TIntegrationAuths;
|
integrationAuth: TIntegrationAuths;
|
||||||
secrets: Record<string, { value: string; comment?: string }>;
|
secrets: Record<string, { value: string; comment?: string; secretMetadata?: ResourceMetadataDTO }>;
|
||||||
accessId: string | null;
|
accessId: string | null;
|
||||||
awsAssumeRoleArn: string | null;
|
awsAssumeRoleArn: string | null;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { INTEGRATION } from "@app/lib/api-docs";
|
import { INTEGRATION } from "@app/lib/api-docs";
|
||||||
|
|
||||||
import { IntegrationMappingBehavior } from "../integration-auth/integration-list";
|
import { IntegrationMappingBehavior, IntegrationMetadataSyncMode } from "../integration-auth/integration-list";
|
||||||
|
|
||||||
export const IntegrationMetadataSchema = z.object({
|
export const IntegrationMetadataSchema = z.object({
|
||||||
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
||||||
@@ -50,6 +50,11 @@ export const IntegrationMetadataSchema = z.object({
|
|||||||
shouldMaskSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldMaskSecrets),
|
shouldMaskSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldMaskSecrets),
|
||||||
shouldProtectSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldProtectSecrets),
|
shouldProtectSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldProtectSecrets),
|
||||||
|
|
||||||
|
metadataSyncMode: z
|
||||||
|
.nativeEnum(IntegrationMetadataSyncMode)
|
||||||
|
.optional()
|
||||||
|
.describe(INTEGRATION.CREATE.metadata.metadataSyncMode),
|
||||||
|
|
||||||
octopusDeployScopeValues: z
|
octopusDeployScopeValues: z
|
||||||
.object({
|
.object({
|
||||||
// in Octopus Deploy Scope Value Format
|
// in Octopus Deploy Scope Value Format
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TResourceMetadataDALFactory = ReturnType<typeof resourceMetadataDALFactory>;
|
||||||
|
|
||||||
|
export const resourceMetadataDALFactory = (db: TDbClient) => {
|
||||||
|
const orm = ormify(db, TableName.ResourceMetadata);
|
||||||
|
|
||||||
|
return orm;
|
||||||
|
};
|
@@ -0,0 +1,10 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
export const ResourceMetadataSchema = z
|
||||||
|
.object({
|
||||||
|
key: z.string().trim().min(1),
|
||||||
|
value: z.string().trim().default("")
|
||||||
|
})
|
||||||
|
.array();
|
||||||
|
|
||||||
|
export type ResourceMetadataDTO = z.infer<typeof ResourceMetadataSchema>;
|
@@ -1,6 +1,7 @@
|
|||||||
import { SecretType, TSecretImports, TSecrets, TSecretsV2 } from "@app/db/schemas";
|
import { SecretType, TSecretImports, TSecrets, TSecretsV2 } from "@app/db/schemas";
|
||||||
import { groupBy, unique } from "@app/lib/fn";
|
import { groupBy, unique } from "@app/lib/fn";
|
||||||
|
|
||||||
|
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
|
||||||
import { TSecretDALFactory } from "../secret/secret-dal";
|
import { TSecretDALFactory } from "../secret/secret-dal";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
@@ -39,6 +40,7 @@ type TSecretImportSecretsV2 = {
|
|||||||
// But for somereason ts consider ? and undefined explicit as different just ts things
|
// But for somereason ts consider ? and undefined explicit as different just ts things
|
||||||
secretValue: string;
|
secretValue: string;
|
||||||
secretComment: string;
|
secretComment: string;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
})[];
|
})[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -160,6 +160,7 @@ export const secretImportServiceFactory = ({
|
|||||||
if (secImport.isReplication && sourceFolder) {
|
if (secImport.isReplication && sourceFolder) {
|
||||||
await secretQueueService.replicateSecrets({
|
await secretQueueService.replicateSecrets({
|
||||||
secretPath: secImport.importPath,
|
secretPath: secImport.importPath,
|
||||||
|
orgId: actorOrgId,
|
||||||
projectId,
|
projectId,
|
||||||
environmentSlug: importEnv.slug,
|
environmentSlug: importEnv.slug,
|
||||||
pickOnlyImportIds: [secImport.id],
|
pickOnlyImportIds: [secImport.id],
|
||||||
@@ -169,6 +170,7 @@ export const secretImportServiceFactory = ({
|
|||||||
} else {
|
} else {
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
|
orgId: actorOrgId,
|
||||||
projectId,
|
projectId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -340,6 +342,7 @@ export const secretImportServiceFactory = ({
|
|||||||
|
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
|
orgId: actorOrgId,
|
||||||
projectId,
|
projectId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
actor,
|
actor,
|
||||||
@@ -415,6 +418,7 @@ export const secretImportServiceFactory = ({
|
|||||||
|
|
||||||
if (membership && sourceFolder) {
|
if (membership && sourceFolder) {
|
||||||
await secretQueueService.replicateSecrets({
|
await secretQueueService.replicateSecrets({
|
||||||
|
orgId: actorOrgId,
|
||||||
secretPath: secretImportDoc.importPath,
|
secretPath: secretImportDoc.importPath,
|
||||||
projectId,
|
projectId,
|
||||||
environmentSlug: secretImportDoc.importEnv.slug,
|
environmentSlug: secretImportDoc.importEnv.slug,
|
||||||
|
@@ -78,6 +78,12 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
|
.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")
|
||||||
|
)
|
||||||
.select(selectAllTableCols(TableName.SecretV2))
|
.select(selectAllTableCols(TableName.SecretV2))
|
||||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
@@ -103,6 +109,15 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
slug,
|
slug,
|
||||||
name: slug
|
name: slug
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "metadataId",
|
||||||
|
label: "secretMetadata" as const,
|
||||||
|
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||||
|
id: metadataId,
|
||||||
|
key: metadataKey,
|
||||||
|
value: metadataValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -221,7 +236,9 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
const secs = await (tx || db.replicaNode())(TableName.SecretV2)
|
const secs = await (tx || db.replicaNode())(TableName.SecretV2)
|
||||||
.where({ folderId })
|
.where({ folderId })
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
void bd.whereNull("userId").orWhere({ userId: userId || null });
|
void bd
|
||||||
|
.whereNull(`${TableName.SecretV2}.userId`)
|
||||||
|
.orWhere({ [`${TableName.SecretV2}.userId` as "userId"]: userId || null });
|
||||||
})
|
})
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.SecretV2JnTag,
|
TableName.SecretV2JnTag,
|
||||||
@@ -233,10 +250,16 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
.select(selectAllTableCols(TableName.SecretV2))
|
.select(selectAllTableCols(TableName.SecretV2))
|
||||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
|
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
|
||||||
|
.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")
|
||||||
|
)
|
||||||
.orderBy("id", "asc");
|
.orderBy("id", "asc");
|
||||||
|
|
||||||
const data = sqlNestRelationships({
|
const data = sqlNestRelationships({
|
||||||
@@ -253,6 +276,15 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
slug,
|
slug,
|
||||||
name: slug
|
name: slug
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "metadataId",
|
||||||
|
label: "secretMetadata" as const,
|
||||||
|
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||||
|
id: metadataId,
|
||||||
|
key: metadataKey,
|
||||||
|
value: metadataValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -367,7 +399,9 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
void bd.whereNull(`${TableName.SecretV2}.userId`).orWhere({ userId: userId || null });
|
void bd
|
||||||
|
.whereNull(`${TableName.SecretV2}.userId`)
|
||||||
|
.orWhere({ [`${TableName.SecretV2}.userId` as "userId"]: userId || null });
|
||||||
})
|
})
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.SecretV2JnTag,
|
TableName.SecretV2JnTag,
|
||||||
@@ -379,13 +413,23 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
.select(
|
.select(
|
||||||
selectAllTableCols(TableName.SecretV2),
|
selectAllTableCols(TableName.SecretV2),
|
||||||
db.raw(`DENSE_RANK() OVER (ORDER BY "key" ${filters?.orderDirection ?? OrderByDirection.ASC}) as rank`)
|
db.raw(
|
||||||
|
`DENSE_RANK() OVER (ORDER BY "${TableName.SecretV2}".key ${
|
||||||
|
filters?.orderDirection ?? OrderByDirection.ASC
|
||||||
|
}) as rank`
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
|
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
|
||||||
|
.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((bd) => {
|
.where((bd) => {
|
||||||
const slugs = filters?.tagSlugs?.filter(Boolean);
|
const slugs = filters?.tagSlugs?.filter(Boolean);
|
||||||
if (slugs && slugs.length > 0) {
|
if (slugs && slugs.length > 0) {
|
||||||
@@ -425,6 +469,15 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
slug,
|
slug,
|
||||||
name: slug
|
name: slug
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "metadataId",
|
||||||
|
label: "secretMetadata" as const,
|
||||||
|
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||||
|
id: metadataId,
|
||||||
|
key: metadataKey,
|
||||||
|
value: metadataValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -545,10 +598,17 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
.select(selectAllTableCols(TableName.SecretV2))
|
.select(selectAllTableCols(TableName.SecretV2))
|
||||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"));
|
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
|
||||||
|
.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")
|
||||||
|
);
|
||||||
|
|
||||||
const docs = sqlNestRelationships({
|
const docs = sqlNestRelationships({
|
||||||
data: rawDocs,
|
data: rawDocs,
|
||||||
key: "id",
|
key: "id",
|
||||||
@@ -563,6 +623,15 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
slug,
|
slug,
|
||||||
name: slug
|
name: slug
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "metadataId",
|
||||||
|
label: "secretMetadata" as const,
|
||||||
|
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||||
|
id: metadataId,
|
||||||
|
key: metadataKey,
|
||||||
|
value: metadataValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@@ -6,6 +6,7 @@ import { groupBy } from "@app/lib/fn";
|
|||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
|
||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
|
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
|
||||||
import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types";
|
import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types";
|
||||||
@@ -54,9 +55,11 @@ export const getAllSecretReferences = (maybeSecretReference: string) => {
|
|||||||
export const fnSecretBulkInsert = async ({
|
export const fnSecretBulkInsert = async ({
|
||||||
// TODO: Pick types here
|
// TODO: Pick types here
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId,
|
||||||
inputSecrets,
|
inputSecrets,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
tx
|
tx
|
||||||
@@ -91,6 +94,7 @@ export const fnSecretBulkInsert = async ({
|
|||||||
sanitizedInputSecrets.map((el) => ({ ...el, folderId })),
|
sanitizedInputSecrets.map((el) => ({ ...el, folderId })),
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
const newSecretGroupedByKeyName = groupBy(newSecrets, (item) => item.key);
|
const newSecretGroupedByKeyName = groupBy(newSecrets, (item) => item.key);
|
||||||
const newSecretTags = inputSecrets.flatMap(({ tagIds: secretTags = [], key }) =>
|
const newSecretTags = inputSecrets.flatMap(({ tagIds: secretTags = [], key }) =>
|
||||||
secretTags.map((tag) => ({
|
secretTags.map((tag) => ({
|
||||||
@@ -106,6 +110,7 @@ export const fnSecretBulkInsert = async ({
|
|||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
await secretDAL.upsertSecretReferences(
|
await secretDAL.upsertSecretReferences(
|
||||||
inputSecrets.map(({ references = [], key }) => ({
|
inputSecrets.map(({ references = [], key }) => ({
|
||||||
secretId: newSecretGroupedByKeyName[key][0].id,
|
secretId: newSecretGroupedByKeyName[key][0].id,
|
||||||
@@ -113,6 +118,22 @@ export const fnSecretBulkInsert = async ({
|
|||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await resourceMetadataDAL.insertMany(
|
||||||
|
inputSecrets.flatMap(({ key: secretKey, secretMetadata }) => {
|
||||||
|
if (secretMetadata) {
|
||||||
|
return secretMetadata.map(({ key, value }) => ({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
secretId: newSecretGroupedByKeyName[secretKey][0].id,
|
||||||
|
orgId
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
if (newSecretTags.length) {
|
if (newSecretTags.length) {
|
||||||
const secTags = await secretTagDAL.saveTagsToSecretV2(newSecretTags, tx);
|
const secTags = await secretTagDAL.saveTagsToSecretV2(newSecretTags, tx);
|
||||||
const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId);
|
const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId);
|
||||||
@@ -120,6 +141,7 @@ export const fnSecretBulkInsert = async ({
|
|||||||
[`${TableName.SecretVersionV2}Id` as const]: secVersionsGroupBySecId[secrets_v2Id][0].id,
|
[`${TableName.SecretVersionV2}Id` as const]: secVersionsGroupBySecId[secrets_v2Id][0].id,
|
||||||
[`${TableName.SecretTag}Id` as const]: secret_tagsId
|
[`${TableName.SecretTag}Id` as const]: secret_tagsId
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await secretVersionTagDAL.insertMany(newSecretVersionTags, tx);
|
await secretVersionTagDAL.insertMany(newSecretVersionTags, tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,10 +152,12 @@ export const fnSecretBulkUpdate = async ({
|
|||||||
tx,
|
tx,
|
||||||
inputSecrets,
|
inputSecrets,
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL
|
secretVersionTagDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TFnSecretBulkUpdate) => {
|
}: TFnSecretBulkUpdate) => {
|
||||||
const sanitizedInputSecrets = inputSecrets.map(
|
const sanitizedInputSecrets = inputSecrets.map(
|
||||||
({
|
({
|
||||||
@@ -231,6 +255,34 @@ export const fnSecretBulkUpdate = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inputSecretIdsWithMetadata = inputSecrets
|
||||||
|
.filter((sec) => Boolean(sec.data.secretMetadata))
|
||||||
|
.map((sec) => sec.filter.id);
|
||||||
|
|
||||||
|
await resourceMetadataDAL.delete(
|
||||||
|
{
|
||||||
|
$in: {
|
||||||
|
secretId: inputSecretIdsWithMetadata
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
await resourceMetadataDAL.insertMany(
|
||||||
|
inputSecrets.flatMap(({ filter: { id }, data: { secretMetadata } }) => {
|
||||||
|
if (secretMetadata) {
|
||||||
|
return secretMetadata.map(({ key, value }) => ({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
secretId: id,
|
||||||
|
orgId
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
return newSecrets.map((secret) => ({ ...secret, _id: secret.id }));
|
return newSecrets.map((secret) => ({ ...secret, _id: secret.id }));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -570,6 +622,7 @@ export const reshapeBridgeSecret = (
|
|||||||
color?: string | null;
|
color?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
}[];
|
}[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
}
|
}
|
||||||
) => ({
|
) => ({
|
||||||
secretKey: secret.key,
|
secretKey: secret.key,
|
||||||
@@ -588,6 +641,7 @@ export const reshapeBridgeSecret = (
|
|||||||
secretReminderRepeatDays: secret.reminderRepeatDays,
|
secretReminderRepeatDays: secret.reminderRepeatDays,
|
||||||
secretReminderNote: secret.reminderNote,
|
secretReminderNote: secret.reminderNote,
|
||||||
metadata: secret.metadata,
|
metadata: secret.metadata,
|
||||||
|
secretMetadata: secret.secretMetadata,
|
||||||
createdAt: secret.createdAt,
|
createdAt: secret.createdAt,
|
||||||
updatedAt: secret.updatedAt
|
updatedAt: secret.updatedAt
|
||||||
});
|
});
|
||||||
|
@@ -18,6 +18,7 @@ import { ActorType } from "../auth/auth-type";
|
|||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
import { KmsDataKey } from "../kms/kms-types";
|
import { KmsDataKey } from "../kms/kms-types";
|
||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
|
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
||||||
import { TSecretQueueFactory } from "../secret/secret-queue";
|
import { TSecretQueueFactory } from "../secret/secret-queue";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||||
@@ -74,6 +75,7 @@ type TSecretV2BridgeServiceFactoryDep = {
|
|||||||
"insertV2Bridge" | "insertApprovalSecretV2Tags"
|
"insertV2Bridge" | "insertApprovalSecretV2Tags"
|
||||||
>;
|
>;
|
||||||
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSecretV2BridgeServiceFactory = ReturnType<typeof secretV2BridgeServiceFactory>;
|
export type TSecretV2BridgeServiceFactory = ReturnType<typeof secretV2BridgeServiceFactory>;
|
||||||
@@ -95,7 +97,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
secretApprovalPolicyService,
|
secretApprovalPolicyService,
|
||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
secretApprovalRequestSecretDAL,
|
secretApprovalRequestSecretDAL,
|
||||||
kmsService
|
kmsService,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TSecretV2BridgeServiceFactoryDep) => {
|
}: TSecretV2BridgeServiceFactoryDep) => {
|
||||||
const $validateSecretReferences = async (
|
const $validateSecretReferences = async (
|
||||||
projectId: string,
|
projectId: string,
|
||||||
@@ -141,7 +144,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -186,6 +189,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
projectId,
|
projectId,
|
||||||
secretPath,
|
secretPath,
|
||||||
|
secretMetadata,
|
||||||
...inputSecret
|
...inputSecret
|
||||||
}: TCreateSecretDTO) => {
|
}: TCreateSecretDTO) => {
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
||||||
@@ -255,6 +259,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
const secret = await secretDAL.transaction((tx) =>
|
const secret = await secretDAL.transaction((tx) =>
|
||||||
fnSecretBulkInsert({
|
fnSecretBulkInsert({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
inputSecrets: [
|
inputSecrets: [
|
||||||
{
|
{
|
||||||
version: 1,
|
version: 1,
|
||||||
@@ -272,9 +277,11 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
key: secretName,
|
key: secretName,
|
||||||
userId: inputSecret.type === SecretType.Personal ? actorId : null,
|
userId: inputSecret.type === SecretType.Personal ? actorId : null,
|
||||||
tagIds: inputSecret.tagIds,
|
tagIds: inputSecret.tagIds,
|
||||||
references: nestedReferences
|
references: nestedReferences,
|
||||||
|
secretMetadata
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
resourceMetadataDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
@@ -287,6 +294,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
|
orgId: actorOrgId,
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -309,6 +317,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
projectId,
|
projectId,
|
||||||
secretPath,
|
secretPath,
|
||||||
|
secretMetadata,
|
||||||
...inputSecret
|
...inputSecret
|
||||||
}: TUpdateSecretDTO) => {
|
}: TUpdateSecretDTO) => {
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
||||||
@@ -435,6 +444,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
const updatedSecret = await secretDAL.transaction(async (tx) =>
|
const updatedSecret = await secretDAL.transaction(async (tx) =>
|
||||||
fnSecretBulkUpdate({
|
fnSecretBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
resourceMetadataDAL,
|
||||||
inputSecrets: [
|
inputSecrets: [
|
||||||
{
|
{
|
||||||
filter: { id: secretId },
|
filter: { id: secretId },
|
||||||
@@ -448,6 +459,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
skipMultilineEncoding: inputSecret.skipMultilineEncoding,
|
skipMultilineEncoding: inputSecret.skipMultilineEncoding,
|
||||||
key: inputSecret.newSecretName || secretName,
|
key: inputSecret.newSecretName || secretName,
|
||||||
tags: inputSecret.tagIds,
|
tags: inputSecret.tagIds,
|
||||||
|
secretMetadata,
|
||||||
...encryptedValue
|
...encryptedValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -475,6 +487,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -562,6 +575,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -961,8 +975,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
? secretDAL.findOneWithTags({
|
? secretDAL.findOneWithTags({
|
||||||
folderId,
|
folderId,
|
||||||
type: secretType,
|
type: secretType,
|
||||||
key: secretName,
|
[`${TableName.SecretV2}.key` as "key"]: secretName,
|
||||||
userId: secretType === SecretType.Personal ? actorId : null
|
[`${TableName.SecretV2}.userId` as "userId"]: secretType === SecretType.Personal ? actorId : null
|
||||||
})
|
})
|
||||||
: secretVersionDAL
|
: secretVersionDAL
|
||||||
.findOne({
|
.findOne({
|
||||||
@@ -1113,7 +1127,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1185,11 +1199,14 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
key: el.secretKey,
|
key: el.secretKey,
|
||||||
tagIds: el.tagIds,
|
tagIds: el.tagIds,
|
||||||
references,
|
references,
|
||||||
|
secretMetadata: el.secretMetadata,
|
||||||
type: SecretType.Shared
|
type: SecretType.Shared
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
@@ -1203,6 +1220,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1254,7 +1272,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1319,7 +1337,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1371,6 +1389,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
const secrets = await secretDAL.transaction(async (tx) =>
|
const secrets = await secretDAL.transaction(async (tx) =>
|
||||||
fnSecretBulkUpdate({
|
fnSecretBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
tx,
|
tx,
|
||||||
inputSecrets: inputSecrets.map((el) => {
|
inputSecrets: inputSecrets.map((el) => {
|
||||||
const originalSecret = secretsToUpdateInDBGroupedByKey[el.secretKey][0];
|
const originalSecret = secretsToUpdateInDBGroupedByKey[el.secretKey][0];
|
||||||
@@ -1394,6 +1413,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
key: el.newSecretName || el.secretKey,
|
key: el.newSecretName || el.secretKey,
|
||||||
tags: el.tagIds,
|
tags: el.tagIds,
|
||||||
|
secretMetadata: el.secretMetadata,
|
||||||
...encryptedValue
|
...encryptedValue
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1401,7 +1421,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL
|
secretVersionTagDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
@@ -1410,6 +1431,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1461,7 +1483,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1512,6 +1534,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1815,10 +1838,12 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
if (locallyCreatedSecrets.length) {
|
if (locallyCreatedSecrets.length) {
|
||||||
await fnSecretBulkInsert({
|
await fnSecretBulkInsert({
|
||||||
folderId: destinationFolder.id,
|
folderId: destinationFolder.id,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
tx,
|
tx,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
inputSecrets: locallyCreatedSecrets.map((doc) => {
|
inputSecrets: locallyCreatedSecrets.map((doc) => {
|
||||||
return {
|
return {
|
||||||
@@ -1830,6 +1855,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
skipMultilineEncoding: doc.skipMultilineEncoding,
|
skipMultilineEncoding: doc.skipMultilineEncoding,
|
||||||
reminderNote: doc.reminderNote,
|
reminderNote: doc.reminderNote,
|
||||||
reminderRepeatDays: doc.reminderRepeatDays,
|
reminderRepeatDays: doc.reminderRepeatDays,
|
||||||
|
secretMetadata: doc.secretMetadata,
|
||||||
references: doc.value ? getAllSecretReferences(doc.value).nestedReferences : []
|
references: doc.value ? getAllSecretReferences(doc.value).nestedReferences : []
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@@ -1838,6 +1864,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
if (locallyUpdatedSecrets.length) {
|
if (locallyUpdatedSecrets.length) {
|
||||||
await fnSecretBulkUpdate({
|
await fnSecretBulkUpdate({
|
||||||
folderId: destinationFolder.id,
|
folderId: destinationFolder.id,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
tx,
|
tx,
|
||||||
@@ -1855,6 +1883,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
encryptedComment: doc.encryptedComment,
|
encryptedComment: doc.encryptedComment,
|
||||||
skipMultilineEncoding: doc.skipMultilineEncoding,
|
skipMultilineEncoding: doc.skipMultilineEncoding,
|
||||||
reminderNote: doc.reminderNote,
|
reminderNote: doc.reminderNote,
|
||||||
|
secretMetadata: doc.secretMetadata,
|
||||||
reminderRepeatDays: doc.reminderRepeatDays,
|
reminderRepeatDays: doc.reminderRepeatDays,
|
||||||
...(doc.encryptedValue
|
...(doc.encryptedValue
|
||||||
? {
|
? {
|
||||||
@@ -1938,6 +1967,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(destinationFolder.id);
|
await snapshotService.performSnapshot(destinationFolder.id);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretPath: destinationFolder.path,
|
secretPath: destinationFolder.path,
|
||||||
environmentSlug: destinationFolder.environment.slug,
|
environmentSlug: destinationFolder.environment.slug,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -1949,6 +1979,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(sourceFolder.id);
|
await snapshotService.performSnapshot(sourceFolder.id);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretPath: sourceFolder.path,
|
secretPath: sourceFolder.path,
|
||||||
environmentSlug: sourceFolder.environment.slug,
|
environmentSlug: sourceFolder.environment.slug,
|
||||||
actorId,
|
actorId,
|
||||||
|
@@ -7,6 +7,8 @@ import { SecretsOrderBy } from "@app/services/secret/secret-types";
|
|||||||
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
||||||
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
||||||
|
|
||||||
|
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
||||||
|
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
|
||||||
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
|
||||||
import { TSecretVersionV2DALFactory } from "./secret-version-dal";
|
import { TSecretVersionV2DALFactory } from "./secret-version-dal";
|
||||||
import { TSecretVersionV2TagDALFactory } from "./secret-version-tag-dal";
|
import { TSecretVersionV2TagDALFactory } from "./secret-version-tag-dal";
|
||||||
@@ -58,6 +60,7 @@ export type TCreateSecretDTO = TProjectPermission & {
|
|||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
secretReminderRepeatDays?: number | null;
|
secretReminderRepeatDays?: number | null;
|
||||||
secretReminderNote?: string | null;
|
secretReminderNote?: string | null;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateSecretDTO = TProjectPermission & {
|
export type TUpdateSecretDTO = TProjectPermission & {
|
||||||
@@ -75,6 +78,7 @@ export type TUpdateSecretDTO = TProjectPermission & {
|
|||||||
metadata?: {
|
metadata?: {
|
||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteSecretDTO = TProjectPermission & {
|
export type TDeleteSecretDTO = TProjectPermission & {
|
||||||
@@ -94,6 +98,7 @@ export type TCreateManySecretDTO = Omit<TProjectPermission, "projectId"> & {
|
|||||||
secretComment?: string;
|
secretComment?: string;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
metadata?: {
|
metadata?: {
|
||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
@@ -113,6 +118,7 @@ export type TUpdateManySecretDTO = Omit<TProjectPermission, "projectId"> & {
|
|||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
secretReminderRepeatDays?: number | null;
|
secretReminderRepeatDays?: number | null;
|
||||||
secretReminderNote?: string | null;
|
secretReminderNote?: string | null;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,8 +142,16 @@ export type TSecretReference = { environment: string; secretPath: string; secret
|
|||||||
|
|
||||||
export type TFnSecretBulkInsert = {
|
export type TFnSecretBulkInsert = {
|
||||||
folderId: string;
|
folderId: string;
|
||||||
|
orgId: string;
|
||||||
tx?: Knex;
|
tx?: Knex;
|
||||||
inputSecrets: Array<Omit<TSecretsV2Insert, "folderId"> & { tagIds?: string[]; references: TSecretReference[] }>;
|
inputSecrets: Array<
|
||||||
|
Omit<TSecretsV2Insert, "folderId"> & {
|
||||||
|
tagIds?: string[];
|
||||||
|
references: TSecretReference[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
|
||||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences">;
|
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences">;
|
||||||
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
||||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2">;
|
||||||
@@ -156,10 +170,12 @@ type TRequireReferenceIfValue =
|
|||||||
|
|
||||||
export type TFnSecretBulkUpdate = {
|
export type TFnSecretBulkUpdate = {
|
||||||
folderId: string;
|
folderId: string;
|
||||||
|
orgId: string;
|
||||||
inputSecrets: {
|
inputSecrets: {
|
||||||
filter: Partial<TSecretsV2>;
|
filter: Partial<TSecretsV2>;
|
||||||
data: TRequireReferenceIfValue & { tags?: string[] };
|
data: TRequireReferenceIfValue & { tags?: string[]; secretMetadata?: ResourceMetadataDTO };
|
||||||
}[];
|
}[];
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "bulkUpdate" | "upsertSecretReferences">;
|
secretDAL: Pick<TSecretV2BridgeDALFactory, "bulkUpdate" | "upsertSecretReferences">;
|
||||||
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
||||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "deleteTagsToSecretV2">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "deleteTagsToSecretV2">;
|
||||||
|
@@ -749,7 +749,8 @@ export const createManySecretsRawFnFactory = ({
|
|||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
secretVersionTagV2BridgeDAL,
|
secretVersionTagV2BridgeDAL,
|
||||||
kmsService
|
kmsService,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TCreateManySecretsRawFnFactory) => {
|
}: TCreateManySecretsRawFnFactory) => {
|
||||||
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
||||||
const createManySecretsRawFn = async ({
|
const createManySecretsRawFn = async ({
|
||||||
@@ -760,7 +761,7 @@ export const createManySecretsRawFnFactory = ({
|
|||||||
userId
|
userId
|
||||||
}: TCreateManySecretsRawFn) => {
|
}: TCreateManySecretsRawFn) => {
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
|
||||||
|
const project = await projectDAL.findById(projectId);
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
if (!folder)
|
if (!folder)
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
@@ -814,7 +815,9 @@ export const createManySecretsRawFnFactory = ({
|
|||||||
tagIds: el.tags
|
tagIds: el.tags
|
||||||
})),
|
})),
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: project.orgId,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL: secretVersionTagV2BridgeDAL,
|
secretVersionTagDAL: secretVersionTagV2BridgeDAL,
|
||||||
@@ -909,6 +912,7 @@ export const updateManySecretsRawFnFactory = ({
|
|||||||
secretVersionTagV2BridgeDAL,
|
secretVersionTagV2BridgeDAL,
|
||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
kmsService
|
kmsService
|
||||||
}: TUpdateManySecretsRawFnFactory) => {
|
}: TUpdateManySecretsRawFnFactory) => {
|
||||||
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
||||||
@@ -920,6 +924,7 @@ export const updateManySecretsRawFnFactory = ({
|
|||||||
userId
|
userId
|
||||||
}: TUpdateManySecretsRawFn): Promise<Array<{ id: string }>> => {
|
}: TUpdateManySecretsRawFn): Promise<Array<{ id: string }>> => {
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
|
||||||
|
const project = await projectDAL.findById(projectId);
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
if (!folder)
|
if (!folder)
|
||||||
@@ -988,11 +993,13 @@ export const updateManySecretsRawFnFactory = ({
|
|||||||
const updatedSecrets = await secretDAL.transaction(async (tx) =>
|
const updatedSecrets = await secretDAL.transaction(async (tx) =>
|
||||||
fnSecretV2BridgeBulkUpdate({
|
fnSecretV2BridgeBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: project.orgId,
|
||||||
tx,
|
tx,
|
||||||
inputSecrets: inputSecrets.map((el) => ({
|
inputSecrets: inputSecrets.map((el) => ({
|
||||||
filter: { id: secretsToUpdateInDBGroupedByKey[el.key][0].id, type: SecretType.Shared },
|
filter: { id: secretsToUpdateInDBGroupedByKey[el.key][0].id, type: SecretType.Shared },
|
||||||
data: el
|
data: el
|
||||||
})),
|
})),
|
||||||
|
resourceMetadataDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
|
@@ -47,6 +47,8 @@ import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
|||||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
||||||
|
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
||||||
|
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||||
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
|
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
|
||||||
@@ -104,6 +106,7 @@ type TSecretQueueFactoryDep = {
|
|||||||
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||||
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
|
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
|
||||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create">;
|
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetSecrets = {
|
export type TGetSecrets = {
|
||||||
@@ -120,7 +123,12 @@ export const uniqueSecretQueueKey = (environment: string, secretPath: string) =>
|
|||||||
|
|
||||||
type TIntegrationSecret = Record<
|
type TIntegrationSecret = Record<
|
||||||
string,
|
string,
|
||||||
{ value: string; comment?: string; skipMultilineEncoding?: boolean | null | undefined }
|
{
|
||||||
|
value: string;
|
||||||
|
comment?: string;
|
||||||
|
skipMultilineEncoding?: boolean | null | undefined;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// TODO(akhilmhdh): split this into multiple queue
|
// TODO(akhilmhdh): split this into multiple queue
|
||||||
@@ -157,7 +165,8 @@ export const secretQueueFactory = ({
|
|||||||
auditLogService,
|
auditLogService,
|
||||||
orgService,
|
orgService,
|
||||||
projectUserMembershipRoleDAL,
|
projectUserMembershipRoleDAL,
|
||||||
projectKeyDAL
|
projectKeyDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TSecretQueueFactoryDep) => {
|
}: TSecretQueueFactoryDep) => {
|
||||||
const integrationMeter = opentelemetry.metrics.getMeter("Integrations");
|
const integrationMeter = opentelemetry.metrics.getMeter("Integrations");
|
||||||
const errorHistogram = integrationMeter.createHistogram("integration_secret_sync_errors", {
|
const errorHistogram = integrationMeter.createHistogram("integration_secret_sync_errors", {
|
||||||
@@ -306,7 +315,8 @@ export const secretQueueFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
secretVersionTagV2BridgeDAL
|
secretVersionTagV2BridgeDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateManySecretsRawFn = updateManySecretsRawFnFactory({
|
const updateManySecretsRawFn = updateManySecretsRawFnFactory({
|
||||||
@@ -321,7 +331,8 @@ export const secretQueueFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
secretVersionTagV2BridgeDAL
|
secretVersionTagV2BridgeDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -372,6 +383,7 @@ export const secretQueueFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
|
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
|
||||||
|
content[secretKey].secretMetadata = secret.secretMetadata;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -397,7 +409,8 @@ export const secretQueueFactory = ({
|
|||||||
content[importedSecret.key] = {
|
content[importedSecret.key] = {
|
||||||
skipMultilineEncoding: importedSecret.skipMultilineEncoding,
|
skipMultilineEncoding: importedSecret.skipMultilineEncoding,
|
||||||
comment: importedSecret.secretComment,
|
comment: importedSecret.secretComment,
|
||||||
value: importedSecret.secretValue || ""
|
value: importedSecret.secretValue || "",
|
||||||
|
secretMetadata: importedSecret.secretMetadata
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -597,6 +610,7 @@ export const secretQueueFactory = ({
|
|||||||
_depth: depth,
|
_depth: depth,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
excludeReplication,
|
excludeReplication,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -625,6 +639,7 @@ export const secretQueueFactory = ({
|
|||||||
_deDupeReplicationQueue: deDupeReplicationQueue,
|
_deDupeReplicationQueue: deDupeReplicationQueue,
|
||||||
_depth: depth,
|
_depth: depth,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
secretPath,
|
secretPath,
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
@@ -681,6 +696,7 @@ export const secretQueueFactory = ({
|
|||||||
if (!folder) {
|
if (!folder) {
|
||||||
throw new Error("Secret path not found");
|
throw new Error("Secret path not found");
|
||||||
}
|
}
|
||||||
|
const project = await projectDAL.findById(projectId);
|
||||||
|
|
||||||
// find all imports made with the given environment and secret path
|
// find all imports made with the given environment and secret path
|
||||||
const linkSourceDto = {
|
const linkSourceDto = {
|
||||||
@@ -715,6 +731,7 @@ export const secretQueueFactory = ({
|
|||||||
.map(({ folderId }) =>
|
.map(({ folderId }) =>
|
||||||
syncSecrets({
|
syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: project.orgId,
|
||||||
secretPath: foldersGroupedById[folderId][0]?.path as string,
|
secretPath: foldersGroupedById[folderId][0]?.path as string,
|
||||||
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
|
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
|
||||||
_deDupeQueue: deDupeQueue,
|
_deDupeQueue: deDupeQueue,
|
||||||
@@ -767,6 +784,7 @@ export const secretQueueFactory = ({
|
|||||||
.map((folderId) =>
|
.map((folderId) =>
|
||||||
syncSecrets({
|
syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: project.orgId,
|
||||||
secretPath: referencedFoldersGroupedById[folderId][0]?.path as string,
|
secretPath: referencedFoldersGroupedById[folderId][0]?.path as string,
|
||||||
environmentSlug: referencedFoldersGroupedById[folderId][0]?.environmentSlug as string,
|
environmentSlug: referencedFoldersGroupedById[folderId][0]?.environmentSlug as string,
|
||||||
_deDupeQueue: deDupeQueue,
|
_deDupeQueue: deDupeQueue,
|
||||||
|
@@ -288,6 +288,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -429,6 +430,7 @@ export const secretServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath: path,
|
secretPath: path,
|
||||||
|
orgId: actorOrgId,
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -526,6 +528,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -820,6 +823,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath: path,
|
secretPath: path,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -928,6 +932,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath: path,
|
secretPath: path,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1014,6 +1019,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath: path,
|
secretPath: path,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1385,7 +1391,8 @@ export const secretServiceFactory = ({
|
|||||||
skipMultilineEncoding,
|
skipMultilineEncoding,
|
||||||
tagIds,
|
tagIds,
|
||||||
secretReminderNote,
|
secretReminderNote,
|
||||||
secretReminderRepeatDays
|
secretReminderRepeatDays,
|
||||||
|
secretMetadata
|
||||||
}: TCreateSecretRawDTO) => {
|
}: TCreateSecretRawDTO) => {
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||||
const policy =
|
const policy =
|
||||||
@@ -1412,7 +1419,8 @@ export const secretServiceFactory = ({
|
|||||||
secretValue,
|
secretValue,
|
||||||
tagIds,
|
tagIds,
|
||||||
reminderNote: secretReminderNote,
|
reminderNote: secretReminderNote,
|
||||||
reminderRepeatDays: secretReminderRepeatDays
|
reminderRepeatDays: secretReminderRepeatDays,
|
||||||
|
secretMetadata
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1435,7 +1443,8 @@ export const secretServiceFactory = ({
|
|||||||
tagIds,
|
tagIds,
|
||||||
secretReminderNote,
|
secretReminderNote,
|
||||||
skipMultilineEncoding,
|
skipMultilineEncoding,
|
||||||
secretReminderRepeatDays
|
secretReminderRepeatDays,
|
||||||
|
secretMetadata
|
||||||
});
|
});
|
||||||
return { secret, type: SecretProtectionType.Direct as const };
|
return { secret, type: SecretProtectionType.Direct as const };
|
||||||
}
|
}
|
||||||
@@ -1525,7 +1534,8 @@ export const secretServiceFactory = ({
|
|||||||
secretReminderRepeatDays,
|
secretReminderRepeatDays,
|
||||||
metadata,
|
metadata,
|
||||||
secretComment,
|
secretComment,
|
||||||
newSecretName
|
newSecretName,
|
||||||
|
secretMetadata
|
||||||
}: TUpdateSecretRawDTO) => {
|
}: TUpdateSecretRawDTO) => {
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||||
const policy =
|
const policy =
|
||||||
@@ -1553,7 +1563,8 @@ export const secretServiceFactory = ({
|
|||||||
secretValue,
|
secretValue,
|
||||||
tagIds,
|
tagIds,
|
||||||
reminderNote: secretReminderNote,
|
reminderNote: secretReminderNote,
|
||||||
reminderRepeatDays: secretReminderRepeatDays
|
reminderRepeatDays: secretReminderRepeatDays,
|
||||||
|
secretMetadata
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1577,7 +1588,8 @@ export const secretServiceFactory = ({
|
|||||||
secretName,
|
secretName,
|
||||||
newSecretName,
|
newSecretName,
|
||||||
metadata,
|
metadata,
|
||||||
secretValue
|
secretValue,
|
||||||
|
secretMetadata
|
||||||
});
|
});
|
||||||
return { type: SecretProtectionType.Direct as const, secret };
|
return { type: SecretProtectionType.Direct as const, secret };
|
||||||
}
|
}
|
||||||
@@ -1793,7 +1805,8 @@ export const secretServiceFactory = ({
|
|||||||
secretComment: el.secretComment,
|
secretComment: el.secretComment,
|
||||||
metadata: el.metadata,
|
metadata: el.metadata,
|
||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
secretKey: el.secretKey
|
secretKey: el.secretKey,
|
||||||
|
secretMetadata: el.secretMetadata
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1919,7 +1932,8 @@ export const secretServiceFactory = ({
|
|||||||
secretValue: el.secretValue,
|
secretValue: el.secretValue,
|
||||||
secretComment: el.secretComment,
|
secretComment: el.secretComment,
|
||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
secretKey: el.secretKey
|
secretKey: el.secretKey,
|
||||||
|
secretMetadata: el.secretMetadata
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2262,6 +2276,7 @@ export const secretServiceFactory = ({
|
|||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
orgId: project.orgId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
excludeReplication: true
|
excludeReplication: true
|
||||||
});
|
});
|
||||||
@@ -2370,6 +2385,7 @@ export const secretServiceFactory = ({
|
|||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
orgId: project.orgId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
excludeReplication: true
|
excludeReplication: true
|
||||||
});
|
});
|
||||||
@@ -2828,6 +2844,7 @@ export const secretServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(destinationFolder.id);
|
await snapshotService.performSnapshot(destinationFolder.id);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
orgId: project.orgId,
|
||||||
secretPath: destinationFolder.path,
|
secretPath: destinationFolder.path,
|
||||||
environmentSlug: destinationFolder.environment.slug,
|
environmentSlug: destinationFolder.environment.slug,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -2839,6 +2856,7 @@ export const secretServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(sourceFolder.id);
|
await snapshotService.performSnapshot(sourceFolder.id);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
orgId: project.orgId,
|
||||||
secretPath: sourceFolder.path,
|
secretPath: sourceFolder.path,
|
||||||
environmentSlug: sourceFolder.environment.slug,
|
environmentSlug: sourceFolder.environment.slug,
|
||||||
actorId,
|
actorId,
|
||||||
|
@@ -14,6 +14,8 @@ import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
|||||||
|
|
||||||
import { ActorType } from "../auth/auth-type";
|
import { ActorType } from "../auth/auth-type";
|
||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
|
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
||||||
|
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
|
||||||
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
|
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
|
||||||
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
|
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
|
||||||
@@ -211,6 +213,7 @@ export type TCreateSecretRawDTO = TProjectPermission & {
|
|||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
secretReminderRepeatDays?: number | null;
|
secretReminderRepeatDays?: number | null;
|
||||||
secretReminderNote?: string | null;
|
secretReminderNote?: string | null;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateSecretRawDTO = TProjectPermission & {
|
export type TUpdateSecretRawDTO = TProjectPermission & {
|
||||||
@@ -228,6 +231,7 @@ export type TUpdateSecretRawDTO = TProjectPermission & {
|
|||||||
metadata?: {
|
metadata?: {
|
||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteSecretRawDTO = TProjectPermission & {
|
export type TDeleteSecretRawDTO = TProjectPermission & {
|
||||||
@@ -248,6 +252,7 @@ export type TCreateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & {
|
|||||||
secretComment?: string;
|
secretComment?: string;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
metadata?: {
|
metadata?: {
|
||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
@@ -266,6 +271,7 @@ export type TUpdateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & {
|
|||||||
secretComment?: string;
|
secretComment?: string;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
secretReminderRepeatDays?: number | null;
|
secretReminderRepeatDays?: number | null;
|
||||||
secretReminderNote?: string | null;
|
secretReminderNote?: string | null;
|
||||||
}[];
|
}[];
|
||||||
@@ -293,7 +299,13 @@ export type TSecretReference = { environment: string; secretPath: string };
|
|||||||
export type TFnSecretBulkInsert = {
|
export type TFnSecretBulkInsert = {
|
||||||
folderId: string;
|
folderId: string;
|
||||||
tx?: Knex;
|
tx?: Knex;
|
||||||
inputSecrets: Array<Omit<TSecretsInsert, "folderId"> & { tags?: string[]; references?: TSecretReference[] }>;
|
inputSecrets: Array<
|
||||||
|
Omit<TSecretsInsert, "folderId"> & {
|
||||||
|
tags?: string[];
|
||||||
|
references?: TSecretReference[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
|
}
|
||||||
|
>;
|
||||||
secretDAL: Pick<TSecretDALFactory, "insertMany" | "upsertSecretReferences">;
|
secretDAL: Pick<TSecretDALFactory, "insertMany" | "upsertSecretReferences">;
|
||||||
secretVersionDAL: Pick<TSecretVersionDALFactory, "insertMany">;
|
secretVersionDAL: Pick<TSecretVersionDALFactory, "insertMany">;
|
||||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecret">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecret">;
|
||||||
@@ -389,6 +401,7 @@ export type TCreateManySecretsRawFnFactory = {
|
|||||||
>;
|
>;
|
||||||
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
||||||
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TCreateManySecretsRawFn = {
|
export type TCreateManySecretsRawFn = {
|
||||||
@@ -425,6 +438,7 @@ export type TUpdateManySecretsRawFnFactory = {
|
|||||||
>;
|
>;
|
||||||
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
||||||
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateManySecretsRawFn = {
|
export type TUpdateManySecretsRawFn = {
|
||||||
@@ -460,6 +474,7 @@ export type TSyncSecretsDTO<T extends boolean = false> = {
|
|||||||
_depth?: number;
|
_depth?: number;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
orgId: string;
|
||||||
environmentSlug: string;
|
environmentSlug: string;
|
||||||
// cases for just doing sync integration and webhook
|
// cases for just doing sync integration and webhook
|
||||||
excludeReplication?: T;
|
excludeReplication?: T;
|
||||||
|
@@ -51,7 +51,7 @@ const buildSlackPayload = (notification: TSlackNotification) => {
|
|||||||
*Environment*: ${payload.environment}
|
*Environment*: ${payload.environment}
|
||||||
*Secret path*: ${payload.secretPath || "/"}
|
*Secret path*: ${payload.secretPath || "/"}
|
||||||
|
|
||||||
View the complete details <${appCfg.SITE_URL}/project/${payload.projectId}/approval?requestId=${
|
View the complete details <${appCfg.SITE_URL}/secret-manager/${payload.projectId}/approval?requestId=${
|
||||||
payload.requestId
|
payload.requestId
|
||||||
}|here>.`;
|
}|here>.`;
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ Every new joiner has an onboarding buddy who should ideally be in the the same t
|
|||||||
1. Join the weekly all-hands meeting. It typically happens on Monday's at 8:30am PT.
|
1. Join the weekly all-hands meeting. It typically happens on Monday's at 8:30am PT.
|
||||||
2. Ship something together on day one – even if tiny! It feels great to hit the ground running, with a development environment all ready to go.
|
2. Ship something together on day one – even if tiny! It feels great to hit the ground running, with a development environment all ready to go.
|
||||||
3. Check out the [Areas of Responsibility (AoR) Table](https://docs.google.com/spreadsheets/d/1RnXlGFg83Sgu0dh7ycuydsSobmFfI3A0XkGw7vrVxEI/edit?usp=sharing). This is helpful to know who you can ask about particular areas of Infisical. Feel free to add yourself to the areas you'd be most interesting to dive into.
|
3. Check out the [Areas of Responsibility (AoR) Table](https://docs.google.com/spreadsheets/d/1RnXlGFg83Sgu0dh7ycuydsSobmFfI3A0XkGw7vrVxEI/edit?usp=sharing). This is helpful to know who you can ask about particular areas of Infisical. Feel free to add yourself to the areas you'd be most interesting to dive into.
|
||||||
4. Read the [Infisical Strategy Doc](https://docs.google.com/document/d/1RaJd3RoS2QpWLFHlgfHaXnHqCCwRt6mCGZkbJ75J_D0/edit?usp=sharing).
|
4. Read the [Infisical Strategy Doc](https://docs.google.com/document/d/1uV9IaahYwbZ5OuzDTFdQMSa1P0mpMOnetGB-xqf4G40).
|
||||||
5. Update your LinkedIn profile with one of [Infisical's official banners](https://drive.google.com/drive/u/0/folders/1oSNWjbpRl9oNYwxM_98IqzKs9fAskrb2) (if you want to). You can also coordinate your social posts in the #marketing Slack channel, so that we can boost it from Infisical's official social media accounts.
|
5. Update your LinkedIn profile with one of [Infisical's official banners](https://drive.google.com/drive/u/0/folders/1oSNWjbpRl9oNYwxM_98IqzKs9fAskrb2) (if you want to). You can also coordinate your social posts in the #marketing Slack channel, so that we can boost it from Infisical's official social media accounts.
|
||||||
6. Over the first few weeks, feel free to schedule 1:1s with folks on the team to get to know them a bit better.
|
6. Over the first few weeks, feel free to schedule 1:1s with folks on the team to get to know them a bit better.
|
||||||
7. Change your Slack username in the users channel to `[NAME] (Infisical)`.
|
7. Change your Slack username in the users channel to `[NAME] (Infisical)`.
|
||||||
|
@@ -144,9 +144,6 @@ services:
|
|||||||
- ./frontend/src:/app/src/ # mounted whole src to avoid missing reload on new files
|
- ./frontend/src:/app/src/ # mounted whole src to avoid missing reload on new files
|
||||||
- ./frontend/public:/app/public
|
- ./frontend/public:/app/public
|
||||||
env_file: .env
|
env_file: .env
|
||||||
environment:
|
|
||||||
- NEXT_PUBLIC_ENV=development
|
|
||||||
- INFISICAL_TELEMETRY_ENABLED=false
|
|
||||||
|
|
||||||
pgadmin:
|
pgadmin:
|
||||||
image: dpage/pgadmin4
|
image: dpage/pgadmin4
|
||||||
|
@@ -4,6 +4,27 @@ title: "Changelog"
|
|||||||
|
|
||||||
The changelog below reflects new product developments and updates on a monthly basis.
|
The changelog below reflects new product developments and updates on a monthly basis.
|
||||||
|
|
||||||
|
## December 2024
|
||||||
|
- Added [GCP KMS](https://infisical.com/docs/documentation/platform/kms/overview) integration support.
|
||||||
|
- Added support for [K8s CSI integration](https://infisical.com/docs/integrations/platforms/kubernetes-csi) and ability to point K8s operator to specific secret versions.
|
||||||
|
- Fixed [Java SDK](https://github.com/Infisical/java-sdk) compatibility issues with Alpine Linux.
|
||||||
|
- Fixed SCIM group role assignment issues.
|
||||||
|
- Added Group View Page for improved team management.
|
||||||
|
- Added instance URL to email verification for Infisical accounts.
|
||||||
|
- Added ability to copy full path of nested folders.
|
||||||
|
- Added custom templating support for K8s operator, allowing flexible secret key mapping and additional fields
|
||||||
|
- Optimized secrets versions table performance.
|
||||||
|
|
||||||
|
## November 2024
|
||||||
|
- Improved EnvKey migration functionality with support for Blocks, Inheritance, and Branches.
|
||||||
|
- Added [Hardware Security Module (HSM) Encryption](https://infisical.com/docs/documentation/platform/kms/hsm-integration) support.
|
||||||
|
- Updated permissions handling in [Infisical Terraform Provider](https://registry.terraform.io/providers/Infisical/infisical/latest/docs) to use lists instead of sets.
|
||||||
|
- Enhanced [SCIM](https://infisical.com/docs/documentation/platform/scim/overview) implementation to remove SAML dependency.
|
||||||
|
- Enhanced [OIDC Authentication](https://infisical.com/docs/documentation/platform/identities/oidc-auth/general) implementation and added Default Org Slug support.
|
||||||
|
- Added support for multiple authentication methods per identity.
|
||||||
|
- Added AWS Parameter Store integration sync improvements.
|
||||||
|
- Added new screen and API for managing additional privileges.
|
||||||
|
- Added Dynamic Secrets support for SQL Server.
|
||||||
|
|
||||||
## October 2024
|
## October 2024
|
||||||
- Significantly improved performance of audit log operations in UI.
|
- Significantly improved performance of audit log operations in UI.
|
||||||
|
@@ -4,6 +4,13 @@ sidebarTitle: "Overview"
|
|||||||
description: "Learn how to generate secrets dynamically on-demand."
|
description: "Learn how to generate secrets dynamically on-demand."
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Info>
|
||||||
|
Note that Dynamic Secrets is a paid feature.
|
||||||
|
|
||||||
|
If you're using Infisical Cloud, then it is available under the **Enterprise Tier**
|
||||||
|
If you're self-hosting Infisical, then you should contact sales@infisical.com to purchase an enterprise license to use it.
|
||||||
|
</Info>
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Contrary to static key-value secrets, which require manual input of data into the secure Infisical storage, **dynamic secrets are generated on-demand upon access**.
|
Contrary to static key-value secrets, which require manual input of data into the secure Infisical storage, **dynamic secrets are generated on-demand upon access**.
|
||||||
|
@@ -3,6 +3,13 @@ title: "Approval Workflows"
|
|||||||
description: "Learn how to enable a set of policies to manage changes to sensitive secrets and environments."
|
description: "Learn how to enable a set of policies to manage changes to sensitive secrets and environments."
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Info>
|
||||||
|
Approval Workflows is a paid feature.
|
||||||
|
|
||||||
|
If you're using Infisical Cloud, then it is available under the **Pro Tier** and **Enterprise Tire**.
|
||||||
|
If you're self-hosting Infisical, then you should contact sales@infisical.com to purchase an enterprise license to use it.
|
||||||
|
</Info>
|
||||||
|
|
||||||
## Problem at hand
|
## Problem at hand
|
||||||
|
|
||||||
Updating secrets in high-stakes environments (e.g., production) can have a number of problematic issues:
|
Updating secrets in high-stakes environments (e.g., production) can have a number of problematic issues:
|
||||||
@@ -40,4 +47,4 @@ When a user submits a change to an enviropnment that is under a particular polic
|
|||||||
|
|
||||||
Approvers are notified by email and/or Slack as soon as the request is initiated. In the Infisical Dashboard, they will be able to `approve` and `merge` (or `deny`) a request for a change in a particular environment. After that, depending on the workflows setup, the change will be automatically propagated to the right applications (e.g., using [Infisical Kubernetes Operator](https://infisical.com/docs/integrations/platforms/kubernetes)).
|
Approvers are notified by email and/or Slack as soon as the request is initiated. In the Infisical Dashboard, they will be able to `approve` and `merge` (or `deny`) a request for a change in a particular environment. After that, depending on the workflows setup, the change will be automatically propagated to the right applications (e.g., using [Infisical Kubernetes Operator](https://infisical.com/docs/integrations/platforms/kubernetes)).
|
||||||
|
|
||||||

|

|
||||||
|
@@ -15,7 +15,7 @@ Prerequisites:
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Create a SCIM token in Infisical">
|
<Step title="Create a SCIM token in Infisical">
|
||||||
In Infisical, head to your Organization Settings > Authentication > SCIM Configuration and
|
In Infisical, head to your Organization Settings > Security > SCIM Configuration and
|
||||||
press the **Enable SCIM provisioning** toggle to allow Azure to provision/deprovision users for your organization.
|
press the **Enable SCIM provisioning** toggle to allow Azure to provision/deprovision users for your organization.
|
||||||
|
|
||||||

|

|
||||||
|
@@ -15,7 +15,7 @@ Prerequisites:
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Create a SCIM token in Infisical">
|
<Step title="Create a SCIM token in Infisical">
|
||||||
In Infisical, head to your Organization Settings > Authentication > SCIM Configuration and
|
In Infisical, head to your Organization Settings > Security > SCIM Configuration and
|
||||||
press the **Enable SCIM provisioning** toggle to allow JumpCloud to provision/deprovision users and user groups for your organization.
|
press the **Enable SCIM provisioning** toggle to allow JumpCloud to provision/deprovision users and user groups for your organization.
|
||||||
|
|
||||||

|

|
||||||
|
@@ -15,7 +15,7 @@ Prerequisites:
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Create a SCIM token in Infisical">
|
<Step title="Create a SCIM token in Infisical">
|
||||||
In Infisical, head to your Organization Settings > Authentication > SCIM Configuration and
|
In Infisical, head to your Organization Settings > Security > SCIM Configuration and
|
||||||
press the **Enable SCIM provisioning** toggle to allow Okta to provision/deprovision users and user groups for your organization.
|
press the **Enable SCIM provisioning** toggle to allow Okta to provision/deprovision users and user groups for your organization.
|
||||||
|
|
||||||

|

|
||||||
|
93
docs/documentation/platform/sso/auth0-saml.mdx
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
---
|
||||||
|
title: "Auth0 SAML"
|
||||||
|
description: "Learn how to configure Auth0 SAML for Infisical SSO."
|
||||||
|
---
|
||||||
|
|
||||||
|
<Info>
|
||||||
|
Auth0 SAML SSO feature is a paid feature. If you're using Infisical Cloud,
|
||||||
|
then it is available under the **Pro Tier**. If you're self-hosting Infisical,
|
||||||
|
then you should contact sales@infisical.com to purchase an enterprise license
|
||||||
|
to use it.
|
||||||
|
</Info>
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Prepare the SAML SSO configuration in Infisical">
|
||||||
|
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Auth0, then click **Connect** again.
|
||||||
|
|
||||||
|
Next, note the **Application Callback URL** and **Audience** to use when configuring the Auth0 SAML application.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</Step>
|
||||||
|
<Step title="Create a SAML application in Auth0">
|
||||||
|
2.1. In your Auth0 account, head to Applications and create an application.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Select **Regular Web Application** and press **Create**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2.2. In the Application head to Settings > Application URIs and add the **Application Callback URL** from step 1 into the **Allowed Callback URLs** field.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2.3. In the Application head to Addons > SAML2 Web App and copy the **Issuer**, **Identity Provider Login URL**, and **Identity Provider Certificate** from the **Usage** tab.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2.4. Back in Infisical, set **Issuer**, **Identity Provider Login URL**, and **Certificate** to the corresponding items from step 2.3.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2.5. Back in Auth0, in the **Settings** tab, set the **Application Callback URL** to the **Application Callback URL** from step 1
|
||||||
|
and update the **Settings** field with the JSON under the picture below (replacing `<audience-from-infisical>` with the **Audience** from step 1).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"audience": "<audience-from-infisical>",
|
||||||
|
"mappings": {
|
||||||
|
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email",
|
||||||
|
"given_name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/firstName",
|
||||||
|
"family_name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/lastName"
|
||||||
|
},
|
||||||
|
"signatureAlgorithm": "rsa-sha256",
|
||||||
|
"digestAlgorithm": "sha256",
|
||||||
|
"signResponse": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Click **Save**.
|
||||||
|
</Step>
|
||||||
|
<Step title="Enable SAML SSO in Infisical">
|
||||||
|
Enabling SAML SSO allows members in your organization to log into Infisical via Auth0.
|
||||||
|
|
||||||
|

|
||||||
|
</Step>
|
||||||
|
<Step title="Enforce SAML SSO in Infisical">
|
||||||
|
Enforcing SAML SSO ensures that members in your organization can only access Infisical
|
||||||
|
by logging into the organization via Auth0.
|
||||||
|
|
||||||
|
To enforce SAML SSO, you're required to test out the SAML connection by successfully authenticating at least one Auth0 user with Infisical;
|
||||||
|
Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO.
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
If you are only using one organization on your Infisical instance, you can configure a default organization in the [Server Admin Console](../admin-panel/server-admin#default-organization) to expedite SAML login.
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
If you're configuring SAML SSO on a self-hosted instance of Infisical, make
|
||||||
|
sure to set the `AUTH_SECRET` and `SITE_URL` environment variable for it to
|
||||||
|
work:
|
||||||
|
<div class="height:1px;"/>
|
||||||
|
- `AUTH_SECRET`: A secret key used for signing and verifying JWT. This
|
||||||
|
can be a random 32-byte base64 string generated with `openssl rand -base64
|
||||||
|
32`.
|
||||||
|
<div class="height:1px;"/>
|
||||||
|
- `SITE_URL`: The absolute URL of your self-hosted instance of Infisical including the protocol (e.g. https://app.infisical.com)
|
||||||
|
</Note>
|
@@ -12,7 +12,7 @@ description: "Learn how to configure Microsoft Entra ID for Infisical SSO."
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Prepare the SAML SSO configuration in Infisical">
|
<Step title="Prepare the SAML SSO configuration in Infisical">
|
||||||
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Set up SAML SSO**.
|
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Azure / Entra, then click **Connect** again.
|
||||||
|
|
||||||
Next, copy the **Reply URL (Assertion Consumer Service URL)** and **Identifier (Entity ID)** to use when configuring the Azure SAML application.
|
Next, copy the **Reply URL (Assertion Consumer Service URL)** and **Identifier (Entity ID)** to use when configuring the Azure SAML application.
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ description: "Learn how to configure Google SAML for Infisical SSO."
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Prepare the SAML SSO configuration in Infisical">
|
<Step title="Prepare the SAML SSO configuration in Infisical">
|
||||||
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Set up SAML SSO**.
|
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Google, then click **Connect** again.
|
||||||
|
|
||||||
Next, note the **ACS URL** and **SP Entity ID** to use when configuring the Google SAML application.
|
Next, note the **ACS URL** and **SP Entity ID** to use when configuring the Google SAML application.
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ description: "Learn how to configure JumpCloud SAML for Infisical SSO."
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Prepare the SAML SSO configuration in Infisical">
|
<Step title="Prepare the SAML SSO configuration in Infisical">
|
||||||
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Set up SAML SSO**.
|
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select JumpCloud, then click **Connect** again.
|
||||||
|
|
||||||
Next, copy the **ACS URL** and **SP Entity ID** to use when configuring the JumpCloud SAML application.
|
Next, copy the **ACS URL** and **SP Entity ID** to use when configuring the JumpCloud SAML application.
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ description: "Learn how to configure Keycloak SAML for Infisical SSO."
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Prepare the SAML SSO configuration in Infisical">
|
<Step title="Prepare the SAML SSO configuration in Infisical">
|
||||||
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Manage**.
|
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Keycloak, then click **Connect** again.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ description: "Learn how to configure Okta SAML 2.0 for Infisical SSO."
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Prepare the SAML SSO configuration in Infisical">
|
<Step title="Prepare the SAML SSO configuration in Infisical">
|
||||||
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Set up SAML SSO**.
|
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Okta, then click **Connect** again.
|
||||||
|
|
||||||
Next, copy the **Single sign-on URL** and **Audience URI (SP Entity ID)** to use when configuring the Okta SAML 2.0 application.
|
Next, copy the **Single sign-on URL** and **Audience URI (SP Entity ID)** to use when configuring the Okta SAML 2.0 application.
|
||||||

|

|
||||||
|
@@ -28,6 +28,7 @@ Infisical supports these and many other identity providers:
|
|||||||
- [JumpCloud SAML](/documentation/platform/sso/jumpcloud)
|
- [JumpCloud SAML](/documentation/platform/sso/jumpcloud)
|
||||||
- [Keycloak SAML](/documentation/platform/sso/keycloak-saml)
|
- [Keycloak SAML](/documentation/platform/sso/keycloak-saml)
|
||||||
- [Google SAML](/documentation/platform/sso/google-saml)
|
- [Google SAML](/documentation/platform/sso/google-saml)
|
||||||
|
- [Auth0 SAML](/documentation/platform/sso/auth0-saml)
|
||||||
- [Keycloak OIDC](/documentation/platform/sso/keycloak-oidc)
|
- [Keycloak OIDC](/documentation/platform/sso/keycloak-oidc)
|
||||||
- [Auth0 OIDC](/documentation/platform/sso/auth0-oidc)
|
- [Auth0 OIDC](/documentation/platform/sso/auth0-oidc)
|
||||||
- [General OIDC](/documentation/platform/sso/general-oidc)
|
- [General OIDC](/documentation/platform/sso/general-oidc)
|
||||||
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 580 KiB |
After Width: | Height: | Size: 493 KiB |
BIN
docs/images/sso/auth0-saml/auth0-config-2.png
Normal file
After Width: | Height: | Size: 612 KiB |
BIN
docs/images/sso/auth0-saml/auth0-config-3.png
Normal file
After Width: | Height: | Size: 366 KiB |
BIN
docs/images/sso/auth0-saml/auth0-config.png
Normal file
After Width: | Height: | Size: 431 KiB |
BIN
docs/images/sso/auth0-saml/create-application-2.png
Normal file
After Width: | Height: | Size: 352 KiB |
BIN
docs/images/sso/auth0-saml/create-application.png
Normal file
After Width: | Height: | Size: 253 KiB |
BIN
docs/images/sso/auth0-saml/enable-saml.png
Normal file
After Width: | Height: | Size: 605 KiB |
BIN
docs/images/sso/auth0-saml/infisical-config.png
Normal file
After Width: | Height: | Size: 605 KiB |
BIN
docs/images/sso/auth0-saml/init-config.png
Normal file
After Width: | Height: | Size: 539 KiB |
@@ -17,6 +17,7 @@ Prerequisites:
|
|||||||
If your instance is deployed on AWS, the aws-sdk will automatically retrieve the credentials. Ensure that you assign the provided permission policy to your deployed instance, such as ECS or EC2.
|
If your instance is deployed on AWS, the aws-sdk will automatically retrieve the credentials. Ensure that you assign the provided permission policy to your deployed instance, such as ECS or EC2.
|
||||||
|
|
||||||
The following steps are for instances not deployed on AWS
|
The following steps are for instances not deployed on AWS
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Create an IAM User">
|
<Step title="Create an IAM User">
|
||||||
Navigate to [Create IAM User](https://console.aws.amazon.com/iamv2/home#/users/create) in your AWS Console.
|
Navigate to [Create IAM User](https://console.aws.amazon.com/iamv2/home#/users/create) in your AWS Console.
|
||||||
@@ -40,9 +41,10 @@ The following steps are for instances not deployed on AWS
|
|||||||
<Step title="Obtain the IAM User Credentials">
|
<Step title="Obtain the IAM User Credentials">
|
||||||
Obtain the AWS access key ID and secret access key for your IAM User by navigating to IAM > Users > [Your User] > Security credentials > Access keys.
|
Obtain the AWS access key ID and secret access key for your IAM User by navigating to IAM > Users > [Your User] > Security credentials > Access keys.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Set Up Integration Keys">
|
<Step title="Set Up Integration Keys">
|
||||||
1. Set the access key as **CLIENT_ID_AWS_INTEGRATION**.
|
1. Set the access key as **CLIENT_ID_AWS_INTEGRATION**.
|
||||||
@@ -59,6 +61,7 @@ The following steps are for instances not deployed on AWS
|
|||||||
2. Select **AWS Account** as the **Trusted Entity Type**.
|
2. Select **AWS Account** as the **Trusted Entity Type**.
|
||||||
3. Choose **Another AWS Account** and enter **381492033652** (Infisical AWS Account ID). This restricts the role to be assumed only by Infisical. If self-hosting, provide your AWS account number instead.
|
3. Choose **Another AWS Account** and enter **381492033652** (Infisical AWS Account ID). This restricts the role to be assumed only by Infisical. If self-hosting, provide your AWS account number instead.
|
||||||
4. Optionally, enable **Require external ID** and enter your **project ID** to further enhance security.
|
4. Optionally, enable **Require external ID** and enter your **project ID** to further enhance security.
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
<Step title="Add Required Permissions for the IAM Role">
|
<Step title="Add Required Permissions for the IAM Role">
|
||||||
@@ -89,11 +92,13 @@ The following steps are for instances not deployed on AWS
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
<Step title="Copy the AWS IAM Role ARN">
|
<Step title="Copy the AWS IAM Role ARN">
|
||||||

|

|
||||||
|
</Step>
|
||||||
|
|
||||||
<Step title="Authorize Infisical for AWS Secrets Manager">
|
<Step title="Authorize Infisical for AWS Secrets Manager">
|
||||||
1. Navigate to your project's integrations tab in Infisical.
|
1. Navigate to your project's integrations tab in Infisical.
|
||||||
@@ -104,6 +109,7 @@ The following steps are for instances not deployed on AWS
|
|||||||

|

|
||||||
|
|
||||||
4. Provide the **AWS IAM Role ARN** obtained from the previous step.
|
4. Provide the **AWS IAM Role ARN** obtained from the previous step.
|
||||||
|
|
||||||
</Step> <Step title="Start integration">
|
</Step> <Step title="Start integration">
|
||||||
Select how you want to integration to work by specifying a number of parameters:
|
Select how you want to integration to work by specifying a number of parameters:
|
||||||
|
|
||||||
@@ -127,6 +133,12 @@ The following steps are for instances not deployed on AWS
|
|||||||
|
|
||||||
Optionally, you can add tags or specify the encryption key of all the secrets created via this integration:
|
Optionally, you can add tags or specify the encryption key of all the secrets created via this integration:
|
||||||
|
|
||||||
|
<ParamField path="Tag Sync Mode" type="string" optional>
|
||||||
|
The sync mode for AWS tags. The supported options are `Secret Metadata` and `Custom`. If `Secret Metadata` is selected,
|
||||||
|
the metadata of the Infisical secrets are used as tags in AWS. If custom is selected, then the key/value of the **Secret Tag** field is used. `Secret Metadata` mode
|
||||||
|
is only supported for one-to-one integrations.
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
<ParamField path="Secret Tag" type="string" optional>
|
<ParamField path="Secret Tag" type="string" optional>
|
||||||
The Key/Value of a tag that will be added to secrets in AWS. Please note that it is possible to add multiple tags via API.
|
The Key/Value of a tag that will be added to secrets in AWS. Please note that it is possible to add multiple tags via API.
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
@@ -29,6 +29,15 @@ description: "How to sync secrets from Infisical to Azure App Configuration"
|
|||||||

|

|
||||||
|
|
||||||
Press create integration to start syncing secrets to Azure App Configuration.
|
Press create integration to start syncing secrets to Azure App Configuration.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The Azure App Configuration integration requires the following permissions to be set on the user / service principal
|
||||||
|
for Infisical to sync secrets to Azure App Configuration: `Read Key-Value`, `Write Key-Value`, `Delete Key-Value`.
|
||||||
|
|
||||||
|
Any role with these permissions would work such as the **App Configuration Data Owner** role. Alternatively, you can use the
|
||||||
|
**App Configuration Data Reader** role for read-only access or **App Configuration Data Contributor** role for read/write access.
|
||||||
|
</Note>
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Additional Configuration">
|
<Step title="Additional Configuration">
|
||||||
|
|
||||||
@@ -38,7 +47,7 @@ description: "How to sync secrets from Infisical to Azure App Configuration"
|
|||||||
- `{ "uri": "https://my-key-vault.vault.azure.net/secrets/my-secret" }`
|
- `{ "uri": "https://my-key-vault.vault.azure.net/secrets/my-secret" }`
|
||||||
- `https://my-key-vault.vault.azure.net/secrets/my-secret`
|
- `https://my-key-vault.vault.azure.net/secrets/my-secret`
|
||||||
|
|
||||||
### Azure Labels
|
#### Azure Labels
|
||||||
You can sync secrets from Infisical to Azure with custom labels by enabling the `Use Labels` option during setup:
|
You can sync secrets from Infisical to Azure with custom labels by enabling the `Use Labels` option during setup:
|
||||||
|
|
||||||
**When enabled**: Secrets will be pushed to Azure with your specified label
|
**When enabled**: Secrets will be pushed to Azure with your specified label
|
||||||
|
@@ -17,6 +17,10 @@ description: "How to sync secrets from Infisical to Azure Key Vault"
|
|||||||

|

|
||||||
|
|
||||||
Press on the Azure Key Vault tile and grant Infisical access to Azure Key Vault.
|
Press on the Azure Key Vault tile and grant Infisical access to Azure Key Vault.
|
||||||
|
You can optionally authenticate against a specific tenant by providing the Azure tenant or directory ID.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Start integration">
|
<Step title="Start integration">
|
||||||
Obtain the Vault URI of your key vault in the Overview tab.
|
Obtain the Vault URI of your key vault in the Overview tab.
|
||||||
|
@@ -248,6 +248,7 @@
|
|||||||
"documentation/platform/sso/jumpcloud",
|
"documentation/platform/sso/jumpcloud",
|
||||||
"documentation/platform/sso/keycloak-saml",
|
"documentation/platform/sso/keycloak-saml",
|
||||||
"documentation/platform/sso/google-saml",
|
"documentation/platform/sso/google-saml",
|
||||||
|
"documentation/platform/sso/auth0-saml",
|
||||||
"documentation/platform/sso/keycloak-oidc",
|
"documentation/platform/sso/keycloak-oidc",
|
||||||
"documentation/platform/sso/auth0-oidc",
|
"documentation/platform/sso/auth0-oidc",
|
||||||
"documentation/platform/sso/general-oidc"
|
"documentation/platform/sso/general-oidc"
|
||||||
@@ -940,9 +941,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"integrations": {
|
|
||||||
"intercom": "hsg644ru"
|
|
||||||
},
|
|
||||||
"analytics": {
|
"analytics": {
|
||||||
"koala": {
|
"koala": {
|
||||||
"publicApiKey": "pk_b50d7184e0e39ddd5cdb43cf6abeadd9b97d"
|
"publicApiKey": "pk_b50d7184e0e39ddd5cdb43cf6abeadd9b97d"
|
||||||
|
@@ -1,3 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
**/.next
|
dist
|
||||||
.next
|
|
||||||
|
@@ -1,108 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ["next.config.js"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es2021: true,
|
|
||||||
es6: true
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
"airbnb",
|
|
||||||
"airbnb-typescript",
|
|
||||||
"airbnb/hooks",
|
|
||||||
"plugin:react/recommended",
|
|
||||||
"prettier",
|
|
||||||
"plugin:storybook/recommended"
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: "latest",
|
|
||||||
sourceType: "module",
|
|
||||||
project: "./tsconfig.json",
|
|
||||||
ecmaFeatures: {
|
|
||||||
jsx: true
|
|
||||||
},
|
|
||||||
tsconfigRootDir: __dirname
|
|
||||||
},
|
|
||||||
plugins: ["react", "prettier", "simple-import-sort", "import"],
|
|
||||||
rules: {
|
|
||||||
"@typescript-eslint/no-empty-function": "off",
|
|
||||||
quotes: ["error", "double", { avoidEscape: true }],
|
|
||||||
"comma-dangle": ["error", "only-multiline"],
|
|
||||||
"react/react-in-jsx-scope": "off",
|
|
||||||
"import/prefer-default-export": "off",
|
|
||||||
"react-hooks/exhaustive-deps": "off",
|
|
||||||
"@typescript-eslint/ban-ts-comment": "warn",
|
|
||||||
"react/jsx-props-no-spreading": "off", // switched off for component building
|
|
||||||
// TODO: This rule will be switched ON after complete revamp of frontend
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"jsx-a11y/control-has-associated-label": "off",
|
|
||||||
"no-console": "off",
|
|
||||||
"arrow-body-style": "off",
|
|
||||||
"no-underscore-dangle": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
allow: ["_id"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"jsx-a11y/anchor-is-valid": "off",
|
|
||||||
// all those <a> tags must be converted to label or a p component
|
|
||||||
//
|
|
||||||
"react/require-default-props": "off",
|
|
||||||
"react/jsx-filename-extension": [
|
|
||||||
1,
|
|
||||||
{
|
|
||||||
extensions: [".tsx", ".ts"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
// TODO: turn this rule ON after migration. everything should use arrow functions
|
|
||||||
"react/function-component-definition": [
|
|
||||||
0,
|
|
||||||
{
|
|
||||||
namedComponents: "arrow-function"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"react/no-unknown-property": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
ignore: ["jsx"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
|
||||||
"simple-import-sort/exports": "warn",
|
|
||||||
"simple-import-sort/imports": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
groups: [
|
|
||||||
// Node.js builtins. You could also generate this regex if you use a `.js` config.
|
|
||||||
// For example: `^(${require("module").builtinModules.join("|")})(/|$)`
|
|
||||||
// Note that if you use the `node:` prefix for Node.js builtins,
|
|
||||||
// you can avoid this complexity: You can simply use "^node:".
|
|
||||||
[
|
|
||||||
"^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)"
|
|
||||||
],
|
|
||||||
// Packages `react` related packages
|
|
||||||
["^react", "^next", "^@?\\w"],
|
|
||||||
["^@app"],
|
|
||||||
// Internal packages.
|
|
||||||
["^~(/.*|$)"],
|
|
||||||
// Relative imports
|
|
||||||
["^\\.\\.(?!/?$)", "^\\.\\./?$", "^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
|
|
||||||
// Style imports.
|
|
||||||
["^.+\\.?(css|scss)$"]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
ignorePatterns: ["next.config.js", "cypress/**/*.js", "cypress.config.js"],
|
|
||||||
settings: {
|
|
||||||
"import/resolver": {
|
|
||||||
typescript: {
|
|
||||||
project: ["./tsconfig.json"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
50
frontend/.gitignore
vendored
@@ -1,36 +1,24 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# Logs
|
||||||
|
logs
|
||||||
# dependencies
|
*.log
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
.env
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# next.js
|
|
||||||
/.next/
|
|
||||||
/out/
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# debug
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
# local env files
|
node_modules
|
||||||
.env.local
|
dist
|
||||||
.env.development.local
|
dist-ssr
|
||||||
.env.test.local
|
*.local
|
||||||
.env.production.local
|
|
||||||
.vercel
|
|
||||||
.env.infisical
|
|
||||||
|
|
||||||
.vscode
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
@@ -3,5 +3,8 @@
|
|||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"semi": true
|
"semi": true,
|
||||||
|
"plugins": ["prettier-plugin-tailwindcss"],
|
||||||
|
"tailwindStylesheet": "./src/index.css",
|
||||||
|
"tailwindFunctions": ["clsx", "twMerge"]
|
||||||
}
|
}
|
||||||
|
@@ -1,28 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
module.exports = {
|
|
||||||
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
|
||||||
addons: [
|
|
||||||
"@storybook/addon-links",
|
|
||||||
"@storybook/addon-essentials",
|
|
||||||
"@storybook/addon-interactions",
|
|
||||||
"storybook-dark-mode",
|
|
||||||
{
|
|
||||||
name: "@storybook/addon-styling",
|
|
||||||
options: {
|
|
||||||
postCss: {
|
|
||||||
implementation: require("postcss")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
framework: {
|
|
||||||
name: "@storybook/nextjs",
|
|
||||||
options: {}
|
|
||||||
},
|
|
||||||
core: {
|
|
||||||
disableTelemetry: true
|
|
||||||
},
|
|
||||||
docs: {
|
|
||||||
autodocs: "tag"
|
|
||||||
}
|
|
||||||
};
|
|
@@ -1,29 +0,0 @@
|
|||||||
import { themes } from "@storybook/theming";
|
|
||||||
import "react-day-picker/dist/style.css";
|
|
||||||
import "../src/styles/globals.css";
|
|
||||||
|
|
||||||
export const parameters = {
|
|
||||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
|
||||||
backgrounds: {
|
|
||||||
default: "dark",
|
|
||||||
values: [
|
|
||||||
{
|
|
||||||
name: "dark",
|
|
||||||
value: "rgb(14, 16, 20)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "paper",
|
|
||||||
value: "rgb(30, 31, 34)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
controls: {
|
|
||||||
matchers: {
|
|
||||||
color: /(background|color)$/i,
|
|
||||||
date: /Date$/
|
|
||||||
}
|
|
||||||
},
|
|
||||||
darkMode: {
|
|
||||||
dark: { ...themes.dark, appContentBg: "rgb(14,16,20)", appBg: "rgb(14,16,20)" }
|
|
||||||
}
|
|
||||||
};
|
|
@@ -1,84 +0,0 @@
|
|||||||
ARG POSTHOG_HOST=https://app.posthog.com
|
|
||||||
ARG POSTHOG_API_KEY=posthog-api-key
|
|
||||||
ARG INTERCOM_ID=intercom-id
|
|
||||||
ARG NEXT_INFISICAL_PLATFORM_VERSION=next-infisical-platform-version
|
|
||||||
ARG CAPTCHA_SITE_KEY=captcha-site-key
|
|
||||||
|
|
||||||
FROM node:16-alpine AS deps
|
|
||||||
# Install dependencies only when needed. Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
|
||||||
# RUN apk add --no-cache libc6-compat
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy over dependency files
|
|
||||||
COPY package.json package-lock.json next.config.js ./
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
RUN npm ci --only-production --ignore-scripts
|
|
||||||
|
|
||||||
# Rebuild the source code only when needed
|
|
||||||
FROM node:16-alpine AS builder
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy dependencies
|
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
|
||||||
# Copy all files
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
ENV NODE_ENV production
|
|
||||||
ENV NEXT_PUBLIC_ENV production
|
|
||||||
ARG POSTHOG_HOST
|
|
||||||
ENV NEXT_PUBLIC_POSTHOG_HOST $POSTHOG_HOST
|
|
||||||
ARG POSTHOG_API_KEY
|
|
||||||
ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY
|
|
||||||
ARG INTERCOM_ID
|
|
||||||
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
|
|
||||||
ARG CAPTCHA_SITE_KEY
|
|
||||||
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY $CAPTCHA_SITE_KEY
|
|
||||||
|
|
||||||
# Build
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
|
|
||||||
# Production image
|
|
||||||
FROM node:16-alpine AS runner
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN addgroup --system --gid 1001 nodejs
|
|
||||||
RUN adduser --system --uid 1001 nextjs
|
|
||||||
|
|
||||||
RUN mkdir -p /app/.next/cache/images && chown nextjs:nodejs /app/.next/cache/images
|
|
||||||
VOLUME /app/.next/cache/images
|
|
||||||
|
|
||||||
ARG POSTHOG_API_KEY
|
|
||||||
ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
|
||||||
BAKED_NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY
|
|
||||||
ARG INTERCOM_ID
|
|
||||||
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
|
|
||||||
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
|
|
||||||
ARG SAML_ORG_SLUG
|
|
||||||
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG \
|
|
||||||
BAKED_NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
|
|
||||||
ARG NEXT_INFISICAL_PLATFORM_VERSION
|
|
||||||
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION=$NEXT_INFISICAL_PLATFORM_VERSION
|
|
||||||
ARG CAPTCHA_SITE_KEY
|
|
||||||
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY \
|
|
||||||
BAKED_NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY
|
|
||||||
COPY --chown=nextjs:nodejs --chmod=555 scripts ./scripts
|
|
||||||
COPY --from=builder /app/public ./public
|
|
||||||
RUN chown nextjs:nodejs ./public/data
|
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
||||||
COPY --from=builder --chown=nextjs:nodejs --chmod=777 /app/.next/static ./.next/static
|
|
||||||
RUN chmod -R 777 /app/.next/server
|
|
||||||
|
|
||||||
USER nextjs
|
|
||||||
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
ENV PORT 3000
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
|
||||||
|
|
||||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
|
|
||||||
CMD node scripts/healthcheck.js
|
|
||||||
|
|
||||||
|
|
||||||
CMD ["/app/scripts/start.sh"]
|
|
@@ -1,5 +1,5 @@
|
|||||||
# Base layer
|
# Base layer
|
||||||
FROM node:16-alpine
|
FROM node:20-alpine
|
||||||
|
|
||||||
# Set the working directory
|
# Set the working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -11,9 +11,6 @@ COPY package-lock.json ./
|
|||||||
# Install
|
# Install
|
||||||
RUN npm install --ignore-scripts
|
RUN npm install --ignore-scripts
|
||||||
|
|
||||||
# Copy over next.js config
|
|
||||||
COPY next.config.js ./next.config.js
|
|
||||||
|
|
||||||
# Copy all files
|
# Copy all files
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
@@ -1,22 +1,50 @@
|
|||||||
This is the client repository for Infisical.
|
# React + TypeScript + Vite
|
||||||
|
|
||||||
## Before you get started with development locally
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
Please ensure you have Docker and Docker Compose installed for your OS.
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
### Steps to start server
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
|
|
||||||
- `CD` into the repo
|
## Expanding the ESLint configuration
|
||||||
- run command `docker-compose -f docker-compose.dev.yml up --build --force-recreate`
|
|
||||||
- Visit localhost:8080 and the website should be live
|
|
||||||
|
|
||||||
### Steps to shutdown this Docker compose
|
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||||
|
|
||||||
- `CD` into this repo
|
- Configure the top-level `parserOptions` property like this:
|
||||||
- run command `docker-compose -f docker-compose.dev.yml down`
|
|
||||||
|
|
||||||
### Notes
|
```js
|
||||||
|
export default tseslint.config({
|
||||||
|
languageOptions: {
|
||||||
|
// other options...
|
||||||
|
parserOptions: {
|
||||||
|
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
Any changes made to local files in the `/components`, `/pages`, `/styles` will be hot reloaded. If would like like to watch for other files or folders live, please add them to the docker volume.
|
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
||||||
|
- Optionally add `...tseslint.configs.stylisticTypeChecked`
|
||||||
|
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
|
||||||
|
|
||||||
You will also need to ensure that a .env.local file exists with all required environment variables
|
```js
|
||||||
|
// eslint.config.js
|
||||||
|
import react from 'eslint-plugin-react'
|
||||||
|
|
||||||
|
export default tseslint.config({
|
||||||
|
// Set the react version
|
||||||
|
settings: { react: { version: '18.3' } },
|
||||||
|
plugins: {
|
||||||
|
// Add the react plugin
|
||||||
|
react,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// other rules...
|
||||||
|
// Enable its recommended rules
|
||||||
|
...react.configs.recommended.rules,
|
||||||
|
...react.configs['jsx-runtime'].rules,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
@@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
e2e: {
|
|
||||||
baseUrl: 'http://localhost:8080',
|
|
||||||
viewportWidth: 1480,
|
|
||||||
viewportHeight: 920,
|
|
||||||
},
|
|
||||||
};
|
|
@@ -1,47 +0,0 @@
|
|||||||
/// <reference types="cypress" />
|
|
||||||
|
|
||||||
describe('organization Overview', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.login(`test@localhost.local`, `testInfisical1`)
|
|
||||||
})
|
|
||||||
|
|
||||||
const projectName = "projectY"
|
|
||||||
|
|
||||||
it('can`t create projects with empty names', () => {
|
|
||||||
cy.get('.button').click()
|
|
||||||
cy.get('input[placeholder="Type your project name"]').type('abc').clear()
|
|
||||||
cy.intercept('*').as('anyRequest');
|
|
||||||
cy.get('@anyRequest').should('not.exist');
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can delete a newly-created project', () => {
|
|
||||||
// Create a project
|
|
||||||
cy.get('.button').click()
|
|
||||||
cy.get('input[placeholder="Type your project name"]').type(`${projectName}`)
|
|
||||||
cy.contains('button', 'Create Project').click()
|
|
||||||
cy.url().should('include', '/project')
|
|
||||||
|
|
||||||
// Delete a project
|
|
||||||
cy.get(`[href^="/project/"][href$="/settings"] > a > .group`).click()
|
|
||||||
cy.contains('button', `Delete ${projectName}`).click()
|
|
||||||
cy.contains('button', 'Delete Project').should('have.attr', 'disabled')
|
|
||||||
cy.get('input[placeholder="Type to delete..."]').type('confirm')
|
|
||||||
cy.contains('button', 'Delete Project').should('not.have.attr', 'disabled')
|
|
||||||
cy.url().then((currentUrl) => {
|
|
||||||
let projectId = currentUrl.split("/")[4]
|
|
||||||
cy.intercept('DELETE', `/api/v1/workspace/${projectId}`).as('deleteProject');
|
|
||||||
cy.contains('button', 'Delete Project').click();
|
|
||||||
cy.get('@deleteProject').should('have.property', 'response').and('have.property', 'statusCode', 200);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can display no projects', () => {
|
|
||||||
cy.intercept('/api/v1/workspace', {
|
|
||||||
body: {
|
|
||||||
"workspaces": []
|
|
||||||
},
|
|
||||||
})
|
|
||||||
cy.get('.border-mineshaft-700 > :nth-child(2)').should('have.text', 'You are not part of any projects in this organization yet. When you are, they will appear here.')
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
@@ -1,24 +0,0 @@
|
|||||||
/// <reference types="cypress" />
|
|
||||||
|
|
||||||
describe('Organization Settings', () => {
|
|
||||||
let orgId;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.login(`test@localhost.local`, `testInfisical1`)
|
|
||||||
cy.url().then((currentUrl) => {
|
|
||||||
orgId = currentUrl.split("/")[4]
|
|
||||||
cy.visit(`org/${orgId}/settings`)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can rename org', () => {
|
|
||||||
cy.get('input[placeholder="Acme Corp"]').clear().type('ABC')
|
|
||||||
|
|
||||||
cy.intercept('PATCH', `/api/v1/organization/${orgId}/name`).as('renameOrg');
|
|
||||||
cy.get('form.p-4 > .button').click()
|
|
||||||
cy.get('@renameOrg').should('have.property', 'response').and('have.property', 'statusCode', 200);
|
|
||||||
|
|
||||||
cy.get('.pl-3').should("have.text", "ABC ")
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
@@ -1,84 +0,0 @@
|
|||||||
/// <reference types="cypress" />
|
|
||||||
|
|
||||||
describe('Project Overview', () => {
|
|
||||||
const projectName = "projectY"
|
|
||||||
let projectId;
|
|
||||||
let isFirstTest = true;
|
|
||||||
|
|
||||||
before(() => {
|
|
||||||
cy.login(`test@localhost.local`, `testInfisical1`)
|
|
||||||
|
|
||||||
// Create a project
|
|
||||||
cy.get('.button').click()
|
|
||||||
cy.get('input[placeholder="Type your project name"]').type(`${projectName}`)
|
|
||||||
cy.contains('button', 'Create Project').click()
|
|
||||||
cy.url().should('include', '/project').then((currentUrl) => {
|
|
||||||
projectId = currentUrl.split("/")[4]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
if (isFirstTest) {
|
|
||||||
isFirstTest = false;
|
|
||||||
return; // Skip the rest of the beforeEach for the first test
|
|
||||||
}
|
|
||||||
cy.login(`test@localhost.local`, `testInfisical1`)
|
|
||||||
cy.visit(`/project/${projectId}/secrets/overview`)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can create secrets', () => {
|
|
||||||
cy.contains('button', 'Go to Development').click()
|
|
||||||
cy.contains('button', 'Add a new secret').click()
|
|
||||||
cy.get('input[placeholder="Type your secret name"]').type('SECRET_A')
|
|
||||||
cy.contains('button', 'Create Secret').click()
|
|
||||||
cy.get('.w-80 > .inline-flex > .input').should('have.value', 'SECRET_A')
|
|
||||||
cy.get(':nth-child(6) > .button > .w-min').should('have.text', '1 Commit')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can update secrets', () => {
|
|
||||||
cy.get(':nth-child(2) > .flex > .button').click()
|
|
||||||
cy.get('.overflow-auto > .relative > .absolute').type('VALUE_A')
|
|
||||||
cy.get('.button.text-primary > .svg-inline--fa').click()
|
|
||||||
cy.get(':nth-child(6) > .button > .w-min').should('have.text', '2 Commits')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can`t create duplicate-name secrets', () => {
|
|
||||||
cy.get(':nth-child(2) > .flex > .button').click()
|
|
||||||
cy.contains('button', 'Add Secret').click()
|
|
||||||
cy.get('input[placeholder="Type your secret name"]').type('SECRET_A')
|
|
||||||
cy.intercept('POST', `/api/v3/secrets/SECRET_A`).as('createSecret');
|
|
||||||
cy.contains('button', 'Create Secret').click()
|
|
||||||
cy.get('@createSecret').should('have.property', 'response').and('have.property', 'statusCode', 400);
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can add another secret', () => {
|
|
||||||
cy.get(':nth-child(2) > .flex > .button').click()
|
|
||||||
cy.contains('button', 'Add Secret').click()
|
|
||||||
cy.get('input[placeholder="Type your secret name"]').type('SECRET_B')
|
|
||||||
cy.contains('button', 'Create Secret').click()
|
|
||||||
cy.get(':nth-child(6) > .button > .w-min').should('have.text', '3 Commits')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can delete a secret', () => {
|
|
||||||
cy.get(':nth-child(2) > .flex > .button').click()
|
|
||||||
// cy.get(':nth-child(3) > .shadow-none').trigger('mouseover')
|
|
||||||
cy.get(':nth-child(3) > .shadow-none > .group > .h-10 > .border-red').click()
|
|
||||||
cy.contains('button', 'Delete Secret').should('have.attr', 'disabled')
|
|
||||||
cy.get('input[placeholder="Type to delete..."]').type('SECRET_B')
|
|
||||||
cy.intercept('DELETE', `/api/v3/secrets/SECRET_B`).as('deleteSecret');
|
|
||||||
cy.contains('button', 'Delete Secret').should('not.have.attr', 'disabled')
|
|
||||||
cy.contains('button', 'Delete Secret').click();
|
|
||||||
cy.get('@deleteSecret').should('have.property', 'response').and('have.property', 'statusCode', 200);
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can add a comment', () => {
|
|
||||||
return;
|
|
||||||
cy.get(':nth-child(2) > .flex > .button').click()
|
|
||||||
// for some reason this hover does not want to work
|
|
||||||
cy.get('.overflow-auto').trigger('mouseover').then(() => {
|
|
||||||
cy.get('.shadow-none > .group > .pl-4 > .h-8 > button[aria-label="add-comment"]').should('be.visible').click()
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Using fixtures to represent data",
|
|
||||||
"email": "hello@cypress.io",
|
|
||||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
|
||||||
}
|
|
@@ -1,19 +0,0 @@
|
|||||||
|
|
||||||
Cypress.Commands.add('login', (username, password) => {
|
|
||||||
cy.visit('/login')
|
|
||||||
cy.get('input[placeholder="Enter your email..."]').type(username)
|
|
||||||
cy.get('input[placeholder="Enter your password..."]').type(password)
|
|
||||||
cy.contains('Continue with Email').click()
|
|
||||||
cy.url().should('include', '/overview')
|
|
||||||
})
|
|
||||||
|
|
||||||
// Cypress.Commands.add('login', (username, password) => {
|
|
||||||
// cy.session([username, password], () => {
|
|
||||||
// cy.visit('/login')
|
|
||||||
// cy.get('input[placeholder="Enter your email..."]').type(username)
|
|
||||||
// cy.get('input[placeholder="Enter your password..."]').type(password)
|
|
||||||
// cy.contains('Continue with Email').click()
|
|
||||||
// cy.url().should('include', '/overview')
|
|
||||||
// cy.wait(2000);
|
|
||||||
// })
|
|
||||||
// })
|
|
@@ -1,20 +0,0 @@
|
|||||||
// ***********************************************************
|
|
||||||
// This example support/e2e.js is processed and
|
|
||||||
// loaded automatically before your test files.
|
|
||||||
//
|
|
||||||
// This is a great place to put global configuration and
|
|
||||||
// behavior that modifies Cypress.
|
|
||||||
//
|
|
||||||
// You can change the location of this file or turn off
|
|
||||||
// automatically serving support files with the
|
|
||||||
// 'supportFile' configuration option.
|
|
||||||
//
|
|
||||||
// You can read more here:
|
|
||||||
// https://on.cypress.io/configuration
|
|
||||||
// ***********************************************************
|
|
||||||
|
|
||||||
// Import commands.js using ES2015 syntax:
|
|
||||||
import './commands'
|
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
|
||||||
// require('./commands')
|
|
137
frontend/eslint.config.js
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import globals from "globals";
|
||||||
|
import reactHooks from "eslint-plugin-react-hooks";
|
||||||
|
import reactRefresh from "eslint-plugin-react-refresh";
|
||||||
|
import eslintPluginPrettier from "eslint-plugin-prettier/recommended";
|
||||||
|
import simpleImportSort from "eslint-plugin-simple-import-sort";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import { FlatCompat } from "@eslint/eslintrc";
|
||||||
|
import stylisticPlugin from "@stylistic/eslint-plugin";
|
||||||
|
import importPlugin from "eslint-plugin-import";
|
||||||
|
import pluginRouter from "@tanstack/eslint-plugin-router";
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: import.meta.dirname
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ["dist"] },
|
||||||
|
{
|
||||||
|
extends: [
|
||||||
|
...pluginRouter.configs["flat/recommended"],
|
||||||
|
js.configs.recommended,
|
||||||
|
tseslint.configs.recommended,
|
||||||
|
...compat.extends("airbnb"),
|
||||||
|
...compat.extends("@kesills/airbnb-typescript"),
|
||||||
|
eslintPluginPrettier
|
||||||
|
],
|
||||||
|
files: ["**/*.{ts,tsx}"],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir: import.meta.dirname
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
"react-hooks": reactHooks,
|
||||||
|
"react-refresh": reactRefresh,
|
||||||
|
"simple-import-sort": simpleImportSort,
|
||||||
|
import: importPlugin
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
"import/resolver": {
|
||||||
|
typescript: {
|
||||||
|
project: ["./tsconfig.json"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
"react-refresh/only-export-components": "off",
|
||||||
|
"@typescript-eslint/only-throw-error": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
quotes: ["error", "double", { avoidEscape: true }],
|
||||||
|
"comma-dangle": ["error", "only-multiline"],
|
||||||
|
"react/react-in-jsx-scope": "off",
|
||||||
|
"import/prefer-default-export": "off",
|
||||||
|
"react-hooks/exhaustive-deps": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "warn",
|
||||||
|
"react/jsx-props-no-spreading": "off", // switched off for component building
|
||||||
|
// TODO: This rule will be switched ON after complete revamp of frontend
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"jsx-a11y/control-has-associated-label": "off",
|
||||||
|
"import/no-extraneous-dependencies": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
devDependencies: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-console": "off",
|
||||||
|
"arrow-body-style": "off",
|
||||||
|
"no-underscore-dangle": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
allow: ["_id"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"jsx-a11y/anchor-is-valid": "off",
|
||||||
|
// all those <a> tags must be converted to label or a p component
|
||||||
|
//
|
||||||
|
"react/require-default-props": "off",
|
||||||
|
"react/jsx-filename-extension": [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
extensions: [".tsx", ".ts"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// TODO: turn this rule ON after migration. everything should use arrow functions
|
||||||
|
"react/function-component-definition": [
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
namedComponents: "arrow-function"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"react/no-unknown-property": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
ignore: ["jsx"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
|
"simple-import-sort/exports": "warn",
|
||||||
|
"simple-import-sort/imports": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
groups: [
|
||||||
|
// Node.js builtins. You could also generate this regex if you use a `.js` config.
|
||||||
|
// For example: `^(${require("module").builtinModules.join("|")})(/|$)`
|
||||||
|
// Note that if you use the `node:` prefix for Node.js builtins,
|
||||||
|
// you can avoid this complexity: You can simply use "^node:".
|
||||||
|
[
|
||||||
|
"^(assert|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|https|module|net|os|path|punycode|querystring|readline|repl|stream|string_decoder|sys|timers|tls|tty|url|util|vm|zlib|freelist|v8|process|async_hooks|http2|perf_hooks)(/.*|$)"
|
||||||
|
],
|
||||||
|
// Packages `react` related packages
|
||||||
|
["^react", "^next", "^@?\\w"],
|
||||||
|
["^@app"],
|
||||||
|
// Internal packages.
|
||||||
|
["^~(/.*|$)"],
|
||||||
|
// Relative imports
|
||||||
|
["^\\.\\.(?!/?$)", "^\\.\\./?$", "^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
|
||||||
|
// Style imports.
|
||||||
|
["^.+\\.?(css|scss)$"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"import/first": "error",
|
||||||
|
"import/newline-after-import": "error",
|
||||||
|
"import/no-duplicates": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rules: Object.fromEntries(
|
||||||
|
Object.keys(stylisticPlugin.configs["all-flat"].rules ?? {}).map((key) => [key, "off"])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
29
frontend/index.html
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" href="/infisical.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
content="
|
||||||
|
default-src 'self';
|
||||||
|
connect-src 'self' https://*.posthog.com http://127.0.0.1:* https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web@0.38.2/dist/dotlottie-player.wasm;
|
||||||
|
script-src 'self' https://*.posthog.com https://js.stripe.com https://api.stripe.com https://widget.intercom.io https://js.intercomcdn.com https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web@0.38.2/dist/dotlottie-player.wasm;
|
||||||
|
style-src 'self' 'unsafe-inline' https://hcaptcha.com https://*.hcaptcha.com;
|
||||||
|
child-src https://api.stripe.com;
|
||||||
|
frame-src https://js.stripe.com/ https://api.stripe.com https://www.youtube.com/ https://hcaptcha.com https://*.hcaptcha.com;
|
||||||
|
connect-src 'self' wss://nexus-websocket-a.intercom.io https://api-iam.intercom.io https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com https://api.pwnedpasswords.com http://127.0.0.1:* https://hcaptcha.com https://*.hcaptcha.com;
|
||||||
|
img-src 'self' https://static.intercomassets.com https://js.intercomcdn.com https://downloads.intercomcdn.com https://*.stripe.com https://i.ytimg.com/ data:;
|
||||||
|
media-src https://js.intercomcdn.com;
|
||||||
|
font-src 'self' https://fonts.intercomcdn.com/ https://fonts.gstatic.com;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<title>Infisical</title>
|
||||||
|
<script src="/runtime-ui-env.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
frontend/next-env.d.ts
vendored
@@ -1,5 +0,0 @@
|
|||||||
/// <reference types="next" />
|
|
||||||
/// <reference types="next/image-types/global" />
|
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
|
@@ -1,98 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
|
|
||||||
const ContentSecurityPolicy = `
|
|
||||||
default-src 'self';
|
|
||||||
connect-src 'self' https://*.posthog.com http://127.0.0.1:*;
|
|
||||||
script-src 'self' https://*.posthog.com https://js.stripe.com https://api.stripe.com https://widget.intercom.io https://js.intercomcdn.com https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' 'unsafe-eval';
|
|
||||||
style-src 'self' https://rsms.me 'unsafe-inline' https://hcaptcha.com https://*.hcaptcha.com;
|
|
||||||
child-src https://api.stripe.com;
|
|
||||||
frame-src https://js.stripe.com/ https://api.stripe.com https://www.youtube.com/ https://hcaptcha.com https://*.hcaptcha.com;
|
|
||||||
connect-src 'self' wss://nexus-websocket-a.intercom.io https://api-iam.intercom.io https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com https://api.pwnedpasswords.com http://127.0.0.1:* https://hcaptcha.com https://*.hcaptcha.com;
|
|
||||||
img-src 'self' https://static.intercomassets.com https://js.intercomcdn.com https://downloads.intercomcdn.com https://*.stripe.com https://i.ytimg.com/ data:;
|
|
||||||
media-src https://js.intercomcdn.com;
|
|
||||||
font-src 'self' https://fonts.intercomcdn.com/ https://maxcdn.bootstrapcdn.com https://rsms.me https://fonts.gstatic.com;
|
|
||||||
`;
|
|
||||||
|
|
||||||
// You can choose which headers to add to the list
|
|
||||||
// after learning more below.
|
|
||||||
const securityHeaders = [
|
|
||||||
{
|
|
||||||
key: "X-DNS-Prefetch-Control",
|
|
||||||
value: "on"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "Strict-Transport-Security",
|
|
||||||
value: "max-age=63072000; includeSubDomains; preload"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "X-XSS-Protection",
|
|
||||||
value: "1; mode=block"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "X-Frame-Options",
|
|
||||||
value: "SAMEORIGIN"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "Permissions-Policy",
|
|
||||||
value: "camera=(), microphone=()"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "X-Content-Type-Options",
|
|
||||||
value: "nosniff"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "Referrer-Policy",
|
|
||||||
value: "strict-origin-when-cross-origin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "Content-Security-Policy",
|
|
||||||
value: ContentSecurityPolicy.replace(/\s{2,}/g, " ").trim()
|
|
||||||
}
|
|
||||||
];
|
|
||||||
/**
|
|
||||||
* @type {import('next').NextConfig}
|
|
||||||
**/
|
|
||||||
module.exports = {
|
|
||||||
output: "standalone",
|
|
||||||
i18n: {
|
|
||||||
locales: ["en", "ko", "fr", "pt-BR", "pt-PT", "es"],
|
|
||||||
defaultLocale: "en"
|
|
||||||
},
|
|
||||||
async headers() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
// Apply these headers to all routes in your application.
|
|
||||||
source: "/:path*",
|
|
||||||
headers: securityHeaders
|
|
||||||
}
|
|
||||||
];
|
|
||||||
},
|
|
||||||
webpack: (config, { isServer, webpack }) => {
|
|
||||||
// config
|
|
||||||
config.module.rules.push({
|
|
||||||
test: /\.wasm$/,
|
|
||||||
loader: "base64-loader",
|
|
||||||
type: "javascript/auto"
|
|
||||||
});
|
|
||||||
|
|
||||||
config.module.noParse = /\.wasm$/;
|
|
||||||
|
|
||||||
config.module.rules.forEach((rule) => {
|
|
||||||
(rule.oneOf || []).forEach((oneOf) => {
|
|
||||||
if (oneOf.loader && oneOf.loader.indexOf("file-loader") >= 0) {
|
|
||||||
oneOf.exclude.push(/\.wasm$/);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isServer) {
|
|
||||||
config.resolve.fallback.fs = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform customizations to webpack config
|
|
||||||
config.plugins.push(new webpack.IgnorePlugin({ resourceRegExp: /\/__tests__\// }));
|
|
||||||
|
|
||||||
// Important: return the modified config
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|