Compare commits
37 Commits
revert-310
...
upgrade-in
Author | SHA1 | Date | |
---|---|---|---|
258e561b84 | |||
5802638fc4 | |||
e2e7004583 | |||
7826324435 | |||
2bfc1caec5 | |||
4b9e3e44e2 | |||
b2a680ebd7 | |||
b269bb81fe | |||
5ca7ff4f2d | |||
ec12d57862 | |||
2d16f5f258 | |||
93912da528 | |||
ffc5e61faa | |||
70e68f4441 | |||
a004934a28 | |||
0811192eed | |||
1e09487572 | |||
86202caa95 | |||
285fca4ded | |||
30fb60b441 | |||
e531390922 | |||
e88ce49463 | |||
9214c93ece | |||
7a3bfa9e4c | |||
7aa0e8572c | |||
296efa975c | |||
b3e72c338f | |||
8c4c969bc2 | |||
0d424f332a | |||
72780c61b4 | |||
c4da0305ba | |||
4fdfdc1a39 | |||
ff8f1d3bfb | |||
4de8888843 | |||
da35ec90bc | |||
00e69e6632 | |||
cedb22a39a |
262
.github/workflows/deployment-pipeline.yml
vendored
@ -1,262 +0,0 @@
|
||||
name: Deployment pipeline
|
||||
on: [workflow_dispatch]
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: "infisical-core-deployment"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
infisical-tests:
|
||||
name: Integration tests
|
||||
# https://docs.github.com/en/actions/using-workflows/reusing-workflows#overview
|
||||
uses: ./.github/workflows/run-backend-tests.yml
|
||||
|
||||
infisical-image:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
needs: [infisical-tests]
|
||||
steps:
|
||||
- name: ☁️ Checkout source
|
||||
uses: actions/checkout@v3
|
||||
- name: 📦 Install dependencies to test all dependencies
|
||||
run: npm ci --only-production
|
||||
working-directory: backend
|
||||
- name: Save commit hashes for tag
|
||||
id: commit
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
- name: 🔧 Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: 🐋 Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Set up Depot CLI
|
||||
uses: depot/setup-action@v1
|
||||
- name: 🏗️ Build backend and push to docker hub
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
project: 64mmf0n610
|
||||
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||
push: true
|
||||
context: .
|
||||
file: Dockerfile.standalone-infisical
|
||||
tags: |
|
||||
infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||
infisical/staging_infisical:latest
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }}
|
||||
INFISICAL_PLATFORM_VERSION=${{ steps.commit.outputs.short }}
|
||||
|
||||
gamma-deployment:
|
||||
name: Deploy to gamma
|
||||
runs-on: ubuntu-latest
|
||||
needs: [infisical-image]
|
||||
environment:
|
||||
name: Gamma
|
||||
steps:
|
||||
- uses: twingate/github-action@v1
|
||||
with:
|
||||
# The Twingate Service Key used to connect Twingate to the proper service
|
||||
# Learn more about [Twingate Services](https://docs.twingate.com/docs/services)
|
||||
#
|
||||
# Required
|
||||
service-key: ${{ secrets.TWINGATE_SERVICE_KEY }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "20"
|
||||
- name: Change directory to backend and install dependencies
|
||||
env:
|
||||
DB_CONNECTION_URI: ${{ secrets.DB_CONNECTION_URI }}
|
||||
run: |
|
||||
cd backend
|
||||
npm install
|
||||
npm run migration:latest
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
audience: sts.amazonaws.com
|
||||
aws-region: us-east-1
|
||||
role-to-assume: arn:aws:iam::905418227878:role/deploy-new-ecs-img
|
||||
- name: Save commit hashes for tag
|
||||
id: commit
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
- name: Download task definition
|
||||
run: |
|
||||
aws ecs describe-task-definition --task-definition infisical-core-gamma-stage --query taskDefinition > task-definition.json
|
||||
- name: Render Amazon ECS task definition
|
||||
id: render-web-container
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||
with:
|
||||
task-definition: task-definition.json
|
||||
container-name: infisical-core
|
||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||
environment-variables: "LOG_LEVEL=info"
|
||||
- name: Deploy to Amazon ECS service
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
|
||||
with:
|
||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||
service: infisical-core-gamma-stage
|
||||
cluster: infisical-gamma-stage
|
||||
wait-for-service-stability: true
|
||||
|
||||
production-us:
|
||||
name: US production deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs: [gamma-deployment]
|
||||
environment:
|
||||
name: Production
|
||||
steps:
|
||||
- uses: twingate/github-action@v1
|
||||
with:
|
||||
service-key: ${{ secrets.TWINGATE_SERVICE_KEY }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "20"
|
||||
- name: Change directory to backend and install dependencies
|
||||
env:
|
||||
DB_CONNECTION_URI: ${{ secrets.DB_CONNECTION_URI }}
|
||||
AUDIT_LOGS_DB_CONNECTION_URI: ${{ secrets.AUDIT_LOGS_DB_CONNECTION_URI }}
|
||||
run: |
|
||||
cd backend
|
||||
npm install
|
||||
npm run migration:latest
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
audience: sts.amazonaws.com
|
||||
aws-region: us-east-1
|
||||
role-to-assume: arn:aws:iam::381492033652:role/gha-make-prod-deployment
|
||||
- name: Save commit hashes for tag
|
||||
id: commit
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
- name: Download task definition
|
||||
run: |
|
||||
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
|
||||
- name: Render Amazon ECS task definition
|
||||
id: render-web-container
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||
with:
|
||||
task-definition: task-definition.json
|
||||
container-name: infisical-core-platform
|
||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||
environment-variables: "LOG_LEVEL=info"
|
||||
- name: Deploy to Amazon ECS service
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
|
||||
with:
|
||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||
service: infisical-core-platform
|
||||
cluster: infisical-core-platform
|
||||
wait-for-service-stability: true
|
||||
- name: Post slack message
|
||||
uses: slackapi/slack-github-action@v2.0.0
|
||||
with:
|
||||
webhook: ${{ secrets.SLACK_DEPLOYMENT_WEBHOOK_URL }}
|
||||
webhook-type: incoming-webhook
|
||||
payload: |
|
||||
text: "*Deployment Status Update*: ${{ job.status }}"
|
||||
blocks:
|
||||
- type: "section"
|
||||
text:
|
||||
type: "mrkdwn"
|
||||
text: "*Deployment Status Update*: ${{ job.status }}"
|
||||
- type: "section"
|
||||
fields:
|
||||
- type: "mrkdwn"
|
||||
text: "*Application:*\nInfisical Core"
|
||||
- type: "mrkdwn"
|
||||
text: "*Instance Type:*\nShared Infisical Cloud"
|
||||
- type: "section"
|
||||
fields:
|
||||
- type: "mrkdwn"
|
||||
text: "*Region:*\nUS"
|
||||
- type: "mrkdwn"
|
||||
text: "*Git Tag:*\n<https://github.com/Infisical/infisical/commit/${{ steps.commit.outputs.short }}>"
|
||||
|
||||
|
||||
production-eu:
|
||||
name: EU production deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs: [production-us]
|
||||
environment:
|
||||
name: production-eu
|
||||
steps:
|
||||
- uses: twingate/github-action@v1
|
||||
with:
|
||||
service-key: ${{ secrets.TWINGATE_SERVICE_KEY }}
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
audience: sts.amazonaws.com
|
||||
aws-region: eu-central-1
|
||||
role-to-assume: arn:aws:iam::345594589636:role/gha-make-prod-deployment
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "20"
|
||||
- name: Change directory to backend and install dependencies
|
||||
env:
|
||||
DB_CONNECTION_URI: ${{ secrets.DB_CONNECTION_URI }}
|
||||
run: |
|
||||
cd backend
|
||||
npm install
|
||||
npm run migration:latest
|
||||
- name: Save commit hashes for tag
|
||||
id: commit
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
- name: Download task definition
|
||||
run: |
|
||||
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
|
||||
- name: Render Amazon ECS task definition
|
||||
id: render-web-container
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||
with:
|
||||
task-definition: task-definition.json
|
||||
container-name: infisical-core-platform
|
||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||
environment-variables: "LOG_LEVEL=info"
|
||||
- name: Deploy to Amazon ECS service
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
|
||||
with:
|
||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||
service: infisical-core-platform
|
||||
cluster: infisical-core-platform
|
||||
wait-for-service-stability: true
|
||||
- name: Post slack message
|
||||
uses: slackapi/slack-github-action@v2.0.0
|
||||
with:
|
||||
webhook: ${{ secrets.SLACK_DEPLOYMENT_WEBHOOK_URL }}
|
||||
webhook-type: incoming-webhook
|
||||
payload: |
|
||||
text: "*Deployment Status Update*: ${{ job.status }}"
|
||||
blocks:
|
||||
- type: "section"
|
||||
text:
|
||||
type: "mrkdwn"
|
||||
text: "*Deployment Status Update*: ${{ job.status }}"
|
||||
- type: "section"
|
||||
fields:
|
||||
- type: "mrkdwn"
|
||||
text: "*Application:*\nInfisical Core"
|
||||
- type: "mrkdwn"
|
||||
text: "*Instance Type:*\nShared Infisical Cloud"
|
||||
- type: "section"
|
||||
fields:
|
||||
- type: "mrkdwn"
|
||||
text: "*Region:*\nEU"
|
||||
- type: "mrkdwn"
|
||||
text: "*Git Tag:*\n<https://github.com/Infisical/infisical/commit/${{ steps.commit.outputs.short }}>"
|
||||
|
103
backend/package-lock.json
generated
@ -21,7 +21,7 @@
|
||||
"@fastify/etag": "^5.1.0",
|
||||
"@fastify/formbody": "^7.4.0",
|
||||
"@fastify/helmet": "^11.1.1",
|
||||
"@fastify/multipart": "8.3.0",
|
||||
"@fastify/multipart": "^8.3.1",
|
||||
"@fastify/passport": "^2.4.0",
|
||||
"@fastify/rate-limit": "^9.0.0",
|
||||
"@fastify/request-context": "^5.1.0",
|
||||
@ -48,8 +48,8 @@
|
||||
"@peculiar/x509": "^1.12.1",
|
||||
"@serdnam/pino-cloudwatch-transport": "^1.0.4",
|
||||
"@sindresorhus/slugify": "1.1.0",
|
||||
"@slack/oauth": "^3.0.1",
|
||||
"@slack/web-api": "^7.3.4",
|
||||
"@slack/oauth": "^3.0.2",
|
||||
"@slack/web-api": "^7.8.0",
|
||||
"@ucast/mongo2js": "^1.3.4",
|
||||
"ajv": "^8.12.0",
|
||||
"argon2": "^0.31.2",
|
||||
@ -81,7 +81,7 @@
|
||||
"mongodb": "^6.8.1",
|
||||
"ms": "^2.1.3",
|
||||
"mysql2": "^3.9.8",
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.8",
|
||||
"nodemailer": "^6.9.9",
|
||||
"odbc": "^2.4.9",
|
||||
"openid-client": "^5.6.5",
|
||||
@ -5423,13 +5423,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/busboy": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
|
||||
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz",
|
||||
"integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@fastify/cookie": {
|
||||
"version": "9.3.1",
|
||||
@ -5502,19 +5499,41 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/multipart": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-8.3.0.tgz",
|
||||
"integrity": "sha512-A8h80TTyqUzaMVH0Cr9Qcm6RxSkVqmhK/MVBYHYeRRSUbUYv08WecjWKSlG2aSnD4aGI841pVxAjC+G1GafUeQ==",
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-8.3.1.tgz",
|
||||
"integrity": "sha512-pncbnG28S6MIskFSVRtzTKE9dK+GrKAJl0NbaQ/CG8ded80okWFsYKzSlP9haaLNQhNRDOoHqmGQNvgbiPVpWQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.1.0",
|
||||
"@fastify/deepmerge": "^1.0.0",
|
||||
"@fastify/error": "^3.0.0",
|
||||
"@fastify/busboy": "^3.0.0",
|
||||
"@fastify/deepmerge": "^2.0.0",
|
||||
"@fastify/error": "^4.0.0",
|
||||
"fastify-plugin": "^4.0.0",
|
||||
"secure-json-parse": "^2.4.0",
|
||||
"stream-wormhole": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/multipart/node_modules/@fastify/deepmerge": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-2.0.1.tgz",
|
||||
"integrity": "sha512-hx+wJQr9Ph1hY/dyzY0SxqjumMyqZDlIF6oe71dpRKDHUg7dFQfjG94qqwQ274XRjmUrwKiYadex8XplNHx3CA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@fastify/multipart/node_modules/@fastify/error": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.0.0.tgz",
|
||||
"integrity": "sha512-OO/SA8As24JtT1usTUTKgGH7uLvhfwZPwlptRi2Dp5P4KKmJI3gvsZ8MIHnNwDs4sLf/aai5LzTyl66xr7qMxA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@fastify/passport": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/passport/-/passport-2.4.0.tgz",
|
||||
@ -9049,6 +9068,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.0.tgz",
|
||||
"integrity": "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": ">=18.0.0"
|
||||
},
|
||||
@ -9058,12 +9078,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@slack/oauth": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@slack/oauth/-/oauth-3.0.1.tgz",
|
||||
"integrity": "sha512-TuR9PI6bYKX6qHC7FQI4keMnhj45TNfSNQtTU3mtnHUX4XLM2dYLvRkUNADyiLTle2qu2rsOQtCIsZJw6H0sDA==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@slack/oauth/-/oauth-3.0.2.tgz",
|
||||
"integrity": "sha512-MdPS8AP9n3u/hBeqRFu+waArJLD/q+wOSZ48ktMTwxQLc6HJyaWPf8soqAyS/b0D6IlvI5TxAdyRyyv3wQ5IVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@slack/logger": "^4",
|
||||
"@slack/web-api": "^7.3.4",
|
||||
"@slack/web-api": "^7.8.0",
|
||||
"@types/jsonwebtoken": "^9",
|
||||
"@types/node": ">=18",
|
||||
"jsonwebtoken": "^9",
|
||||
@ -9075,24 +9096,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@slack/types": {
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@slack/types/-/types-2.12.0.tgz",
|
||||
"integrity": "sha512-yFewzUomYZ2BYaGJidPuIgjoYj5wqPDmi7DLSaGIkf+rCi4YZ2Z3DaiYIbz7qb/PL2NmamWjCvB7e9ArI5HkKg==",
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@slack/types/-/types-2.14.0.tgz",
|
||||
"integrity": "sha512-n0EGm7ENQRxlXbgKSrQZL69grzg1gHLAVd+GlRVQJ1NSORo0FrApR7wql/gaKdu2n4TO83Sq/AmeUOqD60aXUA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12.13.0",
|
||||
"npm": ">= 6.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@slack/web-api": {
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.3.4.tgz",
|
||||
"integrity": "sha512-KwLK8dlz2lhr3NO7kbYQ7zgPTXPKrhq1JfQc0etJ0K8LSJhYYnf8GbVznvgDT/Uz1/pBXfFQnoXjrQIOKAdSuw==",
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.8.0.tgz",
|
||||
"integrity": "sha512-d4SdG+6UmGdzWw38a4sN3lF/nTEzsDxhzU13wm10ejOpPehtmRoqBKnPztQUfFiWbNvSb4czkWYJD4kt+5+Fuw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@slack/logger": "^4.0.0",
|
||||
"@slack/types": "^2.9.0",
|
||||
"@types/node": ">=18.0.0",
|
||||
"@types/retry": "0.12.0",
|
||||
"axios": "^1.7.4",
|
||||
"axios": "^1.7.8",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"form-data": "^4.0.0",
|
||||
"is-electron": "2.2.2",
|
||||
@ -9110,6 +9133,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
@ -10526,7 +10550,8 @@
|
||||
"node_modules/@types/retry": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
||||
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
|
||||
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/safe-regex": {
|
||||
"version": "1.1.6",
|
||||
@ -11969,9 +11994,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
|
||||
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
|
||||
"version": "1.7.9",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
|
||||
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
@ -13926,7 +13952,8 @@
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
@ -15942,7 +15969,8 @@
|
||||
"node_modules/is-electron": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz",
|
||||
"integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg=="
|
||||
"integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
@ -18182,6 +18210,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||
"integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@ -18228,6 +18257,7 @@
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
|
||||
"integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eventemitter3": "^4.0.4",
|
||||
"p-timeout": "^3.2.0"
|
||||
@ -18242,12 +18272,14 @@
|
||||
"node_modules/p-queue/node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/p-retry": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
|
||||
"integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/retry": "0.12.0",
|
||||
"retry": "^0.13.1"
|
||||
@ -18271,6 +18303,7 @@
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
|
||||
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-finally": "^1.0.0"
|
||||
},
|
||||
|
@ -45,7 +45,7 @@
|
||||
"test:e2e-coverage": "vitest run --coverage -c vitest.e2e.config.ts",
|
||||
"generate:component": "tsx ./scripts/create-backend-file.ts",
|
||||
"generate:schema": "tsx ./scripts/generate-schema-types.ts && eslint --fix --ext ts ./src/db/schemas",
|
||||
"auditlog-migration:latest": "knex --knexfile ./src/db/auditlog-knexfile.mjs --client pg migrate:latest",
|
||||
"auditlog-migration:latest": "node ./dist/db/rename-migrations-to-mjs.mjs && knex --knexfile ./dist/db/auditlog-knexfile.mjs --client pg migrate:latest",
|
||||
"auditlog-migration:up": "knex --knexfile ./dist/db/auditlog-knexfile.mjs --client pg migrate:up",
|
||||
"auditlog-migration:down": "knex --knexfile ./dist/db/auditlog-knexfile.mjs --client pg migrate:down",
|
||||
"auditlog-migration:list": "knex --knexfile ./dist/db/auditlog-knexfile.mjs --client pg migrate:list",
|
||||
@ -62,7 +62,7 @@
|
||||
"migration:unlock": "npm run auditlog-migration:unlock && knex --knexfile ./dist/db/knexfile.mjs migrate:unlock",
|
||||
"migrate:org": "tsx ./scripts/migrate-organization.ts",
|
||||
"seed:new": "tsx ./scripts/create-seed-file.ts",
|
||||
"seed": "knex --knexfile ./src/db/knexfile.ts --client pg seed:run",
|
||||
"seed": "knex --knexfile ./dist/db/knexfile.ts --client pg seed:run",
|
||||
"db:reset": "npm run migration:rollback -- --all && npm run migration:latest"
|
||||
},
|
||||
"keywords": [],
|
||||
@ -129,7 +129,7 @@
|
||||
"@fastify/etag": "^5.1.0",
|
||||
"@fastify/formbody": "^7.4.0",
|
||||
"@fastify/helmet": "^11.1.1",
|
||||
"@fastify/multipart": "8.3.0",
|
||||
"@fastify/multipart": "8.3.1",
|
||||
"@fastify/passport": "^2.4.0",
|
||||
"@fastify/rate-limit": "^9.0.0",
|
||||
"@fastify/request-context": "^5.1.0",
|
||||
@ -156,8 +156,8 @@
|
||||
"@peculiar/x509": "^1.12.1",
|
||||
"@serdnam/pino-cloudwatch-transport": "^1.0.4",
|
||||
"@sindresorhus/slugify": "1.1.0",
|
||||
"@slack/oauth": "^3.0.1",
|
||||
"@slack/web-api": "^7.3.4",
|
||||
"@slack/oauth": "^3.0.2",
|
||||
"@slack/web-api": "^7.8.0",
|
||||
"@ucast/mongo2js": "^1.3.4",
|
||||
"ajv": "^8.12.0",
|
||||
"argon2": "^0.31.2",
|
||||
@ -189,7 +189,7 @@
|
||||
"mongodb": "^6.8.1",
|
||||
"ms": "^2.1.3",
|
||||
"mysql2": "^3.9.8",
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.8",
|
||||
"nodemailer": "^6.9.9",
|
||||
"odbc": "^2.4.9",
|
||||
"openid-client": "^5.6.5",
|
||||
|
@ -31,7 +31,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||
const projectEncryptionRingBuffer =
|
||||
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
||||
const webhooks = await knex(TableName.Webhook)
|
||||
const webhooks = await knex(TableName.Webhook)
|
||||
.where({})
|
||||
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.Webhook}.envId`)
|
||||
.select(
|
||||
@ -53,10 +53,13 @@ export async function up(knex: Knex): Promise<void> {
|
||||
webhooks.map(async (el) => {
|
||||
let projectKmsService = projectEncryptionRingBuffer.getItem(el.projectId);
|
||||
if (!projectKmsService) {
|
||||
projectKmsService = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId: el.projectId
|
||||
}, knex);
|
||||
projectKmsService = await kmsService.createCipherPairWithDataKey(
|
||||
{
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId: el.projectId
|
||||
},
|
||||
knex
|
||||
);
|
||||
projectEncryptionRingBuffer.push(el.projectId, projectKmsService);
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,13 @@ export async function up(knex: Knex): Promise<void> {
|
||||
dynamicSecretRootCredentials.map(async ({ projectId, ...el }) => {
|
||||
let projectKmsService = projectEncryptionRingBuffer.getItem(projectId);
|
||||
if (!projectKmsService) {
|
||||
projectKmsService = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId
|
||||
}, knex);
|
||||
projectKmsService = await kmsService.createCipherPairWithDataKey(
|
||||
{
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId
|
||||
},
|
||||
knex
|
||||
);
|
||||
projectEncryptionRingBuffer.push(projectId, projectKmsService);
|
||||
}
|
||||
|
||||
|
@ -39,10 +39,13 @@ export async function up(knex: Knex): Promise<void> {
|
||||
secretRotations.map(async ({ projectId, ...el }) => {
|
||||
let projectKmsService = projectEncryptionRingBuffer.getItem(projectId);
|
||||
if (!projectKmsService) {
|
||||
projectKmsService = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId
|
||||
}, knex);
|
||||
projectKmsService = await kmsService.createCipherPairWithDataKey(
|
||||
{
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId
|
||||
},
|
||||
knex
|
||||
);
|
||||
projectEncryptionRingBuffer.push(projectId, projectKmsService);
|
||||
}
|
||||
|
||||
|
@ -76,77 +76,87 @@ const reencryptIdentityK8sAuth = async (knex: Knex) => {
|
||||
)
|
||||
.orderBy(`${TableName.OrgBot}.orgId` as "orgId");
|
||||
|
||||
const updatedIdentityKubernetesConfigs = [];
|
||||
const updatedIdentityKubernetesConfigs = [];
|
||||
|
||||
for (const { encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, orgId, ...el } of identityKubernetesConfigs) {
|
||||
let orgKmsService = orgEncryptionRingBuffer.getItem(orgId);
|
||||
|
||||
if (!orgKmsService) {
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey({
|
||||
for await (const {
|
||||
encryptedSymmetricKey,
|
||||
symmetricKeyKeyEncoding,
|
||||
symmetricKeyTag,
|
||||
symmetricKeyIV,
|
||||
orgId,
|
||||
...el
|
||||
} of identityKubernetesConfigs) {
|
||||
let orgKmsService = orgEncryptionRingBuffer.getItem(orgId);
|
||||
|
||||
if (!orgKmsService) {
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey(
|
||||
{
|
||||
type: KmsDataKey.Organization,
|
||||
orgId
|
||||
}, knex);
|
||||
orgEncryptionRingBuffer.push(orgId, orgKmsService);
|
||||
}
|
||||
|
||||
const key = infisicalSymmetricDecrypt({
|
||||
ciphertext: encryptedSymmetricKey,
|
||||
iv: symmetricKeyIV,
|
||||
tag: symmetricKeyTag,
|
||||
keyEncoding: symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
const decryptedTokenReviewerJwt =
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
el.encryptedTokenReviewerJwt && el.tokenReviewerJwtIV && el.tokenReviewerJwtTag
|
||||
? decryptSymmetric({
|
||||
key,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
iv: el.tokenReviewerJwtIV,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
tag: el.tokenReviewerJwtTag,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
ciphertext: el.encryptedTokenReviewerJwt
|
||||
})
|
||||
: "";
|
||||
|
||||
const decryptedCertificate =
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
el.encryptedCaCert && el.caCertIV && el.caCertTag
|
||||
? decryptSymmetric({
|
||||
key,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
iv: el.caCertIV,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
tag: el.caCertTag,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
ciphertext: el.encryptedCaCert
|
||||
})
|
||||
: "";
|
||||
|
||||
const encryptedKubernetesTokenReviewerJwt = orgKmsService.encryptor({
|
||||
plainText: Buffer.from(decryptedTokenReviewerJwt)
|
||||
}).cipherTextBlob;
|
||||
const encryptedKubernetesCaCertificate = orgKmsService.encryptor({
|
||||
plainText: Buffer.from(decryptedCertificate)
|
||||
}).cipherTextBlob;
|
||||
|
||||
updatedIdentityKubernetesConfigs.push({
|
||||
...el,
|
||||
accessTokenTrustedIps: JSON.stringify(el.accessTokenTrustedIps),
|
||||
encryptedKubernetesCaCertificate,
|
||||
encryptedKubernetesTokenReviewerJwt
|
||||
});
|
||||
},
|
||||
knex
|
||||
);
|
||||
orgEncryptionRingBuffer.push(orgId, orgKmsService);
|
||||
}
|
||||
|
||||
const key = infisicalSymmetricDecrypt({
|
||||
ciphertext: encryptedSymmetricKey,
|
||||
iv: symmetricKeyIV,
|
||||
tag: symmetricKeyTag,
|
||||
keyEncoding: symmetricKeyKeyEncoding as SecretKeyEncoding
|
||||
});
|
||||
|
||||
const decryptedTokenReviewerJwt =
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
el.encryptedTokenReviewerJwt && el.tokenReviewerJwtIV && el.tokenReviewerJwtTag
|
||||
? decryptSymmetric({
|
||||
key,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
iv: el.tokenReviewerJwtIV,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
tag: el.tokenReviewerJwtTag,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
ciphertext: el.encryptedTokenReviewerJwt
|
||||
})
|
||||
: "";
|
||||
|
||||
const decryptedCertificate =
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
el.encryptedCaCert && el.caCertIV && el.caCertTag
|
||||
? decryptSymmetric({
|
||||
key,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
iv: el.caCertIV,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
tag: el.caCertTag,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
|
||||
ciphertext: el.encryptedCaCert
|
||||
})
|
||||
: "";
|
||||
|
||||
const encryptedKubernetesTokenReviewerJwt = orgKmsService.encryptor({
|
||||
plainText: Buffer.from(decryptedTokenReviewerJwt)
|
||||
}).cipherTextBlob;
|
||||
const encryptedKubernetesCaCertificate = orgKmsService.encryptor({
|
||||
plainText: Buffer.from(decryptedCertificate)
|
||||
}).cipherTextBlob;
|
||||
|
||||
updatedIdentityKubernetesConfigs.push({
|
||||
...el,
|
||||
accessTokenTrustedIps: JSON.stringify(el.accessTokenTrustedIps),
|
||||
encryptedKubernetesCaCertificate,
|
||||
encryptedKubernetesTokenReviewerJwt
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < updatedIdentityKubernetesConfigs.length; i += BATCH_SIZE) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await knex(TableName.IdentityKubernetesAuth)
|
||||
|
@ -62,10 +62,13 @@ const reencryptIdentityOidcAuth = async (knex: Knex) => {
|
||||
async ({ encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, orgId, ...el }) => {
|
||||
let orgKmsService = orgEncryptionRingBuffer.getItem(orgId);
|
||||
if (!orgKmsService) {
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId
|
||||
}, knex);
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey(
|
||||
{
|
||||
type: KmsDataKey.Organization,
|
||||
orgId
|
||||
},
|
||||
knex
|
||||
);
|
||||
orgEncryptionRingBuffer.push(orgId, orgKmsService);
|
||||
}
|
||||
const key = infisicalSymmetricDecrypt({
|
||||
|
@ -49,10 +49,13 @@ const reencryptSamlConfig = async (knex: Knex) => {
|
||||
async ({ encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, ...el }) => {
|
||||
let orgKmsService = orgEncryptionRingBuffer.getItem(el.orgId);
|
||||
if (!orgKmsService) {
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: el.orgId
|
||||
}, knex);
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey(
|
||||
{
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: el.orgId
|
||||
},
|
||||
knex
|
||||
);
|
||||
orgEncryptionRingBuffer.push(el.orgId, orgKmsService);
|
||||
}
|
||||
const key = infisicalSymmetricDecrypt({
|
||||
@ -204,10 +207,13 @@ const reencryptLdapConfig = async (knex: Knex) => {
|
||||
async ({ encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, ...el }) => {
|
||||
let orgKmsService = orgEncryptionRingBuffer.getItem(el.orgId);
|
||||
if (!orgKmsService) {
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: el.orgId
|
||||
}, knex);
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey(
|
||||
{
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: el.orgId
|
||||
},
|
||||
knex
|
||||
);
|
||||
orgEncryptionRingBuffer.push(el.orgId, orgKmsService);
|
||||
}
|
||||
const key = infisicalSymmetricDecrypt({
|
||||
@ -353,10 +359,13 @@ const reencryptOidcConfig = async (knex: Knex) => {
|
||||
async ({ encryptedSymmetricKey, symmetricKeyKeyEncoding, symmetricKeyTag, symmetricKeyIV, ...el }) => {
|
||||
let orgKmsService = orgEncryptionRingBuffer.getItem(el.orgId);
|
||||
if (!orgKmsService) {
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: el.orgId
|
||||
}, knex);
|
||||
orgKmsService = await kmsService.createCipherPairWithDataKey(
|
||||
{
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: el.orgId
|
||||
},
|
||||
knex
|
||||
);
|
||||
orgEncryptionRingBuffer.push(el.orgId, orgKmsService);
|
||||
}
|
||||
const key = infisicalSymmetricDecrypt({
|
||||
|
@ -42,7 +42,7 @@ export const getMigrationEnvConfig = () => {
|
||||
console.error("Invalid environment variables. Check the error below");
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"Migration is now automatic at startup. Please remove this step from your workflow and start the application as normal."
|
||||
"Infisical now automatically runs database migrations during boot up, so you no longer need to run them separately."
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(parsedEnv.error.issues);
|
||||
|
@ -111,7 +111,7 @@ export const groupDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
|
||||
if (search) {
|
||||
void query.andWhereRaw(`CONCAT_WS(' ', "firstName", "lastName", "username") ilike '%${search}%'`);
|
||||
void query.andWhereRaw(`CONCAT_WS(' ', "firstName", "lastName", "username") ilike ?`, [`%${search}%`]);
|
||||
} else if (username) {
|
||||
void query.andWhere(`${TableName.Users}.username`, "ilike", `%${username}%`);
|
||||
}
|
||||
|
@ -1718,36 +1718,40 @@ export const SecretSyncs = {
|
||||
SYNC_OPTIONS: (destination: SecretSync) => {
|
||||
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||
return {
|
||||
INITIAL_SYNC_BEHAVIOR: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`,
|
||||
PREPEND_PREFIX: `Optionally prepend a prefix to your secrets' keys when syncing to ${destinationName}.`,
|
||||
APPEND_SUFFIX: `Optionally append a suffix to your secrets' keys when syncing to ${destinationName}.`
|
||||
initialSyncBehavior: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`
|
||||
};
|
||||
},
|
||||
DESTINATION_CONFIG: {
|
||||
AWS_PARAMETER_STORE: {
|
||||
REGION: "The AWS region to sync secrets to.",
|
||||
PATH: "The Parameter Store path to sync secrets to."
|
||||
region: "The AWS region to sync secrets to.",
|
||||
path: "The Parameter Store path to sync secrets to."
|
||||
},
|
||||
AWS_SECRETS_MANAGER: {
|
||||
REGION: "The AWS region to sync secrets to.",
|
||||
MAPPING_BEHAVIOR:
|
||||
"How secrets from Infisical should be mapped to AWS Secrets Manager; one-to-one or many-to-one.",
|
||||
SECRET_NAME: "The secret name in AWS Secrets Manager to sync to when using mapping behavior many-to-one."
|
||||
region: "The AWS region to sync secrets to.",
|
||||
mappingBehavior: "How secrets from Infisical should be mapped to AWS Secrets Manager; one-to-one or many-to-one.",
|
||||
secretName: "The secret name in AWS Secrets Manager to sync to when using mapping behavior many-to-one."
|
||||
},
|
||||
GITHUB: {
|
||||
ORG: "The name of the GitHub organization.",
|
||||
OWNER: "The name of the GitHub account owner of the repository.",
|
||||
REPO: "The name of the GitHub repository.",
|
||||
ENV: "The name of the GitHub environment."
|
||||
scope: "The GitHub scope that secrets should be synced to",
|
||||
org: "The name of the GitHub organization.",
|
||||
owner: "The name of the GitHub account owner of the repository.",
|
||||
repo: "The name of the GitHub repository.",
|
||||
env: "The name of the GitHub environment."
|
||||
},
|
||||
AZURE_KEY_VAULT: {
|
||||
VAULT_BASE_URL:
|
||||
"The base URL of the Azure Key Vault to sync secrets to. Example: https://example.vault.azure.net/"
|
||||
vaultBaseUrl: "The base URL of the Azure Key Vault to sync secrets to. Example: https://example.vault.azure.net/"
|
||||
},
|
||||
AZURE_APP_CONFIGURATION: {
|
||||
CONFIGURATION_URL:
|
||||
configurationUrl:
|
||||
"The URL of the Azure App Configuration to sync secrets to. Example: https://example.azconfig.io/",
|
||||
LABEL: "An optional label to assign to secrets created in Azure App Configuration."
|
||||
label: "An optional label to assign to secrets created in Azure App Configuration."
|
||||
},
|
||||
GCP: {
|
||||
scope: "The Google project scope that secrets should be synced to.",
|
||||
projectId: "The ID of the Google project secrets should be synced to."
|
||||
},
|
||||
DATABRICKS: {
|
||||
scope: "The Databricks secret scope that secrets should be synced to."
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -12,6 +12,10 @@ import {
|
||||
AzureKeyVaultConnectionListItemSchema,
|
||||
SanitizedAzureKeyVaultConnectionSchema
|
||||
} from "@app/services/app-connection/azure-key-vault";
|
||||
import {
|
||||
DatabricksConnectionListItemSchema,
|
||||
SanitizedDatabricksConnectionSchema
|
||||
} from "@app/services/app-connection/databricks";
|
||||
import { GcpConnectionListItemSchema, SanitizedGcpConnectionSchema } from "@app/services/app-connection/gcp";
|
||||
import { GitHubConnectionListItemSchema, SanitizedGitHubConnectionSchema } from "@app/services/app-connection/github";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -22,7 +26,8 @@ const SanitizedAppConnectionSchema = z.union([
|
||||
...SanitizedGitHubConnectionSchema.options,
|
||||
...SanitizedGcpConnectionSchema.options,
|
||||
...SanitizedAzureKeyVaultConnectionSchema.options,
|
||||
...SanitizedAzureAppConfigurationConnectionSchema.options
|
||||
...SanitizedAzureAppConfigurationConnectionSchema.options,
|
||||
...SanitizedDatabricksConnectionSchema.options
|
||||
]);
|
||||
|
||||
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||
@ -30,7 +35,8 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||
GitHubConnectionListItemSchema,
|
||||
GcpConnectionListItemSchema,
|
||||
AzureKeyVaultConnectionListItemSchema,
|
||||
AzureAppConfigurationConnectionListItemSchema
|
||||
AzureAppConfigurationConnectionListItemSchema,
|
||||
DatabricksConnectionListItemSchema
|
||||
]);
|
||||
|
||||
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
|
@ -0,0 +1,54 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreateDatabricksConnectionSchema,
|
||||
SanitizedDatabricksConnectionSchema,
|
||||
UpdateDatabricksConnectionSchema
|
||||
} from "@app/services/app-connection/databricks";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
export const registerDatabricksConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.Databricks,
|
||||
server,
|
||||
sanitizedResponseSchema: SanitizedDatabricksConnectionSchema,
|
||||
createSchema: CreateDatabricksConnectionSchema,
|
||||
updateSchema: UpdateDatabricksConnectionSchema
|
||||
});
|
||||
|
||||
// The below endpoints are not exposed and for Infisical App use
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/secret-scopes`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
secretScopes: z.object({ name: z.string() }).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
const secretScopes = await server.services.appConnection.databricks.listSecretScopes(
|
||||
connectionId,
|
||||
req.permission
|
||||
);
|
||||
|
||||
return { secretScopes };
|
||||
}
|
||||
});
|
||||
};
|
@ -41,7 +41,7 @@ export const registerGitHubConnectionRouter = async (server: FastifyZodProvider)
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
@ -67,7 +67,7 @@ export const registerGitHubConnectionRouter = async (server: FastifyZodProvider)
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
@ -97,7 +97,7 @@ export const registerGitHubConnectionRouter = async (server: FastifyZodProvider)
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
const { repo, owner } = req.query;
|
||||
|
@ -3,6 +3,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
|
||||
import { registerAwsConnectionRouter } from "./aws-connection-router";
|
||||
import { registerAzureAppConfigurationConnectionRouter } from "./azure-app-configuration-connection-router";
|
||||
import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connection-router";
|
||||
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
||||
import { registerGcpConnectionRouter } from "./gcp-connection-router";
|
||||
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
||||
|
||||
@ -14,5 +15,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
|
||||
[AppConnection.GitHub]: registerGitHubConnectionRouter,
|
||||
[AppConnection.GCP]: registerGcpConnectionRouter,
|
||||
[AppConnection.AzureKeyVault]: registerAzureKeyVaultConnectionRouter,
|
||||
[AppConnection.AzureAppConfiguration]: registerAzureAppConfigurationConnectionRouter
|
||||
[AppConnection.AzureAppConfiguration]: registerAzureAppConfigurationConnectionRouter,
|
||||
[AppConnection.Databricks]: registerDatabricksConnectionRouter
|
||||
};
|
||||
|
@ -0,0 +1,17 @@
|
||||
import {
|
||||
CreateDatabricksSyncSchema,
|
||||
DatabricksSyncSchema,
|
||||
UpdateDatabricksSyncSchema
|
||||
} from "@app/services/secret-sync/databricks";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||
|
||||
export const registerDatabricksSyncRouter = async (server: FastifyZodProvider) =>
|
||||
registerSyncSecretsEndpoints({
|
||||
destination: SecretSync.Databricks,
|
||||
server,
|
||||
responseSchema: DatabricksSyncSchema,
|
||||
createSchema: CreateDatabricksSyncSchema,
|
||||
updateSchema: UpdateDatabricksSyncSchema
|
||||
});
|
@ -4,6 +4,7 @@ import { registerAwsParameterStoreSyncRouter } from "./aws-parameter-store-sync-
|
||||
import { registerAwsSecretsManagerSyncRouter } from "./aws-secrets-manager-sync-router";
|
||||
import { registerAzureAppConfigurationSyncRouter } from "./azure-app-configuration-sync-router";
|
||||
import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
|
||||
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
|
||||
import { registerGcpSyncRouter } from "./gcp-sync-router";
|
||||
import { registerGitHubSyncRouter } from "./github-sync-router";
|
||||
|
||||
@ -15,5 +16,6 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
|
||||
[SecretSync.GitHub]: registerGitHubSyncRouter,
|
||||
[SecretSync.GCPSecretManager]: registerGcpSyncRouter,
|
||||
[SecretSync.AzureKeyVault]: registerAzureKeyVaultSyncRouter,
|
||||
[SecretSync.AzureAppConfiguration]: registerAzureAppConfigurationSyncRouter
|
||||
[SecretSync.AzureAppConfiguration]: registerAzureAppConfigurationSyncRouter,
|
||||
[SecretSync.Databricks]: registerDatabricksSyncRouter
|
||||
};
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
AzureAppConfigurationSyncSchema
|
||||
} from "@app/services/secret-sync/azure-app-configuration";
|
||||
import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/services/secret-sync/azure-key-vault";
|
||||
import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks";
|
||||
import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/gcp";
|
||||
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
|
||||
|
||||
@ -27,7 +28,8 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
|
||||
GitHubSyncSchema,
|
||||
GcpSyncSchema,
|
||||
AzureKeyVaultSyncSchema,
|
||||
AzureAppConfigurationSyncSchema
|
||||
AzureAppConfigurationSyncSchema,
|
||||
DatabricksSyncSchema
|
||||
]);
|
||||
|
||||
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||
@ -36,7 +38,8 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||
GitHubSyncListItemSchema,
|
||||
GcpSyncListItemSchema,
|
||||
AzureKeyVaultSyncListItemSchema,
|
||||
AzureAppConfigurationSyncListItemSchema
|
||||
AzureAppConfigurationSyncListItemSchema,
|
||||
DatabricksSyncListItemSchema
|
||||
]);
|
||||
|
||||
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
export enum AppConnection {
|
||||
GitHub = "github",
|
||||
AWS = "aws",
|
||||
Databricks = "databricks",
|
||||
GCP = "gcp",
|
||||
AzureKeyVault = "azure-key-vault",
|
||||
AzureAppConfiguration = "azure-app-configuration"
|
||||
|
@ -5,12 +5,17 @@ import { TAppConnectionServiceFactoryDep } from "@app/services/app-connection/ap
|
||||
import { TAppConnection, TAppConnectionConfig } from "@app/services/app-connection/app-connection-types";
|
||||
import {
|
||||
AwsConnectionMethod,
|
||||
getAwsAppConnectionListItem,
|
||||
getAwsConnectionListItem,
|
||||
validateAwsConnectionCredentials
|
||||
} from "@app/services/app-connection/aws";
|
||||
import {
|
||||
DatabricksConnectionMethod,
|
||||
getDatabricksConnectionListItem,
|
||||
validateDatabricksConnectionCredentials
|
||||
} from "@app/services/app-connection/databricks";
|
||||
import {
|
||||
GcpConnectionMethod,
|
||||
getGcpAppConnectionListItem,
|
||||
getGcpConnectionListItem,
|
||||
validateGcpConnectionCredentials
|
||||
} from "@app/services/app-connection/gcp";
|
||||
import {
|
||||
@ -33,11 +38,12 @@ import {
|
||||
|
||||
export const listAppConnectionOptions = () => {
|
||||
return [
|
||||
getAwsAppConnectionListItem(),
|
||||
getAwsConnectionListItem(),
|
||||
getGitHubConnectionListItem(),
|
||||
getGcpAppConnectionListItem(),
|
||||
getGcpConnectionListItem(),
|
||||
getAzureKeyVaultConnectionListItem(),
|
||||
getAzureAppConfigurationConnectionListItem()
|
||||
getAzureAppConfigurationConnectionListItem(),
|
||||
getDatabricksConnectionListItem()
|
||||
].sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
@ -90,6 +96,8 @@ export const validateAppConnectionCredentials = async (
|
||||
switch (app) {
|
||||
case AppConnection.AWS:
|
||||
return validateAwsConnectionCredentials(appConnection);
|
||||
case AppConnection.Databricks:
|
||||
return validateDatabricksConnectionCredentials(appConnection);
|
||||
case AppConnection.GitHub:
|
||||
return validateGitHubConnectionCredentials(appConnection);
|
||||
case AppConnection.GCP:
|
||||
@ -118,6 +126,8 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
||||
return "Assume Role";
|
||||
case GcpConnectionMethod.ServiceAccountImpersonation:
|
||||
return "Service Account Impersonation";
|
||||
case DatabricksConnectionMethod.ServicePrincipal:
|
||||
return "Service Principal";
|
||||
default:
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
throw new Error(`Unhandled App Connection Method: ${method}`);
|
||||
|
@ -5,5 +5,6 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
||||
[AppConnection.GitHub]: "GitHub",
|
||||
[AppConnection.GCP]: "GCP",
|
||||
[AppConnection.AzureKeyVault]: "Azure Key Vault",
|
||||
[AppConnection.AzureAppConfiguration]: "Azure App Configuration"
|
||||
[AppConnection.AzureAppConfiguration]: "Azure App Configuration",
|
||||
[AppConnection.Databricks]: "Databricks"
|
||||
};
|
||||
|
@ -23,6 +23,8 @@ import {
|
||||
TValidateAppConnectionCredentials
|
||||
} from "@app/services/app-connection/app-connection-types";
|
||||
import { ValidateAwsConnectionCredentialsSchema } from "@app/services/app-connection/aws";
|
||||
import { ValidateDatabricksConnectionCredentialsSchema } from "@app/services/app-connection/databricks";
|
||||
import { databricksConnectionService } from "@app/services/app-connection/databricks/databricks-connection-service";
|
||||
import { ValidateGitHubConnectionCredentialsSchema } from "@app/services/app-connection/github";
|
||||
import { githubConnectionService } from "@app/services/app-connection/github/github-connection-service";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
@ -46,7 +48,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
||||
[AppConnection.GitHub]: ValidateGitHubConnectionCredentialsSchema,
|
||||
[AppConnection.GCP]: ValidateGcpConnectionCredentialsSchema,
|
||||
[AppConnection.AzureKeyVault]: ValidateAzureKeyVaultConnectionCredentialsSchema,
|
||||
[AppConnection.AzureAppConfiguration]: ValidateAzureAppConfigurationConnectionCredentialsSchema
|
||||
[AppConnection.AzureAppConfiguration]: ValidateAzureAppConfigurationConnectionCredentialsSchema,
|
||||
[AppConnection.Databricks]: ValidateDatabricksConnectionCredentialsSchema
|
||||
};
|
||||
|
||||
export const appConnectionServiceFactory = ({
|
||||
@ -365,6 +368,7 @@ export const appConnectionServiceFactory = ({
|
||||
connectAppConnectionById,
|
||||
listAvailableAppConnectionsForUser,
|
||||
github: githubConnectionService(connectAppConnectionById),
|
||||
gcp: gcpConnectionService(connectAppConnectionById)
|
||||
gcp: gcpConnectionService(connectAppConnectionById),
|
||||
databricks: databricksConnectionService(connectAppConnectionById, appConnectionDAL, kmsService)
|
||||
};
|
||||
};
|
||||
|
@ -4,6 +4,12 @@ import {
|
||||
TAwsConnectionInput,
|
||||
TValidateAwsConnectionCredentials
|
||||
} from "@app/services/app-connection/aws";
|
||||
import {
|
||||
TDatabricksConnection,
|
||||
TDatabricksConnectionConfig,
|
||||
TDatabricksConnectionInput,
|
||||
TValidateDatabricksConnectionCredentials
|
||||
} from "@app/services/app-connection/databricks";
|
||||
import {
|
||||
TGitHubConnection,
|
||||
TGitHubConnectionConfig,
|
||||
@ -31,6 +37,7 @@ export type TAppConnection = { id: string } & (
|
||||
| TGcpConnection
|
||||
| TAzureKeyVaultConnection
|
||||
| TAzureAppConfigurationConnection
|
||||
| TDatabricksConnection
|
||||
);
|
||||
|
||||
export type TAppConnectionInput = { id: string } & (
|
||||
@ -39,6 +46,7 @@ export type TAppConnectionInput = { id: string } & (
|
||||
| TGcpConnectionInput
|
||||
| TAzureKeyVaultConnectionInput
|
||||
| TAzureAppConfigurationConnectionInput
|
||||
| TDatabricksConnectionInput
|
||||
);
|
||||
|
||||
export type TCreateAppConnectionDTO = Pick<
|
||||
@ -55,11 +63,13 @@ export type TAppConnectionConfig =
|
||||
| TGitHubConnectionConfig
|
||||
| TGcpConnectionConfig
|
||||
| TAzureKeyVaultConnectionConfig
|
||||
| TAzureAppConfigurationConnectionConfig;
|
||||
| TAzureAppConfigurationConnectionConfig
|
||||
| TDatabricksConnectionConfig;
|
||||
|
||||
export type TValidateAppConnectionCredentials =
|
||||
| TValidateAwsConnectionCredentials
|
||||
| TValidateGitHubConnectionCredentials
|
||||
| TValidateGcpConnectionCredentials
|
||||
| TValidateAzureKeyVaultConnectionCredentials
|
||||
| TValidateAzureAppConfigurationConnectionCredentials;
|
||||
| TValidateAzureAppConfigurationConnectionCredentials
|
||||
| TValidateDatabricksConnectionCredentials;
|
||||
|
@ -9,7 +9,7 @@ import { AppConnection, AWSRegion } from "@app/services/app-connection/app-conne
|
||||
import { AwsConnectionMethod } from "./aws-connection-enums";
|
||||
import { TAwsConnectionConfig } from "./aws-connection-types";
|
||||
|
||||
export const getAwsAppConnectionListItem = () => {
|
||||
export const getAwsConnectionListItem = () => {
|
||||
const { INF_APP_CONNECTION_AWS_ACCESS_KEY_ID } = getConfig();
|
||||
|
||||
return {
|
||||
|
@ -0,0 +1,3 @@
|
||||
export enum DatabricksConnectionMethod {
|
||||
ServicePrincipal = "service-principal"
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { encryptAppConnectionCredentials } from "@app/services/app-connection/app-connection-fns";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
import { DatabricksConnectionMethod } from "./databricks-connection-enums";
|
||||
import {
|
||||
TAuthorizeDatabricksConnection,
|
||||
TDatabricksConnection,
|
||||
TDatabricksConnectionConfig
|
||||
} from "./databricks-connection-types";
|
||||
|
||||
export const getDatabricksConnectionListItem = () => {
|
||||
return {
|
||||
name: "Databricks" as const,
|
||||
app: AppConnection.Databricks as const,
|
||||
methods: Object.values(DatabricksConnectionMethod) as [DatabricksConnectionMethod.ServicePrincipal]
|
||||
};
|
||||
};
|
||||
|
||||
const authorizeDatabricksConnection = async ({
|
||||
clientId,
|
||||
clientSecret,
|
||||
workspaceUrl
|
||||
}: Pick<TDatabricksConnection["credentials"], "workspaceUrl" | "clientId" | "clientSecret">) => {
|
||||
const { data } = await request.post<TAuthorizeDatabricksConnection>(
|
||||
`${removeTrailingSlash(workspaceUrl)}/oidc/v1/token`,
|
||||
"grant_type=client_credentials&scope=all-apis",
|
||||
{
|
||||
auth: {
|
||||
username: clientId,
|
||||
password: clientSecret
|
||||
},
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return { accessToken: data.access_token, expiresAt: data.expires_in * 1000 + Date.now() };
|
||||
};
|
||||
|
||||
export const getDatabricksConnectionAccessToken = async (
|
||||
{ id, orgId, credentials }: TDatabricksConnection,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
) => {
|
||||
const { clientSecret, clientId, workspaceUrl, accessToken, expiresAt } = credentials;
|
||||
|
||||
// get new token if less than 10 minutes from expiry
|
||||
if (Date.now() < expiresAt - 10_000) {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
const authData = await authorizeDatabricksConnection({ clientId, clientSecret, workspaceUrl });
|
||||
|
||||
const updatedCredentials: TDatabricksConnection["credentials"] = {
|
||||
...credentials,
|
||||
...authData
|
||||
};
|
||||
|
||||
const encryptedCredentials = await encryptAppConnectionCredentials({
|
||||
credentials: updatedCredentials,
|
||||
orgId,
|
||||
kmsService
|
||||
});
|
||||
|
||||
await appConnectionDAL.updateById(id, { encryptedCredentials });
|
||||
|
||||
return authData.accessToken;
|
||||
};
|
||||
|
||||
export const validateDatabricksConnectionCredentials = async (appConnection: TDatabricksConnectionConfig) => {
|
||||
const { credentials } = appConnection;
|
||||
|
||||
try {
|
||||
const { accessToken, expiresAt } = await authorizeDatabricksConnection(appConnection.credentials);
|
||||
|
||||
return {
|
||||
...credentials,
|
||||
accessToken,
|
||||
expiresAt
|
||||
};
|
||||
} catch (e: unknown) {
|
||||
throw new BadRequestError({
|
||||
message: `Unable to validate connection - verify credentials`
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,77 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { AppConnections } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
BaseAppConnectionSchema,
|
||||
GenericCreateAppConnectionFieldsSchema,
|
||||
GenericUpdateAppConnectionFieldsSchema
|
||||
} from "@app/services/app-connection/app-connection-schemas";
|
||||
|
||||
import { DatabricksConnectionMethod } from "./databricks-connection-enums";
|
||||
|
||||
export const DatabricksConnectionServicePrincipalInputCredentialsSchema = z.object({
|
||||
clientId: z.string().trim().min(1, "Client ID required"),
|
||||
clientSecret: z.string().trim().min(1, "Client Secret required"),
|
||||
workspaceUrl: z.string().trim().url().min(1, "Workspace URL required")
|
||||
});
|
||||
|
||||
export const DatabricksConnectionServicePrincipalOutputCredentialsSchema = z
|
||||
.object({
|
||||
accessToken: z.string(),
|
||||
expiresAt: z.number()
|
||||
})
|
||||
.merge(DatabricksConnectionServicePrincipalInputCredentialsSchema);
|
||||
|
||||
const BaseDatabricksConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Databricks) });
|
||||
|
||||
export const DatabricksConnectionSchema = z.intersection(
|
||||
BaseDatabricksConnectionSchema,
|
||||
z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z.literal(DatabricksConnectionMethod.ServicePrincipal),
|
||||
credentials: DatabricksConnectionServicePrincipalOutputCredentialsSchema
|
||||
})
|
||||
])
|
||||
);
|
||||
|
||||
export const SanitizedDatabricksConnectionSchema = z.discriminatedUnion("method", [
|
||||
BaseDatabricksConnectionSchema.extend({
|
||||
method: z.literal(DatabricksConnectionMethod.ServicePrincipal),
|
||||
credentials: DatabricksConnectionServicePrincipalOutputCredentialsSchema.pick({
|
||||
clientId: true,
|
||||
workspaceUrl: true
|
||||
})
|
||||
})
|
||||
]);
|
||||
|
||||
export const ValidateDatabricksConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||
z.object({
|
||||
method: z
|
||||
.literal(DatabricksConnectionMethod.ServicePrincipal)
|
||||
.describe(AppConnections?.CREATE(AppConnection.Databricks).method),
|
||||
credentials: DatabricksConnectionServicePrincipalInputCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.Databricks).credentials
|
||||
)
|
||||
})
|
||||
]);
|
||||
|
||||
export const CreateDatabricksConnectionSchema = ValidateDatabricksConnectionCredentialsSchema.and(
|
||||
GenericCreateAppConnectionFieldsSchema(AppConnection.Databricks)
|
||||
);
|
||||
|
||||
export const UpdateDatabricksConnectionSchema = z
|
||||
.object({
|
||||
credentials: DatabricksConnectionServicePrincipalInputCredentialsSchema.optional().describe(
|
||||
AppConnections.UPDATE(AppConnection.Databricks).credentials
|
||||
)
|
||||
})
|
||||
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Databricks));
|
||||
|
||||
export const DatabricksConnectionListItemSchema = z.object({
|
||||
name: z.literal("Databricks"),
|
||||
app: z.literal(AppConnection.Databricks),
|
||||
// the below is preferable but currently breaks with our zod to json schema parser
|
||||
// methods: z.tuple([z.literal(AwsConnectionMethod.ServicePrincipal), z.literal(AwsConnectionMethod.AccessKey)]),
|
||||
methods: z.nativeEnum(DatabricksConnectionMethod).array()
|
||||
});
|
@ -0,0 +1,60 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { getDatabricksConnectionAccessToken } from "@app/services/app-connection/databricks/databricks-connection-fns";
|
||||
import {
|
||||
TDatabricksConnection,
|
||||
TDatabricksListSecretScopesResponse
|
||||
} from "@app/services/app-connection/databricks/databricks-connection-types";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
type TGetAppConnectionFunc = (
|
||||
app: AppConnection,
|
||||
connectionId: string,
|
||||
actor: OrgServiceActor
|
||||
) => Promise<TDatabricksConnection>;
|
||||
|
||||
const listDatabricksSecretScopes = async (
|
||||
appConnection: TDatabricksConnection,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
) => {
|
||||
const {
|
||||
credentials: { workspaceUrl }
|
||||
} = appConnection;
|
||||
|
||||
const accessToken = await getDatabricksConnectionAccessToken(appConnection, appConnectionDAL, kmsService);
|
||||
|
||||
const { data } = await request.get<TDatabricksListSecretScopesResponse>(
|
||||
`${removeTrailingSlash(workspaceUrl)}/api/2.0/secrets/scopes/list`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// not present in response if no scopes exists
|
||||
return data.scopes ?? [];
|
||||
};
|
||||
|
||||
export const databricksConnectionService = (
|
||||
getAppConnection: TGetAppConnectionFunc,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
) => {
|
||||
const listSecretScopes = async (connectionId: string, actor: OrgServiceActor) => {
|
||||
const appConnection = await getAppConnection(AppConnection.Databricks, connectionId, actor);
|
||||
|
||||
const secretScopes = await listDatabricksSecretScopes(appConnection, appConnectionDAL, kmsService);
|
||||
|
||||
return secretScopes;
|
||||
};
|
||||
|
||||
return {
|
||||
listSecretScopes
|
||||
};
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
import {
|
||||
CreateDatabricksConnectionSchema,
|
||||
DatabricksConnectionSchema,
|
||||
ValidateDatabricksConnectionCredentialsSchema
|
||||
} from "./databricks-connection-schemas";
|
||||
|
||||
export type TDatabricksConnection = z.infer<typeof DatabricksConnectionSchema>;
|
||||
|
||||
export type TDatabricksConnectionInput = z.infer<typeof CreateDatabricksConnectionSchema> & {
|
||||
app: AppConnection.Databricks;
|
||||
};
|
||||
|
||||
export type TValidateDatabricksConnectionCredentials = typeof ValidateDatabricksConnectionCredentialsSchema;
|
||||
|
||||
export type TDatabricksConnectionConfig = DiscriminativePick<
|
||||
TDatabricksConnection,
|
||||
"method" | "app" | "credentials"
|
||||
> & {
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export type TAuthorizeDatabricksConnection = {
|
||||
access_token: string;
|
||||
scope: string;
|
||||
token_type: string;
|
||||
expires_in: number;
|
||||
};
|
||||
|
||||
export type TDatabricksListSecretScopesResponse = {
|
||||
scopes?: { name: string; backend_type: string; keyvault_metadata: { resource_id: string; dns_name: string } }[];
|
||||
};
|
4
backend/src/services/app-connection/databricks/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from "./databricks-connection-enums";
|
||||
export * from "./databricks-connection-fns";
|
||||
export * from "./databricks-connection-schemas";
|
||||
export * from "./databricks-connection-types";
|
@ -17,7 +17,7 @@ import {
|
||||
TGcpConnectionConfig
|
||||
} from "./gcp-connection-types";
|
||||
|
||||
export const getGcpAppConnectionListItem = () => {
|
||||
export const getGcpConnectionListItem = () => {
|
||||
return {
|
||||
name: "GCP" as const,
|
||||
app: AppConnection.GCP as const,
|
||||
|
@ -10,14 +10,14 @@ import {
|
||||
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||
|
||||
const AwsParameterStoreSyncDestinationConfigSchema = z.object({
|
||||
region: z.nativeEnum(AWSRegion).describe(SecretSyncs.DESTINATION_CONFIG.AWS_PARAMETER_STORE.REGION),
|
||||
region: z.nativeEnum(AWSRegion).describe(SecretSyncs.DESTINATION_CONFIG.AWS_PARAMETER_STORE.region),
|
||||
path: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Parameter Store Path required")
|
||||
.max(2048, "Cannot exceed 2048 characters")
|
||||
.regex(/^\/([/]|(([\w-]+\/)+))?$/, 'Invalid path - must follow "/example/path/" format')
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AWS_PARAMETER_STORE.PATH)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AWS_PARAMETER_STORE.path)
|
||||
});
|
||||
|
||||
export const AwsParameterStoreSyncSchema = BaseSecretSyncSchema(SecretSync.AWSParameterStore).extend({
|
||||
|
@ -15,12 +15,12 @@ const AwsSecretsManagerSyncDestinationConfigSchema = z
|
||||
z.object({
|
||||
mappingBehavior: z
|
||||
.literal(AwsSecretsManagerSyncMappingBehavior.OneToOne)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AWS_SECRETS_MANAGER.MAPPING_BEHAVIOR)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AWS_SECRETS_MANAGER.mappingBehavior)
|
||||
}),
|
||||
z.object({
|
||||
mappingBehavior: z
|
||||
.literal(AwsSecretsManagerSyncMappingBehavior.ManyToOne)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AWS_SECRETS_MANAGER.MAPPING_BEHAVIOR),
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AWS_SECRETS_MANAGER.mappingBehavior),
|
||||
secretName: z
|
||||
.string()
|
||||
.regex(
|
||||
@ -29,12 +29,12 @@ const AwsSecretsManagerSyncDestinationConfigSchema = z
|
||||
)
|
||||
.min(1, "Secret name is required")
|
||||
.max(256, "Secret name cannot exceed 256 characters")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AWS_SECRETS_MANAGER.SECRET_NAME)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AWS_SECRETS_MANAGER.secretName)
|
||||
})
|
||||
])
|
||||
.and(
|
||||
z.object({
|
||||
region: z.nativeEnum(AWSRegion).describe(SecretSyncs.DESTINATION_CONFIG.AWS_SECRETS_MANAGER.REGION)
|
||||
region: z.nativeEnum(AWSRegion).describe(SecretSyncs.DESTINATION_CONFIG.AWS_SECRETS_MANAGER.region)
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -11,7 +11,7 @@ import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { TAzureAppConfigurationSyncWithCredentials } from "./azure-app-configuration-sync-types";
|
||||
|
||||
type TAzureAppConfigurationSecretSyncFactoryDeps = {
|
||||
type TAzureAppConfigurationSyncFactoryDeps = {
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
};
|
||||
@ -22,10 +22,10 @@ interface AzureAppConfigKeyValue {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export const azureAppConfigurationSecretSyncFactory = ({
|
||||
export const azureAppConfigurationSyncFactory = ({
|
||||
kmsService,
|
||||
appConnectionDAL
|
||||
}: TAzureAppConfigurationSecretSyncFactoryDeps) => {
|
||||
}: TAzureAppConfigurationSyncFactoryDeps) => {
|
||||
const $getCompleteAzureAppConfigValues = async (accessToken: string, baseURL: string, url: string) => {
|
||||
let result: AzureAppConfigKeyValue[] = [];
|
||||
let currentUrl = url;
|
||||
|
@ -14,8 +14,8 @@ const AzureAppConfigurationSyncDestinationConfigSchema = z.object({
|
||||
configurationUrl: z
|
||||
.string()
|
||||
.min(1, "App Configuration URL required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AZURE_APP_CONFIGURATION.CONFIGURATION_URL),
|
||||
label: z.string().optional().describe(SecretSyncs.DESTINATION_CONFIG.AZURE_APP_CONFIGURATION.LABEL)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AZURE_APP_CONFIGURATION.configurationUrl),
|
||||
label: z.string().optional().describe(SecretSyncs.DESTINATION_CONFIG.AZURE_APP_CONFIGURATION.label)
|
||||
});
|
||||
|
||||
const AzureAppConfigurationSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true };
|
||||
|
@ -10,15 +10,12 @@ import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
import { SecretSyncError } from "../secret-sync-errors";
|
||||
import { GetAzureKeyVaultSecret, TAzureKeyVaultSyncWithCredentials } from "./azure-key-vault-sync-types";
|
||||
|
||||
type TAzureKeyVaultSecretSyncFactoryDeps = {
|
||||
type TAzureKeyVaultSyncFactoryDeps = {
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
};
|
||||
|
||||
export const azureKeyVaultSecretSyncFactory = ({
|
||||
kmsService,
|
||||
appConnectionDAL
|
||||
}: TAzureKeyVaultSecretSyncFactoryDeps) => {
|
||||
export const azureKeyVaultSyncFactory = ({ kmsService, appConnectionDAL }: TAzureKeyVaultSyncFactoryDeps) => {
|
||||
const $getAzureKeyVaultSecrets = async (accessToken: string, vaultBaseUrl: string) => {
|
||||
const paginateAzureKeyVaultSecrets = async () => {
|
||||
let result: GetAzureKeyVaultSecret[] = [];
|
||||
|
@ -15,7 +15,7 @@ const AzureKeyVaultSyncDestinationConfigSchema = z.object({
|
||||
.string()
|
||||
.url("Invalid vault base URL format")
|
||||
.min(1, "Vault base URL required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AZURE_KEY_VAULT.VAULT_BASE_URL)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.AZURE_KEY_VAULT.vaultBaseUrl)
|
||||
});
|
||||
|
||||
const AzureKeyVaultSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true };
|
||||
|
@ -0,0 +1,10 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
export const DATABRICKS_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "Databricks",
|
||||
destination: SecretSync.Databricks,
|
||||
connection: AppConnection.Databricks,
|
||||
canImportSecrets: false
|
||||
};
|
@ -0,0 +1,164 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { getDatabricksConnectionAccessToken } from "@app/services/app-connection/databricks";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import {
|
||||
TDatabricksDeleteSecret,
|
||||
TDatabricksListSecretKeys,
|
||||
TDatabricksListSecretKeysResponse,
|
||||
TDatabricksPutSecret,
|
||||
TDatabricksSyncWithCredentials
|
||||
} from "@app/services/secret-sync/databricks/databricks-sync-types";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||
|
||||
import { TSecretMap } from "../secret-sync-types";
|
||||
|
||||
type TDatabricksSecretSyncFactoryDeps = {
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
};
|
||||
|
||||
const DATABRICKS_SCOPE_SECRET_LIMIT = 1000;
|
||||
|
||||
const listDatabricksSecrets = async ({ workspaceUrl, scope, accessToken }: TDatabricksListSecretKeys) => {
|
||||
const { data } = await request.get<TDatabricksListSecretKeysResponse>(
|
||||
`${removeTrailingSlash(workspaceUrl)}/api/2.0/secrets/list`,
|
||||
{
|
||||
params: {
|
||||
scope
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// not present in response if no secrets exist in scope
|
||||
return data.secrets ?? [];
|
||||
};
|
||||
const putDatabricksSecret = async ({ workspaceUrl, scope, key, value, accessToken }: TDatabricksPutSecret) =>
|
||||
request.post(
|
||||
`${removeTrailingSlash(workspaceUrl)}/api/2.0/secrets/put`,
|
||||
{
|
||||
scope,
|
||||
key,
|
||||
string_value: value
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const deleteDatabricksSecrets = async ({ workspaceUrl, scope, key, accessToken }: TDatabricksDeleteSecret) =>
|
||||
request.post(
|
||||
`${removeTrailingSlash(workspaceUrl)}/api/2.0/secrets/delete`,
|
||||
{
|
||||
scope,
|
||||
key
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const databricksSyncFactory = ({ kmsService, appConnectionDAL }: TDatabricksSecretSyncFactoryDeps) => {
|
||||
const syncSecrets = async (secretSync: TDatabricksSyncWithCredentials, secretMap: TSecretMap) => {
|
||||
if (Object.keys(secretSync).length > DATABRICKS_SCOPE_SECRET_LIMIT) {
|
||||
throw new Error(
|
||||
`Databricks does not support storing more than ${DATABRICKS_SCOPE_SECRET_LIMIT} secrets per scope.`
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
destinationConfig: { scope },
|
||||
connection
|
||||
} = secretSync;
|
||||
|
||||
const { workspaceUrl } = connection.credentials;
|
||||
|
||||
const accessToken = await getDatabricksConnectionAccessToken(connection, appConnectionDAL, kmsService);
|
||||
|
||||
for await (const entry of Object.entries(secretMap)) {
|
||||
const [key, { value }] = entry;
|
||||
|
||||
try {
|
||||
await putDatabricksSecret({
|
||||
key,
|
||||
value,
|
||||
workspaceUrl,
|
||||
scope,
|
||||
accessToken
|
||||
});
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const databricksSecretKeys = await listDatabricksSecrets({
|
||||
workspaceUrl,
|
||||
scope,
|
||||
accessToken
|
||||
});
|
||||
|
||||
for await (const secret of databricksSecretKeys) {
|
||||
if (!(secret.key in secretMap)) {
|
||||
await deleteDatabricksSecrets({
|
||||
key: secret.key,
|
||||
workspaceUrl,
|
||||
scope,
|
||||
accessToken
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const removeSecrets = async (secretSync: TDatabricksSyncWithCredentials, secretMap: TSecretMap) => {
|
||||
const {
|
||||
destinationConfig: { scope },
|
||||
connection
|
||||
} = secretSync;
|
||||
|
||||
const { workspaceUrl } = connection.credentials;
|
||||
|
||||
const accessToken = await getDatabricksConnectionAccessToken(connection, appConnectionDAL, kmsService);
|
||||
|
||||
const databricksSecretKeys = await listDatabricksSecrets({
|
||||
workspaceUrl,
|
||||
scope,
|
||||
accessToken
|
||||
});
|
||||
|
||||
for await (const secret of databricksSecretKeys) {
|
||||
if (secret.key in secretMap) {
|
||||
await deleteDatabricksSecrets({
|
||||
key: secret.key,
|
||||
workspaceUrl,
|
||||
scope,
|
||||
accessToken
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getSecrets = async (secretSync: TDatabricksSyncWithCredentials) => {
|
||||
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||
};
|
||||
|
||||
return {
|
||||
syncSecrets,
|
||||
removeSecrets,
|
||||
getSecrets
|
||||
};
|
||||
};
|
@ -0,0 +1,43 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSyncs } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import {
|
||||
BaseSecretSyncSchema,
|
||||
GenericCreateSecretSyncFieldsSchema,
|
||||
GenericUpdateSecretSyncFieldsSchema
|
||||
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
const DatabricksSyncDestinationConfigSchema = z.object({
|
||||
scope: z.string().trim().min(1, "Databricks scope required").describe(SecretSyncs.DESTINATION_CONFIG.DATABRICKS.scope)
|
||||
});
|
||||
|
||||
const DatabricksSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: false };
|
||||
|
||||
export const DatabricksSyncSchema = BaseSecretSyncSchema(SecretSync.Databricks, DatabricksSyncOptionsConfig).extend({
|
||||
destination: z.literal(SecretSync.Databricks),
|
||||
destinationConfig: DatabricksSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const CreateDatabricksSyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||
SecretSync.Databricks,
|
||||
DatabricksSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: DatabricksSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateDatabricksSyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||
SecretSync.Databricks,
|
||||
DatabricksSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: DatabricksSyncDestinationConfigSchema.optional()
|
||||
});
|
||||
|
||||
export const DatabricksSyncListItemSchema = z.object({
|
||||
name: z.literal("Databricks"),
|
||||
connection: z.literal(AppConnection.Databricks),
|
||||
destination: z.literal(SecretSync.Databricks),
|
||||
canImportSecrets: z.literal(false)
|
||||
});
|
@ -0,0 +1,40 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { TDatabricksConnection } from "@app/services/app-connection/databricks";
|
||||
|
||||
import {
|
||||
CreateDatabricksSyncSchema,
|
||||
DatabricksSyncListItemSchema,
|
||||
DatabricksSyncSchema
|
||||
} from "./databricks-sync-schemas";
|
||||
|
||||
export type TDatabricksSync = z.infer<typeof DatabricksSyncSchema>;
|
||||
|
||||
export type TDatabricksSyncInput = z.infer<typeof CreateDatabricksSyncSchema>;
|
||||
|
||||
export type TDatabricksSyncListItem = z.infer<typeof DatabricksSyncListItemSchema>;
|
||||
|
||||
export type TDatabricksSyncWithCredentials = TDatabricksSync & {
|
||||
connection: TDatabricksConnection;
|
||||
};
|
||||
|
||||
export type TDatabricksListSecretKeysResponse = {
|
||||
secrets?: { key: string; last_updated_timestamp: number }[];
|
||||
};
|
||||
|
||||
type TBaseDatabricksSecretRequest = {
|
||||
scope: string;
|
||||
workspaceUrl: string;
|
||||
accessToken: string;
|
||||
};
|
||||
|
||||
export type TDatabricksListSecretKeys = TBaseDatabricksSecretRequest;
|
||||
|
||||
export type TDatabricksPutSecret = {
|
||||
key: string;
|
||||
value?: string;
|
||||
} & TBaseDatabricksSecretRequest;
|
||||
|
||||
export type TDatabricksDeleteSecret = {
|
||||
key: string;
|
||||
} & TBaseDatabricksSecretRequest;
|
4
backend/src/services/secret-sync/databricks/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from "./databricks-sync-constants";
|
||||
export * from "./databricks-sync-fns";
|
||||
export * from "./databricks-sync-schemas";
|
||||
export * from "./databricks-sync-types";
|
@ -1,5 +1,6 @@
|
||||
import z from "zod";
|
||||
|
||||
import { SecretSyncs } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
BaseSecretSyncSchema,
|
||||
@ -14,8 +15,8 @@ import { GcpSyncScope } from "./gcp-sync-enums";
|
||||
const GcpSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true };
|
||||
|
||||
const GcpSyncDestinationConfigSchema = z.object({
|
||||
scope: z.literal(GcpSyncScope.Global),
|
||||
projectId: z.string().min(1, "Project ID is required")
|
||||
scope: z.literal(GcpSyncScope.Global).describe(SecretSyncs.DESTINATION_CONFIG.GCP.scope),
|
||||
projectId: z.string().min(1, "Project ID is required").describe(SecretSyncs.DESTINATION_CONFIG.GCP.projectId)
|
||||
});
|
||||
|
||||
export const GcpSyncSchema = BaseSecretSyncSchema(SecretSync.GCPSecretManager, GcpSyncOptionsConfig).extend({
|
||||
|
@ -14,21 +14,21 @@ import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types"
|
||||
const GitHubSyncDestinationConfigSchema = z
|
||||
.discriminatedUnion("scope", [
|
||||
z.object({
|
||||
scope: z.literal(GitHubSyncScope.Organization),
|
||||
org: z.string().min(1, "Organization name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.ORG),
|
||||
scope: z.literal(GitHubSyncScope.Organization).describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.scope),
|
||||
org: z.string().min(1, "Organization name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.org),
|
||||
visibility: z.nativeEnum(GitHubSyncVisibility),
|
||||
selectedRepositoryIds: z.number().array().optional()
|
||||
}),
|
||||
z.object({
|
||||
scope: z.literal(GitHubSyncScope.Repository),
|
||||
owner: z.string().min(1, "Repository owner name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.OWNER),
|
||||
repo: z.string().min(1, "Repository name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.REPO)
|
||||
scope: z.literal(GitHubSyncScope.Repository).describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.scope),
|
||||
owner: z.string().min(1, "Repository owner name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.owner),
|
||||
repo: z.string().min(1, "Repository name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.repo)
|
||||
}),
|
||||
z.object({
|
||||
scope: z.literal(GitHubSyncScope.RepositoryEnvironment),
|
||||
owner: z.string().min(1, "Repository owner name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.OWNER),
|
||||
repo: z.string().min(1, "Repository name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.REPO),
|
||||
env: z.string().min(1, "Environment name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.ENV)
|
||||
scope: z.literal(GitHubSyncScope.RepositoryEnvironment).describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.scope),
|
||||
owner: z.string().min(1, "Repository owner name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.owner),
|
||||
repo: z.string().min(1, "Repository name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.repo),
|
||||
env: z.string().min(1, "Environment name required").describe(SecretSyncs.DESTINATION_CONFIG.GITHUB.env)
|
||||
})
|
||||
])
|
||||
.superRefine((options, ctx) => {
|
||||
|
@ -4,7 +4,8 @@ export enum SecretSync {
|
||||
GitHub = "github",
|
||||
GCPSecretManager = "gcp-secret-manager",
|
||||
AzureKeyVault = "azure-key-vault",
|
||||
AzureAppConfiguration = "azure-app-configuration"
|
||||
AzureAppConfiguration = "azure-app-configuration",
|
||||
Databricks = "databricks"
|
||||
}
|
||||
|
||||
export enum SecretSyncInitialSyncBehavior {
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
AWS_SECRETS_MANAGER_SYNC_LIST_OPTION,
|
||||
AwsSecretsManagerSyncFns
|
||||
} from "@app/services/secret-sync/aws-secrets-manager";
|
||||
import { DATABRICKS_SYNC_LIST_OPTION, databricksSyncFactory } from "@app/services/secret-sync/databricks";
|
||||
import { GITHUB_SYNC_LIST_OPTION, GithubSyncFns } from "@app/services/secret-sync/github";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
@ -19,11 +20,8 @@ import {
|
||||
|
||||
import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal";
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import {
|
||||
AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION,
|
||||
azureAppConfigurationSecretSyncFactory
|
||||
} from "./azure-app-configuration";
|
||||
import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSecretSyncFactory } from "./azure-key-vault";
|
||||
import { AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION, azureAppConfigurationSyncFactory } from "./azure-app-configuration";
|
||||
import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./azure-key-vault";
|
||||
import { GCP_SYNC_LIST_OPTION } from "./gcp";
|
||||
import { GcpSyncFns } from "./gcp/gcp-sync-fns";
|
||||
|
||||
@ -33,7 +31,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
||||
[SecretSync.GitHub]: GITHUB_SYNC_LIST_OPTION,
|
||||
[SecretSync.GCPSecretManager]: GCP_SYNC_LIST_OPTION,
|
||||
[SecretSync.AzureKeyVault]: AZURE_KEY_VAULT_SYNC_LIST_OPTION,
|
||||
[SecretSync.AzureAppConfiguration]: AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION
|
||||
[SecretSync.AzureAppConfiguration]: AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION,
|
||||
[SecretSync.Databricks]: DATABRICKS_SYNC_LIST_OPTION
|
||||
};
|
||||
|
||||
export const listSecretSyncOptions = () => {
|
||||
@ -41,7 +40,7 @@ export const listSecretSyncOptions = () => {
|
||||
};
|
||||
|
||||
type TSyncSecretDeps = {
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update">;
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
};
|
||||
|
||||
@ -103,12 +102,17 @@ export const SecretSyncFns = {
|
||||
case SecretSync.GCPSecretManager:
|
||||
return GcpSyncFns.syncSecrets(secretSync, secretMap);
|
||||
case SecretSync.AzureKeyVault:
|
||||
return azureKeyVaultSecretSyncFactory({
|
||||
return azureKeyVaultSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).syncSecrets(secretSync, secretMap);
|
||||
case SecretSync.AzureAppConfiguration:
|
||||
return azureAppConfigurationSecretSyncFactory({
|
||||
return azureAppConfigurationSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).syncSecrets(secretSync, secretMap);
|
||||
case SecretSync.Databricks:
|
||||
return databricksSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).syncSecrets(secretSync, secretMap);
|
||||
@ -137,17 +141,22 @@ export const SecretSyncFns = {
|
||||
secretMap = await GcpSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.AzureKeyVault:
|
||||
secretMap = await azureKeyVaultSecretSyncFactory({
|
||||
secretMap = await azureKeyVaultSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.AzureAppConfiguration:
|
||||
secretMap = await azureAppConfigurationSecretSyncFactory({
|
||||
secretMap = await azureAppConfigurationSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.Databricks:
|
||||
return databricksSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).getSecrets(secretSync);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@ -174,12 +183,17 @@ export const SecretSyncFns = {
|
||||
case SecretSync.GCPSecretManager:
|
||||
return GcpSyncFns.removeSecrets(secretSync, secretMap);
|
||||
case SecretSync.AzureKeyVault:
|
||||
return azureKeyVaultSecretSyncFactory({
|
||||
return azureKeyVaultSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).removeSecrets(secretSync, secretMap);
|
||||
case SecretSync.AzureAppConfiguration:
|
||||
return azureAppConfigurationSecretSyncFactory({
|
||||
return azureAppConfigurationSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).removeSecrets(secretSync, secretMap);
|
||||
case SecretSync.Databricks:
|
||||
return databricksSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).removeSecrets(secretSync, secretMap);
|
||||
|
@ -7,7 +7,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
||||
[SecretSync.GitHub]: "GitHub",
|
||||
[SecretSync.GCPSecretManager]: "GCP Secret Manager",
|
||||
[SecretSync.AzureKeyVault]: "Azure Key Vault",
|
||||
[SecretSync.AzureAppConfiguration]: "Azure App Configuration"
|
||||
[SecretSync.AzureAppConfiguration]: "Azure App Configuration",
|
||||
[SecretSync.Databricks]: "Databricks"
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
@ -16,5 +17,6 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.GitHub]: AppConnection.GitHub,
|
||||
[SecretSync.GCPSecretManager]: AppConnection.GCP,
|
||||
[SecretSync.AzureKeyVault]: AppConnection.AzureKeyVault,
|
||||
[SecretSync.AzureAppConfiguration]: AppConnection.AzureAppConfiguration
|
||||
[SecretSync.AzureAppConfiguration]: AppConnection.AzureAppConfiguration,
|
||||
[SecretSync.Databricks]: AppConnection.Databricks
|
||||
};
|
||||
|
@ -64,7 +64,7 @@ export type TSecretSyncQueueFactory = ReturnType<typeof secretSyncQueueFactory>;
|
||||
type TSecretSyncQueueFactoryDep = {
|
||||
queueService: Pick<TQueueServiceFactory, "queue" | "start">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update">;
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">;
|
||||
keyStore: Pick<TKeyStoreFactory, "acquireLock" | "setItemWithExpiry" | "getItem">;
|
||||
folderDAL: TSecretFolderDALFactory;
|
||||
secretV2BridgeDAL: Pick<
|
||||
|
@ -13,7 +13,7 @@ const SyncOptionsSchema = (secretSync: SecretSync, options: TSyncOptionsConfig =
|
||||
initialSyncBehavior: (options.canImportSecrets
|
||||
? z.nativeEnum(SecretSyncInitialSyncBehavior)
|
||||
: z.literal(SecretSyncInitialSyncBehavior.OverwriteDestination)
|
||||
).describe(SecretSyncs.SYNC_OPTIONS(secretSync).INITIAL_SYNC_BEHAVIOR)
|
||||
).describe(SecretSyncs.SYNC_OPTIONS(secretSync).initialSyncBehavior)
|
||||
// prependPrefix: z
|
||||
// .string()
|
||||
// .trim()
|
||||
|
@ -8,6 +8,12 @@ import {
|
||||
TAwsSecretsManagerSyncListItem,
|
||||
TAwsSecretsManagerSyncWithCredentials
|
||||
} from "@app/services/secret-sync/aws-secrets-manager";
|
||||
import {
|
||||
TDatabricksSync,
|
||||
TDatabricksSyncInput,
|
||||
TDatabricksSyncListItem,
|
||||
TDatabricksSyncWithCredentials
|
||||
} from "@app/services/secret-sync/databricks";
|
||||
import {
|
||||
TGitHubSync,
|
||||
TGitHubSyncInput,
|
||||
@ -43,7 +49,8 @@ export type TSecretSync =
|
||||
| TGitHubSync
|
||||
| TGcpSync
|
||||
| TAzureKeyVaultSync
|
||||
| TAzureAppConfigurationSync;
|
||||
| TAzureAppConfigurationSync
|
||||
| TDatabricksSync;
|
||||
|
||||
export type TSecretSyncWithCredentials =
|
||||
| TAwsParameterStoreSyncWithCredentials
|
||||
@ -51,7 +58,8 @@ export type TSecretSyncWithCredentials =
|
||||
| TGitHubSyncWithCredentials
|
||||
| TGcpSyncWithCredentials
|
||||
| TAzureKeyVaultSyncWithCredentials
|
||||
| TAzureAppConfigurationSyncWithCredentials;
|
||||
| TAzureAppConfigurationSyncWithCredentials
|
||||
| TDatabricksSyncWithCredentials;
|
||||
|
||||
export type TSecretSyncInput =
|
||||
| TAwsParameterStoreSyncInput
|
||||
@ -59,7 +67,8 @@ export type TSecretSyncInput =
|
||||
| TGitHubSyncInput
|
||||
| TGcpSyncInput
|
||||
| TAzureKeyVaultSyncInput
|
||||
| TAzureAppConfigurationSyncInput;
|
||||
| TAzureAppConfigurationSyncInput
|
||||
| TDatabricksSyncInput;
|
||||
|
||||
export type TSecretSyncListItem =
|
||||
| TAwsParameterStoreSyncListItem
|
||||
@ -67,7 +76,8 @@ export type TSecretSyncListItem =
|
||||
| TGitHubSyncListItem
|
||||
| TGcpSyncListItem
|
||||
| TAzureKeyVaultSyncListItem
|
||||
| TAzureAppConfigurationSyncListItem;
|
||||
| TAzureAppConfigurationSyncListItem
|
||||
| TDatabricksSyncListItem;
|
||||
|
||||
export type TSyncOptionsConfig = {
|
||||
canImportSecrets: boolean;
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Available"
|
||||
openapi: "GET /api/v1/app-connections/databricks/available"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Create"
|
||||
openapi: "POST /api/v1/app-connections/databricks"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Delete"
|
||||
openapi: "DELETE /api/v1/app-connections/databricks/{connectionId}"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Get by ID"
|
||||
openapi: "GET /api/v1/app-connections/databricks/{connectionId}"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Get by Name"
|
||||
openapi: "GET /api/v1/app-connections/databricks/connection-name/{connectionName}"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "List"
|
||||
openapi: "GET /api/v1/app-connections/databricks"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Update"
|
||||
openapi: "PATCH /api/v1/app-connections/databricks/{connectionId}"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Create"
|
||||
openapi: "POST /api/v1/secret-syncs/databricks"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Delete"
|
||||
openapi: "DELETE /api/v1/secret-syncs/databricks/{syncId}"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Get by ID"
|
||||
openapi: "GET /api/v1/secret-syncs/databricks/{syncId}"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Get by Name"
|
||||
openapi: "GET /api/v1/secret-syncs/databricks/sync-name/{syncName}"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "List"
|
||||
openapi: "GET /api/v1/secret-syncs/databricks"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Remove Secrets"
|
||||
openapi: "POST /api/v1/secret-syncs/databricks/{syncId}/remove-secrets"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Sync Secrets"
|
||||
openapi: "POST /api/v1/secret-syncs/databricks/{syncId}/sync-secrets"
|
||||
---
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: "Update"
|
||||
openapi: "PATCH /api/v1/secret-syncs/databricks/{syncId}"
|
||||
---
|
@ -4,9 +4,6 @@ title: "Backend development guide"
|
||||
|
||||
Suppose you're interested in implementing a new feature in Infisical's backend, let's call it "feature-x." Here are the general steps you should follow.
|
||||
|
||||
## Database schema migration
|
||||
In order to run [schema migrations](https://en.wikipedia.org/wiki/Schema_migration#:~:text=A%20schema%20migration%20is%20performed,some%20newer%20or%20older%20version) you need to expose your database connection string. Create a `.env.migration` file to set the database connection URI for migration scripts, or alternatively, export the `DB_CONNECTION_URI` environment variable.
|
||||
|
||||
## Creating new database model
|
||||
If your feature involves a change in the database, you need to first address this by generating the necessary database schemas.
|
||||
|
||||
|
BIN
docs/images/app-connections/databricks/add-service-principal.png
Normal file
After ![]() (image error) Size: 392 KiB |
BIN
docs/images/app-connections/databricks/create-databricks-service-principal-method.png
Normal file
After ![]() (image error) Size: 774 KiB |
After ![]() (image error) Size: 446 KiB |
After ![]() (image error) Size: 1.2 MiB |
After ![]() (image error) Size: 382 KiB |
After ![]() (image error) Size: 743 KiB |
BIN
docs/images/app-connections/databricks/service-principal-ids.png
Normal file
After ![]() (image error) Size: 512 KiB |
After ![]() (image error) Size: 410 KiB |
BIN
docs/images/app-connections/databricks/workspace-settings.png
Normal file
After ![]() (image error) Size: 363 KiB |
BIN
docs/images/secret-syncs/databricks/databricks-created.png
Normal file
After ![]() (image error) Size: 1.3 MiB |
BIN
docs/images/secret-syncs/databricks/databricks-destination.png
Normal file
After ![]() (image error) Size: 808 KiB |
BIN
docs/images/secret-syncs/databricks/databricks-details.png
Normal file
After ![]() (image error) Size: 792 KiB |
BIN
docs/images/secret-syncs/databricks/databricks-options.png
Normal file
After ![]() (image error) Size: 853 KiB |
BIN
docs/images/secret-syncs/databricks/databricks-review.png
Normal file
After ![]() (image error) Size: 827 KiB |
BIN
docs/images/secret-syncs/databricks/databricks-source.png
Normal file
After ![]() (image error) Size: 778 KiB |
BIN
docs/images/secret-syncs/databricks/select-databricks-option.png
Normal file
After ![]() (image error) Size: 804 KiB |
@ -62,7 +62,7 @@ Infisical currently only supports one method for connecting to Azure, which is O
|
||||
## Setup Azure Connection in Infisical
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to the App Connections">
|
||||
<Step title="Navigate to App Connections">
|
||||
Navigate to the **App Connections** tab on the **Organization Settings** page. 
|
||||
</Step>
|
||||
|
@ -61,7 +61,7 @@ Infisical currently only supports one method for connecting to Azure, which is O
|
||||
## Setup Azure Connection in Infisical
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to the App Connections">
|
||||
<Step title="Navigate to App Connections">
|
||||
Navigate to the **App Connections** tab on the **Organization Settings** page. 
|
||||
</Step>
|
||||
|
64
docs/integrations/app-connections/databricks.mdx
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
title: "Databricks Connection"
|
||||
description: "Learn how to configure a Databricks Connection for Infisical."
|
||||
---
|
||||
|
||||
Infisical supports the use of [service principals](https://docs.databricks.com/en/admin/users-groups/service-principals.html) to connect with your Databricks workspaces.
|
||||
|
||||
## Configure a Service Principal for Infisical
|
||||
|
||||
<Steps>
|
||||
<Step title="Databricks Workspace Settings">
|
||||
Navigate to your Databricks Workspace **Settings** via the dropdown in the top right.
|
||||

|
||||
</Step>
|
||||
<Step title="Manage Service Principals">
|
||||
Under the **Identity & Access** tab, click the **Manage** button in the **Service Principals** section.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Service Principal Management">
|
||||
Click the **Add Service Principal** button.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Add Service Principal">
|
||||
Select the **Add New** option and create a service principal for Infisical.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Generate Service Principal Secret">
|
||||
Click on your new service principal, select the **Secrets** tab and click the **Generate Secret** button.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Service Principal Secret">
|
||||
Copy your service principal **Secret** and **Client ID** for use in the following steps.
|
||||
|
||||

|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Setup Databricks Connection in Infisical
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to App Connections">
|
||||
Navigate to the **App Connections** tab on the **Organization Settings**
|
||||
page. 
|
||||
</Step>
|
||||
<Step title="Add Connection">
|
||||
Select the **Databricks Connection** option from the connection options modal.
|
||||

|
||||
</Step>
|
||||
<Step title="Authorize Connection">
|
||||
Select the **Service Principal** method, add your **workspace URL** and **service principal credentials**, then click **Connect to
|
||||
Databricks**. 
|
||||
</Step>
|
||||
<Step title="Connection Created">
|
||||
Your **Databricks Connection** is now available for use. 
|
||||
</Step>
|
||||
</Steps>
|
@ -81,7 +81,7 @@ Infisical supports [service account impersonation](https://cloud.google.com/iam/
|
||||
## Setup GCP Connection in Infisical
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to the App Connections">
|
||||
<Step title="Navigate to App Connections">
|
||||
Navigate to the **App Connections** tab on the **Organization Settings**
|
||||
page. 
|
||||
|
@ -69,7 +69,7 @@ Infisical supports two methods for connecting to GitHub.
|
||||
## Setup GitHub Connection in Infisical
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to the App Connections">
|
||||
<Step title="Navigate to App Connections">
|
||||
Navigate to the **App Connections** tab on the **Organization Settings** page.
|
||||

|
||||
</Step>
|
||||
@ -135,7 +135,7 @@ Infisical supports two methods for connecting to GitHub.
|
||||
## Setup GitHub Connection in Infisical
|
||||
|
||||
<Steps>
|
||||
<Step title="Navigate to the App Connections">
|
||||
<Step title="Navigate to App Connections">
|
||||
Navigate to the **App Connections** tab on the **Organization Settings** page.
|
||||

|
||||
</Step>
|
||||
|
140
docs/integrations/secret-syncs/databricks.mdx
Normal file
@ -0,0 +1,140 @@
|
||||
---
|
||||
title: "Databricks Sync"
|
||||
description: "Learn how to configure a Databricks Sync for Infisical."
|
||||
---
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)
|
||||
- Create a [Databricks Connection](/integrations/app-connections/databricks)
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Infisical UI">
|
||||
1. Navigate to **Project** > **Integrations** and select the **Secret Syncs** tab. Click on the **Add Sync** button.
|
||||

|
||||
|
||||
2. Select the **Databricks** option.
|
||||

|
||||
|
||||
3. Configure the **Source** from where secrets should be retrieved, then click **Next**.
|
||||

|
||||
|
||||
- **Environment**: The project environment to retrieve secrets from.
|
||||
- **Secret Path**: The folder path to retrieve secrets from.
|
||||
|
||||
<Tip>
|
||||
If you need to sync secrets from multiple folder locations, check out [secret imports](/documentation/platform/secret-reference#secret-imports).
|
||||
</Tip>
|
||||
|
||||
4. Configure the **Destination** to where secrets should be deployed, then click **Next**.
|
||||

|
||||
|
||||
- **Databricks Connection**: The Databricks Connection to authenticate with.
|
||||
- **Scope**: The Databricks secret scope to sync secrets to.
|
||||
|
||||
<Note>
|
||||
You must create a secret scope in your Databricks workspace prior to configuration. Ensure your service principal has [Write permissions](https://docs.databricks.com/en/security/auth/access-control/index.html#secret-acls) for the specified secret scope.
|
||||
</Note>
|
||||
|
||||
5. Configure the **Sync Options** to specify how secrets should be synced, then click **Next**.
|
||||

|
||||
|
||||
- **Initial Sync Behavior**: Determines how Infisical should resolve the initial sync.
|
||||
- **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical.
|
||||
<Note>
|
||||
Databricks does not support importing secrets.
|
||||
</Note>
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
|
||||
6. Configure the **Details** of your Databricks Sync, then click **Next**.
|
||||

|
||||
|
||||
- **Name**: The name of your sync. Must be slug-friendly.
|
||||
- **Description**: An optional description for your sync.
|
||||
|
||||
7. Review your Databricks Sync configuration, then click **Create Sync**.
|
||||

|
||||
|
||||
8. If enabled, your Databricks Sync will begin syncing your secrets to the destination endpoint.
|
||||

|
||||
|
||||
</Tab>
|
||||
<Tab title="API">
|
||||
To create an **Databricks Sync**, make an API request to the [Create Databricks Sync](/api-reference/endpoints/secret-syncs/databricks/create) API endpoint.
|
||||
|
||||
### Sample request
|
||||
|
||||
```bash Request
|
||||
curl --request POST \
|
||||
--url https://app.infisical.com/api/v1/secret-syncs/databricks \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"name": "my-databricks-sync",
|
||||
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||
"description": "an example sync",
|
||||
"connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||
"environment": "dev",
|
||||
"secretPath": "/my-secrets",
|
||||
"isEnabled": true,
|
||||
"syncOptions": {
|
||||
"initialSyncBehavior": "overwrite-destination"
|
||||
},
|
||||
"destinationConfig": {
|
||||
"scope": "my-scope"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Sample response
|
||||
|
||||
```bash Response
|
||||
{
|
||||
"secretSync": {
|
||||
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||
"name": "my-databricks-sync",
|
||||
"description": "an example sync",
|
||||
"isEnabled": true,
|
||||
"version": 1,
|
||||
"folderId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||
"connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||
"createdAt": "2023-11-07T05:31:56Z",
|
||||
"updatedAt": "2023-11-07T05:31:56Z",
|
||||
"syncStatus": "succeeded",
|
||||
"lastSyncJobId": "123",
|
||||
"lastSyncMessage": null,
|
||||
"lastSyncedAt": "2023-11-07T05:31:56Z",
|
||||
"importStatus": null,
|
||||
"lastImportJobId": null,
|
||||
"lastImportMessage": null,
|
||||
"lastImportedAt": null,
|
||||
"removeStatus": null,
|
||||
"lastRemoveJobId": null,
|
||||
"lastRemoveMessage": null,
|
||||
"lastRemovedAt": null,
|
||||
"syncOptions": {
|
||||
"initialSyncBehavior": "overwrite-destination"
|
||||
},
|
||||
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||
"connection": {
|
||||
"app": "databricks",
|
||||
"name": "my-databricks-connection",
|
||||
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
|
||||
},
|
||||
"environment": {
|
||||
"slug": "dev",
|
||||
"name": "Development",
|
||||
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
|
||||
},
|
||||
"folder": {
|
||||
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||
"path": "/my-secrets"
|
||||
},
|
||||
"destination": "databricks",
|
||||
"destinationConfig": {
|
||||
"scope": "my-scope"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
@ -1,28 +1,34 @@
|
||||
---
|
||||
title: "Components"
|
||||
description: "Infisical's components span multiple clients, an API, and a storage backend."
|
||||
description: "Understand Infisical's core architectural components and how they work together."
|
||||
---
|
||||
|
||||
## Infisical API
|
||||
## Overview
|
||||
|
||||
The Infisical API (sometimes referred to as the **backend**) contains the core platform logic.
|
||||
Infisical is architected around several key components that work in concert to provide a secure and streamlined secret management experience. These components span the client, API, and storage layers, ensuring that secrets are protected at every stage of their lifecycle.
|
||||
|
||||
## Storage backend
|
||||
## 1. API (Backend)
|
||||
|
||||
Infisical relies on a storage backend to store data including users and secrets. Infisical's storage backend is Postgres.
|
||||
Infisical exposes a well-documented [REST API](https://infisical.com/docs/api-reference/overview/introduction) that enables programmatic interaction with the platform, enabling a wide range of use cases.
|
||||
|
||||
## Redis
|
||||
## 2. Storage Backend
|
||||
|
||||
Infisical uses [Redis](https://redis.com) to enable more complex workflows including a queuing system to manage long running asynchronous tasks, cron jobs, as well as reliable cache for frequently used resources.
|
||||
Infisical relies on a robust storage backend to durably store secrets, users, and other platform data. Infisical's storage backend is [PostgreSQL](https://www.postgresql.org/).
|
||||
|
||||
## Infisical Web UI
|
||||
## 3. Caching Layer
|
||||
|
||||
The Web UI is the browser-based portal that connects to the Infisical API.
|
||||
Infisical uses [Redis](https://redis.com) to enable more complex workflows including a queuing system to manage long-running asynchronous tasks, cron jobs, as well as reliable cache for frequently used resources.
|
||||
|
||||
## Infisical clients
|
||||
## 4. Clients
|
||||
|
||||
Clients are any application or infrastructure that connecting to the Infisical API using one of the below methods:
|
||||
- Public API: Making API requests directly to the Infisical API.
|
||||
- Client SDK: A platform-specific library with method abstractions for working with secrets. Currently, there are three official SDKs: [Node SDK](https://infisical.com/docs/sdks/languages/node), [Python SDK](https://infisical.com/docs/sdks/languages/python), and [Java SDK](https://infisical.com/docs/sdks/languages/java).
|
||||
- CLI: A terminal-based interface for interacting with the Infisical API.
|
||||
- Kubernetes Operator: This operator retrieves secrets from Infisical and securely store
|
||||
Clients are interfaces through which users and applications interact with the Infisical API:
|
||||
|
||||
- **Web UI**: A browser-based portal providing a user-friendly interface for managing secrets, configurations, and performing administrative tasks.
|
||||
|
||||
- [**CLI**](https://infisical.com/docs/cli): A terminal-based tool for interacting with the Infisical API, enabling automation, scripting, and integration into CI/CD pipelines.
|
||||
|
||||
- **SDKs (Software Development Kits)**: Platform-specific libraries with method abstractions for working with secrets. Supported languages include [Node.js](https://infisical.com/docs/sdks/languages/node), [Python](https://infisical.com/docs/sdks/languages/python), [Java](https://infisical.com/docs/sdks/languages/java), [Golang](https://infisical.com/docs/sdks/languages/go), [Ruby](https://infisical.com/docs/sdks/languages/ruby) and [.NET](https://infisical.com/docs/sdks/languages/csharp).
|
||||
|
||||
- [**Kubernetes Operator**](https://infisical.com/docs/integrations/platforms/kubernetes): A Kubernetes-native component that facilitates the secure retrieval and management of secrets within a Kubernetes cluster. The operator supports multiple custom resource definitions (CRDs) for syncing secrets.
|
||||
|
||||
- [**Infisical Agent**](https://infisical.com/docs/integrations/platforms/infisical-agent): Daemon that automatically fetches and manages access tokens and secrets to be used in various client resources.
|
||||
|
166
docs/mint.json
@ -285,7 +285,7 @@
|
||||
"pages": [
|
||||
"self-hosting/overview",
|
||||
{
|
||||
"group": "Containerized installation methods",
|
||||
"group": "Installation methods",
|
||||
"pages": [
|
||||
"self-hosting/deployment-options/standalone-infisical",
|
||||
"self-hosting/deployment-options/docker-swarm",
|
||||
@ -293,12 +293,12 @@
|
||||
"self-hosting/deployment-options/kubernetes-helm"
|
||||
]
|
||||
},
|
||||
"self-hosting/guides/upgrading-infisical",
|
||||
"self-hosting/configuration/envars",
|
||||
"self-hosting/configuration/requirements",
|
||||
{
|
||||
"group": "Guides",
|
||||
"pages": [
|
||||
"self-hosting/configuration/schema-migrations",
|
||||
"self-hosting/guides/mongo-to-postgres",
|
||||
"self-hosting/guides/custom-certificates"
|
||||
]
|
||||
@ -392,10 +392,11 @@
|
||||
"group": "Connections",
|
||||
"pages": [
|
||||
"integrations/app-connections/aws",
|
||||
"integrations/app-connections/github",
|
||||
"integrations/app-connections/gcp",
|
||||
"integrations/app-connections/azure-app-configuration",
|
||||
"integrations/app-connections/azure-key-vault",
|
||||
"integrations/app-connections/azure-app-configuration"
|
||||
"integrations/app-connections/databricks",
|
||||
"integrations/app-connections/gcp",
|
||||
"integrations/app-connections/github"
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -409,10 +410,11 @@
|
||||
"pages": [
|
||||
"integrations/secret-syncs/aws-parameter-store",
|
||||
"integrations/secret-syncs/aws-secrets-manager",
|
||||
"integrations/secret-syncs/github",
|
||||
"integrations/secret-syncs/gcp-secret-manager",
|
||||
"integrations/secret-syncs/azure-app-configuration",
|
||||
"integrations/secret-syncs/azure-key-vault",
|
||||
"integrations/secret-syncs/azure-app-configuration"
|
||||
"integrations/secret-syncs/databricks",
|
||||
"integrations/secret-syncs/gcp-secret-manager",
|
||||
"integrations/secret-syncs/github"
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -825,27 +827,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "GitHub",
|
||||
"group": "Azure App Configuration",
|
||||
"pages": [
|
||||
"api-reference/endpoints/app-connections/github/list",
|
||||
"api-reference/endpoints/app-connections/github/available",
|
||||
"api-reference/endpoints/app-connections/github/get-by-id",
|
||||
"api-reference/endpoints/app-connections/github/get-by-name",
|
||||
"api-reference/endpoints/app-connections/github/create",
|
||||
"api-reference/endpoints/app-connections/github/update",
|
||||
"api-reference/endpoints/app-connections/github/delete"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "GCP",
|
||||
"pages": [
|
||||
"api-reference/endpoints/app-connections/gcp/list",
|
||||
"api-reference/endpoints/app-connections/gcp/available",
|
||||
"api-reference/endpoints/app-connections/gcp/get-by-id",
|
||||
"api-reference/endpoints/app-connections/gcp/get-by-name",
|
||||
"api-reference/endpoints/app-connections/gcp/create",
|
||||
"api-reference/endpoints/app-connections/gcp/update",
|
||||
"api-reference/endpoints/app-connections/gcp/delete"
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/list",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/available",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/get-by-id",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/get-by-name",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/create",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/update",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/delete"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -861,15 +851,39 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Azure App Configuration",
|
||||
"group": "Databricks",
|
||||
"pages": [
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/list",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/available",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/get-by-id",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/get-by-name",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/create",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/update",
|
||||
"api-reference/endpoints/app-connections/azure-app-configuration/delete"
|
||||
"api-reference/endpoints/app-connections/databricks/list",
|
||||
"api-reference/endpoints/app-connections/databricks/available",
|
||||
"api-reference/endpoints/app-connections/databricks/get-by-id",
|
||||
"api-reference/endpoints/app-connections/databricks/get-by-name",
|
||||
"api-reference/endpoints/app-connections/databricks/create",
|
||||
"api-reference/endpoints/app-connections/databricks/update",
|
||||
"api-reference/endpoints/app-connections/databricks/delete"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "GCP",
|
||||
"pages": [
|
||||
"api-reference/endpoints/app-connections/gcp/list",
|
||||
"api-reference/endpoints/app-connections/gcp/available",
|
||||
"api-reference/endpoints/app-connections/gcp/get-by-id",
|
||||
"api-reference/endpoints/app-connections/gcp/get-by-name",
|
||||
"api-reference/endpoints/app-connections/gcp/create",
|
||||
"api-reference/endpoints/app-connections/gcp/update",
|
||||
"api-reference/endpoints/app-connections/gcp/delete"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "GitHub",
|
||||
"pages": [
|
||||
"api-reference/endpoints/app-connections/github/list",
|
||||
"api-reference/endpoints/app-connections/github/available",
|
||||
"api-reference/endpoints/app-connections/github/get-by-id",
|
||||
"api-reference/endpoints/app-connections/github/get-by-name",
|
||||
"api-reference/endpoints/app-connections/github/create",
|
||||
"api-reference/endpoints/app-connections/github/update",
|
||||
"api-reference/endpoints/app-connections/github/delete"
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -908,30 +922,17 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "GitHub",
|
||||
"group": "Azure App Configuration",
|
||||
"pages": [
|
||||
"api-reference/endpoints/secret-syncs/github/list",
|
||||
"api-reference/endpoints/secret-syncs/github/get-by-id",
|
||||
"api-reference/endpoints/secret-syncs/github/get-by-name",
|
||||
"api-reference/endpoints/secret-syncs/github/create",
|
||||
"api-reference/endpoints/secret-syncs/github/update",
|
||||
"api-reference/endpoints/secret-syncs/github/delete",
|
||||
"api-reference/endpoints/secret-syncs/github/sync-secrets",
|
||||
"api-reference/endpoints/secret-syncs/github/remove-secrets"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "GCP Secret Manager",
|
||||
"pages": [
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/list",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/get-by-id",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/get-by-name",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/create",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/update",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/delete",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/sync-secrets",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/import-secrets",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/remove-secrets"
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/list",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/get-by-id",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/get-by-name",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/create",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/update",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/delete",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/sync-secrets",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/import-secrets",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/remove-secrets"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -949,20 +950,45 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Azure App Configuration",
|
||||
"group": "Databricks",
|
||||
"pages": [
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/list",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/get-by-id",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/get-by-name",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/create",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/update",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/delete",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/sync-secrets",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/import-secrets",
|
||||
"api-reference/endpoints/secret-syncs/azure-app-configuration/remove-secrets"
|
||||
"api-reference/endpoints/secret-syncs/databricks/list",
|
||||
"api-reference/endpoints/secret-syncs/databricks/get-by-id",
|
||||
"api-reference/endpoints/secret-syncs/databricks/get-by-name",
|
||||
"api-reference/endpoints/secret-syncs/databricks/create",
|
||||
"api-reference/endpoints/secret-syncs/databricks/update",
|
||||
"api-reference/endpoints/secret-syncs/databricks/delete",
|
||||
"api-reference/endpoints/secret-syncs/databricks/sync-secrets",
|
||||
"api-reference/endpoints/secret-syncs/databricks/remove-secrets"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "GCP Secret Manager",
|
||||
"pages": [
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/list",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/get-by-id",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/get-by-name",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/create",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/update",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/delete",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/sync-secrets",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/import-secrets",
|
||||
"api-reference/endpoints/secret-syncs/gcp-secret-manager/remove-secrets"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "GitHub",
|
||||
"pages": [
|
||||
"api-reference/endpoints/secret-syncs/github/list",
|
||||
"api-reference/endpoints/secret-syncs/github/get-by-id",
|
||||
"api-reference/endpoints/secret-syncs/github/get-by-name",
|
||||
"api-reference/endpoints/secret-syncs/github/create",
|
||||
"api-reference/endpoints/secret-syncs/github/update",
|
||||
"api-reference/endpoints/secret-syncs/github/delete",
|
||||
"api-reference/endpoints/secret-syncs/github/sync-secrets",
|
||||
"api-reference/endpoints/secret-syncs/github/remove-secrets"
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -1,60 +0,0 @@
|
||||
---
|
||||
title: "Schema migration"
|
||||
description: "Learn how to run Postgres schema migrations."
|
||||
---
|
||||
|
||||
Running schema migrations is a requirement before deploying Infisical.
|
||||
Each time you decide to upgrade your version of Infisical, it's necessary to run schema migrations for that specific version.
|
||||
The guide below outlines a step-by-step guide to help you manually run schema migrations for Infisical.
|
||||
|
||||
### Prerequisites
|
||||
- Docker installed on your machine
|
||||
- An active PostgreSQL database
|
||||
- Postgres database connection string
|
||||
|
||||
<Steps>
|
||||
<Step title="Pull the Infisical Docker Image">
|
||||
First, ensure you have the correct version of the Infisical Docker image. You can pull it from Docker Hub using the following command:
|
||||
```bash
|
||||
docker pull infisical/infisical:<version>
|
||||
```
|
||||
Replace `<version>` with the specific version number you intend to deploy. View available versions [here](https://hub.docker.com/r/infisical/infisical/tags)
|
||||
</Step>
|
||||
|
||||
<Step title="Set Up the Environment Variable">
|
||||
The Docker image requires a `DB_CONNECTION_URI` environment variable. This connection string should point to your PostgreSQL database. The format generally looks like this: `postgresql://username:password@host:port/database`.
|
||||
</Step>
|
||||
|
||||
<Step title="Run the Migration ">
|
||||
To run the schema migration for the version of Infisical you want to deploy, use the following Docker command:
|
||||
|
||||
```bash
|
||||
docker run --env DB_CONNECTION_URI=<your_connection_string> infisical/infisical:<version> npm run migration:latest
|
||||
```
|
||||
Replace `<your_connection_string>` with your actual PostgreSQL connection string, and `<version>` with the desired version number.
|
||||
</Step>
|
||||
|
||||
<Step title="Verify the Migration">
|
||||
After running the migration, it's good practice to check if the migration was successful. You can do this by checking the logs or accessing your database to ensure the schema has been updated accordingly.
|
||||
</Step>
|
||||
<Step title="Rollback If Needed">
|
||||
If you need to rollback a migration by one step, use the following command:
|
||||
|
||||
```bash
|
||||
docker run --env DB_CONNECTION_URI=<your_connection_string> infisical/infisical:<version> npm run migration:rollback
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Repeat for Each Version">
|
||||
It's important to run schema migrations for each version of the Infisical you deploy. For instance, if you're updating from `infisical/infisical:1` to `infisical/infisical:2`, ensure you run the schema migrations for `infisical/infisical:2` before deploying it.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Tip>
|
||||
In a production setting, we recommend a more structured approach to deploying migrations prior to upgrading Infisical. This can be accomplished via CI automation.
|
||||
</Tip>
|
||||
|
||||
### Additional discussion
|
||||
- Always back up your database before running migrations, especially in a production environment.
|
||||
- Test the migration process in a staging environment before applying it to production.
|
||||
- Keep track of the versions and their corresponding migrations to avoid any inconsistencies.
|
@ -157,23 +157,6 @@ The [Docker stack file](https://github.com/Infisical/infisical/tree/main/docker-
|
||||
3lznscvk7k5t infisical_spolo2 replicated 1/1 ghcr.io/zalando/spilo-16:3.2-p2
|
||||
v04ml7rz2j5q infisical_spolo3 replicated 1/1 ghcr.io/zalando/spilo-16:3.2-p2
|
||||
```
|
||||
|
||||
<Note>
|
||||
You'll notice that service `infisical_infisical` will not be in running state.
|
||||
This is expected as the database does not yet have the desired schemas.
|
||||
Once the database schema migrations have been successfully applied, this issue should be resolved.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Run schema migrations">
|
||||
Run the schema migration to initialize the database. Follow the [guide here](/self-hosting/configuration/schema-migrations) to learn how.
|
||||
|
||||
To run the migrations, you'll need to connect to the Postgres instance deployed on your Docker swarm. The default Postgres user credentials are defined in the Docker swarm: username: `postgres`, password: `postgres` and database: `postgres`.
|
||||
We recommend you change these credentials when deploying to production and creating a separate DB for Infisical.
|
||||
|
||||
<Info>
|
||||
After running the schema migrations, be sure to update the `.env` file to have the correct `DB_CONNECTION_URI`.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="View service status">
|
||||
|
@ -78,18 +78,6 @@ description: "Learn how to use Helm chart to install Infisical on your Kubernete
|
||||
</Tabs>
|
||||
</Step>
|
||||
|
||||
<Step title="Database schema migration ">
|
||||
Infisical relies on a relational database, which means that database schemas need to be migrated before the instance can become operational.
|
||||
|
||||
To automate this process, the chart includes a option named `infisical.autoDatabaseSchemaMigration`.
|
||||
When this option is enabled, a deployment/upgrade will only occur _after_ a successful schema migration.
|
||||
|
||||
<Info>
|
||||
If you are using in-cluster Postgres, you may notice the migration job failing initially.
|
||||
This is expected as it is waiting for the database to be in ready state.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Routing traffic to Infisical">
|
||||
By default, this chart uses Nginx as its Ingress controller to direct traffic to Infisical services.
|
||||
|
||||
|
@ -22,11 +22,6 @@ The following guide provides a detailed step-by-step walkthrough on how you can
|
||||
|
||||
Remember to replace `<version>` with the docker image tag of your choice.
|
||||
</Step>
|
||||
<Step title="Run Postgres schema migration ">
|
||||
Before you can start the instance of Infisical, you need to run the database schema migrations.
|
||||
Follow the step by [step guide here](/self-hosting/configuration/schema-migrations) on running schema migrations for Infisical.
|
||||
|
||||
</Step>
|
||||
<Step title="Start Infisical">
|
||||
For a minimal installation of Infisical, you must configure `ENCRYPTION_KEY`, `AUTH_SECRET`, `DB_CONNECTION_URI`, `SITE_URL`, and `REDIS_URL`. [View all available configurations](/self-hosting/configuration/envars).
|
||||
|
||||
|
57
docs/self-hosting/guides/upgrading-infisical.mdx
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
|
||||
title: "Upgrade Your Infisical Instance"
|
||||
description: "How to upgrade Infisical self-hosted instance"
|
||||
|
||||
---
|
||||
|
||||
Keeping your Infisical instance up to date is key to making sure you receive the latest performance improvements, security patches, and feature updates.
|
||||
We release updates approximately once a week, which may include new features, bug fixes, performance enhancements, and critical security patches.
|
||||
|
||||
Since secrets management is a critical component of your infrastructure, we aim to avoid disruptive changes that will impact fetching secrets in downstream clients.
|
||||
If a release requires specific attention, a note will be attached to the corresponding [release](https://github.com/Infisical/infisical/releases) version.
|
||||
|
||||
During an upgrade, two key components are updated:
|
||||
|
||||
- **Infisical Application:** The core application code is updated.
|
||||
- **PostgreSQL Database Schema:** Schema migrations run automatically to ensure your database remains in sync with the updated application.
|
||||
|
||||
> **Before You Upgrade:**
|
||||
> **Always back up your database.** While our automated migration system is robust, having a backup ensures you can recover quickly in the event of an issue.
|
||||
|
||||
## Automated Schema Migrations
|
||||
|
||||
In previous versions (prior to `v0.111.0-postgres`), schema migrations had to be executed manually before starting the application.
|
||||
Now, migrations run automatically during boot-up. This improvement streamlines the upgrade process, reduces manual steps, and minimizes the risk of inconsistencies between your database schema and application code.
|
||||
|
||||
### Benefits of Automated Migrations
|
||||
|
||||
- **Seamless Integration:**
|
||||
Migrations are now part of the boot-up process, removing the need for manual intervention.
|
||||
|
||||
- **Synchronous Upgrades:**
|
||||
In multi-instance deployments, one instance acquires a lock and performs the migration while the others wait. This ensures that if a migration fails, the rollout is halted to prevent inconsistencies.
|
||||
|
||||
- **Reduced Room for Error:**
|
||||
Automatic migrations help ensure that your database schema always remains in sync with your application code.
|
||||
|
||||
## Upgrade Steps
|
||||
|
||||
1. **Back Up Your Data:**
|
||||
- Ensure you have a complete backup of your Postgres database.
|
||||
- Verify that your backup is current and accessible.
|
||||
|
||||
2. **Select the Upgrade Version:**
|
||||
- Visit the [Infisical releases page](https://github.com/Infisical/infisical/releases) for a list of available versions.
|
||||
- Look for releases with the prefix `infisical/` as there are other releases that are not related to the Infisical instance.
|
||||
|
||||
3. **Start the Upgrade Process:**
|
||||
- Launch the new version of Infisical. During startup, the application will automatically compare the current database schema with the updated schema in the code.
|
||||
- If any differences are detected, Infisical will apply the necessary migrations automatically.
|
||||
|
||||
4. **Multi-Instance Coordination:**
|
||||
- In environments with multiple instances, one instance will acquire a lock and perform the migration while the other instances wait.
|
||||
- Once the migration is complete, all instances will operate with the updated schema.
|
||||
|
||||
5. **Verify the Upgrade:**
|
||||
- Review the logs for any migration errors or warnings.
|
@ -48,9 +48,3 @@ This ensures that if there is a failure in one availability zone, the working re
|
||||
<Accordion title="Can Infisical run in an air-gapped environment without any internet access?" defaultOpen >
|
||||
Yes, Infisical can function in an air-gapped environment. To do so, update your ECS task to use the publicly available AWS Elastic Container Registry (ECR) image instead of the default Docker Hub image. Additionally, it's necessary to configure VPC endpoints, which allows your system to access AWS ECR via a private network route instead of the internet, ensuring all connectivity remains within the secure, private network.
|
||||
</Accordion>
|
||||
<Accordion title="Since RDS is in a private subnet, how do run the Postgres schema migrations?">
|
||||
Since the Amazon RDS instance is housed within a private network to enhance security, it is not directly accessible from the internet. This means that in order to run the required [Postgres schema migrations](/self-hosting/configuration/schema-migrations), you need to connect to this instance of RDS. There are many approaches you can take:
|
||||
- To automate schema migrations, you may setup CI/CD pipeline with access to the same RDS network to run the schema migrations before making deployment to ECS. This ensures that if migrations fail, your Infisical instances continues to run.
|
||||
- If you would like to run the migrations manually, consider using AWS Systems Manager Session Manager to access the RDS within the VPC on your local machine.
|
||||
- If your organization already has mechanisms in place for secure access to the VPC, such as VPNs or Direct Connect, these can also be utilized for performing database migrations manually.
|
||||
</Accordion>
|
||||
|
@ -222,10 +222,10 @@
|
||||
"org-members-description": "Manage members of your organization. These users could afterwards be formed into projects.",
|
||||
"search-members": "Search members...",
|
||||
"add-dialog": {
|
||||
"add-member-to-project": "Add a member to your project",
|
||||
"add-member-to-project": "Add users to your project",
|
||||
"already-all-invited": "All the users in your organization are already invited.",
|
||||
"add-user-org-first": "Add more users to the organization first.",
|
||||
"user-will-email": "The user will receive an email with the instructions.",
|
||||
"user-will-email": "Users will receive an email with instructions to gain access.",
|
||||
"looking-add": "<0>If you are looking to add users to your org,</0><1>click here</1>",
|
||||
"add-user-to-org": "Add Users to Organization"
|
||||
}
|
||||
|
1
frontend/public/lotties/settings-cog.json
Normal file
@ -57,6 +57,10 @@ export const ProjectPermissionCan: FunctionComponent<Props<ProjectPermissionSet>
|
||||
const finalChild =
|
||||
typeof children === "function" ? children(isAllowed, ability as any) : children;
|
||||
|
||||
if (!isAllowed && renderGuardBanner) {
|
||||
return <ProjectPermissionGuardBanner />;
|
||||
}
|
||||
|
||||
if (!isAllowed && passThrough) {
|
||||
return <Tooltip content={label}>{finalChild}</Tooltip>;
|
||||
}
|
||||
@ -65,10 +69,6 @@ export const ProjectPermissionCan: FunctionComponent<Props<ProjectPermissionSet>
|
||||
return <Tooltip content={allowedLabel}>{finalChild}</Tooltip>;
|
||||
}
|
||||
|
||||
if (!isAllowed && renderGuardBanner) {
|
||||
return <ProjectPermissionGuardBanner />;
|
||||
}
|
||||
|
||||
if (!isAllowed) return null;
|
||||
|
||||
return finalChild;
|
||||
|